#* JSON Parsing and Encoding
# Example:
include :json
#Parsing
some_json = ' { "animals" : [ "dog, "cat", "turtle"] } '
json.parse some_json #Result: [animals: [ "dog", "cat", "turtle"] ]
#Encoding
json.encode [hi: [1, 2, 3]] #Result: '{"hi" : [1,2,3]}'
*#
include :scanner
include :assert, :assertions
include :json_helpers
json = object.new
unmatched = object.new
matched? = { val | val != unmatched }
parser = object.new
parser.init = { input |
my.input = input
s = scanner.new(input)
my.set_pos = { pos |
s.pos = pos
}
my.current_pos = { s.pos }
my.scan = { matcher |
s.scan matcher
}
}
parser.parse = {
trim_space
result = parse_object
true? matched?(result)
{ result }
{ result = parse_array
true? matched?(result)
{ result }
{ null }
}
}
parser.parse_number = {
result = scan(/-?(0|[1-9]\d*)(\.\d+)?((e|E)(\+|-)?\d+)?\b/)
true? result
{ result.full_match.to_f }
{ unmatched }
}
parser.parse_value = {
result = null
trim_space
when { scan "true" } { true }
{ scan "false" } { false }
{ scan "null" } { null }
{ matched? result = parse_number } { result }
{ matched? result = parse_string } { result }
{ matched? result = parse_array } { result }
{ matched? result = parse_object } { result }
{ true } { unmatched }
}
parser.parse_string = {
true? scan('"') {
result = json_decode(my.input, my.current_pos)
true? result
{ set_pos result[1]
result[0] }
{ unmatched }
}
{ unmatched }
}
parser.parse_array = {
true? scan("[")
{
result = null
continue = true
a = []
while { continue && matched?(result = parse_value) } {
a << result
continue = scan ","
}
true? scan("]")
{ a }
{ unmatched }
}
{ unmatched }
}
parser.parse_object = {
true? scan(/{\s*/)
{
result = null
key = null
continue = true
o = [:]
while { continue && matched?(key = parse_string) } {
true? scan(/\s*:\s*/) {
o[key] = parse_value
continue = scan(/\s*,\s*/)
}
{
continue = false
}
}
true? scan(/\s*}/)
{ o }
{ unmatched }
}
{ unmatched }
}
parser.trim_space = { scan /\s+/ }
json.parse = { input |
parser.new(input).parse
}
#*
include :assert :assertions
squish assertions
test = { input, method, value = unmatched |
result = parser.new(input).call_method("parse_#{method}")
assert matched?(result), "Failed to parse #{input} with #{method}"
true? matched?(value)
{ assert_equal value, result }
}
test "0" :number 0
test "1" :number 1
test "1.1" :number 1.1
test "-0.5123" :number -0.5123
test "1e+10" :number
test "true" :value true
test "false" :value false
test "null" :value null
test "1" :value 1
test '"\""' :value "\""
test '"hello"' :string
test '"\u20AC"' :string
test '"\t"' :string
*#
json.encode = { obj |
when { obj.array? } { json.encode_array obj }
{ obj.hash? } { json.encode_hash obj }
{ obj.number? } { obj.to_s }
{ obj.string? } { json.encode_string obj }
{ true } { p "hi"; throw "Cannot convert #{obj} to json" }
}
json.encode_array = { a |
"[" << a.map(my->encode).join(",") << "]"
}
json.encode_hash = { h |
"{" << h.map({ k, v | "#{json.encode k.to_s} : #{json.encode v}"}).join(",") << "}"
}
json.encode_string = { s |
json_encode s
}
export json, "json"