JSON parser implementation.
class json parser
import idealruntimepatternslist pattern
import idealmachinechannelsstring writer
auto constructor class parse result
character handler the character handler
var list[readonly data] tokens
var string or null error
json parser(character handler the character handler)
this • the character handler = the character handler
tokens = base list[readonly data] • new()
private void tokenize(string input)
tokensclear()
var nonnegative start : 0
while start < inputsize && error is null
start = scan(input, start)
list[readonly data] test tokenize(string input)
tokenize(input)
assert !has error()
return tokens
readonly value parse(string input)
tokenize(input)
assert !has error()
assert !has error()
return resultthe value
private nonnegative scan(string input, nonnegative start)
next : input[start]
var nonnegative index : start + 1
if the character handleris whitespace(next)
while index < inputsize && the character handleris whitespace(input[index])
index += 1
return index
if next == '"'
result : string writer • new()
while index < inputsize
next in input : input[index]
if next in input == '"'
tokensappend(resultelements())
return index + 1
else if next in input == '\\'
if index >= inputsize
report error("Escape at the end of input")
return index
index += 1
escaped character : input[index]
if escaped character == '"' || escaped character == '\\' || escaped character == '/'
resultwrite(escaped character)
else if escaped character == 'b'
resultwrite('\b')
else if escaped character == 'f'
resultwrite('\f')
else if escaped character == 'n'
resultwrite('\n')
else if escaped character == 'r'
resultwrite('\r')
else if escaped character == 't'
resultwrite('\t')
else if escaped character == 'u'
if index + 4 >= inputsize
report error("Unicode escape at the end of input")
return index
var code : hex digit(input[index + 1])
code = code * 16 + hex digit(input[index + 2])
code = code * 16 + hex digit(input[index + 3])
code = code * 16 + hex digit(input[index + 4])
resultwrite(the character handlerfrom code(code))
index += 4
else
report error("Unrecognized escape character: " ++ escaped character)
return index
else
resultwrite(next in input)
index += 1
report error("No closing quote in a string")
return index
if the character handleris digit(next)
return scan number(input, start, false)
if next == '-'
if index < inputsize
return scan number(input, index, true)
else
report error("Minus at the end of input")
return index
if next == json tokenOPEN BRACEthe character
return index
if next == json tokenCLOSE BRACEthe character
return index
if next == json tokenOPEN BRACKETthe character
return index
if next == json tokenCLOSE BRACKETthe character
return index
if next == json tokenCOMMAthe character
return index
if next == json tokenCOLONthe character
return index
if next == 't'
return scan symbol(input, start, "true", true)
if next == 'f'
return scan symbol(input, start, "false", false)
if next == 'n'
return scan symbol(input, start, "null", missinginstance)
report error("Unrecognized character in a string: " ++ next)
return index
private nonnegative hex digit(character the character)
result : the character handlerfrom digit(the character, 16)
if result is nonnegative
return result
else
report error("Unrecognized character in hex escape: " ++ the character)
return 0
private nonnegative scan number(string input, nonnegative start, boolean negate)
next : input[start]
if !the character handleris digit(next)
report error("Unrecognized digit: " ++ next)
return start
assert digit is nonnegative
var nonnegative result : digit
var index : start + 1
while index < inputsize && the character handleris digit(input[index])
assert next digit is nonnegative
result = result * radixDEFAULT RADIX + next digit
index += 1
tokensappend(negate ? -result : result)
return index
private nonnegative scan symbol(string input, nonnegative start, string symbol, deeply immutable data value)
prefix : list pattern[character] • new(symbol) • match prefix(inputskip(start))
if prefix is_not null
if prefix == symbolsize
tokensappend(value)
return start + prefix
report error("Can't scan symbol: " ++ symbol)
return start + 1
private parse result parse value(nonnegative start)
if start >= tokenssize
return parse error("End of tokens when parsing value")
next : tokens[start]
if next == json tokenOPEN BRACE
return parse object(start)
else if next == json tokenOPEN BRACKET
return parse array(start)
if next is json token
return parse error("Unexpected token: " ++ next)
return parse result • new(next, start + 1)
private parse result parse object(nonnegative start)
if tokens[start] != json tokenOPEN BRACE
return parse error("Open brace expected")
result : hash dictionary[string, readonly value] • new()
var index : start + 1
while index < tokenssize
next : tokens[index]
if next == json tokenCLOSE BRACE
return parse result • new(result, index + 1)
if next is_not string
return parse error("Expected string identifier in object")
index += 1
while index >= tokenssize || tokens[index] != json tokenCOLON
return parse error("Expected colon in object")
index += 1
element : parse value(index)
if has error()
return element
resultput(next, elementthe value)
index = elementend index
if index >= tokenssize
return parse error("No closing brace in object")
if tokens[index] == json tokenCLOSE BRACE
return parse result • new(result, index + 1)
if tokens[index] != json tokenCOMMA
return parse error("Expected comma in object")
index += 1
return parse error("No closing brace in object")
private parse result parse array(nonnegative start)
if tokens[start] != json tokenOPEN BRACKET
return parse error("Open bracket expected")
result : base list[readonly value] • new()
var index : start + 1
while index < tokenssize
if tokens[index] == json tokenCLOSE BRACKET
return parse result • new(result, index + 1)
element : parse value(index)
if has error()
return element
resultappend(elementthe value)
index = elementend index
if index >= tokenssize
return parse error("No closing bracket in array")
if tokens[index] == json tokenCLOSE BRACKET
return parse result • new(result, index + 1)
if tokens[index] != json tokenCOMMA
return parse error("Expected comma in array")
index += 1
return parse error("No closing bracket in array")
private void report error(string message)
error = message
private parse result parse error(string message)
report error(message)
return parse result • new(message, 0)