///|
pub using @runtime {
type Value,
type ActionFn,
type InitFn,
type Tracer,
type SyntaxError,
}
///|
/// A generated parser.
///
/// Usually built from a compiled `Program` by `Parser::from_program`, but
/// any parse function can be wrapped (e.g. by compiler plugins).
pub struct Parser[T] {
priv parse_impl : (String, Map[String, Value[T]], Tracer[T]?) -> Value[T] raise
/// The program this parser interprets, when it has one.
program : @bytecode.Program?
}
///|
/// Wraps a parse function. It receives the input, the parse options (which
/// include `startRule` / `filename` when given) and the tracer, if any.
pub fn[T] Parser::from_fn(
parse : (String, Map[String, Value[T]], Tracer[T]?) -> Value[T] raise,
) -> Parser[T] {
{ parse_impl: parse, program: None, }
}
///|
/// Parses `input`, returning the start rule's result.
///
/// `options` is exposed to actions as PEG.js' `options` object; `start_rule`
/// and `filename` are shorthands for its `startRule` and `filename` entries.
/// Raises `@runtime.SyntaxError` when the input does not match.
pub fn[T] Parser::parse(
self : Parser[T],
input : String,
start_rule? : String,
filename? : String,
tracer? : Tracer[T],
options? : Map[String, Value[T]],
) -> Value[T] raise {
let options = match options {
Some(o) => o
None => {}
}
if start_rule is Some(r) {
options["startRule"] = Str(r)
}
if filename is Some(f) {
options["filename"] = Str(f)
}
(self.parse_impl)(input, options, tracer)
}
///|
/// Raised when a code block of the grammar has no MoonBit implementation.
pub(all) suberror UnboundCode {
UnboundCode(kind~ : String, code~ : String)
} derive(Debug)
///|
pub impl Show for UnboundCode with fn output(self, logger) {
let UnboundCode(kind~, code~) = self
logger.write_string("No binding for \{kind} code block {\{code}}")
}
///|
/// Looks up the implementation of code block number `index` (its position
/// in `Program::functions`) with source text `body`. Keys are tried in order:
///
/// 1. `"#"` — exact, but specific to one compilation of the grammar;
/// 2. the exact body text;
/// 3. the body with surrounding whitespace trimmed (so a grammar can simply
/// say `{ makeNumber }`).
///
/// Identical bodies with different labels in scope share a text key, so
/// actions bound by text should read labels by name (`ActionContext::label`).
pub fn[V] lookup_binding(
bindings : Map[String, V],
index : Int,
body : String,
) -> V? {
match bindings.get("#\{index}") {
Some(f) => Some(f)
None =>
match bindings.get(body) {
Some(f) => Some(f)
None => bindings.get(body.trim().to_owned())
}
}
}