Class: PackratParser::Rule

Inherits:
Parser
  • Object
show all
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 rebuilt on every (memo-missed) entry rather than cached, and that is deliberate. The for ... then comprehension's loop variables currently leak into the enclosing rule-method scope instead of being block-local, so a single built closure shares those slots. If the same closure were reused for a recursive activation (e.g. additive calling additive), the inner activation would clobber the outer's leaked variables. Building fresh gives each activation its own scope. The rebuild is bounded: result memoization means a build happens at most once per (rule, pos).

NOTE: this rebuild is a workaround for the loop-variable leak in the fork's comprehension (parse.y: new_for_comp_gen leaves the loop var in the surrounding scope, like the legacy for). If the comprehension is changed to make loop variables block-local, this clobbering goes away and built can be cached once per (owner, name) again -- e.g. @owner.__built[@name] ||= ....

Instance Method Summary collapse

Methods inherited from Parser

#+, #<<, #>>, #filter, #flat_map, #map, #|

Constructor Details

#initialize(owner, name, body) ⇒ Rule

Returns a new instance of Rule.



128
129
130
131
132
# File 'lib/packrat_parser/parser.rb', line 128

def initialize(owner, name, body)
  @owner = owner
  @name = name
  @body = body
end

Instance Method Details

#call(input, pos) ⇒ Object



134
135
136
137
138
139
140
# File 'lib/packrat_parser/parser.rb', line 134

def call(input, pos)
  memo = @owner.__memo
  key = [@name, pos]
  return memo[key] if memo.key?(key)
  combinator = @body.bind(@owner).call
  memo[key] = combinator.call(input, pos)
end