github RecoLabs/gnata v0.6.0

3 hours ago

Changes

Follow-ups to #35, consolidated in #53. Every expectation is checked against jsonata-js 2.2.2.

Breaking changes

  • Regex maps from custom functions: a regex is a function inside an expression. A custom function receives a regex argument as a {"pattern", "flags"} map, and only that same map turns back into a regex when returned; a map of that shape the function builds, copies or reads from input is a plain object. To migrate, return the regex value you received (README, Regex Arguments).
  • Lambda context: a lambda's body evaluates against the context it was defined in, as in jsonata-js. ($f := function($x){ name & $x }; items.$f("!")) reads the root's name, not each item's; pass the item as an argument instead (README, Lambda Context).
  • functions.EvalFn lost its focus parameter, and the standard library applies function values with a null context (README, functions.EvalFn).
  • Argument validation: every builtin except $contains validates argument counts and types against its jsonata-js signature ($sort([1], 1), $map([1], 2) → T0410; $sum(["a"]) → T0412).
  • Custom function focus is nil when the function is passed as an argument or applied by a bare x ~> $f.
  • WithSequence counts input data, as jsonata-js's sequence option does (D2015), with jsonata-js's last-step exemption.
  • Nesting limits: S0218 past 10,000 parse levels; U1001 past 1,500 nested calls through partial applications, compositions, function arguments, transforms and builtin callbacks.

Fixes — jsonata-js conformance

  • Functions: context arguments, signature validation, tail calls, null-context function arguments, HOF callback arities, partial applications (no context, declared parameters, unwrapped array arguments), $clone, $distinct of variables and parameters, $power with an undefined exponent (D3061).
  • Numbers: $string and & lay out numbers as jsonata-js does, including numbers read from raw JSON (1.50 → "1.5").
  • Regex: a regex is a function value; a called regex searches from its offset (exact also with left context) and converts its argument as JavaScript's String() does; lexing of / and regex literals follows jsonata-js.
  • Dates: $toMillis follows jsonata-js's picture regex with a linear step budget; $fromMillis offset pictures read the parsed presentation.
  • Paths: constructed arrays, [], builtin result sequences, % resolved at parse time, groups on whole paths, stages after #/@ bindings, ** and *.
  • Fast paths: EvalBytes, EvalMap and StreamEvaluator agree with Eval (numeric field names, groups, bindings, number comparisons beyond 2^53); StreamEvaluator.EvalMap of an empty map is {}.

Known differences

  • Large integers: an integer literal from raw JSON with magnitude above 2^53 keeps its digits in $string and & (12345678901234567890 stays as is; jsonata-js prints 12345678901234567000). Arithmetic rounds and comparisons use float64. See README row 36.
  • The README table (rows 1–36) documents every deliberate difference.

Robustness and performance

  • No walk over data recurses (tested at 200,000 levels); function-value nesting is bounded; long chains compile and evaluate in linear time.
  • Parsed $toMillis pictures are cached (about 40% faster than v0.5.3 on a picture). Overall evaluation is about 12% slower than v0.5.3 (geometric mean of the benchmarks), the cost of the parity work; allocations are about 5% lower. Details in #53.

Tests

  • Coverage 93.8%; parity cases run through Eval, EvalBytes, EvalMap and StreamEvaluator.

  • npm: gnata-js@0.6.0

Don't miss a new gnata release

NewReleases is sending notifications on new releases.