A complete JSON parser for MoonBit with full spec compliance, error handling, and pretty-printing support
///|
test "basic parsing example" {
// Parse a JSON string
let json_str = "{\"name\": \"Alice\", \"age\": 30, \"active\": true}"
let result = try! @tonyfettes/json_parser.parse(json_str)
match result {
Ok(Object(obj)) => {
inspect(obj.get("name"), content="Some(String(\"Alice\"))")
inspect(obj.get("age"), content="Some(Number(30))")
inspect(obj.get("active"), content="Some(Bool(true))")
}
Err(err) => println("Parse error: \{err}")
_ => ()
}
}///|
test "construct and access JSON" {
// Create a JSON object manually
let person : Map[String, @tonyfettes/json_parser.JsonValue] = {
"name": String("Bob"),
"age": Number(25),
"hobbies": Array([String("reading"), String("coding")]),
}
let json = @tonyfettes/json_parser.JsonValue::Object(person)
// Convert to string and parse it back to verify
let json_string = json.to_json_string(0)
let reparsed = try! @tonyfettes/json_parser.parse(json_string)
match reparsed {
Ok(Object(obj)) => {
inspect(obj.get("name"), content="Some(String(\"Bob\"))")
inspect(obj.get("age"), content="Some(Number(25))")
}
_ => ()
}
}///|
test "parse JSON arrays" {
let json_str = "[1, 2, 3, 4, 5]"
let result = try! @tonyfettes/json_parser.parse(json_str)
match result {
Ok(Array(arr)) => {
inspect(arr.length(), content="5")
inspect(arr[0], content="Number(1)")
inspect(arr[4], content="Number(5)")
}
_ => fail("Expected array")
}
}///|
test "parse nested JSON" {
let json_str =
#|{
#| "users": [
#| {"id": 1, "name": "Alice"},
#| {"id": 2, "name": "Bob"}
#| ],
#| "total": 2
#|}
let result = try! @tonyfettes/json_parser.parse(json_str)
match result {
Ok(Object(root)) => {
// Access nested values
match root.get("users") {
Some(Array(users)) => {
inspect(users.length(), content="2")
// Access first user
match users[0] {
Object(user) =>
inspect(user.get("name"), content="Some(String(\"Alice\"))")
_ => ()
}
}
_ => ()
}
inspect(root.get("total"), content="Some(Number(2))")
}
_ => fail("Expected object")
}
}///|
test "handle parse errors" {
// Invalid JSON - unexpected character
let invalid1 = try! @tonyfettes/json_parser.parse("@invalid")
match invalid1 {
Err(UnexpectedChar(_pos, _ch)) => ()
_ => fail("Expected UnexpectedChar error")
}
// Unclosed string
let invalid2 = try! @tonyfettes/json_parser.parse("\"unclosed")
match invalid2 {
Err(UnexpectedEnd) => ()
_ => fail("Expected UnexpectedEnd error")
}
// Invalid number
let invalid3 = try! @tonyfettes/json_parser.parse("123.456.789")
match invalid3 {
Err(_) => () // Any error is acceptable
Ok(_) => fail("Should have failed to parse")
}
}///|
test "parse strings with escapes" {
let json_with_escapes = "\"Line 1\\nLine 2\\tTabbed\\u0041BC\""
let result = try! @tonyfettes/json_parser.parse(json_with_escapes)
match result {
Ok(String(s)) => {
// Contains newline, tab, and Unicode escape for 'A'
assert_true(s.contains("\n"))
assert_true(s.contains("\t"))
assert_true(s.contains("ABC"))
}
_ => fail("Expected string")
}
}///|
test "pretty print JSON" {
let data : Map[String, @tonyfettes/json_parser.JsonValue] = {
"name": String("Charlie"),
"scores": Array([Number(95), Number(87), Number(92)]),
}
let json = @tonyfettes/json_parser.JsonValue::Object(data)
let formatted = json.to_json_string(0)
// The output will be nicely formatted with indentation
// Verify by parsing it back
let reparsed = try! @tonyfettes/json_parser.parse(formatted)
match reparsed {
Ok(Object(obj)) =>
inspect(obj.get("name"), content="Some(String(\"Charlie\"))")
_ => ()
}
}///|
enum JsonValue {
Null
Bool(Bool)
Number(Double)
String(String)
Array(Array[JsonValue])
Object(Map[String, JsonValue])
}///|
suberror ParseError {
UnexpectedChar(Position, Char) // Unexpected character at position
UnexpectedEnd // Unexpected end of input
InvalidNumber(Position, String) // Invalid number format
InvalidEscape(Position, Char) // Invalid escape sequence
InvalidUnicode(Position, String) // Invalid Unicode escape
Expected(Position, String) // Expected a specific token
}moon test -p tonyfettes/json_parsermoon test -p tonyfettes/json_parser --updateimpl Eq for ParseErrorimpl Show for ParseErrortype PositionA complete JSON parser for MoonBit with full spec compliance, error handling, and pretty-printing support