PEGO

60. Direct Rules in the Typed Runtime

  • The typed runtime ran every rule as the Node runtime does: a method per expression, captures in a frame with a trail that marks and resets undo, and the action through useCtx and tctx.result, reading the frame. A CPU profile of JSON ParseAST spread the time over those methods, setCapture and reset, newFrame and freeFrame, and the action plumbing (about 10%). A rule without a cut or #recover that does not contain an Operators Rule nor a left-recursion leader is now compiled into the method that calls it (design 012, "Direct rules"; internal/gen/gogen/flowrule.go): failures jump to labels, captures are Go variables that backtracking points save and restore, and the action is a Go expression over them, without the checks of tctx.result when it makes a struct with the rule's range. Every rule of the JSON, XML and outline grammars is direct; in the calculators, all but the Operators Rule expression and the three left-recursive rules.
  • The parity test of ParseAST gained grammars for the new code paths (testdata/typed/direct_*.pego: captures and an environment restored by choices, optionals and repetition elements, elements with captures of their own that become records, $n, built-ins, errors in actions, recovered errors undone and replayed from the memo, @error, anchors, bounded repetitions and alternatives that can never be tried), and now runs in both position units. Leaving out any one restoration (captures, environment, recovered errors), the clearing of element captures at each iteration, or sharing one character variable among choices makes it fail.
  • Effect (min of 6 interleaved runs, Apple M3 Max): ParseAST JSON 6.44 → 3.86 ms (−40%), XML 6.96 → 4.34 ms (−38%), Arith_LeftRec 14.3 → 10.3 ms (−28%), Outline 3.32 → 2.46 ms (−26%), Arith_OperatorsRule 6.40 → 6.03 ms (−6%); XML 4.3 → 3.2 MB (no frames), the others unchanged in bytes. JSON ParseAST now takes the time of Recognize (3.9 ms) and about half that of Parse (7.2 ms). Generated Parse and Recognize are unchanged.
  • The original implementation kept cut-bearing rules and left-recursion leaders on general dispatch. 78, 79, 80, 81, 82, 83 and 84 extend eligible ordinary rules with local cuts and complete capture-name layouts, including dead captures. Other paths cover LR bodies under runtime-owned growth, Operators Rule-line matchers, framed cut-bearing LR/Operators Rule bodies and recovery. Record 84 also covers owned/projected repetitions. Recovery layouts the structural emitter cannot match remain general.