package eval

Import Path
	github.com/freeeve/gochickpeas/gql/internal/eval (on go.dev)

Dependency Relation
	imports 11 packages, and imported by 4 packages

Involved Source Files Binary operator evaluation shared by the interpreter and the compiled path: string predicates (STARTS WITH / ENDS WITH / CONTAINS) and the arithmetic operators (+, -, *, /) over numbers, strings, lists, and temporals/durations, with checked integer arithmetic (overflow and division by zero yield Null -- eval has no per-row error channel). Split from funcs.go, which holds the scalar built-in functions. Package eval is the interpreted expression evaluator: the portable path that evaluates bound AST expressions per row against the graph seam. Port of the Rust engine's eval.rs; the columnar compiled path (M16) mirrors it and both must produce identical results. The core interpreter: evaluate a bound expression against a slot-indexed row. Aggregate functions are handled by the executor, never here (they evaluate to Null if reached). The scalar-function registry: the FuncOp enumeration and the name-resolution helpers that map a (case-insensitive) function name to a resolved op. The per-row evaluation kernels live in funcs.go; carrying a resolved FuncOp lets the compiled path skip name dispatch. The arithmetic/string-predicate scalar-function kernels, shared verbatim by the interpreter and the compiled path so both produce identical results. No scalar function touches the graph except startNode/endNode, which resolve in evalScalarFunc. The FuncOp enumeration and name resolution live in funcop.go. Scalar built-in application helpers: positional argument access, the range/substring/toString value builders, and the datetime/duration constructors with their checked integer arithmetic. Split from funcs.go, which holds the function registry (ResolveFuncOp) and the ApplyFunc dispatch. List-scoped expression forms: the all/any/none/single quantifiers, reduce folds, and list comprehensions. Each binds its iteration variable in a slot appended past the current row. Correlated subquery matching: EXISTS { MATCH ... }, COUNT { MATCH ... }, and pattern comprehensions, all sharing one anchored DFS over the pattern. The DFS's evaluation-invariant setup (anchor-reversal, level slots, the extended scope, compiled node and rel matchers) is shaped once per pattern and cached on the Ctx with its evaluation scratch, so the per-row cost is a row refresh instead of map/slice/matcher rebuilds. The correlated-subquery DFS walk: level-by-level candidate generation over a shaped pattern (anchored endpoints, lazily compiled matchers, the quantified-hop reach) -- the evaluation half of subquery.go's shape/count entry points. Temporal arithmetic for the runtime value type: epoch-millis-backed date/datetime values and durations, with civil-calendar decomposition. Query-side only -- stored i64-millis columns coerce against these on compare. Pure integer math (port of the Rust temporal.rs). Temporal component access and duration arithmetic: reading a named component off an epoch-millis instant or a (months, days, millis) duration, applying a duration to an instant (checked calendar add), and the Euclidean integer-math helpers they share. Split from temporal.go, which holds the ISO parsing and formatting.
Package-Level Type Names (total 3)
/* sort by: | */
Ctx carries the evaluation environment: the graph and the query's parameter values. It replaces the Rust engine's thread-local ParamScope/NamedParamScope -- explicit and threaded through every evaluation, so nested subquery evaluation and reuse across goroutines need no scope choreography. CompileWhere, when set, compiles a subquery WHERE against the walk's inner slot map and returns a per-row evaluator; nil (or ForceInterp) keeps the interpreted path. The executor layer installs it -- it can reach the compiler without an import cycle -- so a subquery's filter runs the same compiled machinery as a stage filter instead of re-interpreting the AST (and re-evaluating its constant subexpressions) once per candidate. DecorBuilds int DecorTables shares decorrelated COUNT{} side tables across sibling compiled subqueries within one execution, keyed by the subquery's canonical identity (endpoint variable names substituted, so the same subquery written against two outer variables collides) and the resolved anchor node. Params are fixed per Ctx, so identities that embed parameter slots stay valid for the Ctx's lifetime. DecorBuilds counts table builds across the execution -- tests assert a shared identity builds once, not once per sibling. ForceInterp pins every expression to the interpreted path even on a native-capable graph -- the differential test hook behind the dual-path harness. G graph.Graph MatchEpoch is bumped once per match-call by the executor, so a loop-invariant carried IN list's membership cache rebuilds per call and is reused across that call's candidates (replaces the Rust thread-local epoch). Named are the explicit $name parameter values. Params are the auto-lifted parameter values, indexed by slot. NamedValue resolves an explicit $name parameter; an unsupplied name is Null (a missing parameter reads as null rather than an error). ParamValue resolves an auto-lifted slot; out of range is Null. func Eval(ctx *Ctx, expr ast.Expr, row []value.Value, slots map[string]int) value.Value func LitValue(ctx *Ctx, l ast.Literal) value.Value func SubqueryCount(ctx *Ctx, pattern *ast.Pattern, where ast.Expr, outerRow []value.Value, outerSlots map[string]int, stopAtFirst bool) int func SubqueryGroupCount(ctx *Ctx, pattern *ast.Pattern, where ast.Expr, outerRow []value.Value, outerSlots map[string]int, anchorVar, groupVar string) map[graph.NodeID]int func github.com/freeeve/gochickpeas/gql/internal/compile.ConstValue(ctx *Ctx, e ast.Expr, slots map[string]int, g *chickpeas.Snapshot) (value.Value, bool) func github.com/freeeve/gochickpeas/gql/internal/compile.HoistConstIn(ctx *Ctx, c *compile.Compiled, isConst func(int) bool, sample []value.Value, slots map[string]int) *compile.Compiled func github.com/freeeve/gochickpeas/gql/internal/compile.New(ctx *Ctx, e ast.Expr, slots map[string]int, g *chickpeas.Snapshot) *compile.Compiled func github.com/freeeve/gochickpeas/gql/internal/compile.(*Compiled).Eval(ctx *Ctx, row []value.Value, slots map[string]int) value.Value func github.com/freeeve/gochickpeas/gql/internal/exec.Execute(ctx *Ctx, p *plan.Plan) ([][]value.Value, error) func github.com/freeeve/gochickpeas/gql/internal/exec.ExecuteProfiled(ctx *Ctx, p *plan.Plan) *explain.Profile func github.com/freeeve/gochickpeas/gql/internal/exec.RowEval.Eval(ctx *Ctx, row []value.Value, slots map[string]int) value.Value
DecorTableKey identifies one shared decorrelated side table. Anchor uint32 Canon string
FuncOp is a resolved scalar function; the compiled path carries this instead of the name string so per-row evaluation skips name dispatch. Purity contract: every FuncOp must be a pure function of its argument values -- deterministic, graph-less, no ambient state (no randomness, no clock). Constant folding, IN-list membership baking, carried-list hoisting, and pushdown placement all evaluate a resolved function fewer times than once-per-row on the strength of this contract, and none of them re-checks it. A volatile function (rand, a statement clock) must NOT be registered here: left unresolved, the compiler's interpreter fallback keeps it out of every one of those paths. The determinism roll call in funcop_test.go enforces the contract behaviorally for each registered op. func ResolveFuncOp(name string) (FuncOp, bool) func ApplyFunc(op FuncOp, argv []value.Value) value.Value const FuncAbs const FuncAcos const FuncAsin const FuncAtan const FuncCardinality const FuncCeil const FuncCharLength const FuncCoalesce const FuncCos const FuncDate const FuncDateTime const FuncDegrees const FuncDuration const FuncElementID const FuncExp const FuncFloor const FuncHead const FuncID const FuncIsNormalized const FuncLast const FuncLeft const FuncLength const FuncLn const FuncLocalDateTime const FuncLog10 const FuncLower const FuncLTrim const FuncMod const FuncNodes const FuncNormalize const FuncNullIf const FuncPower const FuncRadians const FuncRange const FuncRels const FuncRight const FuncRound const FuncRTrim const FuncSign const FuncSin const FuncSize const FuncSqrt const FuncSubstring const FuncTail const FuncTan const FuncToBoolean const FuncToFloat const FuncToInteger const FuncToString const FuncTrim const FuncUpper
Package-Level Functions (total 20)
ApplyDuration applies a duration (months, days, millis) to an epoch-millis temporal, sign = +1 (add) or -1 (subtract). Months are a calendar add (day clamped to the target month length, e.g. Jan 31 + 1 month = Feb 28); days and millis are absolute. Every step is checked: ok=false on overflow, so the caller yields Null rather than a wrapped nonsense instant. Note the boundary -- shifting a recent instant by i64-scale milliseconds to a very negative-but-representable value is exact and stays ok; only a genuine overflow declines.
ApplyFunc applies a resolved scalar function to its evaluated arguments.
Arith is +, -, *, /: string + concatenates; temporal +/- duration is a calendar add; Int op Int is checked (overflow and division by zero yield Null -- eval has no per-row error channel, so Null is the honest result); mixed numerics coerce through float64, where division by zero is also Null. Shared with the compiled path.
CivilFromDays is the inverse: civil (year, month, day) from days since 1970-01-01.
CmpBool maps a three-valued comparison through a predicate on the ordering; incomparable is Null. Shared with the compiled path.
Component is a temporal component accessor (.year/.month/.day/.hour/... /.epochMillis) on an epoch-millis temporal; ok is false for an unknown component (the caller yields Null).
Concat is the || operator: string or list concatenation only -- unlike +, it never adds numbers; any other operand pair is Null.
DaysFromCivil is days since 1970-01-01 for a civil (year, month, day) -- Howard Hinnant's algorithm.
DaysInMonth is the day count of (year, month), accounting for leap years (used to clamp a calendar + duration).
DurationComponent is a duration component accessor on a (months, days, millis) duration, with the groups independent (Neo4j's convention): the months group answers years/quarters/months (months is the group TOTAL, so duration({years: 1}).months = 12), the days group weeks/days, and the millis group hours/minutes/seconds/milliseconds (each the group total at that unit, so PT2H reads .minutes = 120). ok=false for an unknown component.
Eval evaluates expr against row (slot-indexed bindings).
IsKnownScalarFunc reports whether name is a scalar function the engine evaluates (case-insensitive): a ResolveFuncOp op or the graph-resolved startNode/endNode. The binder consults this to reject unknown function names at plan time rather than silently evaluating them to null.
ISOString renders an epoch-millis temporal as ISO-8601, the inverse of ParseISO: 'YYYY-MM-DD' for a Date; 'YYYY-MM-DDTHH:MM:SS' for the datetime kinds, with '.mmm' appended only when the sub-second remainder is non-zero. This is the single temporal string formatter -- any other surface that stringifies a temporal should delegate here so the forms cannot drift.
LitValue converts a parsed literal into a runtime value, resolving parameters from the context.
ParseISO parses an ISO-ish temporal string into epoch milliseconds (UTC): YYYY-MM-DD, optionally THH:MM[:SS[.mmm]] and a trailing Z/offset (the offset is accepted but treated as UTC -- no real zone handling). ok is false on a malformed string.
ParseISODuration parses an ISO-8601 duration string (PnYnMnWnD[TnHnMnS], e.g. 'P100D', 'PT12H', 'P1Y2M3DT4H5M6S') into calendar components: months, days, and sub-day milliseconds. A leading '-'/'+' sign negates the whole duration, and the seconds field accepts a millisecond fraction ('PT1.5S'); ok is false on a malformed string or a component that overflows.
ResolveFuncOp resolves a scalar-function name (case-insensitive); ok is false for an unknown function (which yields null at evaluation).
StrPred is a string predicate (STARTS WITH / ENDS WITH / CONTAINS): Bool when both operands are strings, Null otherwise. Shared with the compiled path.
SubqueryCount counts matches of a correlated subquery against the current outer row, anchoring pattern nodes that name an outer variable to their bound value. stopAtFirst short-circuits for EXISTS. A single fixed-length pattern (quantifiers are treated as one hop).
SubqueryGroupCount evaluates a correlated subquery ONCE with the group variable left free, returning the match count bucketed by the node the group variable binds to. It is the decorrelated form of SubqueryCount: where SubqueryCount re-walks the pattern once per outer entity bound to the group variable, a single SubqueryGroupCount call -- anchored on the SHARED endpoint (anchorVar, still bound from the outer row) -- yields every entity's count in one pass over the anchored set, so the per-entity cost collapses from a walk to an O(1) map read. anchorVar must name an outer variable bound in outerRow; groupVar names the pattern endpoint to bucket by, left UNBOUND here even when it is itself an outer variable (its outer binding is ignored -- enumeration rebinds it per match). The returned map is keyed only by node id, so it is independent of what the outer query calls the group variable: two subqueries with the same pattern, WHERE, and anchor share one table (see task 084 / rustychickpeas 091). The result for a given entity equals SubqueryCount with that entity bound -- the invariant the decor parity test asserts.
Package-Level Constants (total 52)
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
Resolved scalar functions.
MSPerDay is the milliseconds in a civil day.