Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Keep a master offset in Match instead of slicing.

Instead of successively slicing strings in matches, we keep the
parsed text under @source, and an offset in the later in @offset.

Btw, that @offset might be very useful for keeping trace of the
localisation of the match in the source text (e.g. for subsequent
semantic passes; semantic error detection for instance).
  • Loading branch information...
commit 13ccfb3b3bf2aa4322f3f3a146dbbb73be53e253 1 parent aaac09b
@blambeau blambeau authored
Showing with 41 additions and 20 deletions.
  1. +25 −19 lib/citrus.rb
  2. +16 −1 test/match_test.rb
View
44 lib/citrus.rb
@@ -636,7 +636,7 @@ def parse(string, options={})
raise ParseError, input
end
- Match.new(string.slice(opts[:offset], length), events)
+ Match.new(string, events, opts[:offset])
end
# Tests whether or not this rule matches on the given +string+. Returns the
@@ -1239,14 +1239,11 @@ def to_citrus # :nodoc:
# instantiated as needed. This class provides several convenient tree
# traversal methods that help when examining and interpreting parse results.
class Match
- def initialize(string, events=[])
- @string = string
+ def initialize(source, events=[], offset = 0)
+ @source = source
+ @offset = offset
if events.length > 0
- if events[-1] != string.length
- raise ArgumentError, "Invalid events for length #{string.length}"
- end
-
elisions = []
while events[0].elide?
@@ -1261,18 +1258,29 @@ def initialize(string, events=[])
end
else
# Create a default stream of events for the given string.
- events = [Rule.for(string), CLOSE, string.length]
+ events = [Rule.for(source), CLOSE, source.length]
end
@events = events
end
+ # The main parsed text.
+ attr_reader :source
+
+ # The index of this match in the source text.
+ attr_reader :offset
+
# The array of events for this match.
attr_reader :events
# Returns the length of this match.
def length
- @string.length
+ @events.last
+ end
+
+ # Returns the slice of the source text that this match captures.
+ def string
+ @string ||= @source[offset, length]
end
# Returns a hash of capture names to arrays of matches with that name,
@@ -1296,16 +1304,14 @@ def first
# Allows methods of this match's string to be called directly and provides
# a convenient interface for retrieving the first match with a given name.
def method_missing(sym, *args, &block)
- if @string.respond_to?(sym)
- @string.__send__(sym, *args, &block)
+ if string.respond_to?(sym)
+ string.__send__(sym, *args, &block)
else
captures[sym].first
end
end
- def to_s
- @string
- end
+ alias_method :to_s, :string
# 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.:
@@ -1339,9 +1345,9 @@ def [](key, *args)
def ==(other)
case other
when String
- @string == other
+ string == other
when Match
- @string == other.to_s
+ string == other.to_s
else
super
end
@@ -1350,7 +1356,7 @@ def ==(other)
alias_method :eql?, :==
def inspect
- @string.inspect
+ string.inspect
end
# Prints the entire subtree of this match using the given +indent+ to
@@ -1372,7 +1378,7 @@ def dump(indent=' ')
rule = stack.pop
space = indent * (stack.size / 3)
- string = @string.slice(os, event)
+ string = self.string.slice(os, event)
lines[start] = "#{space}#{string.inspect} rule=#{rule}, offset=#{os}, length=#{event}"
last_length = event unless last_length
@@ -1425,7 +1431,7 @@ def process_events!
os = stack.pop
start = stack.pop
- match = Match.new(@string.slice(os, event), @events[start..index])
+ match = Match.new(source, @events[start..index], @offset + os)
capture!(rule, match)
if stack.size == 1
View
17 test/match_test.rb
@@ -20,6 +20,14 @@ def test_match_inequality
assert_equal(false, match2 == match1)
end
+ def test_string
+ match1 = Match.new('abcdef')
+ assert_equal 'abcdef', match1.string
+
+ match2 = Match.new('abcdef', [Rule.for('bcd'), -1, 3], 1)
+ assert_equal 'bcd', match2.string
+ end
+
def test_matches
a = Rule.for('a')
b = Rule.for('b')
@@ -59,11 +67,18 @@ def test_matches
CLOSE, 3
]
- match.matches.each do |m|
+ match.matches.each_with_index do |m, i|
assert_equal(sub_events, m.events)
+ assert_equal(i*3, m.offset)
+ assert_equal(3, m.length)
+ assert_equal("abc", m.string)
assert_equal("abc", m)
assert(m.matches)
assert_equal(3, m.matches.length)
+ m.matches.each_with_index do |m2,i2|
+ assert_equal(i*3+i2, m2.offset)
+ assert_equal(1, m2.length)
+ end
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.