walrat 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/walrat.rb +70 -0
- data/lib/walrat/additions/proc.rb +32 -0
- data/lib/walrat/additions/regexp.rb +33 -0
- data/lib/walrat/additions/string.rb +99 -0
- data/lib/walrat/additions/symbol.rb +42 -0
- data/lib/walrat/and_predicate.rb +49 -0
- data/lib/walrat/array_result.rb +29 -0
- data/lib/walrat/continuation_wrapper_exception.rb +35 -0
- data/lib/walrat/grammar.rb +259 -0
- data/lib/walrat/left_recursion_exception.rb +34 -0
- data/lib/walrat/location_tracking.rb +126 -0
- data/lib/walrat/match_data_wrapper.rb +84 -0
- data/lib/walrat/memoizing.rb +55 -0
- data/lib/walrat/memoizing_cache.rb +126 -0
- data/lib/walrat/no_parameter_marker.rb +30 -0
- data/lib/walrat/node.rb +63 -0
- data/lib/walrat/not_predicate.rb +49 -0
- data/lib/walrat/parse_error.rb +48 -0
- data/lib/walrat/parser_state.rb +205 -0
- data/lib/walrat/parslet.rb +38 -0
- data/lib/walrat/parslet_choice.rb +155 -0
- data/lib/walrat/parslet_combination.rb +34 -0
- data/lib/walrat/parslet_combining.rb +190 -0
- data/lib/walrat/parslet_merge.rb +96 -0
- data/lib/walrat/parslet_omission.rb +74 -0
- data/lib/walrat/parslet_repetition.rb +114 -0
- data/lib/walrat/parslet_repetition_default.rb +77 -0
- data/lib/walrat/parslet_sequence.rb +241 -0
- data/lib/walrat/predicate.rb +68 -0
- data/lib/walrat/proc_parslet.rb +60 -0
- data/lib/walrat/regexp_parslet.rb +84 -0
- data/lib/walrat/skipped_substring_exception.rb +46 -0
- data/lib/walrat/string_enumerator.rb +47 -0
- data/lib/walrat/string_parslet.rb +89 -0
- data/lib/walrat/string_result.rb +34 -0
- data/lib/walrat/symbol_parslet.rb +82 -0
- data/lib/walrat/version.rb +26 -0
- 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
|