walrat 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|