UWS core expression reference
github.com/OpenUdon/uws/expressions exposes the existing core grammar from
UWS 1.12 sections 5.6–5.7.
It adds no expression language feature and performs no source/provider I/O.
Published specification/schema/archive bytes remain unchanged.
Parse(text, Context) produces an immutable expression with source/operator,
optional source operand, or JSON scalar literal accessors. JSON-number lexemes
remain json.Number; parsing never converts them to float64. Errors expose
only stable syntax/version/context/limit categories, not literal values.
Context names the document version, field kind and whether a structural loop
is in scope. Empty version selects the current 1.12 grammar, independently of
document admission. Version syntax is canonical major-1 semver; the caller
still admits only its supported published document versions. Response-body
dot paths and loop-only $batchIndex require 1.11+. Bare numeric text is allowed
only for non-await wait and batchSize from 1.11 onward. Those fields accept a
source or number, not a comparison; await.wait uses predicate context.
The parser accepts the nine published expression source families, dot paths, response-body JSON Pointer fragments and a single binary comparison with one ASCII space around its operator. Comparison operands may be another source or a JSON scalar string/number/boolean/null. Parentheses, boolean connectives, arithmetic and function calls remain implementation-profile extensions. Runtime-function templates and browser-profile strings are not core-expression fields and must never be rewritten or scanned as such by a consumer.
Pointer checks the canonical fragment grammar, then percent-decodes before
splitting tokens and applies only ~0/~1. An initial slash is required for a
nonempty canonical pointer fragment; percent-encoded root delimiters are not
a canonical grammar form. Pointer evaluation will enforce canonical array
indexes; dot-walk numeric segments retain the published dot-path behavior.
Source and expression input is bounded to the existing 64 KiB mock ceiling. Parsing does not resolve source values, grant execution or establish source shape correctness. Evaluation and optional document portability checking are separate C08 rows; ordinary validation and the existing mock remain unchanged at the parser milestone boundary.
Scoped reference evaluation
NewEvaluator(document) uses an admitted immutable caller snapshot of the
UWS model and Evaluator.Evaluate(ctx, text, field) resolves only in-memory
orchestrator state. It never dispatches a leaf, fetches a source or discovers
credentials. Current workflow records are frame-local; completed steps resolve
from the nearest matching iteration then its enclosing iterations. Qualified
child/foreign invocation records are excluded. Current responses must belong
to the current invocation. Component variables remain visible when unshadowed;
top-level declarations win on overlapping names.
Input indirections retain the existing nesting ceiling of 32. Numeric comparisons use exact rationals with the existing 256-byte / ±10000 exponent resource limits. Nonfinite, unsupported or invalid JSON operands refuse; missing path children propagate null. JSON Pointer array indexes are canonical, while ordinary dot-walk indexes preserve their existing behavior. ResolveItems returns a source-ordered independent slice. Context cancellation is checked before evaluation and recursive expression resolution.
Callers must preserve numbers before constructing the snapshot. The legacy
UWS model's custom JSON unmarshalling historically converts some any fields
to float64; setting UseNumber on an outer decoder does not override a custom
UnmarshalJSON method. UseNumber on the source-value projection, then populate
Variables/Components/input/result values with json.Number, preserves exact
integers and numeric lexemes. The shared fixture loader demonstrates this
boundary without changing the ordinary model decoder's compatibility.
The separately versioned corpus docs/examples/expressions/v1/cases.json
contains 18 source/version/context/visibility/number vectors and is consumed by
the reference tests. C08.4 pins it as a conformance supplement; it does not
edit any published schema or earlier executable contract.
Optional strict portability and mock adoption
CheckPortability(document) returns deterministic code/path diagnostics for
core expression fields only. Ordinary Document.Validate is unchanged. Core
controls, outputs/structural-result values, simple criteria, standard source-bound request values and step input
bindings use the parser; regex/JSONPath/XPath queries, extensions, opaque bodies,
trigger options, function templates and native browser profile placeholders
remain owned by their profiles. Source-bound browser request.body values use
the same core reference grammar and invocation scopes as other source payloads;
browser path/query/header/cookie fields are not interpreted. Authentication and
registration credential/session/input declarations remain opaque. Legacy expr
wrappers receive an explicit diagnostic. Traversal
is depth bounded; diagnostics are capped at 128 and carry no expression values.
Root x-* request extensions remain profile-owned; identically named keys inside
ordinary body/query payloads are still checked as data bindings.
Known loop invocation contexts are tracked so a reusable operation's batch
reference is checked in its actual call scope, not merely its declaration.
Trigger-route workflows and direct top-level step routes contribute their
actual entry contexts. Direct steps bypass the enclosing workflow loop body.
Explicit goto targets also contribute fresh root contexts.
The strict checker also diagnoses $item/$index outside known iterations;
forEach creates item/index context after dependencies, when, non-await wait
and its collection expression are checked in the incoming context,
while its separate batch-index behavior remains unchanged. Ordinary Parse
continues accepting the declared grammar independently of availability.
The mock now delegates source lookup, comparison and item resolution to the shared evaluator. Its historical generic numeric-evaluation and encoded-root pointer extensions remain in a small explicit compatibility adapter. Quoted comparison literals are never rewritten. The strict checker diagnoses those forms in their real core field context. Unshadowed component variables now follow the existing published precedence rule rather than being discarded when unrelated top-level variables exist. No provider behavior, schema, ordinary-validator rule or published-version byte changes in this adoption.