Skip to content

Expression

Audience: Developers integrating ExpresZo into their projects.

parser.parse(str) returns an Expression object. Expressions are similar to JavaScript functions, i.e. they can be "called" with variables bound to passed-in values.

evaluate(variables?: object, resolver?: VariableResolver)

Evaluate the expression, with variables bound to the values in {variables}. Each variable in the expression is bound to the corresponding member of the variables object. If there are unbound variables, evaluate will throw an exception.

import { Parser } from '@pro-fa/expreszo';

const parser = new Parser();
const expr = parser.parse("2 ^ x");
console.log(expr.evaluate({ x: 3 })); // 8

The optional resolver argument is a per-call variable resolver. It has the same shape as parser.resolve(name) => { alias } | { value } | undefined — but applies only to the current evaluate() call, so a single parsed Expression can be evaluated multiple times against different data sources without mutating parser state. The per-call resolver is consulted before parser.resolve; the variables object still takes precedence over both.

const parser = new Parser();
const expr = parser.parse('$user.name');

const resolveAlice = (name) => name === '$user' ? { value: { name: 'Alice' } } : undefined;
const resolveBob   = (name) => name === '$user' ? { value: { name: 'Bob'   } } : undefined;

expr.evaluate({}, resolveAlice); // 'Alice'
expr.evaluate({}, resolveBob);   // 'Bob'

See Per-Expression Variable Resolver for details.

substitute(variable: string, expression: Expression | string | number)

Create a new Expression with the specified variable replaced with another expression. This is similar to function composition. If expression is a string or number, it will be parsed into an Expression.

const parser = new Parser();
const expr = parser.parse("2 * x + 1");
console.log(expr.toString());              // ((2*x)+1)

const expr2 = expr.substitute("x", "4 * x");
console.log(expr2.toString());             // ((2*(4*x))+1)
console.log(expr2.evaluate({ x: 3 }));     // 25

simplify(variables: object)

Simplify constant sub-expressions and replace variable references with literal values. This is basically a partial evaluation, that does as much of the calculation as it can with the provided variables. Function calls are not evaluated (except the built-in operator functions), since they may not be deterministic.

Simplify is pretty simple. For example, it doesn't know that addition and multiplication are associative, so ((2*(4*x))+1) from the previous example cannot be simplified unless you provide a value for x. 2*4*x+1 can however, because it's parsed as (((2*4)*x)+1), so the (2*4) sub-expression will be replaced with "8", resulting in ((8*x)+1).

const parser = new Parser();
const expr = parser.parse("x * (y * atan(1))").simplify({ y: 4 });
console.log(expr.toString());          // (x*3.141592653589793)
console.log(expr.evaluate({ x: 2 }));  // 6.283185307179586

variables(options?: object)

Get an array of the unbound variables in the expression.

const parser = new Parser();
const expr = parser.parse("x * (y * atan(1))");
console.log(expr.variables());                     // ['x', 'y']
console.log(expr.simplify({ y: 4 }).variables());  // ['x']

By default, variables will return "top-level" objects, so for example, parser.parse('x.y.z').variables() returns ['x']. If you want to get the whole chain of object members, you can call it with { withMembers: true }. So parser.parse('x.y.z').variables({ withMembers: true }) would return ['x.y.z'].

symbols(options?: object)

Get an array of variables, including any built-in functions used in the expression.

const parser = new Parser();
const expr = parser.parse("min(x, y, z)");
console.log(expr.symbols());                          // ['min', 'x', 'y', 'z']
console.log(expr.simplify({ y: 4, z: 5 }).symbols()); // ['min', 'x']

Like variables, symbols accepts an option argument { withMembers: true } to include object members.

toString()

Convert the expression to a string. toString() surrounds every sub-expression with parentheses (except literal values, variables, and function calls), so it's useful for debugging precedence errors.