vendored parslet, deemed stable enough and better without dependency

This commit is contained in:
Torsten Ruger
2014-04-27 15:34:35 +03:00
parent 6fafeda66d
commit b1203363d4
42 changed files with 3415 additions and 2 deletions

View 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'

View 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