Class: PackratParser::Rule
- Defined in:
- lib/packrat_parser/parser.rb
Overview
A lazy, memoizing reference to a named grammar rule.
Rule methods on a PackratParser subclass return a Rule instead of building
their combinator immediately. This is essential: a comprehension evaluates
its generator receivers eagerly (to call flat_map/map on them), so a
self-referential rule like additive would recurse forever at build time if
the body ran on every reference. Returning a lazy Rule breaks that cycle.
Memoizing the result per (rule, pos) gives the packrat property: each rule is evaluated at most once per input position, so parsing stays linear.
The combinator graph is built once per rule and cached on the owner. This relies on the comprehension's loop variables being block-local: each closure built from the rule body owns its loop-variable bindings, so reusing one cached closure across recursive activations (e.g. additive calling additive) is safe. (An earlier fork leaked the loop variables into the rule-method scope, which forced a rebuild per entry so activations would not clobber each other's bindings; that workaround is no longer needed now that the comprehension scopes them.)
Instance Method Summary collapse
- #call(input, pos) ⇒ Object
-
#initialize(owner, name, body) ⇒ Rule
constructor
A new instance of Rule.
-
#parse(input) ⇒ Object
Parse
inputstarting from this rule, e.g.
Methods inherited from Parser
#*, #<<, #>>, #filter, #flat_map, #map, #opt, #rep, #rep1, #|
Constructor Details
#initialize(owner, name, body) ⇒ Rule
Returns a new instance of Rule.
186 187 188 189 190 |
# File 'lib/packrat_parser/parser.rb', line 186 def initialize(owner, name, body) @owner = owner @name = name @body = body end |
Instance Method Details
#call(input, pos) ⇒ Object
192 193 194 195 196 197 198 199 200 |
# File 'lib/packrat_parser/parser.rb', line 192 def call(input, pos) # Two-level memo table (rule -> pos -> result). Keying on a single [name, # pos] Array would allocate one Array per rule invocation and hash it; # nesting keeps the per-call key a bare Integer. memo = (@owner.__memo[@name] ||= {}) return memo[pos] if memo.key?(pos) combinator = (@owner.__built[@name] ||= @body.bind(@owner).call) memo[pos] = combinator.call(input, pos) end |
#parse(input) ⇒ Object
Parse input starting from this rule, e.g. parser.number.parse("123").
Delegates to the owner so memo reset, whitespace handling, and the
full-consumption check are applied exactly as for the start symbol.
205 206 207 |
# File 'lib/packrat_parser/parser.rb', line 205 def parse(input) @owner.parse(input, @name) end |