ns jsonexample fn main() let o: obj = match parseJsonObj('{ "a": { "b": { "c": [42] }} }') Success(o) => o, Failure(err) => panic!("There was an error during Json parsing: ${err}") end assertEq!(o.a.b.c[0], 42) end fn parseJsonObj(s: String) -> Result let lexer = JsonLexer(s) let parser = JsonParser(lexer) try parser.object() catch (parseError: JsonParseError) Failure(parseError) end end enum JsonToken LBrace, RBrace, LSquare, RSquare, Comma, String(String), Number(Number), Boolean(Boolean) end platform class String pub fn matchAt(startIndex: Int, matchers: Map R), opts: obj + { ignoreWhitespace: Boolean }) -> Option let slice: String = if opts.ignoreWhitespace then /[\t\n\r ]*(.*)/.match(self[startIndex..]).rest as String else self[startIndex..] end for [regex, result] in matchers do if regex.test(slice) is Some(match) then if result is Fn then Some(result(match)) else Some(result) end end end None end end platform class Regex int RegexMatchBase val match: String val groups: [String], val namedGroups: Map end pub int RegexMatch props!::() end platform fn doMatch(s) -> Option pub fn match(s: String) -> Option doMatch(s).map { base -> dyn obj { ...base, propertyMissing: { name: String -> base.namedGroups.get(name).unwrap() } } } end end class JsonLexer(val source: String) var index: Int fn matchCurrent() -> Option source.matchAt(index, [ /\{/ => JsonToken::LBrace, /}/ => JsonToken::RBrace, /\[/ => JsonToken::LSquare, /]/ => JsonToken::RSquare, /[0-9]+/ => { rawNumber: String => JsonToken::Number(number::parse(rawNumber))) } /true/ => JsonToken::Boolean(true), /false/ => JsonToken::False(false), // The following is not a good way for matching strings but whatever /"%w*"/ => { stringWithQuotes => JsonToken::String(stringWithQuotes[1..-1]) } ], obj { ignoreWhitespace: true }) end pub fn nextToken() -> Option matchCurrent().ifSome { token -> match token JsonToken::LBrace | JsonToken::RBrace | JsonToken::LSquare | JsonToken::RSquare | JsonToken::Comma => index++, JsonToken::String(s) => index += s.len() + 2, JsonToken::Number(n) => index += n.toString().len(), JsonToken::Boolean(b) => match b true => index += 4, false => index += 5, end end } end pub fn peek() -> Option alias matchCurrent end class JsonParser(val lexer: JsonLexer) macro parseError!($message) throw JsonParserError($message) end macro expect!($tokenType) match lexer.nextToken() Some(token) => match token JsonToken::$tokenType => do end, _ => parseError!("Expected ${$tokenType}") end, _ => parseError!("Expected ${$tokenType}") end end macro peek!($tokenType) match lexer.peek() Some(token) => match token JsonToken::$tokenType => true, _ => false end, None => false end end pub fn object() -> obj expect!(LBrace) let result = obj {} while keyValue() is Some((key, value)) do result[key] = value end expect!(RBrace) result end fn keyValue() -> Option<(String, Any)> let key = match lexer.peek() Some(token) => do match token JsonToken::String(s) => s, _ => parseError!("Expected String") end end, _ => do return None end end let value = value() Some((key, value)) end fn value() -> Any match lexer.peek() Some(token) => match token JsonToken::LBrace => object(), JsonToken::LSquare => array(), _ => do match lexer.nextToken() JsonToken::String(s) => s, JsonToken::Number(n) => n, JsonToken::Boolean(b) => b, _ => parseError!("Expected String, Number, or Boolean") end end end, None => parseError!("Expected LBrace, LSquare, String, Number, or Boolean") end end fn array() -> [Any] expect!(LSquare) let results: List = [] while true do match lexer.peek() Some(token) => match token JsonToken::LBrace => results << object(), JsonToken::LSquare => results << array(), _ => do match lexer.nextToken() JsonToken::String(s) => results << s, JsonToken::Number(n) => results << n, JsonToken::Boolean(b) => results << b, _ => parseError!("Expected String, Number, or Boolean") end end end, None => panic! "Expected LBrace, LSquare, String, Number, or Boolean" end match lexer.nextToken() Some(token) => match token JsonToken::Comma => continue, JsonToken::RSquare => break end _ => panic! "Expected Comma or RSquare" end end expect!(RSquare) end end