161 lines
4.4 KiB
Ruby
161 lines
4.4 KiB
Ruby
|
|
|
|
# Optimizes the parsers by pattern matching on the parser atoms and replacing
|
|
# matches with better versions. See the file qed/accelerators.md for a more
|
|
# in-depth description.
|
|
#
|
|
# Example:
|
|
# quote = str('"')
|
|
# parser = quote >> (quote.absent? >> any).repeat >> quote
|
|
#
|
|
# A = Accelerator # for making what follows a bit shorter
|
|
# optimized_parser = A.apply(parser,
|
|
# A.rule( (A.str(:x).absent? >> A.any).repeat ) { GobbleUp.new(x) })
|
|
#
|
|
# optimized_parser.parse('"Parsing is now fully optimized! (tm)"')
|
|
#
|
|
module Parslet::Accelerator
|
|
|
|
# An expression to match against a tree of parser atoms. Normally, an
|
|
# expression is produced by Parslet::Accelerator.any,
|
|
# Parslet::Accelerator.str or Parslet::Accelerator.re.
|
|
#
|
|
# Expressions can be chained much like parslet atoms can be:
|
|
#
|
|
# expr.repeat(1) # matching repetition
|
|
# expr.absent? # matching absent?
|
|
# expr.present? # matching present?
|
|
# expr1 >> expr2 # matching a sequence
|
|
# expr1 | expr2 # matching an alternation
|
|
#
|
|
# @see Parslet::Accelerator.str
|
|
# @see Parslet::Accelerator.re
|
|
# @see Parslet::Accelerator.any
|
|
#
|
|
# @see Parslet::Accelerator
|
|
#
|
|
class Expression
|
|
attr_reader :type
|
|
attr_reader :args
|
|
|
|
def initialize(type, *args)
|
|
@type = type
|
|
@args = args
|
|
end
|
|
|
|
# @return [Expression]
|
|
def >> other_expr
|
|
join_or_new :seq, other_expr
|
|
end
|
|
|
|
# @return [Expression]
|
|
def | other_expr
|
|
join_or_new :alt, other_expr
|
|
end
|
|
|
|
# @return [Expression]
|
|
def absent?
|
|
Expression.new(:absent, self)
|
|
end
|
|
# @return [Expression]
|
|
def present?
|
|
Expression.new(:present, self)
|
|
end
|
|
|
|
# @return [Expression]
|
|
def repeat min=0, max=nil
|
|
Expression.new(:rep, min, max, self)
|
|
end
|
|
|
|
# @return [Expression]
|
|
def as name
|
|
Expression.new(:as, name)
|
|
end
|
|
|
|
# @api private
|
|
# @return [Expression]
|
|
def join_or_new tag, other_expr
|
|
if type == tag
|
|
@args << other_expr
|
|
else
|
|
Expression.new(tag, self, other_expr)
|
|
end
|
|
end
|
|
end
|
|
|
|
module_function
|
|
# Returns a match expression that will match `str` parslet atoms.
|
|
#
|
|
# @return [Parslet::Accelerator::Expression]
|
|
#
|
|
def str variable, *constraints
|
|
Expression.new(:str, variable, *constraints)
|
|
end
|
|
|
|
# Returns a match expression that will match `match` parslet atoms.
|
|
#
|
|
# @return [Parslet::Accelerator::Expression]
|
|
#
|
|
def re variable, *constraints
|
|
Expression.new(:re, variable, *constraints)
|
|
end
|
|
|
|
# Returns a match expression that will match `any` parslet atoms.
|
|
#
|
|
# @return [Parslet::Accelerator::Expression]
|
|
#
|
|
def any
|
|
Expression.new(:re, ".")
|
|
end
|
|
|
|
# Given a parslet atom and an expression, will determine if the expression
|
|
# matches the atom. If successful, returns the bindings into the pattern
|
|
# that were made. If no bindings had to be made to make the match successful,
|
|
# the empty hash is returned.
|
|
#
|
|
# @param atom [Parslet::Atoms::Base] parslet atom to match against
|
|
# @param expr [Parslet::Accelerator::Expression] expression to match
|
|
# @return [nil, Hash] bindings for the match, nil on failure
|
|
#
|
|
def match atom, expr
|
|
engine = Engine.new
|
|
|
|
return engine.bindings if engine.match(atom, expr)
|
|
end
|
|
|
|
# Constructs an accelerator rule. A rule is a matching expression and the
|
|
# code that should be executed once the expression could be bound to a
|
|
# parser.
|
|
#
|
|
# Example:
|
|
# Accelerator.rule(Accelerator.any) { Parslet.match('.') }
|
|
#
|
|
def rule expression, &action
|
|
[expression, action]
|
|
end
|
|
|
|
# Given a parslet atom and a set of rules, tries to match the rules
|
|
# recursively through the parslet atom. Once a rule could be matched,
|
|
# its action block will be called.
|
|
#
|
|
# Example:
|
|
# quote = str('"')
|
|
# parser = quote >> (quote.absent? >> any).repeat >> quote
|
|
#
|
|
# A = Accelerator # for making what follows a bit shorter
|
|
# optimized_parser = A.apply(parser,
|
|
# A.rule( (A.str(:x).absent? >> A.any).repeat ) { GobbleUp.new(x) })
|
|
#
|
|
# optimized_parser.parse('"Parsing is now fully optimized! (tm)"')
|
|
#
|
|
# @param atom [Parslet::Atoms::Base] a parser to optimize
|
|
# @param *rules [Parslet::Accelerator::Rule] rules produced by .rule
|
|
# @return [Parslet::Atoms::Base] optimized parser
|
|
#
|
|
def apply atom, *rules
|
|
Application.new(atom, rules).call
|
|
end
|
|
end
|
|
|
|
require 'parslet/accelerator/engine'
|
|
require 'parslet/accelerator/application' |