expr

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 8, 2026 License: AGPL-3.0 Imports: 8 Imported by: 0

Documentation

Overview

Package expr is Atlas's boundary to a FEEL engine. It wraps github.com/pblumer/feel so the rest of Atlas depends on this small, stable surface rather than on the FEEL library's full API.

It implements the ADR-0008 contract: expressions are compiled once at deploy time (parse + type-check + lower to a closure) and evaluated many times with no re-parsing and minimal allocation on the hot path. Compilation also reports an expression's inputs — the variable names it reads — so the engine can load only those from a scope instead of materializing the whole variable set.

Reuse of an existing FEEL engine (versus building our own subset) is exactly what ADR-0008 permits when the library offers a genuine compile-once/eval-many API; feel does. See ADR-0015.

Index

Constants

This section is empty.

Variables

View Source
var Null = value.Null

Null is the FEEL null value.

Functions

func AsInt

func AsInt(v Value) (int, bool)

AsInt returns a FEEL number as an int and true if v is a whole number within int range, else 0 and false. Used for a multi-instance loop cardinality (ADR-0077).

func DurationNanos

func DurationNanos(v Value) (int64, bool)

DurationNanos returns the length in nanoseconds of a FEEL days-and-time duration value (e.g. the result of duration("PT1H")), and whether v is one. A years-and- months duration is calendar-dependent and not convertible to a fixed length, so it reports false — matching the ISO-8601 duration subset Atlas accepts (ADR-0057).

func InstantNanos

func InstantNanos(v Value) (int64, bool)

InstantNanos returns the unix-nanosecond instant of a FEEL date-time value, or a date (resolved to midnight in its own zone), and whether v is one of those (ADR-0057).

func IsTrue

func IsTrue(v Value) bool

IsTrue reports whether v is FEEL boolean true. A non-boolean (including null, the result of a failed comparison) is not true — which is how a sequence-flow condition that doesn't evaluate to true is simply not taken.

func ToJSON

func ToJSON(v Value) (string, bool)

ToJSON encodes a FEEL value as canonical JSON, reporting whether it could. It is canonical — object keys are sorted (encoding/json sorts map keys) and numbers keep their exact decimal text — so the same value always yields the same bytes, which replay depends on. ok is false only if the value contains something with no JSON image (it never does for lists/contexts of scalars).

Types

type Compiled

type Compiled struct {
	// contains filtered or unexported fields
}

Compiled is a FEEL expression compiled once. It is immutable and safe for concurrent evaluation (like the CompiledProcess it lives in). Evaluate it with Eval; Inputs reports the variables it reads, and Source the text it was written as.

func Compile

func Compile(src string, vars ...string) (*Compiled, error)

Compile parses, type-checks and lowers src into a reusable form. vars declares the variable names the expression is allowed to reference; referencing a name not in vars is a compile error, which is how deploy-time validation catches a script that reads an undeclared variable.

func CompileAuto

func CompileAuto(src string) (*Compiled, error)

CompileAuto compiles src while discovering the variable names it references: it starts with no declared variables and, each time the FEEL compiler reports an "unknown variable", declares that name and retries, until the expression compiles. This lets a script author reference process variables without declaring them up front, while still letting the compiler — not us — decide what is a genuine free variable (bound loop/quantifier names, path members and built-ins never surface as unknown). A syntax or type error (not a missing declaration) is returned as-is.

func (*Compiled) Eval

func (c *Compiled) Eval(vars map[string]Value) (Value, error)

Eval evaluates the expression against the given variable bindings. Names in vars that the expression does not read are ignored; declared names absent from vars evaluate as FEEL null.

func (*Compiled) Inputs

func (c *Compiled) Inputs() []string

Inputs returns the variable names the expression actually reads (sorted, unique) — a subset of the names passed to Compile.

func (*Compiled) Source added in v0.4.0

func (c *Compiled) Source() string

Source returns the expression as it was written. A compiled expression is otherwise opaque, so an explanation of what the engine decided — a loop's condition on the replay, a message naming the expression that failed — would have to re-read the model to quote it. Kept once at compile time (deploy), so nothing on the hot path pays for it.

type Value

type Value = value.Value

Value is a FEEL runtime value. Re-exported so engine code refers to expr.Value rather than importing the FEEL value package throughout.

func AsList

func AsList(v Value) ([]Value, bool)

AsList returns the elements of a FEEL list value and true, or nil and false if v is not a list. It fans a multi-instance activity's input collection into its per-iteration items (ADR-0077).

func Bool

func Bool(b bool) Value

Bool returns a FEEL boolean value.

func DateTime added in v0.5.0

func DateTime(t time.Time) Value

DateTime returns a FEEL date-and-time value for a Go instant, for building bindings. It exists because a binding that must be *compared* to a moment — a task's due date against `now` — cannot be a number: FEEL's `<` on numbers and its `<` on instants are different comparisons, and only the second one accepts `now + duration("P3D")` on the other side. Binding the instant rather than calling now() inside the expression is also what makes a scan deterministic: every row is judged against one moment instead of each against its own.

func FromJSON

func FromJSON(raw any) Value

FromJSON converts a decoded JSON value (map[string]any / []any / json.Number / string / bool / nil, as produced by encoding/json with UseNumber) into a FEEL value: an object becomes a context, an array a list, recursively. A number that fails to parse degrades to FEEL null rather than erroring, matching FromStored's defensive contract.

func FromStored

func FromStored(kind ValueKind, b bool, text string) Value

FromStored reconstructs a FEEL value from Atlas's stored form — the inverse of Classify — for binding variables into an evaluation. An unparseable number, or unparseable stored JSON, becomes null.

func ListOf

func ListOf(elems ...Value) Value

ListOf builds a FEEL list value from the given elements — the inverse of AsList, for assembling a multi-instance output collection (ADR-0077).

func Number

func Number(i int64) Value

Number returns a FEEL number value for an integer, for building bindings.

func ParseJSON

func ParseJSON(text string) (Value, error)

ParseJSON decodes canonical JSON text into a FEEL value, the inverse of ToJSON. Numbers are read exactly (json.Number) so decimals aren't routed through a float before FEEL parses them.

func String

func String(s string) Value

String returns a FEEL string value.

type ValueKind

type ValueKind uint8

ValueKind classifies a FEEL value into the subset Atlas persists: the scalars, plus KindJSON for structured values (objects and lists).

const (
	KindNull ValueKind = iota
	KindBool
	KindNumber
	KindString
	// KindJSON is a structured value (list or context). Its text is a canonical
	// JSON encoding, re-parsed by FromStored into a FEEL list/context.
	KindJSON
)

func Classify

func Classify(v Value) (ValueKind, bool, string)

Classify reduces a FEEL value to a storable (kind, bool, text) triple: text is the number's canonical decimal string or the string's contents. Lists and contexts are stored under KindJSON as canonical JSON so they round-trip; other non-scalars (temporals, ranges, functions) are rendered to their canonical FEEL text and stored as a string — lossy but stable — until Atlas models them.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL