LyteNyte Grid logo for light mode. Links back to the documentation home page.
Expressions

Expressions Overview

Use a custom expression language for advanced calculations and application-specific logic.

What Are Expressions

An expression is a text string that evaluates to a value. Expressions range from simple arithmetic, such as 1 + 1, which evaluates to 2, to complex calculations, such as sum(Sales Revenue) / sum(Sales Quantity), which computes the average revenue per unit.

LyteNyte Grid lets developers define a custom expression language that application users can use to describe computations. Two common use cases are:

Expressions In Action

The demo below uses expressions to filter rows in LyteNyte Grid. Type an expression to update the filter, or select one of the provided examples.

Note

For a detailed explanation of expression filters, see the Expression Filters guide.

Expression Filters

Fork code on stack blitzFork code on code sandbox

Expression Engine

As raw text, sum(Sales Revenue) / sum(Sales Quantity) has no inherent meaning. LyteNyte Grid provides an expression engine that evaluates these strings to return actual values. The expression engine processes the input in three steps:

  1. Tokenization: Breaks the expression into its fundamental building blocks, or tokens.
  2. Parsing: Converts the generated tokens into an Abstract Syntax Tree (AST), which maps relationships between expression components for accurate evaluation.
  3. Evaluation: Resolves the AST to compute a final value. The grid then uses this result to filter rows, dynamically compute cell values, or drive other application logic.

Evaluator

LyteNyte Grid’s expression engine is exposed as an exported class called Evaluator. Fundamentally, this class serves as a calculator that evaluates mathematical expressions.

The code below shows the simplest use case:

import { Evaluator } from "@1771technologies/lytenyte-pro/expressions";
const result = new Evaluator().run("1 + 1"); // 2

Evaluator supports a plugin system for adding more advanced expression features. LyteNyte Grid includes a standard set of plugins that provide a ready-to-use, JavaScript-like expression language.

The demo below shows this advanced expression language, including identifiers such as age, functions such as fullName(), and interpolated strings.

Standard Expression Language

Result
46number
Illustrative Examples
Context
user
object
firstName"John"
string
lastName"Smith"
string
age23
number
location"London"
string
fullName() => …
fn
Fork code on stack blitzFork code on code sandbox

Expression Plugins

Expression plugins extend the Evaluator class with additional capabilities. Pass an array of plugins when creating an instance of the Evaluator to enable specific features. For example:

import { Evaluator, standardPlugins, stringsPlugin } from "@1771technologies/lytenyte-pro/expressions";
// Evaluator with standard expression language support
const standardEvaluator = new Evaluator(standardPlugins);
// Evaluator with string support (+ basic math operations)
const stringEvaluator = new Evaluator([stringsPlugin]);

Expression Context

For an expression such as age + 21, the Evaluator must resolve the identifier age to a value. The Evaluator.run method accepts a context object as its second argument. The context object provides values at runtime for identifiers used in the expression.

const context = {
age: 23,
user: { firstName: "John", lastName: "Smith" },
fullName: () => context.user.firstName + " " + context.user.lastName,
};
evaluator.run("age * 2", context); // 46
evaluator.run("user.firstName", context); // "John"
evaluator.run("fullName()", context); // "John Smith"

The context can include any value type, such as primitives, objects, arrays, and functions. Expressions can call functions defined in the context directly. This mechanism allows you to expose domain-specific data and operations to expression authors.

Standard Expression Language

The base Evaluator, without plugins installed, supports the arithmetic operators +, -, *, /, %, and **, where ** performs exponentiation.

The standardPlugins export from LyteNyte Grid turns the base Evaluator into a JavaScript-like expression language. The table below lists each plugin, its syntax, and an example.

Plugin Syntax Example
stringsPlugin String literals and template literals "hello", 'world', `Hi ${name}`
booleansPlugin true, false, null, undefined literals status == true
comparisonPlugin == != < > <= >= age >= 18
logicalPlugin && || ?? and unary ! a && b || c
membershipPlugin in and not in "key" in obj
ternaryPlugin condition ? then : else x > 0 ? "pos" : "neg"
collectionsPlugin Array and object literals, spread (...) [1, 2, ...rest]
arrowsPlugin Arrow functions (single and multi-parameter) x => x * 2, (a, b) => a + b
pipePlugin Pipe operator |> items |> filter(x => x > 0)
accessPlugin .prop, [computed], ?., function calls user.name, obj?.city
quotedIdentifierPlugin @"name" quoted identifiers for names with spaces @"Age Group", @"Sub-Category"
dateLiteralPlugin d"<date string>" any formattable date string d"2023-02-02", d"2023/01/01"

Literals

Numbers support decimal, hexadecimal (0xFF), octal (0o77), binary (0b1010), scientific notation (1e10), and underscore separators (1_000_000).

Strings support single quotes, double quotes, and standard escape sequences (\n, \t, \uXXXX, \xHH).

Template literals use backticks and support embedded expressions:

`${user.firstName} scored ${points * multiplier} points`

Identifiers that contain spaces or special characters can be written using the @"..." quoted identifier syntax. Quoted identifiers resolve the same way as plain identifiers, they look up the name in the expression context:

@"Age Group" // resolves context["Age Group"]
@"Sub-Category" == "Road Bikes"
@"Age Group".toUpperCase() // member access works as normal

Both single and double quotes are accepted: @'Age Group'.

Operators

Operators follow standard precedence, from lowest to highest:

Operators Description
?: Ternary conditional
|> Pipe
|| Logical OR
&& Logical AND
?? Nullish coalescing
== != Equality
< > <= >= in not in Relational and membership
+ - Addition and subtraction
* / % Multiplication, division, modulo
** Exponentiation (right-associative)
! - + Unary NOT, negation, identity
. […] ?. () Member access, computed, optional, call

Member Access

Use . dot notation or [computed] bracket notation to access nested values. The ?. operator returns undefined when the value on the left is null or undefined, instead of throwing an error.

user.address.city; // throws if address is null
user.address?.city; // returns undefined if address is null
data["dynamic-key"]; // computed bracket access

Call a function from the context or a method on a value:

fullName(); // calls fullName from the context
"hello".toUpperCase(); // method call (this binding preserved)
items.filter((x) => x > 0); // method with arrow function argument

Arrow Functions

Arrow functions are first-class values. Use them as callbacks for array methods or any function in the context that accepts a function argument:

items.map((x) => x * 2);
items.filter((x, i) => x > 0 && i < 10);

Single-parameter arrows do not require parentheses. Multi-parameter arrows do.

Note

Arrow functions in the expression language are not closures. Identifiers in the surrounding scope are not captured. The expression language does not support scoping rules or variable declarations.

Pipe Operator

The |> operator passes the left-hand value as the last argument to the right-hand function or call expression. Chains read left-to-right without deep nesting:

// Without pipe
round(multiply(price, 1.1), 2)
// With pipe
price |> multiply(1.1) |> round(2)

If the right side is a bare identifier, the input is passed as the sole argument: value |> format.

Logical Fallbacks

The && and || operators short-circuit, so the right-hand expression evaluates only when needed. The ?? operator returns the right-hand expression only when the left-hand value is null or undefined, unlike ||, which also returns the right-hand expression for falsy values such as 0 or "".

user && user.name; // safe: user.name only evaluated if user is truthy
count || "none"; // returns "none" if count is 0 (falsy)
label ?? "default"; // returns "default" only if label is null/undefined

Standalone Expression Language

The expression engine is independent of LyteNyte Grid. The Evaluator, standardPlugins, and plugin system are general-purpose APIs that you can import and use in any JavaScript or TypeScript project. LyteNyte Grid features, including filters, computed fields, and the ExpressionEditor component, build on the same public API.

Expressions supports use cases outside LyteNyte Grid, including:

  • Rule Engines: Evaluate user-defined conditions against application state, such as access control rules, notification triggers, or business logic configured by non-developers.
  • Formula Inputs: Let users enter calculations in a form field, such as in a pricing configurator where a sales rep enters basePrice * quantity * (1 - discount).
  • Template Rendering: Use template literals and the pipe operator to build lightweight text templates that generate messages or documents from a context object, without requiring a full templating library.
  • Configuration DSLs: Store expressions as strings in a database or config file and evaluate them at runtime. This keeps computed logic out of the codebase and allows updates without redeployment.
  • Workflow automation: Evaluate step conditions in a visual workflow builder, where each node’s transition condition is an expression authored by the user.

Next Steps