lib/citrus.rb in citrus-1.2.2 vs lib/citrus.rb in citrus-1.3.0
- old
+ new
@@ -2,11 +2,11 @@
# elegance and expressiveness of the language with the simplicity and power of
# parsing expressions.
#
# http://mjijackson.com/citrus
module Citrus
- VERSION = [1, 2, 2]
+ VERSION = [1, 3, 0]
Infinity = 1.0 / 0
autoload :File, 'citrus/file'
@@ -417,17 +417,17 @@
end
private
def extend_match(match)
- match.ext = ext if ext
+ match.extend(ext) if ext
end
def create_match(data, offset)
match = Match.new(data, offset)
extend_match(match)
- match.name = name
+ match.names << name if name
match
end
end
# A Proxy is a Rule that is a placeholder for another rule. It stores the
@@ -435,17 +435,17 @@
# actual Rule object at runtime. This lazy evaluation permits us to create
# Proxy objects for rules that we may not know the definition of yet.
module Proxy
include Rule
- def initialize(name='<proxy>')
- self.rule_name = name
+ def initialize(rule_name='<proxy>')
+ self.rule_name = rule_name
end
# Sets the name of the rule this rule is proxy for.
- def rule_name=(name)
- @rule_name = name.to_sym
+ def rule_name=(rule_name)
+ @rule_name = rule_name.to_sym
end
# The name of this proxy's rule.
attr_reader :rule_name
@@ -458,12 +458,12 @@
# +nil+ if no match can be made.
def match(input, offset=0)
m = input.match(rule, offset)
if m
extend_match(m)
- # If this Proxy has a name then it should rename all of its matches.
- m.name = name if name
+ # This proxy's name should be added to the names of the match.
+ m.names << name if name
m
end
end
end
@@ -673,33 +673,39 @@
# label:expr
#
class Label
include Predicate
- def initialize(label='<label>', rule='')
- @label = label.to_sym
+ def initialize(label_name='<label>', rule='')
super(rule)
+ self.label_name = label_name
end
- # The symbol this rule uses to re-name all its matches.
- attr_reader :label
+ # Sets the name of this label.
+ def label_name=(label_name)
+ @label_name = label_name.to_sym
+ end
+ # The name this rule adds to all its matches.
+ attr_reader :label_name
+
# Returns the Match for this rule on +input+ at the given +offset+, +nil+ if
# no match can be made. When a Label makes a match, it re-names the match to
# the value of its label.
def match(input, offset=0)
m = rule.match(input, offset)
if m
extend_match(m)
- m.name = label
+ # This label's name should be added to the names of the match.
+ m.names << label_name
m
end
end
# Returns the Citrus notation of this rule as a string.
def to_s
- label.to_s + ':' + rule.embed
+ label_name.to_s + ':' + rule.embed
end
end
# A Repeat is a Predicate that specifies a minimum and maximum number of times
# its rule must match. The Citrus notation is an integer, +N+, followed by an
@@ -792,11 +798,16 @@
# Returns the Match for this rule on +input+ at the given +offset+, +nil+ if
# no match can be made.
def match(input, offset=0)
rules.each do |rule|
m = input.match(rule, offset)
- return create_match([m], offset) if m
+ if m
+ extend_match(m)
+ # This choice's name should be added to the names of the match.
+ m.names << name if name
+ return m
+ end
end
nil
end
# Returns the Citrus notation of this rule as a string.
@@ -849,22 +860,30 @@
end
@offset = offset
end
- # The name by which this match can be accessed from a parent match. This
- # will be the name of the rule that generated the match in most cases.
- # However, if the match is the result of a Label this will be the value of
- # the label.
- attr_accessor :name
-
- # A module that will be used to extend this match.
- attr_accessor :ext
-
# The offset in the input at which this match occurred.
attr_reader :offset
+ # An array of all names of this match. A name is added to a match object
+ # for each rule that returns that object when matching. These names can then
+ # be used to determine which rules were satisfied by a given match.
+ def names
+ @names ||= []
+ end
+
+ # The name of the lowest level rule that originally created this match.
+ def name
+ names.first
+ end
+
+ # Returns +true+ if this match has the given +name+.
+ def has_name?(name)
+ names.include?(name)
+ end
+
# An array of all sub-matches of this match.
def matches
@matches ||= []
end
@@ -895,11 +914,11 @@
# Returns an array of all sub-matches with the given +name+. If +deep+ is
# +false+, returns only sub-matches that are immediate descendants of this
# match.
def find(name, deep=true)
sym = name.to_sym
- ms = matches.select {|m| sym == m.name }
+ ms = matches.select {|m| m.has_name?(sym) }
ms.concat(matches.map {|m| m.find(name, deep) }.flatten) if deep
ms
end
# A shortcut for retrieving the first immediate sub-match of this match. If
@@ -923,25 +942,11 @@
alias eql? ==
# Uses #match to allow sub-matches of this match to be called by name as
# instance methods.
def method_missing(sym, *args)
- # Extend this object only when needed and immediately redefine
- # #method_missing so that the new version is used on all future calls.
- extend(ext) if ext
- redefine_method_missing!
- __send__(sym, *args)
- end
-
- private
-
- def redefine_method_missing! # :nodoc:
- instance_eval(<<-RUBY, __FILE__, __LINE__ + 1)
- def method_missing(sym, *args)
- m = first(sym)
- return m if m
- raise 'No match named "%s" in %s (%s)' % [sym, self, name]
- end
- RUBY
+ m = first(sym)
+ return m if m
+ raise 'No match named "%s" in %s (%s)' % [sym, self, name]
end
end
end