Skip to main content

Expression Engine

The expression package exposes a backend-agnostic expression contract. The current framework runtime wires a pure-Go expr-lang engine and makes it available to API handlers and mold field transformations.

The older js package is still a separate Goja JavaScript runtime. Use expression for business rules and derived fields that should stay behind the stable expression.Engine interface.

Runtime Backend​

VEF currently provides the engine from the core boot graph, backed by github.com/expr-lang/expr.

The current backend is pure Go. The expression module itself does not require CGO; whether your application enables CGO depends on the database drivers or other native integrations you choose.

Core API​

type Engine interface {
Evaluate(ctx context.Context, source string, env any) (Value, error)
Compile(source string, opts ...CompileOption) (Program, error)
}

type Program interface {
Run(ctx context.Context, env any) (Value, error)
Source() string
}
  • Evaluate compiles and evaluates a source expression in one step.
  • Compile returns a reusable Program; the current backend parses and validates the expression eagerly.
  • env is the variable environment. It can be a map or a struct with JSON tags.
  • Context cancellation is best-effort. The current expr-lang backend honors an already-canceled context before starting synchronous evaluation, but cannot interrupt evaluation already in flight.

Evaluating Values​

import (
"context"

"github.com/coldsmirk/vef-framework-go/expression"
)

total, err := expression.EvaluateAs[float64](
context.Background(),
engine,
"price * qty",
struct {
Price float64 `json:"price"`
Qty float64 `json:"qty"`
}{Price: 2, Qty: 3},
)

EvaluateAs[T] evaluates and decodes the result into T. For manual result handling, use the Value helpers:

APIContract
NewValue(raw)wraps a backend result; backend adapters normally construct it
Value.Interface()returns the raw backend result
Value.IsNil()reports whether the raw backend result is nil
Value.Bool()returns a boolean result or expression.ErrUnexpectedType for non-bool values
Value.Decode(target)JSON-decodes the raw value into a non-nil pointer target
DecodeValue[T](value)generic helper that decodes into a new T

Decoding goes through JSON, so very large integers can lose precision in the same way as JSON number conversion. A nil result decodes as JSON null; for non-pointer scalar targets this produces the type's zero value.

Predicate Matching​

Use Match for boolean predicates:

ok, err := expression.Match(
context.Background(),
engine,
">= 5",
map[string]any{"$": 10},
)

Match compiles the expression with expression.AsPredicate(), then expects a boolean result.

Handler Injection​

The core boot graph registers a handler parameter resolver for expression.Engine, so API handlers can request it directly:

func CalculateTotal(
ctx context.Context,
engine expression.Engine,
input CalculateTotalInput,
) (CalculateTotalOutput, error) {
total, err := expression.EvaluateAs[float64](
ctx,
engine,
"price * qty",
input,
)
if err != nil {
return CalculateTotalOutput{}, err
}

return CalculateTotalOutput{Total: total}, nil
}

Application code should depend on expression.Engine, not the internal expr-lang adapter.

Mold expr Fields​

The framework also registers a mold field transformer named expr. It evaluates the expression against the containing struct and writes the decoded result into the tagged field:

type LineItem struct {
Price float64 `json:"price"`
Qty float64 `json:"qty"`
Total float64 `json:"total" mold:"expr=price * qty"`
}

Important details:

  • The containing struct is the expression environment.
  • Fields are transformed in declaration order, so an expr field can reference sibling fields declared above it, including earlier derived fields.
  • A reference to a field declared below the current field sees that field's zero value.
  • Mold splits tag functions on commas. If an expression contains a comma, escape it as 0x2C in the tag parameter.

Error Behavior​

Backend evaluation failures are wrapped under expression.ErrEvaluationFailed. Type conversion failures surface from Value.Bool(), Value.Decode(...), or DecodeValue[T](...).

Error details:

Error surfaceContract
expression.ErrEvaluationFailedAPI-facing result error for backend compile/evaluation failures
ErrCodeEvaluationFailed2500
i18n keyexpression_evaluation_failed
expression.ErrUnexpectedTypereturned by Value.Bool() for non-bool values; message text is expression: unexpected result type
empty mold expr tagreturns expression: empty expression in field tag
non-settable mold target fieldreturns expression: target field is not settable

With the current expr-lang backend, malformed expressions fail during Compile(...); runtime type/value failures can still surface from Program.Run(...).

Supporting public APIs include CompileOption, CompileOptions, CompileOptions.Predicate, AsPredicate(), ErrCodeEvaluationFailed, and ErrUnexpectedType.