For most use cases evaluate is the right choice. Use parse + execute
separately when you need to:
import { parse, parseSafe } from "@imogenz/escalc";
// Throws ParserError on syntax errors
const ast = parse("[price] * (1 + [tax])");
// Returns a discriminated union instead of throwing
const result = parseSafe("[price] * (1 + [tax])");
if (result.type === "success") {
const ast = result.expression;
}
| Option | Type | Default | Description |
|---|---|---|---|
stopOnFirstError |
boolean |
true |
When false, the parser attempts to recover and continue past errors. The ParserError will have an incompleteExpression AST built up to the point of failure. |
With stopOnFirstError: false the parser collects all errors it can recover from
and attaches the partial AST as incompleteExpression:
import { parseSafe } from "@imogenz/escalc";
const result = parseSafe("1 + + 2", { stopOnFirstError: false });
if (result.type === "error") {
console.log(result.error.errors.length); // number of issues
console.log(result.error.errors[0].detailedMessage);
const partialAst = result.error.incompleteExpression; // may be non-null
}
import { execute, executeSafe } from "@imogenz/escalc";
const ast = parse("[x] ** 2");
// Execute with different parameter sets without re-parsing
execute(ast, { params: new Map([["x", 3]]) }); // => 9
execute(ast, { params: new Map([["x", 4]]) }); // => 16
execute accepts the same options as evaluate:
| Option | Type | Description |
|---|---|---|
params |
Map<string, unknown> |
Named parameter values. |
lazyParams |
Map<string, () => unknown> |
On-demand parameters. |
functions |
Map<string, ExpressionFunction> |
Custom functions. |
calculator |
Calculator |
Custom operator implementation. |
import { parse, execute } from "@imogenz/escalc";
const formula = parse("[base] + [bonus] * [multiplier]");
const employees = [
{ base: 50000, bonus: 5000, multiplier: 1.1 },
{ base: 60000, bonus: 8000, multiplier: 1.2 },
];
const results = employees.map(({ base, bonus, multiplier }) =>
execute(formula, {
params: new Map([
["base", base],
["bonus", bonus],
["multiplier", multiplier],
]),
}),
);
// => [55500, 69600]
The LogicalExpression type is a discriminated union. Every node has a type
property you can switch on:
import { parse, type LogicalExpression } from "@imogenz/escalc";
function countNodes(expr: LogicalExpression): number {
switch (expr.type) {
case "value":
return 1;
case "unary":
return 1 + countNodes(expr.expression);
case "binary":
return 1 + countNodes(expr.left) + countNodes(expr.right);
case "ternary":
return (
1 +
countNodes(expr.left) +
countNodes(expr.middle) +
countNodes(expr.right)
);
case "function":
return 1 + expr.arguments.reduce((n, a) => n + countNodes(a), 0);
}
}
Each node also carries a location: SourceRegion | null with character offset,
line, and column information.