rubyx/lib/parslet/accelerator/engine.rb

112 lines
2.5 KiB
Ruby
Raw Normal View History

require 'parslet/atoms/visitor'
module Parslet::Accelerator
# @api private
class Apply
def initialize(engine, expr)
@engine = engine
@expr = expr
end
def visit_parser(root)
false
end
def visit_entity(name, block)
false
end
def visit_named(name, atom)
match(:as) do |key|
@engine.try_bind(key, name)
end
end
def visit_repetition(tag, min, max, atom)
match(:rep) do |e_min, e_max, expr|
e_min == min && e_max == max && @engine.match(atom, expr)
end
end
def visit_alternative(alternatives)
match(:alt) do |*expressions|
return false if alternatives.size != expressions.size
alternatives.zip(expressions).all? do |atom, expr|
@engine.match(atom, expr)
end
end
end
def visit_sequence(sequence)
match(:seq) do |*expressions|
return false if sequence.size != expressions.size
sequence.zip(expressions).all? do |atom, expr|
@engine.match(atom, expr)
end
end
end
def visit_lookahead(positive, atom)
match(:absent) do |expr|
return positive == false && @engine.match(atom, expr)
end
match(:present) do |expr|
return positive == true && @engine.match(atom, expr)
end
end
def visit_re(regexp)
match(:re) do |*bind_conditions|
bind_conditions.all? { |bind_cond|
@engine.try_bind(bind_cond, regexp) }
end
end
def visit_str(str)
match(:str) do |*bind_conditions|
bind_conditions.all? { |bind_cond|
@engine.try_bind(bind_cond, str) }
end
end
def match(type_tag)
expr_tag = @expr.type
if expr_tag == type_tag
yield *@expr.args
end
end
end
# @api private
class Engine
attr_reader :bindings
def initialize
@bindings = {}
end
def match(atom, expr)
atom.accept(
Apply.new(self, expr))
end
def try_bind(variable, value)
if bound? variable
return value == lookup(variable)
else
case variable
when Symbol
bind(variable, value)
else
# This does not look like a variable - let's try matching it against
# the value:
variable === value
end
end
end
def bound? var
@bindings.has_key? var
end
def lookup var
@bindings[var]
end
def bind var, val
@bindings[var] = val
end
end
end