expr

package module
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Jun 12, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

README

expr

A small expression language for Go programs. You write a line that looks like Go, expr compiles it, and you run it against whatever data you have lying around: a map, a struct, a pointer to a struct. Handy for conditions, templates, and little bits of user-supplied logic you don't want to turn into a full plugin system.

It is built directly on go/parser, so the syntax will feel familiar: it is mostly a subset of Go's own expression grammar, with a few ergonomic additions like JSON-style object and array literals.

Install

go get github.com/deepnoodle-ai/expr

Expressions

If you can read Go, you can read most expr. The inputs are plain values from your host program; expressions select fields, call the functions you expose, and produce a value.

// Boolean conditions over maps, structs, or JSON-shaped data.
user.age >= 18 && contains(user.roles, "admin")

// Select fields, index lists, and call only the functions your app registers.
sprintf("%s <%s>", user.name, lower(user.emails[0]))

// JSON-style arrays and objects are expression values too.
{
    "ok":            user.age >= 18,
    "display_name":  upper(user.name),
    "public_roles":  filter(user.roles, it != "internal"),
    "primary_email": user.emails[0],
}

// Higher-order forms bind `it` and `index` for each element.
any(orders, it.status == "paid" && count(it.items, it.price >= 100) > 0)

Every snippet above is still a single expression: no statements, no mutation, no loops, no function definitions. Your Go code decides which data and functions are visible.

Templates

Templates use the same expressions inside ${...}. Each expression is compiled once at construction time. It is evaluated again on every Render.

tmpl, err := expr.NewTemplate(
    `Hello ${user.name}! You have ${len(filter(tasks, !it.done))} task(s).`,
    expr.WithBuiltins(),
)
out, err := tmpl.Render(ctx, env)

The text outside ${...} is just text. The expression inside can use the same selectors, functions, literals, and higher-order forms as any other compiled expression. nil renders as the empty string; maps, slices, arrays, and structs render as compact JSON (${config} produces {"retries":3}, not map[retries:3]).

Using the Go API

p, err := expr.Compile(
    `user.age >= 18 && contains(user.roles, "admin")`,
    expr.WithBuiltins(),
)
if err != nil {
    panic(err)
}

ok, err := p.Run(ctx, map[string]any{
    "user": map[string]any{
        "age":   36,
        "roles": []any{"admin", "editor"},
    },
})
if err != nil {
    panic(err)
}
fmt.Println(ok) // true

Compile does the parsing work once. The returned *Program is immutable and safe to share between goroutines. Compile at startup. Run per request.

No functions are registered by default, so the surface area is exactly as wide as you want it. WithBuiltins() opts you into a small standard set (len, contains, has, keys, entries, upper, lower, int, float, string, bool, sprintf). entries(m) returns the sorted key-value pairs of a string-keyed map as [{"key":k,"value":v}, ...], making maps iterable through higher-order forms. WithFunctions lets you register any Go function as a callable identifier:

p, err := expr.Compile(`greet(upper(name))`, expr.WithFunctions(map[string]any{
    "upper": strings.ToUpper,
    "greet": func(name string) string { return "Hello, " + name + "!" },
}))

Mix and match, or skip the builtins entirely and expose only the handful that make sense for your sandbox. Opt-in groups — expr.MathFuncs() (min, max, abs, floor, ceil, round), expr.StringFuncs() (trim, split, join, replace, startsWith, endsWith), and expr.CollectionFuncs() (first, last, sum, slice, sort, reverse) — add the usual helpers via WithFunctions without widening the default set.

What the environment can be

Whatever you pass to Run is what the expression sees. A map[string]any works. So does a struct, or a pointer to one. Exported fields and zero-arg methods both become identifiers inside the expression.

p, err := expr.Compile(`Subtotal() > 100 && len(Items) >= 2`, expr.WithBuiltins())
v, err := p.Run(ctx, order) // order is some struct with a Subtotal() method

Struct tags are opt-in when your expression surface should match a JSON-shaped API contract:

p, err := expr.Compile(
    `result.environment.status == "ready"`,
    expr.WithStructTags("json"),
)

JSON-style literals

Object and array literals work the way you'd hope, without the Go ceremony:

p, err := expr.Compile(`{"items": [1, 2, 3], "count": 3, "ok": true}`)

Under the hood, bare [...] becomes []any{...} and {"k": v} becomes map[string]any{"k": v}. Strings, comments, and real Go composite literals are left alone, so nothing you already had stops working.

Higher-order forms

A set of always-available forms for working with lists: map, filter, flatMap, any, all, find, count, sortBy. Inside the body, it is the current element and index is its position:

p, err := expr.Compile(`filter(users, it.age >= 18 && index < 10)`)

Every iterating form also accepts a three-argument shape that names the element explicitly, which makes nested forms readable and lets you reference an outer element from inside an inner body:

// Named bindings: r is the review, c is the comment.
map(reviews, r, map(r.comments, c, r.author + ": " + c))

flatMap works like map but splices list body results element-by-element into the output, which flattens one level of nesting. sortBy evaluates a key expression per element and returns a stable-sorted copy of the list.

Two more special forms use laziness for control flow instead of iteration: try(value, default) falls back when value errors, and if(cond, then, else) evaluates only the branch the condition selects, so if(n != 0, total/n, 0) can't divide by zero. These forms are always registered (no WithBuiltins needed), but you can shadow any of them by registering a function or env value of the same name.

Pipelines

The pipe operator a | f(x) compiles as f(a, x), so chained transformations read left to right instead of inside out:

p, err := expr.Compile(
    `checks | filter(!it.ok) | map(it.name) | join(", ")`,
    expr.WithBuiltins(),
    expr.WithFunctions(expr.StringFuncs()),
)

The pipe is compile-time sugar over ordinary calls, so it composes with everything above: builtins, your registered functions, and the higher-order forms in both shapes. In Go this token means bitwise or, which expr has always rejected, so the pipe changed the meaning of no existing expression. Design rationale lives in RFC 0001.

What it isn't

expr evaluates a single expression. No statements, no :=, no if/for, no blocks, no function definitions. An expression takes an environment and produces a value. That's the whole shape of it.

If you need mutation or side effects, register a Go function that does the thing. Call it from the expression. Your host program stays in charge of what's allowed, and the expression stays easy to reason about.

Safety

expr is meant to run expressions you don't fully trust. The parser and evaluator both have bounds (MaxSourceLength, MaxEvalDepth) to keep a pathological input from eating your stack, and the language itself gives untrusted code nowhere dangerous to go: no loops, no assignments, no way to define new functions from inside an expression.

How it compares

expr-lang/expr and cel-go are the obvious neighbors. Both are excellent, and both are much larger. expr is deliberately smaller:

Library Non-test Go LOC Parser Notes
deepnoodle/expr ~4k go/parser Go subset, JSON literals, ${...} templates
expr-lang/expr ~30k Custom Own grammar, optimizer, type checker
cel-go ~70.0k Custom (protos) CEL spec, protobuf-native, type system

That's an order of magnitude smaller than expr-lang, nearly two than cel-go. You give up a pile of features you may never need. In exchange: no dependencies, low complexity, and syntax anyone who writes Go already knows.

Performance

Small doesn't mean slow. Throughout this section, deepnoodle/expr is this library and expr-lang/expr (sometimes just "expr-lang") is the separate github.com/expr-lang/expr project. Across a set of canonical expressions borrowed from that project's own bench suite, deepnoodle/expr is competitive with expr-lang/expr and faster than cel-go on nearly every case. The one exception is plain array indexing, where cel-go wins by a few nanoseconds.

Eval benchmark

The same numbers the chart is built from (Apple M1, ns per eval of a pre-compiled expression, lower is better):

Case Expression deepnoodle/expr expr-lang/expr cel-go
predicate (Origin == "MOW" || Country == "RU") && (Value >= 100 || Adults == 1) 83 51 101
len len(arr) (100 elements) 50 20 85
filter filter(Ints, it % 7 == 0) (1000 elements) 57,200 45,800 120,900
filterLen len(filter(Ints, it % 7 == 0)) 57,000 43,200 121,700
arrayIndex arr[50] 41 47 36

expr-lang/expr is a bit faster on almost every case. This is what you'd expect, since it compiles to bytecode and runs on its own VM. deepnoodle/expr walks the AST directly, which is simpler, but it does implement a number of optimizations on top of that. The performance gap is usually tens of nanoseconds per eval, which rarely matters in practice. In exchange, deepnoodle/expr stays dramatically smaller and simpler.

Full benchmarks and the chart generator live in internal/benchcmp/; regenerate the charts with:

cd internal/benchcmp
go test -run=^$ -bench=. -benchtime=300ms ./... | go run ./chart ../../docs/assets

More

  • docs/reference/spec.md is the authoritative language reference.
  • docs/guides/ has deeper guides on registering Go functions, designing an env, sandboxing untrusted expressions, using templates in anger, and higher-order patterns. Start with examples.md for worked multi-line expressions.
  • examples/ has runnable versions of everything above.
  • llms.txt is a condensed reference sized for LLM context windows, if you're pointing an assistant at this library.

License

Apache 2.0. See LICENSE.

Documentation

Overview

Package expr is a zero-dependency expression evaluator built on top of go/parser. It accepts the subset of Go expression syntax useful for conditions, templates, and parameter interpolation: identifiers, selectors, index expressions, arithmetic, comparisons, logical operators, calls to registered functions, and pipelines (`a | f(x)` compiles as `f(a, x)`).

expr is intentionally small and adds no external dependencies.

Evaluating an expression

Compile parses an expression once and returns a *Program that can be run against many inputs. Programs are immutable and safe for concurrent evaluation.

p, err := expr.Compile("upper(user.name)",
    expr.WithBuiltins(),
    expr.WithFunctions(map[string]any{"upper": strings.ToUpper}),
)
v, err := p.Run(ctx, env)

Environments

env may be a map[string]any, a struct, or a pointer to a struct. Identifiers resolve to map keys, struct fields, or bound methods. Callables stored in env are also invocable (see Program.Run).

Templates

NewTemplate pre-compiles a `${...}` interpolator:

t, err := expr.NewTemplate("Hello ${user.name}!", expr.WithBuiltins())
out, err := t.Render(ctx, env)

Index

Constants

View Source
const MaxEvalDepth = 256

MaxEvalDepth bounds the recursion depth of Program.Run. Expressions whose AST nests deeper return ErrEvaluate. 256 is enough for any hand-written expression and keeps the Go stack well under 1 MiB.

View Source
const MaxSourceLength = 64 * 1024

MaxSourceLength is the maximum number of bytes Compile will accept. Longer inputs return ErrCompile without invoking the Go parser, which protects against adversarial nesting depths that could exhaust the parser's own stack.

Variables

View Source
var ErrCompile = errors.New("expr: compile error")

ErrCompile wraps parse failures so callers can match with errors.Is.

View Source
var ErrEvaluate = errors.New("expr: evaluate error")

ErrEvaluate wraps runtime failures so callers can match with errors.Is.

Functions

func Builtins

func Builtins() map[string]any

Builtins returns a fresh copy of the default builtin function set made available to every expression. The returned map is owned by the caller and safe to mutate.

The defaults are chosen to be small, deterministic, side-effect free, and useful for typical condition/template work:

len(v)            rune count for strings; element count for
                  slice/array/map/chan
string(v)         stringified form of v
int(v)            numeric conversion to int64; strings parse strictly
                  as base-10 integers
float(v)          numeric conversion to float64; strings parse strictly
bool(v)           truthiness check (matches IsTruthy)
contains(h, n)    substring for strings, element membership for
                  slices/arrays (using loose numeric equality), or
                  key presence for string-keyed maps
has(m, k)         true if map m has key k; errors if m is not a map
keys(m)           sorted string keys of a map
entries(m)        sorted key-value pairs of a string-keyed map; each
                  element is map[string]any{"key": k, "value": v}
lower(s), upper(s)  case conversion
sprintf(fmt, ...) fmt.Sprintf-style formatting with cycle guards

if(cond, then, else) is not in this map: it is a special form (always available, lazily evaluated) — see higher_order.go. Opt-in extension sets live in MathFuncs, StringFuncs, and CollectionFuncs.

func CollectionFuncs added in v1.1.0

func CollectionFuncs() map[string]any

CollectionFuncs returns the opt-in list helper set. Like Builtins, the returned map is a fresh copy owned by the caller. Register it with WithFunctions.

first(xs), last(xs)  first/last element; nil for empty or nil lists
sum(xs)              numeric sum; int64 when every element is
                     integral, float64 otherwise; empty → 0
slice(xs, i, j)      elements [i, j) of a list, or the rune range
                     of a string; negative indices count from the
                     end and out-of-range bounds clamp
sort(xs)             ascending copy; all numbers (numeric order) or
                     all strings (lexicographic); mixed types error
reverse(xs)          reversed copy; never mutates input

func IsTruthy

func IsTruthy(value any) bool

IsTruthy reports whether a Go value should be treated as truthy in expr conditionals. The rules are:

  • nil: false
  • bool: itself
  • numeric: non-zero is truthy
  • string: non-empty
  • slices, arrays, maps: non-empty is truthy
  • chan, func, interface, pointer: non-nil is truthy
  • anything else: truthy

This is also exposed as the bool() builtin. Note that string content is not inspected; bool("false") is true because the string is non-empty. Callers who need to parse boolean strings should do so explicitly.

func MathFuncs added in v1.1.0

func MathFuncs() map[string]any

MathFuncs returns the opt-in numeric helper set. Like Builtins, the returned map is a fresh copy owned by the caller. Register it with WithFunctions:

expr.Compile(src, expr.WithBuiltins(), expr.WithFunctions(expr.MathFuncs()))

The groups are separate from Builtins so a minimal sandbox stays minimal: hosts opt in to exactly the surface they want.

min(a, ...), max(a, ...)  smallest/largest argument; int64 when every
                          argument is integral, float64 otherwise
abs(n)                    absolute value; int64 in, int64 out
floor(v), ceil(v), round(v)  float64 results; integers pass through

func StringFuncs added in v1.1.0

func StringFuncs() map[string]any

StringFuncs returns the opt-in string helper set. Like Builtins, the returned map is a fresh copy owned by the caller. Register it with WithFunctions.

trim(s)                strings.TrimSpace
split(s, sep)          list of substrings
join(xs, sep)          concatenate a list of strings
replace(s, old, new)   strings.ReplaceAll
startsWith(s, prefix)  strings.HasPrefix
endsWith(s, suffix)    strings.HasSuffix

Types

type Func

type Func func(ctx context.Context, args []any) (any, error)

Func is a native dispatcher signature. Functions registered with this type skip the reflect-based call path entirely: the evaluator invokes them directly with the evaluated argument list and the current context. Use this form for hot builtins or any user function where an extra allocation per call matters.

If the function needs arity or type validation it must do it itself; the dispatcher performs no checks.

type Option

type Option func(*compileConfig)

Option configures a Compile or NewTemplate call. Options are applied in order, so a later WithFunctions overrides an earlier WithBuiltins for any shared name.

func WithBuiltins

func WithBuiltins() Option

WithBuiltins registers the standard builtin function set returned by Builtins. Pair it with WithFunctions to extend or override individual entries; options apply in the order they are passed, so a later WithFunctions wins over an earlier WithBuiltins for any shared name.

func WithEvalBudget added in v1.1.0

func WithEvalBudget(n int) Option

WithEvalBudget bounds the total work a single Run may perform. Each AST node evaluated — including every per-element re-evaluation of a higher-order form's predicate — consumes one unit; when the budget is exhausted, Run fails with an ErrEvaluate-wrapped error.

MaxSourceLength and MaxEvalDepth bound memory and stack, but not CPU: nested higher-order forms multiply, so a ~60-byte expression like map(xs, map(xs, map(xs, it))) over a 10k-element list is 10^12 predicate evaluations. Context deadlines cap wall-clock time but still let one expression burn a core for the full timeout; a budget makes hostile-input behavior deterministic and cheap to reject.

n <= 0 means unlimited (the default). The budget counts evaluator steps, not time spent inside registered functions — bound those separately (see docs/guides/sandboxing.md).

func WithFieldTags added in v0.0.2

func WithFieldTags(names ...string) Option

WithFieldTags is an alias for WithStructTags.

func WithFunctions

func WithFunctions(funcs map[string]any) Option

WithFunctions registers the given functions as callable identifiers in the compiled expression. Entries merge into (and override) whatever is already registered by earlier options.

Functions may take any Go types as arguments; expr converts evaluated values to the declared parameter types at call time. Return signatures of `T`, `(T, error)`, and `()` are supported. Variadic functions are also supported.

Invalid registrations — a nil entry, a value that is not a Go function, or a function with an unsupported signature (more than two return values, or a second return that is not error) — cause Compile to fail with ErrCompile. Surfacing the mistake at load time is the point of the Compile/Run split; before this check the error would hide until the expression first called the bad entry.

func WithStructTags added in v0.0.2

func WithStructTags(names ...string) Option

WithStructTags enables struct field lookup by the named struct tags. Tags are checked in the order provided before falling back to the Go exported field name. Tag options after a comma are ignored, so `json:"name,omitempty"` resolves as `name` and `json:",omitempty"` falls back to the Go field name.

The option is opt-in; without it, struct fields resolve only by Go field name, preserving expr's default behavior. `expr:"-"` hides a field when the expr tag is configured; duplicate resolved names return an ambiguity error at evaluation time.

func WithTemplateDelimiters added in v1.2.0

func WithTemplateDelimiters(open, close string) Option

WithTemplateDelimiters replaces the default `${` / `}` expression delimiters for a NewTemplate call. The opener must end with at least one `{` and the closer must be the matching run of `}`:

tmpl, err := expr.NewTemplate(src, expr.WithTemplateDelimiters("${{", "}}"))

A GitHub-Actions-style `${{ expr }}` opener avoids collisions with shell parameter expansion and JavaScript template literals, so text like `echo ${HOME}` passes through as a literal. The `$$` escape applies only when the opener starts with `$`.

The option applies only to NewTemplate; passing it to Compile fails with ErrCompile.

func WithTemplateFormatter added in v1.2.0

func WithTemplateFormatter(fn func(v any) (string, bool)) Option

WithTemplateFormatter installs a custom value renderer for a NewTemplate call. It runs first for every interpolated result, including nil and strings; returning false falls through to the default rendering chain (nil to empty, strings pass through, composites to JSON, everything else fmt-style).

expr.WithTemplateFormatter(func(v any) (string, bool) {
    if t, ok := v.(time.Time); ok {
        return t.Format(time.Stamp), true
    }
    return "", false
})

The option applies only to NewTemplate; passing it to Compile fails with ErrCompile.

type Program

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

Program is a compiled expression. Programs are immutable and safe for concurrent evaluation across goroutines.

func Compile

func Compile(code string, opts ...Option) (*Program, error)

Compile parses an expression once for repeated evaluation. The returned Program is immutable and safe for concurrent use. Input longer than MaxSourceLength is rejected without calling the parser.

Functions are registered via Options and baked into the returned Program. JSON-style array and object literals ([1, 2, 3], {"k": v}) are always accepted; see docs/reference/spec.md for the exact rules.

func (*Program) Identifiers added in v1.1.0

func (p *Program) Identifiers() []string

Identifiers returns the sorted, de-duplicated set of top-level identifier names the expression references — the names Run will try to resolve through the environment. Hosts can use it to validate an expression against a known env shape at load time, to track dependencies for cache invalidation, or to decide which values are worth computing before a Run.

Excluded from the result:

  • the literals true, false, and nil
  • `it` and `index` where they are bound by an iterating higher-order form (map, filter, flatMap, any, all, find, count, sortBy)
  • the named element binding of a three-arg form (the `o` in `filter(orders, o, o.paid)`), both the binding identifier itself and references to it inside the body
  • names registered via WithFunctions / WithBuiltins, which resolve without the env
  • special-form names (map, filter, try, if, ...) in call position, which are always available

The analysis is static and therefore best-effort in one corner: env entries can shadow registered functions and special forms at Run time, so an excluded name may still be read from the env when a host deliberately shadows it. Every name that can only resolve through the env is always included.

func (*Program) Run

func (p *Program) Run(ctx context.Context, env any) (any, error)

Run evaluates the program against env. env may be a map[string]any, a struct, or a pointer to a struct — identifier lookups resolve to map keys, struct fields, or bound methods (in that order of preference). Identifiers not found in env are then looked up against the functions registered via WithFunctions / WithBuiltins. The literals true, false, and nil are recognized directly. Any unsupported syntax node (slice expressions, type assertions, function literals, channel operations, etc.) returns an error wrapping ErrEvaluate.

Env values that are themselves Go functions are callable from the expression: `f(x)` first looks up `f` in env, and if it finds a function value it is invoked through the same reflect-based dispatch used for WithFunctions-registered functions. Prefer registering functions via Options when possible — those go through a faster prepared path and participate in "did you mean…" diagnostics — but putting callables in env is a useful escape hatch for per-request closures or hosts that want to rebind helpers on every Run.

expr checks ctx.Err() at the top of every AST node, so any pure-expression evaluation exits within one node of cancellation. Registered functions whose first parameter is context.Context receive ctx automatically; well-behaved callees can then cancel their own work. expr does not forcibly terminate user code that ignores ctx — Go provides no mechanism to kill a goroutine, and recovering from a blocked callee would leak it. Passing a nil ctx falls back to context.Background.

func (*Program) Source

func (p *Program) Source() string

Source returns the original expression text.

type Template

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

Template is a pre-compiled `${...}` string interpolator. Each expression inside the template is compiled once by NewTemplate and re-evaluated per call.

Parsing uses go/scanner to find the closing `}` of each expression, so expression bodies may contain braces inside string, rune, and comment tokens as well as nested composite literals like `map[string]any{"k": 1}` without tripping the template parser.

Escaping: `$$` is always rewritten to a literal `$`. `$${name}` therefore emits the literal text `${name}`. A bare `$` that is not followed by `$` or `{` is emitted verbatim, so `$5` or `$foo` pass through unchanged.

The `${` / `}` delimiters can be replaced per template with WithTemplateDelimiters; the `$$` escape applies only when the configured opener starts with `$`.

func NewTemplate

func NewTemplate(raw string, opts ...Option) (*Template, error)

NewTemplate parses raw and pre-compiles every `${...}` expression with the given Options. Strings without any `${...}` are accepted and become constant templates that return raw unchanged from Template.Render.

func (*Template) Render

func (t *Template) Render(ctx context.Context, env any) (string, error)

Render evaluates each `${...}` expression against env and concatenates the results with the surrounding literal text. env follows the same rules as Program.Run: it may be a map[string]any, a struct, or a pointer to a struct. Templates with no expressions return the raw source unchanged without invoking any script.

Value rendering rules for each `${...}` result (a formatter installed with WithTemplateFormatter runs first and can override any of them):

  • nil renders as the empty string. Optional fields that resolve to nil silently produce no output rather than the literal "<nil>". This matches the convention used by Jinja, Liquid, Handlebars, and most text-oriented interpolators, and means callers do not need a null-coalescing operator for the common case of optional values.
  • string values pass through unchanged.
  • maps, slices, arrays, and structs render as compact JSON with HTML escaping disabled, so `${config}` interpolates as `{"retries":3}` rather than Go's map syntax. A composite that marshals to a JSON string (time.Time, custom marshalers) renders as the string itself, unquoted. Values JSON cannot represent (cycles, channels, funcs) fall back to the fmt-style formatting used for scalars.
  • Everything else is formatted with fmt.Sprintf("%v", v).

The nil-to-empty rule means a template cannot distinguish "value was nil" from "value was the empty string" in its output. Callers that need that distinction should wrap the expression in something that returns a sentinel.

func (*Template) Segments added in v1.2.0

func (t *Template) Segments() []TemplateSegment

Segments returns the parsed segments of the template in source order: literal runs and compiled expressions, each carrying its offset and line:column position in the raw source. Hosts use it for syntax highlighting, per-expression variable extraction (via TemplateSegment.Program's Identifiers method), and live validation, without re-implementing the template scanner.

func (*Template) Source

func (t *Template) Source() string

Source returns the unparsed template source.

type TemplateSegment added in v1.2.0

type TemplateSegment struct {
	Literal string
	Source  string
	Offset  int
	Line    int
	Column  int
	// Program is the compiled expression for expression segments,
	// nil for literal segments. Hosts can call Identifiers() on it
	// for per-segment variable extraction, editor hints, or live
	// validation.
	Program *Program
}

TemplateSegment is the public projection of one parsed template segment, exposed by Template.Segments. A segment is either a literal run of text (Literal non-empty, Source empty) or an interpolated expression (Source holds the expression body).

Offset is the byte offset of the segment's start in the raw template: the first byte of the literal text, or the first byte of the opening delimiter for expression segments. Line and Column are the 1-based line and byte-based column of that offset. For literal segments containing `$$` escapes, Literal holds the decoded text, which may be shorter than the raw source it spans.

Directories

Path Synopsis
cmd
expr command
examples
basic command
compile_once command
env_shapes command
Env shapes: one expression read against a struct, a map, and a pointer-to-struct wrapped view.
Env shapes: one expression read against a struct, a map, and a pointer-to-struct wrapped view.
funcs command
higher_order command
Higher-order builtins: map, filter, any, all, find, count.
Higher-order builtins: map, filter, any, all, find, count.
higher_order_patterns command
Higher-order patterns: validation bag, summary object, filter+map projection, named bindings, flatMap, sortBy, and entries.
Higher-order patterns: validation bag, summary object, filter+map projection, named bindings, flatMap, sortBy, and entries.
register_functions command
Registering Go functions with WithFunctions.
Registering Go functions with WithFunctions.
sandboxing command
Sandboxing: compile an untrusted policy expression, run it with a deadline, and catch both eval errors and cancellation.
Sandboxing: compile an untrusted policy expression, run it with a deadline, and catch both eval errors and cancellation.
structs command
template command
templates_in_anger command
Templates in anger: compile once, render many, with a registered `join` helper so variable-length lists render as human-readable text instead of JSON, and a formatter that overrides nil rendering.
Templates in anger: compile once, render many, with a registered `join` helper so variable-length lists render as human-readable text instead of JSON, and a formatter that overrides nil rendering.
internal
jsonlit
Package jsonlit rewrites JSON-style array and object literals into the Go composite-literal syntax that go/parser.ParseExpr accepts.
Package jsonlit rewrites JSON-style array and object literals into the Go composite-literal syntax that go/parser.ParseExpr accepts.
optaccess
Package optaccess rewrites the optional-access operators `?.` and `?[` into calls on internal sentinel functions that the evaluator dispatches as special forms.
Package optaccess rewrites the optional-access operators `?.` and `?[` into calls on internal sentinel functions that the evaluator dispatches as special forms.
require
Package require provides a minimal subset of the testify/require API so that tests in this module can run without a third-party dependency.
Package require provides a minimal subset of the testify/require API so that tests in this module can run without a third-party dependency.

Jump to

Keyboard shortcuts

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