// examples/parser_playground — runnable demonstration of the
// `src/parser_combinator` direction as a language-tooling workflow: parse
// and evaluate arithmetic with correct precedence/associativity, parse
// JSON with precise error positions, recover from malformed JSON while
// collecting diagnostics, and feed a grammar incrementally chunk by chunk.
//
// Run with:
// moon run examples/parser_playground
///|
fn main {
println("=== 1. arithmetic: parse + eval with precedence ===")
for src in ["1 + 2 * 3", "(1 + 2) * 3", "2 ^ 3 ^ 2", "10 - 4 - 3"] {
match @pc.parse_and_eval(src) {
Ok(v) => println("\{src} = \{v}")
Err(_) => println("\{src} -> parse error")
}
}
println("")
println("=== 2. expression AST round-trip ===")
let parser = @pc.expr_parser()
match parser.parse_string("1 + 2 * 3") {
Ok(ast, _) =>
println("printed AST: \{@pc.print_expr(ast)} -> \{@pc.eval_expr(ast)}")
Fail(_, ..) => println("parse failed")
}
println("")
println("=== 3. JSON parsing with precise errors ===")
match
@pc.parse_json(
"{\"name\": \"router\", \"port\": 8080, \"tags\": [\"a\", \"b\"]}",
) {
Ok(json) => println("parsed json : \{@pc.print_json(json)}")
Err(_) => println("unexpected parse failure")
}
let bad = "{\"name\": \"router\", \"port\": }"
let jp = @pc.json_parser()
match jp.explain_failure(bad) {
Some(msg) => println("bad json : \{msg}")
None => println("bad json unexpectedly parsed")
}
println("")
println("=== 4. error recovery collects diagnostics ===")
let (recovered, errors) = @pc.parse_json_recover("[1, oops, 3]")
match recovered {
Some(json) => println("recovered : \{@pc.print_json(json)}")
None => println("no recovery possible")
}
println("diagnostics : \{errors.length()} error(s) collected")
println("")
println("=== 5. incremental parsing, chunk by chunk ===")
// framed-number protocol: '<' digits '>' — a prefix that fails at the
// data boundary yields NeedMore instead of a false parse failure.
let digits = @pc.many1(
@pc.satisfy(fn(c) { c >= '0' && c <= '9' }, label="digit"),
)
let framed = @pc.map(@pc.between(@pc.pchar('<'), digits, @pc.pchar('>')), fn(
ds,
) {
let mut n = 0
for c in ds {
n = n * 10 + (c.to_int() - '0'.to_int())
}
n
})
let grammar = @pc.lift(framed)
let first = grammar.run_incremental("<12")
match first {
NeedMore(_) =>
println("after chunk '<12': parser needs more input (no false failure)")
Done(_) => println("after chunk '<12': unexpectedly done")
}
let result = @pc.drive(first, ["34", "5>"], closed=true)
match result.value() {
Some(n) => println("streamed 3 chunks '<12'+'34'+'5>' -> \{n}")
None => println("stream parse failed")
}
}