class Citrus::Match
The base class for all matches. Matches are organized into a tree where any match may contain any number of other matches. Nodes of the tree are lazily instantiated as needed. This class provides several convenient tree traversal methods that help when examining and interpreting parse results.
Attributes
The array of events for this match.
The index of this match in the input.
Public Class Methods
# File lib/citrus.rb, line 1274 def initialize(input, events=[], offset=0) @input = input @offset = offset @captures = nil @matches = nil if events.length > 0 elisions = [] while events[0].elide? elisions.unshift(events.shift) events.slice!(-2, events.length) end events[0].extend_match(self) elisions.each do |rule| rule.extend_match(self) end else # Create a default stream of events for the given string. string = input.to_str events = [Rule.for(string), CLOSE, string.length] end @events = events end
Public Instance Methods
# File lib/citrus.rb, line 1380 def ==(other) case other when String string == other when Match string == other.to_s else super end end
Returns the capture at the given key
. If it is an Integer (and
an optional length) or a Range, the result of to_a with the same arguments is
returned. Otherwise, the value at key
in captures is returned.
# File lib/citrus.rb, line 1371 def [](key, *args) case key when Integer, Range to_a[key, *args] else captures[key] end end
Convenient method for captures.first.
# File lib/citrus.rb, line 1334 def capture(name) captures[name].first end
Returns a hash of capture names to arrays of matches with that name, in the order they appeared in the input.
# File lib/citrus.rb, line 1328 def captures(name = nil) process_events! unless @captures name ? @captures[name] : @captures end
Prints the entire subtree of this match using the given indent
to indicate nested match levels. Useful for debugging.
# File lib/citrus.rb, line 1399 def dump(indent=' ') lines = [] stack = [] offset = 0 close = false index = 0 last_length = nil while index < @events.size event = @events[index] if close os = stack.pop start = stack.pop rule = stack.pop space = indent * (stack.size / 3) string = self.string.slice(os, event) lines[start] = "#{space}#{string.inspect} rule=#{rule}, offset=#{os}, length=#{event}" last_length = event unless last_length close = false elsif event == CLOSE close = true else if last_length offset += last_length last_length = nil end stack << event stack << index stack << offset end index += 1 end puts lines.compact.join("\n") end
A shortcut for retrieving the first immediate submatch of this match.
# File lib/citrus.rb, line 1345 def first matches.first end
# File lib/citrus.rb, line 1393 def inspect string.inspect end
Returns the length of this match.
# File lib/citrus.rb, line 1312 def length events.last end
Returns an array of all immediate submatches of this match.
# File lib/citrus.rb, line 1339 def matches process_events! unless @matches @matches end
Convenient shortcut for input.source
# File lib/citrus.rb, line 1317 def source (input.respond_to?(:source) && input.source) || input end
Returns the slice of the source text that this match captures.
# File lib/citrus.rb, line 1322 def string @string ||= input.to_str[offset, length] end
Returns this match plus all sub matches in an array.
# File lib/citrus.rb, line 1364 def to_a [self] + matches end
This alias allows strings to be compared to the string value of Match objects. It is most useful in assertions in unit tests, e.g.:
assert_equal("a string", match)
The default value for a match is its string value. This method is overridden in most cases to be more meaningful according to the desired interpretation.
Private Instance Methods
# File lib/citrus.rb, line 1510 def capture!(rule, match) # We can lookup matches that were created by proxy by the name of # the rule they are proxy for. if Proxy === rule if @captures.key?(rule.rule_name) @captures[rule.rule_name] << match else @captures[rule.rule_name] = [match] end end # We can lookup matches that were created by rules with labels by # that label. if rule.label if @captures.key?(rule.label) @captures[rule.label] << match else @captures[rule.label] = [match] end end end
Returns a new Hash that is to be used for @captures. This hash normalizes
String keys to Symbols, returns nil
for unknown Numeric keys,
and an empty Array for all other unknown keys.
# File lib/citrus.rb, line 1535 def captures_hash Hash.new do |hash, key| case key when String hash[key.to_sym] when Numeric nil else [] end end end
Initializes both the @captures and @matches instance variables.
# File lib/citrus.rb, line 1444 def process_events! @captures = captures_hash @matches = [] capture!(@events[0], self) @captures[0] = self stack = [] offset = 0 close = false index = 0 last_length = nil capture = true while index < @events.size event = @events[index] if close start = stack.pop if Rule === start rule = start os = stack.pop start = stack.pop match = Match.new(input, @events[start..index], @offset + os) capture!(rule, match) if stack.size == 1 @matches << match @captures[@matches.size] = match end capture = true end last_length = event unless last_length close = false elsif event == CLOSE close = true else stack << index # We can calculate the offset of this rule event by adding back the # last match length. if last_length offset += last_length last_length = nil end if capture && stack.size != 1 stack << offset stack << event # We should not create captures when traversing a portion of the # event stream that is masked by a proxy in the original rule # definition. capture = false if Proxy === event end end index += 1 end end