GEL Evaluator Technical Manual

This document describes the GEL Evaluator — a core component of the General Expression Language (GEL) runtime. It assumes familiarity with the GEL manual describing the lexer, parser, and abstract syntax tree (AST).


Table of Contents

  1. 1. Introduction
  2. 2. GEL Evaluator Overview
  3. 3. Usage & API
  4. 4. Implementation Details
  5. 5. GEL Engine
  6. 6. Examples
  7. 7. Error Handling
  8. 8. References

1. Introduction

The GelEvaluator class processes a parsed GEL AST and executes it against a data record. It supports both strict and non-strict evaluation modes. In strict mode, missing fields cause an exception; in non-strict mode, they are treated as "missing value" and evaluate to false or undefined, depending on context.

This evaluator supports asynchronous function calls, enabling integration with external APIs or asynchronous computations.


2. GEL Evaluator Overview

Once an expression string is parsed (into an Expression AST), the GelEvaluator traverses that AST to produce a final value. The evaluator:

Important: The evaluator depends on the AST structure produced by the GEL Parser. Refer to the previously published GEL Technical Manual for details on AST nodes and grammar.

3. Usage & API

Below is a summarized API reference for GelEvaluator. For more detail, see Section 4.

Method Signature Description
evaluateRule (expr, data, strictEval) => Promise<boolean> Evaluates an expression as a boolean rule.
Returns true or false.
strictEval controls error-handling for missing fields.
evaluateTransform (expr, data, strictEval) => Promise<unknown> Evaluates an expression as a transform.
Returns any value (number, string, boolean, array, etc.).
Missing data in non-strict mode yields undefined.
evaluateExpressionAsValue (expr, data, strictEval) => Promise<unknown> Internal helper. Recursively evaluates the AST node, returning a single result or array result.
evaluateSubscript (node, data, strictEval) => Promise<unknown> Evaluates arr[index], obj[key], or arr[*] patterns.
Throws (strict mode) or returns missing sentinel (non-strict) if invalid.
evaluateFunctionCall (node, data, strictEval) => Promise<unknown> Invokes the appropriate function from the config registry, passing in evaluated arguments.
Supports element-wise mapping if an argument uses [*] subscript.
evaluateComparison (node, data, strictEval) => Promise<boolean|boolean[]> Evaluates operators like eq, in, has, etc.
May produce a single boolean or an array of booleans for wildcard comparisons.
evaluateLogical (node, data, strictEval) => Promise<boolean> Evaluates and, or, and possible not negation flags.
Uses evaluateRule internally to get a boolean from sub-expressions.

4. Implementation Details

The GelEvaluator includes various methods to navigate and compute values from AST nodes. Each node type from the GEL manual is handled in evaluateExpressionAsValue via a switch statement:

private async evaluateExpressionAsValue(
  expr: Expression,
  data: Record<string, unknown>,
  strictEval: boolean
): Promise<unknown> {
  switch (expr.type) {
    case "literal":
      return expr.value;

    case "field_reference":
      return this.getFieldValue(expr, data, strictEval);

    case "inset":
      // Evaluate each item in the set
      return Promise.all(expr.values.map(...));

    case "subscript":
      return this.evaluateSubscript(expr, data, strictEval);

    case "function_call":
      return this.evaluateFunctionCall(expr, data, strictEval);

    case "comparison":
      return this.evaluateComparison(expr, data, strictEval);

    case "logical":
      return this.evaluateLogical(expr, data, strictEval);

    default:
      throw new Error("Unknown expression type");
  }
}

4.1 Handling Strict vs. Non-Strict Modes

Many evaluation paths check strictEval to decide how to respond to missing fields or invalid indexes:

4.2 Wildcard Subscripting (arr[*])

[*] patterns produce an array of results when indexing. If a function argument has [*], the evaluator maps that function call over each element of the array. The final returned value is an array of results.

4.3 Function Registry and Async Calls

The config object defines available functions. evaluateFunctionCall looks up the function by name, then calls it asynchronously:

const fn = this.config.functions[node.functionName];
const argVals = await Promise.all(node.args.map(...));
const result = await fn.apply(null, argVals);

5. GEL Engine

The GelEngine is a higher-level utility class that combines parsing (from the other manual) and evaluation in one object:

Note: The GelParser is documented in the main GEL Technical Manual. In GelEngine, we see how the parser and evaluator integrate:

public parse(input: string): Expression {
  const parser = new GelParser(input, this.config);
  return parser.parse();
}

public evaluate(
  rule: Expression,
  data: Record<string, unknown>,
  strictMode: boolean
): Promise<boolean> {
  return this.evaluator.evaluateRule(rule, data, strictMode);
}

6. Examples

6.1 Evaluating a Boolean Rule

// 1) Parse the expression into an AST const parser = new GelParser("not user.is_admin eq true", config); const ast = parser.parse(); // 2) Evaluate as boolean const evaluator = new GelEvaluator(config); const result = await evaluator.evaluateRule(ast, { user: { is_admin: false } }, false); // => true

6.2 Handling Missing Fields

const expr = parser.parse("file.extension eq 'exe'"); const resultStrict = await evaluator.evaluateRule(expr, {}, true); // => throws UndefinedFieldError const resultNonStrict = await evaluator.evaluateRule(expr, {}, false); // => false (missing => false in non-strict mode)

6.3 Wildcard Subscript

const expr = parser.parse("any(process.args[*] eq '--verbose')"); const data = { process: { args: [ "--help", "--verbose", "--quiet" ] } }; const result = await evaluator.evaluateRule(expr, data, false); // => true (one of the elements matched eq '--verbose')

7. Error Handling

Errors thrown by the evaluator include:

In non-strict mode, the evaluator uses an internal missing value sentinel to avoid throwing. This sentinel eventually resolves to:

Note: This approach simplifies scenarios where data may be partially missing but the user wants to proceed with partial checks rather than failing the entire evaluation.

8. References

Reference Description Link
engine.ts Higher-level GelEngine showcasing parsing + evaluation. View Code
End of Evaluator Manual
For more examples, see unit tests or integration tests in the main repository.