package scala.util.parsing.json
import scala.util.parsing.combinator._
import scala.util.parsing.combinator.syntactical._
import scala.util.parsing.combinator.lexical._
sealed abstract class JSONType {
def toString (formatter : JSONFormat.ValueFormatter) : String
override def toString = toString(JSONFormat.defaultFormatter)
}
object JSONFormat {
type ValueFormatter = Any => String
val defaultFormatter : ValueFormatter = (x : Any) => x match {
case s : String => "\"" + quoteString(s) + "\""
case jo : JSONObject => jo.toString(defaultFormatter)
case ja : JSONArray => ja.toString(defaultFormatter)
case other => other.toString
}
def quoteString (s : String) : String =
s.map {
case '"' => "\\\""
case '\\' => "\\\\"
case '/' => "\\/"
case '\b' => "\\b"
case '\f' => "\\f"
case '\n' => "\\n"
case '\r' => "\\r"
case '\t' => "\\t"
case c if ((c >= '\u0000' && c <= '\u001f') || (c >= '\u007f' && c <= '\u009f')) => "\\u%04x".format(c: Int)
case c => c
}.mkString
}
case class JSONObject (obj : Map[String,Any]) extends JSONType {
def toString (formatter : JSONFormat.ValueFormatter) =
"{" + obj.map({ case (k,v) => formatter(k.toString) + " : " + formatter(v) }).mkString(", ") + "}"
}
case class JSONArray (list : List[Any]) extends JSONType {
def toString (formatter : JSONFormat.ValueFormatter) =
"[" + list.map(formatter).mkString(", ") + "]"
}
class Parser extends StdTokenParsers with ImplicitConversions {
type Tokens = Lexer
val lexical = new Tokens
lexical.reserved ++= List("true", "false", "null")
lexical.delimiters ++= List("{", "}", "[", "]", ":", ",")
type NumericParser = String => Any
protected var defaultNumberParser : NumericParser = {_.toDouble}
protected val numberParser = new ThreadLocal[NumericParser]() {
override def initialValue() = defaultNumberParser
}
def root = jsonObj | jsonArray
def jsonObj = "{" ~> repsep(objEntry, ",") <~ "}" ^^ { case vals : List[_] => JSONObject(Map(vals : _*)) }
def jsonArray = "[" ~> repsep(value, ",") <~ "]" ^^ { case vals : List[_] => JSONArray(vals) }
def objEntry = stringVal ~ (":" ~> value) ^^ { case x ~ y => (x, y) }
def value: Parser[Any] = (jsonObj | jsonArray | number | "true" ^^^ true | "false" ^^^ false | "null" ^^^ null | stringVal)
def stringVal = accept("string", { case lexical.StringLit(n) => n} )
def number = accept("number", { case lexical.NumericLit(n) => numberParser.get.apply(n)} )
}