A JavaScript engine for parsing and evaluating expressions.
LPE turns formulas into Lisp-style abstract syntax trees (ASTs), evaluates them with your data and functions, and converts ASTs back into expression text.
Property access and callable dispatch follow the evaluator integration contract. Review it when supplying application data and callbacks.
English · Русский
Quick start · Examples · Syntax · API · Development · Documentation
- Parse and inspect formulas. Work with nested arrays that represent function calls, operators and literals.
- Evaluate against a context. Supply application data and JavaScript functions alongside the standard library.
- Compose expressions. Use function chains, arrays, objects and local variables with
VAR … RETURN. - Use asynchronous values. Promise arguments are resolved during evaluation; reactive streams can be integrated through a
streamAdapter. - Generate expression text. Convert ASTs back into LPE with
deparse.
Build from source with Node.js and npm. These examples describe the current master branch.
git clone https://github.com/luxms/luxmsbi-lpe.git
cd luxmsbi-lpe
npm ci
npm run buildThe build creates dist/lpe.js. Run this command from the repository root:
node -e "console.log(require('./dist/lpe').eval_lpe('2 + 3 * 4'))"Output: 14.
The package is named @luxms/lpe. The examples below load the local build directly. The bundle supports CommonJS, AMD and the browser global lpe; TypeScript declarations are also included.
const { parse, eval_lpe, eval_lisp, deparse } = require('./dist/lpe');
const context = { price: 120, quantity: 3 };
eval_lpe('price * quantity', context); // 360
const ast = parse('price * quantity');
// ['*', 'price', 'quantity']
eval_lisp(ast, context); // 360
deparse(ast); // 'price * quantity'parse builds an AST without running the expression. Use eval_lisp to evaluate an existing AST, or eval_lpe to parse and evaluate in one call. deparse generates expression text; it does not preserve the original formatting or comments.
const { eval_lpe } = require('./dist/lpe');
eval_lpe('amount.double()', {
amount: 21,
double: value => value * 2,
}); // 42The dot passes the value on its left as the first argument: amount.double() calls double(amount). For JavaScript method calls on an object, use ~> instead.
const { eval_lpe } = require('./dist/lpe');
eval_lpe(`
VAR subtotal = price * quantity
VAR discount = subtotal * 0.1
RETURN subtotal - discount
`, { price: 120, quantity: 3 }); // 324Bindings are evaluated in order, so each variable can use the ones declared before it. VAR and RETURN are uppercase keywords.
const { eval_lpe } = require('./dist/lpe');
const result = eval_lpe('price * quantity', {
price: Promise.resolve(120),
quantity: 3,
});
result.then(value => console.log(value)); // 360| Feature | Expression | Result |
|---|---|---|
| Arithmetic | 2 + 3 * 4 |
14 |
| Logic | (3 > 2) and not false |
true |
| Array | [1, 2, 3] |
[1, 2, 3] |
| Object | {name = "Ada", score = 42} |
{name: "Ada", score: 42} |
| Property access | {score = 42}.score |
42 |
| Empty object | {=} |
{} |
| Empty array | {} |
[] |
| JavaScript method | "hello" ~> toUpperCase() |
"HELLO" |
| Sequential statements | x := 2; x + 3 |
5 |
Separate top-level statements with ; or a newline. Function arguments accept , or ;. Comments use //, -- or /* … */.
When mixing and and or, use parentheses to make grouping explicit: the parser gives both operators the same precedence.
How expressions map to ASTs
The first element of an AST array identifies the operation or function. Numbers remain numbers; bare names are strings. String literals have an explicit quote node.
| Source | AST |
|---|---|
2 + 3 * 4 |
["+", 2, ["*", 3, 4]] |
double(21) |
["double", 21] |
"hello" |
["\"", "hello"] |
[1, 2, 3] |
["[", 1, 2, 3] |
a.b() |
[".", "a", ["b"]] |
a(); b() |
["begin", ["a"], ["b"]] |
| Function | Purpose |
|---|---|
parse(source, options?) |
Parse LPE source into an AST. |
eval_lpe(source, context?, options?) |
Parse and evaluate an expression. |
eval_lisp(ast, context?, options?) |
Evaluate an AST with a context and the standard library. |
deparse(ast, options?) |
Convert an AST into LPE source. |
The module also exports LPESyntaxError, STDLIB and helpers for custom forms, variables, documentation and localization. See runtime exports and option types.
Build before running tests: the test suite imports dist/lpe.js.
npm ci
npm run build
npm test| Command | Purpose |
|---|---|
npm run watch |
Rebuild when source files change. |
npm run t |
Run the parser tests in test/test.js. |
npm run test-lisp |
Run the evaluator tests in test/test-lisp.js. |
For a command-line AST preview on systems with /dev/stdin:
echo "2 + 3 * 4" | npm run cliUse npm run eval in place of npm run cli to evaluate the expression. Both scripts read /dev/stdin; on Windows, use the node -e command from Quick start.
- Language and engine notes — syntax, evaluation rules and implementation details.
- Standard library reference — built-in functions and examples, in Russian.
- Changelog — release history.
- Browser and AMD examples — sample integrations.
For bug reports, include the expression, context and expected result in a GitHub issue. For code changes, add a regression test and run the build and test suite before opening a pull request.
MIT · Copyright (c) 2019 Luxms Inc.