walrat 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/lib/walrat.rb +70 -0
  2. data/lib/walrat/additions/proc.rb +32 -0
  3. data/lib/walrat/additions/regexp.rb +33 -0
  4. data/lib/walrat/additions/string.rb +99 -0
  5. data/lib/walrat/additions/symbol.rb +42 -0
  6. data/lib/walrat/and_predicate.rb +49 -0
  7. data/lib/walrat/array_result.rb +29 -0
  8. data/lib/walrat/continuation_wrapper_exception.rb +35 -0
  9. data/lib/walrat/grammar.rb +259 -0
  10. data/lib/walrat/left_recursion_exception.rb +34 -0
  11. data/lib/walrat/location_tracking.rb +126 -0
  12. data/lib/walrat/match_data_wrapper.rb +84 -0
  13. data/lib/walrat/memoizing.rb +55 -0
  14. data/lib/walrat/memoizing_cache.rb +126 -0
  15. data/lib/walrat/no_parameter_marker.rb +30 -0
  16. data/lib/walrat/node.rb +63 -0
  17. data/lib/walrat/not_predicate.rb +49 -0
  18. data/lib/walrat/parse_error.rb +48 -0
  19. data/lib/walrat/parser_state.rb +205 -0
  20. data/lib/walrat/parslet.rb +38 -0
  21. data/lib/walrat/parslet_choice.rb +155 -0
  22. data/lib/walrat/parslet_combination.rb +34 -0
  23. data/lib/walrat/parslet_combining.rb +190 -0
  24. data/lib/walrat/parslet_merge.rb +96 -0
  25. data/lib/walrat/parslet_omission.rb +74 -0
  26. data/lib/walrat/parslet_repetition.rb +114 -0
  27. data/lib/walrat/parslet_repetition_default.rb +77 -0
  28. data/lib/walrat/parslet_sequence.rb +241 -0
  29. data/lib/walrat/predicate.rb +68 -0
  30. data/lib/walrat/proc_parslet.rb +60 -0
  31. data/lib/walrat/regexp_parslet.rb +84 -0
  32. data/lib/walrat/skipped_substring_exception.rb +46 -0
  33. data/lib/walrat/string_enumerator.rb +47 -0
  34. data/lib/walrat/string_parslet.rb +89 -0
  35. data/lib/walrat/string_result.rb +34 -0
  36. data/lib/walrat/symbol_parslet.rb +82 -0
  37. data/lib/walrat/version.rb +26 -0
  38. metadata +110 -0
@@ -0,0 +1,77 @@
1
+ # Copyright 2007-2010 Wincent Colaiuta. All rights reserved.
2
+ # Redistribution and use in source and binary forms, with or without
3
+ # modification, are permitted provided that the following conditions are met:
4
+ #
5
+ # 1. Redistributions of source code must retain the above copyright notice,
6
+ # this list of conditions and the following disclaimer.
7
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
8
+ # this list of conditions and the following disclaimer in the documentation
9
+ # and/or other materials provided with the distribution.
10
+ #
11
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
12
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
15
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
16
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
17
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
19
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
20
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21
+ # POSSIBILITY OF SUCH DAMAGE.
22
+
23
+ require 'walrat'
24
+
25
+ module Walrat
26
+ # ParsletRepetitionDefault is a subclass that modifies the behaviour of its
27
+ # parent, ParsletRepetition, in a very small way. Namely, if the outcome of
28
+ # parsing is a ZeroWidthParse success then it is caught and the default value
29
+ # (defined at initialization time) is returned instead.
30
+ class ParsletRepetitionDefault < ParsletRepetition
31
+ # Possible re-factoring to consider for the future: roll the functionality
32
+ # of this class in to ParsletRepetition itself.
33
+ # Benefit of keeping it separate is that the ParsletRepetition itself is
34
+ # kept simple.
35
+ def initialize parseable, min, max = nil, default = nil
36
+ super parseable, min, max
37
+ self.default = default
38
+ end
39
+
40
+ def parse string, options = {}
41
+ catch :ZeroWidthParseSuccess do
42
+ return super string, options
43
+ end
44
+ @default.clone rescue @default
45
+ end
46
+
47
+ def eql?(other)
48
+ other.instance_of? ParsletRepetitionDefault and
49
+ @min == other.min and
50
+ @max == other.max and
51
+ @parseable.eql? other.parseable and
52
+ @default == other.default
53
+ end
54
+
55
+ protected
56
+
57
+ # For determining equality.
58
+ attr_reader :default
59
+
60
+ private
61
+
62
+ def hash_offset
63
+ 69
64
+ end
65
+
66
+ def update_hash
67
+ # let super calculate its share of the hash first
68
+ @hash = super + @default.hash
69
+ end
70
+
71
+ def default=(default)
72
+ @default = (default.clone rescue default)
73
+ @default.extend LocationTracking
74
+ update_hash
75
+ end
76
+ end # class ParsletRepetitionDefault
77
+ end # module Walrat
@@ -0,0 +1,241 @@
1
+ # Copyright 2007-2010 Wincent Colaiuta. All rights reserved.
2
+ # Redistribution and use in source and binary forms, with or without
3
+ # modification, are permitted provided that the following conditions are met:
4
+ #
5
+ # 1. Redistributions of source code must retain the above copyright notice,
6
+ # this list of conditions and the following disclaimer.
7
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
8
+ # this list of conditions and the following disclaimer in the documentation
9
+ # and/or other materials provided with the distribution.
10
+ #
11
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
12
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
15
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
16
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
17
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
19
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
20
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21
+ # POSSIBILITY OF SUCH DAMAGE.
22
+
23
+ require 'walrat'
24
+
25
+ module Walrat
26
+ class ParsletSequence < ParsletCombination
27
+ attr_reader :hash
28
+
29
+ # first and second may not be nil.
30
+ def initialize first, second, *others
31
+ raise ArgumentError if first.nil?
32
+ raise ArgumentError if second.nil?
33
+ @components = [first, second] + others
34
+ update_hash
35
+ end
36
+
37
+ # Override so that sequences are appended to an existing sequence:
38
+ # Consider the following example:
39
+ #
40
+ # A & B
41
+ #
42
+ # This constitutes a single sequence:
43
+ #
44
+ # (A & B)
45
+ #
46
+ # If we then make this a three-element sequence:
47
+ #
48
+ # A & B & C
49
+ #
50
+ # We are effectively creating an nested sequence containing the original
51
+ # sequence and an additional element:
52
+ #
53
+ # ((A & B) & C)
54
+ #
55
+ # Although such a nested sequence is correctly parsed it produces unwanted
56
+ # nesting in the results because instead of returning a one-dimensional
57
+ # array of results:
58
+ #
59
+ # [a, b, c]
60
+ #
61
+ # It returns a nested array:
62
+ #
63
+ # [[a, b], c]
64
+ #
65
+ # The solution to this unwanted nesting is to allowing appending to an
66
+ # existing sequence by using the private "append" method.
67
+ #
68
+ # This ensures that:
69
+ #
70
+ # A & B & C
71
+ #
72
+ # Translates to a single sequence:
73
+ #
74
+ # (A & B & C)
75
+ #
76
+ # And a single, uni-dimensional results array:
77
+ #
78
+ # [a, b, c]
79
+ def &(next_parslet)
80
+ append next_parslet
81
+ end
82
+
83
+ SKIP_FIRST = true
84
+ NO_SKIP = false
85
+
86
+ def parse string, options = {}
87
+ parse_common NO_SKIP, string, options
88
+ end
89
+
90
+ def parse_remainder string, options = {}
91
+ parse_common SKIP_FIRST, string, options
92
+ end
93
+
94
+ def parse_common skip_first, string, options = {}
95
+ raise ArgumentError if string.nil?
96
+ state = ParserState.new(string, options)
97
+ last_caught = nil # keep track of the last kind of throw to be caught
98
+ left_recursion = false # keep track of whether left recursion was detected
99
+
100
+ @components.each_with_index do |parseable, index|
101
+ if index == 0 # for first component only
102
+ if skip_first
103
+ next
104
+ end
105
+ begin
106
+ check_left_recursion(parseable, options)
107
+ rescue LeftRecursionException => e
108
+ left_recursion = true
109
+ continuation = nil
110
+ value = callcc { |c| continuation = c }
111
+ if value == continuation # first time that we're here
112
+ e.continuation = continuation # pack continuation into exception
113
+ raise e # and propagate
114
+ else
115
+ grammar = state.options[:grammar]
116
+ rule_name = state.options[:rule_name]
117
+ state.parsed grammar.wrap(value, rule_name)
118
+ next
119
+ end
120
+ end
121
+ end
122
+
123
+ catch :ProcessNextComponent do
124
+ catch :NotPredicateSuccess do
125
+ catch :AndPredicateSuccess do
126
+ catch :ZeroWidthParseSuccess do
127
+ begin
128
+ parsed = parseable.memoizing_parse state.remainder, state.options
129
+ state.parsed parsed
130
+ rescue SkippedSubstringException => e
131
+ state.skipped e
132
+ rescue ParseError => e
133
+ # failed, will try to skip; save original error in case
134
+ # skipping fails
135
+ if options.has_key?(:skipping_override)
136
+ skipping_parslet = options[:skipping_override]
137
+ elsif options.has_key?(:skipping)
138
+ skipping_parslet = options[:skipping]
139
+ else
140
+ skipping_parslet = nil
141
+ end
142
+ raise e if skipping_parslet.nil? # no skipper defined, raise original error
143
+ begin
144
+ # guard against self references (possible infinite recursion) here?
145
+ parsed = skipping_parslet.memoizing_parse state.remainder, state.options
146
+ state.skipped(parsed)
147
+ redo # skipping succeeded, try to redo
148
+ rescue ParseError
149
+ raise e # skipping didn't help either, raise original error
150
+ end
151
+ end
152
+ last_caught = nil
153
+
154
+ # can't use "next" here because it would only break out of
155
+ # innermost "do" rather than continuing the iteration
156
+ throw :ProcessNextComponent
157
+ end
158
+ last_caught = :ZeroWidthParseSuccess
159
+ throw :ProcessNextComponent
160
+ end
161
+ last_caught = :AndPredicateSuccess
162
+ throw :ProcessNextComponent
163
+ end
164
+ last_caught = :NotPredicateSuccess
165
+ end
166
+ end
167
+
168
+ if left_recursion
169
+ results = recurse(state)
170
+ else
171
+ results = state.results
172
+ end
173
+
174
+ return results if skip_first
175
+
176
+ if results.respond_to? :empty? and results.empty? and last_caught
177
+ throw last_caught
178
+ else
179
+ results
180
+ end
181
+ end
182
+
183
+ # Left-recursion helper
184
+ def recurse state
185
+ return state.results if state.remainder == '' # further recursion is not possible
186
+ new_state = ParserState.new state.remainder, state.options
187
+ last_successful_result = nil
188
+ while state.remainder != ''
189
+ begin
190
+ new_results = parse_remainder new_state.remainder, new_state.options
191
+ new_state.parsed new_results
192
+ last_successful_result = ArrayResult[last_successful_result || state.results, new_results]
193
+ rescue ParseError
194
+ break
195
+ end
196
+ end
197
+ last_successful_result || state.results
198
+ end
199
+
200
+ def eql?(other)
201
+ return false if not other.instance_of? ParsletSequence
202
+ other_components = other.components
203
+ return false if @components.length != other_components.length
204
+ for i in 0..(@components.length - 1)
205
+ return false unless @components[i].eql? other_components[i]
206
+ end
207
+ true
208
+ end
209
+
210
+ protected
211
+
212
+ # For determining equality.
213
+ attr_reader :components
214
+
215
+ private
216
+
217
+ def hash_offset
218
+ 40
219
+ end
220
+
221
+ def update_hash
222
+ # fixed offset to avoid unwanted collisions with similar classes
223
+ @hash = hash_offset
224
+ @components.each { |parseable| @hash += parseable.hash }
225
+ end
226
+
227
+ # Appends another Parslet, ParsletCombination or Predicate to the receiver
228
+ # and returns the receiver.
229
+ #
230
+ # Raises if next_parslet is nil.
231
+ # Cannot use << as a method name because Ruby cannot parse it without the
232
+ # self, and self is not allowed as en explicit receiver for private
233
+ # messages.
234
+ def append next_parslet
235
+ raise ArgumentError if next_parslet.nil?
236
+ @components << next_parslet.to_parseable
237
+ update_hash
238
+ self
239
+ end
240
+ end # class ParsletSequence
241
+ end # module Walrat
@@ -0,0 +1,68 @@
1
+ # Copyright 2007-2010 Wincent Colaiuta. All rights reserved.
2
+ # Redistribution and use in source and binary forms, with or without
3
+ # modification, are permitted provided that the following conditions are met:
4
+ #
5
+ # 1. Redistributions of source code must retain the above copyright notice,
6
+ # this list of conditions and the following disclaimer.
7
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
8
+ # this list of conditions and the following disclaimer in the documentation
9
+ # and/or other materials provided with the distribution.
10
+ #
11
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
12
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
15
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
16
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
17
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
19
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
20
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21
+ # POSSIBILITY OF SUCH DAMAGE.
22
+
23
+ require 'walrat'
24
+
25
+ module Walrat
26
+ # Predicates parse input without consuming it.
27
+ # On success they throw a subclass-specific symbol (see the AndPredicate and
28
+ # NotPredicate classes).
29
+ # On failure they raise a ParseError.
30
+ class Predicate
31
+ include Walrat::ParsletCombining
32
+ include Walrat::Memoizing
33
+
34
+ attr_reader :hash
35
+
36
+ # Raises if parseable is nil.
37
+ def initialize parseable
38
+ raise ArgumentError, 'nil parseable' if parseable.nil?
39
+ @parseable = parseable
40
+
41
+ # fixed offset to avoid collisions with @parseable objects
42
+ @hash = @parseable.hash + hash_offset
43
+ end
44
+
45
+ def to_parseable
46
+ self
47
+ end
48
+
49
+ def parse string, options = {}
50
+ raise NotImplementedError # subclass responsibility
51
+ end
52
+
53
+ def eql? other
54
+ other.instance_of? self.class and other.parseable.eql? @parseable
55
+ end
56
+
57
+ protected
58
+
59
+ # for equality comparisons
60
+ attr_reader :parseable
61
+
62
+ private
63
+
64
+ def hash_offset
65
+ 10
66
+ end
67
+ end
68
+ end # module Walrat
@@ -0,0 +1,60 @@
1
+ # Copyright 2007-2010 Wincent Colaiuta. All rights reserved.
2
+ # Redistribution and use in source and binary forms, with or without
3
+ # modification, are permitted provided that the following conditions are met:
4
+ #
5
+ # 1. Redistributions of source code must retain the above copyright notice,
6
+ # this list of conditions and the following disclaimer.
7
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
8
+ # this list of conditions and the following disclaimer in the documentation
9
+ # and/or other materials provided with the distribution.
10
+ #
11
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
12
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
15
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
16
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
17
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
19
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
20
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21
+ # POSSIBILITY OF SUCH DAMAGE.
22
+
23
+ require 'walrat'
24
+
25
+ module Walrat
26
+ class ProcParslet < Parslet
27
+ attr_reader :hash
28
+
29
+ def initialize proc
30
+ raise ArgumentError, 'nil proc' if proc.nil?
31
+ self.expected_proc = proc
32
+ end
33
+
34
+ def parse string, options = {}
35
+ raise ArgumentError, 'nil string' if string.nil?
36
+ @expected_proc.call string, options
37
+ end
38
+
39
+ def eql?(other)
40
+ other.instance_of? ProcParslet and other.expected_proc == @expected_proc
41
+ end
42
+
43
+ protected
44
+
45
+ # For equality comparisons.
46
+ attr_reader :expected_proc
47
+
48
+ private
49
+
50
+ def expected_proc=(proc)
51
+ @expected_proc = (proc.clone rescue proc)
52
+ update_hash
53
+ end
54
+
55
+ def update_hash
56
+ # fixed offset to avoid collisions with @parseable objects
57
+ @hash = @expected_proc.hash + 105
58
+ end
59
+ end # class ProcParslet
60
+ end # module Walrat
@@ -0,0 +1,84 @@
1
+ # Copyright 2007-2010 Wincent Colaiuta. All rights reserved.
2
+ # Redistribution and use in source and binary forms, with or without
3
+ # modification, are permitted provided that the following conditions are met:
4
+ #
5
+ # 1. Redistributions of source code must retain the above copyright notice,
6
+ # this list of conditions and the following disclaimer.
7
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
8
+ # this list of conditions and the following disclaimer in the documentation
9
+ # and/or other materials provided with the distribution.
10
+ #
11
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
12
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
15
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
16
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
17
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
18
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
19
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
20
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21
+ # POSSIBILITY OF SUCH DAMAGE.
22
+
23
+ require 'walrat'
24
+
25
+ module Walrat
26
+ class RegexpParslet < Parslet
27
+ attr_reader :hash
28
+
29
+ def initialize regexp
30
+ raise ArgumentError, 'nil regexp' if regexp.nil?
31
+ self.expected_regexp = /\A#{regexp}/ # for efficiency, anchor all regexps
32
+ end
33
+
34
+ def parse string, options = {}
35
+ raise ArgumentError, 'nil string' if string.nil?
36
+ if string =~ @expected_regexp
37
+ wrapper = MatchDataWrapper.new $~
38
+ match = $~[0]
39
+
40
+ if (line_count = match.scan(/\r\n|\r|\n/).length) != 0 # count number of newlines in match
41
+ column_end = match.jlength - match.jrindex(/\r|\n/) - 1 # calculate characters on last line
42
+ else # no newlines in match
43
+ column_end = match.jlength + (options[:column_start] || 0)
44
+ end
45
+
46
+ wrapper.start = [options[:line_start], options[:column_start]]
47
+ wrapper.end = [wrapper.line_start + line_count, column_end]
48
+ wrapper.source_text = match.to_s.clone
49
+ wrapper
50
+ else
51
+ raise ParseError.new('non-matching characters "%s" while parsing regular expression "%s"' % [string, @expected_regexp.inspect],
52
+ :line_end => (options[:line_start] || 0),
53
+ :column_end => (options[:column_start] || 0))
54
+ end
55
+ end
56
+
57
+ def eql?(other)
58
+ other.instance_of? RegexpParslet and
59
+ other.expected_regexp == @expected_regexp
60
+ end
61
+
62
+ def inspect
63
+ '#<%s:0x%x @expected_regexp=%s>' %
64
+ [self.class.to_s, self.object_id, @expected_regexp.inspect]
65
+ end
66
+
67
+ protected
68
+
69
+ # For equality comparisons.
70
+ attr_reader :expected_regexp
71
+
72
+ private
73
+
74
+ def expected_regexp=(regexp)
75
+ @expected_regexp = (regexp.clone rescue regexp)
76
+ update_hash
77
+ end
78
+
79
+ def update_hash
80
+ # fixed offset to avoid collisions with @parseable objects
81
+ @hash = @expected_regexp.hash + 15
82
+ end
83
+ end # class RegexpParslet
84
+ end # module Walrat