Parser¶
Audience: Developers integrating ExpresZo into their .NET projects.
The Parser class is the main entry point. Construct one, reuse it across many calls - Parser and the Expressions it produces are immutable after construction and safe for concurrent use.
Quick Start¶
using System.Text.Json;
using Expreszo;
// Create a parser instance
var parser = new Parser();
// Parse once, evaluate many times
var expr = parser.Parse("2 * x + 1");
using var a = JsonDocument.Parse("""{"x":3}""");
using var b = JsonDocument.Parse("""{"x":5}""");
expr.Evaluate(a); // Value.Number(7)
expr.Evaluate(b); // Value.Number(11)
// Or the one-shot overload
parser.Evaluate("2 * x + 1", a); // Value.Number(7)
Constructor¶
ParserOptions¶
| Property | Type | Default | Description |
|---|---|---|---|
AllowMemberAccess |
bool |
true |
Whether dot access (obj.prop) is permitted. When false, the parser rejects any . access with an AccessException. |
Example:
var strict = new Parser(new ParserOptions { AllowMemberAccess = false });
strict.Evaluate("obj.prop"); // throws AccessException
Operator toggling (
in/as/=individually) is not supported in this port - the built-in operator set is fixed. If you need to restrict the language surface, validate the AST after parsing usingExpression.Accept<T>(INodeVisitor<T>).
Methods¶
Parse(string expression)¶
Converts an expression string into an Expression object that can be evaluated many times.
Throws ParseException for syntax errors.
Evaluate(string expression, JsonDocument? values = null, VariableResolver? resolver = null)¶
Synchronous shorthand for Parse(expression).Evaluate(values, resolver).
using var values = JsonDocument.Parse("""{"x":2,"y":3}""");
parser.Evaluate("x + y", values); // Value.Number(5)
The optional resolver is a per-call variable resolver. It runs before the parser-level resolver when a name isn't found in values.
VariableResolver lookup = name =>
name.StartsWith("$") ? new VariableResolveResult.Bound(LookupValue(name[1..])) : VariableResolveResult.NotResolved;
parser.Evaluate("$a + $b", values: null, resolver: lookup);
Throws AsyncRequiredException if any function in the expression requires asynchronous evaluation. Use EvaluateAsync in that case.
EvaluateAsync(string expression, JsonDocument? values = null, VariableResolver? resolver = null, CancellationToken ct = default)¶
Asynchronous version. Returns ValueTask<Value> - which completes synchronously for expressions that only use sync functions, so there's no overhead when async isn't needed.
Honours the cancellation token at every function-call boundary.
Value Types¶
All evaluation results are Expreszo.Value, a sealed discriminated union:
| Variant | Description |
|---|---|
Value.Number(double V) |
IEEE 754 double. Small non-negative integers (0–255) are interned. |
Value.String(string V) |
Immutable string. |
Value.Boolean(bool V) |
Singleton True / False via Value.Boolean.Of(...). |
Value.Null |
Singleton Value.Null.Instance. |
Value.Undefined |
Singleton Value.Undefined.Instance. Distinct from Null. |
Value.Array(ImmutableArray<Value> Items) |
Structural equality. |
Value.Object(FrozenDictionary<string, Value> Props) |
Structural equality. |
Value.Function(ExprFunc Invoke, string? Name) |
Callable. Lambdas captured from expressions show up as Value.Function too. |
Pattern-match to branch:
var result = parser.Evaluate("...", doc);
var display = result switch
{
Value.Number n => $"num {n.V}",
Value.String s => $"str {s.V}",
Value.Boolean b => $"bool {b.V}",
Value.Array a => $"array[{a.Items.Length}]",
Value.Object o => $"object({o.Props.Count})",
Value.Null => "null",
Value.Undefined => "undefined",
Value.Function => "[function]",
_ => "?",
};
See Values & JsonDocument for details on the boundary between Value and System.Text.Json.
Thread Safety¶
Parseris immutable after construction; share instances freely.Expressionis immutable and cachesToString()/ symbol lists lazily with thread-safe initialisation; share instances freely.- Evaluations are per-call: each
Evaluate/EvaluateAsyncbuilds a freshScopefrom the suppliedJsonDocument, so a singleExpressioncan be evaluated concurrently as long as each call passes its ownJsonDocument.
Variable Resolution Order¶
When an expression references a name, ExpresZo looks it up in this order:
- Built-in function - e.g.
max,sum,map. - Unary operator function - e.g.
sin,cos,sqrt,length. Lets expressions pass baresinas a function value. - Local and parent scopes - variables assigned with
=, lambda parameters. - Values from the input
JsonDocument- top-level properties become root-scope bindings. - Per-call
VariableResolver- passed toEvaluate/EvaluateAsync. - Numeric constants -
PI,E,Infinity,NaN.
If none of these resolve the name, a VariableException is thrown.
See Advanced Features - Custom Variable Resolution for resolver patterns.
Built-in Constants¶
These names are always resolvable:
| Name | Value |
|---|---|
PI |
Math.PI |
E |
Math.E |
Infinity |
double.PositiveInfinity |
NaN |
double.NaN |
true / false |
boolean literals |
null |
Value.Null.Instance |
undefined |
Value.Undefined.Instance (keyword, not constant) |
See Also¶
- Expression -
Evaluate,Simplify,Substitute,Variables,Symbols,ToString,Accept. - Expression Syntax - language reference.
- Advanced Features - async, resolvers, coalesce, CASE, object literals.
- Values & JsonDocument - I/O boundary details.