Expand description
Pratt expression-operator plugin for the tabnas parser engine, layered on the jsonic relaxed-JSON base grammar.
The algorithm is Pratt parsing, and draws heavily from the explanation
written by Aleksey Kladov at
https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html.
See prattify for the core.
Expressions parse into LISP-style S-expressions: an array whose first
element describes the operator and whose remaining elements are the
operand terms, so 1+2*3 becomes ["+", 1, ["*", 2, 3]] once the
operator descriptions are reduced to their source text. A caller
supplied evaluator can reduce the tree to a value during the parse.
TypeScript is canonical: ts/src/expr.ts defines the behaviour, the
option names, the defaults and the order of alternates. The shared
fixtures in test/spec/*.tsv are the parity contract across
TypeScript, Go and Rust.
fn main() -> Result<(), Box<dyn std::error::Error>> {
let value = tabnas_expr::parse("a:1+2*3")?;
assert_eq!(tabnas_expr::simplify(&value).to_string(),
r#"{"a":["+",1,["*",2,3]]}"#);
Ok(())
}§Untrusted input
A parsed expression is data, never instructions. This plugin reads expression text of unknown provenance and returns S-expressions for a caller supplied evaluator to reduce, and that hand-off is where hostile input could become execution. Treat every operand and operator as untrusted text, and never let a document select which evaluator or operator implementation runs.
Structs§
- Eval
Site - The site an evaluator is called from: the rule the reduction belongs to
and the live parse context, and neither when
evaluationis called on a finished tree. - Expr
Error - The error a failed parse produces, re-exported so callers need not depend on the engine crate directly.
- Expr
Options - Options for the plugin.
- Op
- The full operator description carried by a parsed expression and handed to an evaluator.
- OpDef
- One entry of the
opoption map: the definition of an operator. - OpRef
- One occurrence of an operator: the shared description and the token
that introduced it. The canonical
makeOpbuilds the same pair by copying the description and attaching the token. - Preval
Def - Paren preval configuration: whether a value may precede the opening
paren (
foo(1),a[1]), whether one is required, and which values are allowed to.
Constants§
- MAX_
SAFE_ INTEGER - JavaScript’s
Number.MAX_SAFE_INTEGER, the binding power an operator with no declaredrighttakes: it binds tightest on the right. - MIN_
SAFE_ INTEGER - JavaScript’s
Number.MIN_SAFE_INTEGER, the binding power an operator with no declaredlefttakes: it binds loosest on the left. - NODE_
LIMIT - The most nodes one expression may hold.
- PLUGIN_
NAME - The name this plugin registers under.
- RULE_
LIMIT - The deepest rule stack a parse may reach.
- VERSION
- VERSION is this crate’s version. It MUST equal
ts/package.json“version” and theversionfield inrs/Cargo.toml: the release orchestrator rewrites all of them, andtests/version_test.rsfails the build if they drift. MirrorsVERSIONints/src/expr.tsandconst VERSIONingo/expr.go.
Functions§
- apply
- Install the expression grammar on a parser through
Tabnas::use_plugin, so that a derived instance rebuilds it. - defaults
- The default operator table, as the plugin declares it to the engine.
- evaluation
- Reduce an expression tree with an evaluator, innermost first.
- expr
- Install the expression grammar on a parser that already carries the
jsonic base rules (
val,map,list,pair,elem). - is_op
- Whether a value is an expression node, the port of the canonical
isOp. - make
- Build a parser with the jsonic base grammar and the default operator table.
- make_
with - Build a parser with the jsonic base grammar and the given options.
- op_of
- The operator heading an expression node.
- opify
- Normalize an operator built by hand, filling the derived term count
when it is unset, as the parser’s own operator builder does. The Rust
spelling of the canonical
testing.opify. - parse
- Parse one document with the default operator table.
- parse_
scope - Parse one document and hand the unrealized value to a closure, with the expression arena held open for as long as it runs.
- parse_
simplified - Parse one document and reduce it to the S-expression form the shared fixtures compare.
- parse_
with - Parse one document with a parser this crate configured, and realize the expression nodes in the result.
- plugin
- The plugin, for
Tabnas::use_plugin, carrying the default operator table as its declared defaults. - plugin_
with - The plugin with typed options, which is how an evaluator and any
operator data that no
Valuecan carry reach the grammar. - prattify
- Embed a new operator into an expression tree, in place, according to operator precedence.
- realize
- Replace every expression handle in a parsed value with the plain array the canonical port returns: the operator description followed by the operand terms.
- simplify
- Reduce a parsed value to the S-expression form the shared fixtures compare: every operator description becomes its source text, and a term that was never filled is dropped.
- terms_
of - The operand terms of an expression node.
Type Aliases§
- Evaluate
- Resolve the value of an operation. The Rust spelling of the canonical
Evaluatecallback: it takes the operator and its already reduced terms, and returns the value the expression stands for.