G050: One Generated Form per Option Set
Resource checks can cost generated parsing even when no budget control is active. A generated Go parser therefore has one form of its functions, chosen at generation time by the options.
Mechanism and Invariants
The generator prints each execution function (rule bodies and other functions taking the parser as their first parameter) once, from Flow IR, in the form the options select:
- Without
-budget-controlsand without-documentthe parser holds the ordinary form only: the 16 runtime helpers that have a hand-writtenOrdinaryform (NewNode,NewTerminal,NewBranch,NewMatch,SetCapture,Expect,Peek,MatchLiteral,MatchLiteralFold,MatchAny,AtEnd,AtLineStart,AtEndBatch,AtLineStartBatch,AtLineEnd,SpendRead), no budget charges, no control reads in conditions and class tests, and a compact peek. A parse that passes a control option fails. - With
-budget-controlsthe parser holds the controlled form only: budget charges, the general runtime helpers and class tests that consult the control. A parse that sets no limit and no cancelable context has no control and runs the same code: the inline charges (Spend,Control.Take) accept a nil control and do nothing, the class tests of large classes take the compact path, and the helpers test the control themselves. The terms of an action carry no charge, and neither do the generated constructors of typed values: only repetition is charged, by the runtime's loops over the elements of a list (map,filter,foldl,foldr), one step per element. - With
-documentthe in-place reads of literals and characters carry the guardp.Document == nil, so that a Document parse reaches the primitives that record what a rule examined. Without-budget-controlsthat form has no charges and no control reads.
There is no second body, no run-time choice between forms, and no splice. Metered paths keep their checks, cancellation and budgets. Budget accounting is a resource contract, not an optimization in itself. A user who needs both forms generates two parsers.
Scope and State
Applied by default in generated Go Node/recognition and typed Go output. Generated TypeScript chooses its form with the same option but prints its own charges (see G210). Closure and VM budget-control implementations do not use this Go mechanism.
Evidence and Implementation
A parser generated with -budget-controls has one controlled form of its functions. The parser.go of
the same parser generated without the option is 0.50 to 0.85 of its size (every grammar of parsers/,
Node-only, with -types, with -types -recognize or with -types -typed-only; larger grammars save
more, and duckdb with -typed-only is the smallest at 0.50). Against a parser generated without -budget-controls, a parse of the controlled form that passes no
option takes 1.08 to 1.13 times as long (Parse) and 1.08 to 1.17 times (ParseAST) on the JSON, XML,
YAML and CSV inputs, and one that passes a limit takes 1.25 to 1.33 times (Parse) and 1.26 to 1.45
times (ParseAST). The aim of 1.15 times for a parse with a limit is not met; with every charge
made free JSON still takes 1.15 times, so the rest is the shape of the controlled bodies. The
table is in 103. Records
096 and
121 hold earlier measurements of the ordinary body of a
function and of the inlined runtime helpers.
Source owners: Flow printer, helper names, metered runtime helpers.
The comparison policy distinguishes same-source controls, historical changes and full checkpoints.