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 ¶
- Variables
- func AsInt(v Value) (int, bool)
- func DurationNanos(v Value) (int64, bool)
- func InstantNanos(v Value) (int64, bool)
- func IsTrue(v Value) bool
- func ToJSON(v Value) (string, bool)
- type Compiled
- type Value
- func AsList(v Value) ([]Value, bool)
- func Bool(b bool) Value
- func DateTime(t time.Time) Value
- func FromJSON(raw any) Value
- func FromStored(kind ValueKind, b bool, text string) Value
- func ListOf(elems ...Value) Value
- func Number(i int64) Value
- func ParseJSON(text string) (Value, error)
- func String(s string) Value
- type ValueKind
Constants ¶
This section is empty.
Variables ¶
var Null = value.Null
Null is the FEEL null value.
Functions ¶
func AsInt ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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
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 ¶
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 ¶
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 DateTime ¶ added in v0.5.0
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 ¶
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 ¶
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 ¶
ListOf builds a FEEL list value from the given elements — the inverse of AsList, for assembling a multi-instance output collection (ADR-0077).
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).
func Classify ¶
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.