vendored parslet, deemed stable enough and better without dependency
This commit is contained in:
62
lib/parslet/accelerator/application.rb
Normal file
62
lib/parslet/accelerator/application.rb
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
# @api private
|
||||
module Parslet::Accelerator
|
||||
class Application
|
||||
def initialize atom, rules
|
||||
@atom = atom
|
||||
@rules = rules
|
||||
end
|
||||
|
||||
def call
|
||||
@atom.accept(self)
|
||||
end
|
||||
|
||||
def visit_parser(root)
|
||||
transform root.accept(self)
|
||||
end
|
||||
def visit_entity(name, block)
|
||||
transform Parslet::Atoms::Entity.new(name) { block.call.accept(self) }
|
||||
end
|
||||
def visit_named(name, atom)
|
||||
transform Parslet::Atoms::Named.new(atom.accept(self), name)
|
||||
end
|
||||
def visit_repetition(tag, min, max, atom)
|
||||
transform Parslet::Atoms::Repetition.new(atom.accept(self), min, max, tag)
|
||||
end
|
||||
def visit_alternative(alternatives)
|
||||
transform Parslet::Atoms::Alternative.new(
|
||||
*alternatives.map { |atom| atom.accept(self) })
|
||||
end
|
||||
def visit_sequence(sequence)
|
||||
transform Parslet::Atoms::Sequence.new(
|
||||
*sequence.map { |atom| atom.accept(self) })
|
||||
end
|
||||
def visit_lookahead(positive, atom)
|
||||
transform Parslet::Atoms::Lookahead.new(atom, positive)
|
||||
end
|
||||
def visit_re(regexp)
|
||||
transform Parslet::Atoms::Re.new(regexp)
|
||||
end
|
||||
def visit_str(str)
|
||||
transform Parslet::Atoms::Str.new(str)
|
||||
end
|
||||
|
||||
def transform atom
|
||||
@rules.each do |expr, action|
|
||||
# Try and match each rule in turn
|
||||
binding = Parslet::Accelerator.match(atom, expr)
|
||||
if binding
|
||||
# On a successful match, allow the rule action to transform the
|
||||
# parslet into something new.
|
||||
ctx = Parslet::Context.new(binding)
|
||||
return ctx.instance_eval(&action)
|
||||
end
|
||||
end # rules.each
|
||||
|
||||
# If no rule matches, this is the fallback - a clean new parslet atom.
|
||||
return atom
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'parslet/context'
|
112
lib/parslet/accelerator/engine.rb
Normal file
112
lib/parslet/accelerator/engine.rb
Normal file
@ -0,0 +1,112 @@
|
||||
|
||||
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
|
Reference in New Issue
Block a user