JSON

Adapted from the fastparse JSON example.

using CombinedParsers


@with_names begin
    space    = Repeat( CharIn(" \r\n") )
    hexDigit = CharIn('0':'9','a':'f','A':'F')
    strChars = CharNotIn("\"\\")
    decimal = Repeat1( CharIn('0':'9') )
    exponent = Sequence(
	CharIn("eE"),
	map(v->parse(Int,v), !(('+'|'-'|missing) * decimal))
    )[2]
    fractional = map(
        v->parse(Float64,v), ## result_type inferred and
	Number,              ## defined explicitely as supertype
	!( "." * decimal ) )
    integral   =  "0" | CharIn('1':'9') * Optional(decimal)

    number = Sequence(map(v->parse(Int,v), !(('+'|'-'|missing) * integral)),
	              (fractional | 0),
		      ( exponent | 0 )) do v
                          (i,f,e) = v
                          ((i+f)*10^e)::Union{Float64,Int64}
                      end


    unicodeEscape = "u" * hexDigit * hexDigit * hexDigit * hexDigit
    escape        = "\\" * ( CharIn("\"/\\\\bfnrt") | unicodeEscape )

    lstring = ( space * "\"" * !Repeat(strChars | escape) * "\"" )[3]

    data = Either{Any}(Any[
	lstring,
	parser("true"=>true),
	parser("false"=>false),
	parser("null"=>nothing),
	number])
    ## data = (lstring | parser("true"=>true) | ("false"=>false) | ("null"=>nothing) | number) | NamedTuple | Vector
    @syntax jsonExpr = ( space * data * space )[2]

    array = ( "[" * join(Repeat(jsonExpr),",") * "]" )[2]
    push!(data, array)

    pair = map(lstring * space * ":" * jsonExpr ) do (k,s,d,v)
        Pair{Symbol,result_type(jsonExpr)}(Symbol(k),v)
    end;

    obj = ( "{" * map(v->(;v...),join(Repeat(pair),",")) * space * "}")[2]
    push!(data, obj)
end;
julia> parse(pair, "\"a\" : 1")Pair{Symbol, Any}(:a, 1)
julia> jsonExpr""" {"a_number" : 5.0, "an_array" : ["string", 9]} """(a_number = 5.0, an_array = Any["string", 9])
jsonExpr"""
{
    "firstName": "John",
    "lastName": "Smith",
    "age": 25,
    "address": {
        "streetAddress": "21 2nd Street",
        "city": "New York",
        "state": "NY",
        "postalCode": 10021
    },
    "phoneNumbers":
    [
        {
            "type": "home",
            "number": "212 555-1234"
        },
        {
            "type": "fax",
            "number": "646 555-4567"
        }
    ]
}
"""
(firstName = "John", lastName = "Smith", age = 25, address = (streetAddress = "21 2nd Street", city = "New York", state = "NY", postalCode = 10021), phoneNumbers = Any[(type = "home", number = "212 555-1234"), (type = "fax", number = "646 555-4567")])