Skip to main content

Crate acts_expr

Crate acts_expr 

Source
Expand description

A small expression evaluator for the expressions a workflow carries.

acts-expr compiles a source string once into an Expr and evaluates it against a Context as often as needed — the same compiled expression can be evaluated from any number of threads with different contexts. It has no dependencies, allocates one Vec per compiled expression (nodes reference their children by index, not by pointer) and never touches a global.

§The expression surface

  • literals: 12, 1.5, 1e3, 'text', "text", true, false, null
  • arithmetic: + - * / %, unary -, with ( ) to group
  • comparison: == != < <= > >= (which do not chain, so 1 < 2 < 3 is a parse error rather than a type error)
  • logic: &&, ||, unary !, both short-circuiting
  • access: a.b, a['b'], list[0], and calls f(x), a.f(x)

== compares numbers across int, uint and float, lists and objects element-wise, and values of different kinds are not equal (so a workflow that compares a missing value with a string is false, not an error). Arithmetic requires numbers, + also concatenates two strings, and && || ! require bools. Two ints stay an int (so / truncates and % is the remainder) and an overflow is an error; a uint — what JSON gives a host for a value above i64::MAX — stays exact against an int, and any float either side makes the result a float.

§Built-in methods

A value carries a few methods of its own, so the common test needs nothing injected:

  • a string: length(), startsWith(p), endsWith(p), indexOf(p), contains(p), and is_match(pattern) — a regular expression, behind the regex feature
  • a list: length(), contains(value) (an element, compared by value)
  • a map: length() (its entries)

length() and indexOf() count characters, not bytes. These are found last: the object’s own method, then a function of that name in the context, then the built-in — so a host that injects length defines what it means.

§Injection

Everything an expression reads is in the Context: scalars, lists and objects (Value::Map) as values, and methods as Value::Functions. A function is called as f(x), or as a method with its receiver first — x.f() — and a function stored inside an object is called that way through the object, so an injected object carries its own methods.

use acts_expr::{Context, Expr, Value};

let mut context = Context::new();
context
    .set("count", 3)
    .set("status", "ok")
    .set("step1", Value::map([
        ("value", Value::from(7)),
        ("id", Value::from("step1")),
    ]))
    .set(
        "upper",
        Value::function(|args| match args {
            [Value::Str(text)] => Ok(Value::from(text.to_uppercase())),
            _ => Err(acts_expr::Error::Type { operation: "upper", got: "other" }),
        }),
    );

let expr = Expr::compile("step1.value + count * 2 >= 13 && status != 'bad'")?;
assert_eq!(expr.eval(&context)?, Value::Bool(true));

// A method: `upper` from the context, called with `status` as its receiver.
let name = Expr::compile("status.upper()")?;
assert_eq!(name.eval(&context)?, Value::from("OK"));

§Deliberately not in the surface

The evaluator answers the expressions a workflow writes — conditions, arithmetic on task data, string and object access — and nothing more. There is no assignment, no statement, no ternary (?:), no collection literal ([...], {...}), no in, no comprehension, and no method beyond the built-ins above — every other method is one the host injected. Each of the absent forms is rejected with a message that says so.

Structs§

Context
The variables and methods one evaluation sees.
Expr
A compiled expression.

Enums§

Error
Everything acts-expr can fail with.
Value
One expression value.

Functions§

eval
Compile and evaluate in one step, for a caller that has no reason to keep the compiled expression.

Type Aliases§

Map
An object: a string-keyed map of values. Usually behind an Arc, so reading a member is a pointer clone.
NativeFunction
A native method. The receiver of a method call is its first argument, so name.upper() and upper(name) are the same call.
Result
The crate’s result type: the error is Error unless a caller names another.