walrus 0.2 → 0.3
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/bin/walrus +19 -12
- data/lib/walrus.rb +27 -70
- data/lib/walrus/additions/string.rb +21 -35
- data/lib/walrus/compile_error.rb +19 -16
- data/lib/walrus/compiler.rb +66 -55
- data/lib/walrus/contrib/spec/walruscloth_spec.rb +21 -17
- data/lib/walrus/contrib/walruscloth.rb +19 -11
- data/lib/walrus/document.rb +41 -33
- data/lib/walrus/grammar.rb +474 -162
- data/lib/walrus/grammar/assignment_expression.rb +33 -0
- data/lib/walrus/grammar/block_directive.rb +37 -0
- data/lib/walrus/grammar/comment.rb +33 -0
- data/lib/walrus/grammar/def_directive.rb +80 -0
- data/lib/walrus/grammar/echo_directive.rb +56 -0
- data/lib/walrus/grammar/escape_sequence.rb +33 -0
- data/lib/walrus/grammar/import_directive.rb +54 -0
- data/lib/walrus/grammar/include_directive.rb +36 -0
- data/lib/walrus/grammar/instance_variable.rb +33 -0
- data/lib/walrus/grammar/literal.rb +33 -0
- data/lib/walrus/grammar/message_expression.rb +34 -0
- data/lib/walrus/grammar/multiline_comment.rb +70 -0
- data/lib/walrus/grammar/placeholder.rb +47 -0
- data/lib/walrus/grammar/raw_directive.rb +50 -0
- data/lib/walrus/grammar/raw_text.rb +56 -0
- data/lib/walrus/grammar/ruby_directive.rb +41 -0
- data/lib/walrus/grammar/ruby_expression.rb +42 -0
- data/lib/walrus/grammar/set_directive.rb +34 -0
- data/lib/walrus/grammar/silent_directive.rb +51 -0
- data/lib/walrus/grammar/slurp_directive.rb +36 -0
- data/lib/walrus/grammar/super_directive.rb +34 -0
- data/lib/walrus/parser.rb +26 -408
- data/lib/walrus/runner.rb +37 -20
- data/lib/walrus/template.rb +34 -25
- data/lib/walrus/version.rb +24 -1
- metadata +57 -71
- data/ext/extconf.rb +0 -16
- data/ext/jindex.c +0 -92
- data/lib/walrus/diff.rb +0 -95
- data/lib/walrus/grammar/additions/proc.rb +0 -26
- data/lib/walrus/grammar/additions/regexp.rb +0 -27
- data/lib/walrus/grammar/additions/string.rb +0 -58
- data/lib/walrus/grammar/additions/symbol.rb +0 -49
- data/lib/walrus/grammar/and_predicate.rb +0 -46
- data/lib/walrus/grammar/array_result.rb +0 -25
- data/lib/walrus/grammar/continuation_wrapper_exception.rb +0 -34
- data/lib/walrus/grammar/left_recursion_exception.rb +0 -33
- data/lib/walrus/grammar/location_tracking.rb +0 -115
- data/lib/walrus/grammar/match_data_wrapper.rb +0 -71
- data/lib/walrus/grammar/memoizing.rb +0 -47
- data/lib/walrus/grammar/memoizing_cache.rb +0 -103
- data/lib/walrus/grammar/node.rb +0 -66
- data/lib/walrus/grammar/not_predicate.rb +0 -46
- data/lib/walrus/grammar/parse_error.rb +0 -45
- data/lib/walrus/grammar/parser_state.rb +0 -187
- data/lib/walrus/grammar/parslet.rb +0 -34
- data/lib/walrus/grammar/parslet_choice.rb +0 -128
- data/lib/walrus/grammar/parslet_combination.rb +0 -32
- data/lib/walrus/grammar/parslet_combining.rb +0 -160
- data/lib/walrus/grammar/parslet_merge.rb +0 -94
- data/lib/walrus/grammar/parslet_omission.rb +0 -63
- data/lib/walrus/grammar/parslet_repetition.rb +0 -106
- data/lib/walrus/grammar/parslet_repetition_default.rb +0 -64
- data/lib/walrus/grammar/parslet_sequence.rb +0 -214
- data/lib/walrus/grammar/predicate.rb +0 -63
- data/lib/walrus/grammar/proc_parslet.rb +0 -58
- data/lib/walrus/grammar/regexp_parslet.rb +0 -79
- data/lib/walrus/grammar/skipped_substring_exception.rb +0 -42
- data/lib/walrus/grammar/string_enumerator.rb +0 -53
- data/lib/walrus/grammar/string_parslet.rb +0 -81
- data/lib/walrus/grammar/string_result.rb +0 -30
- data/lib/walrus/grammar/symbol_parslet.rb +0 -69
- data/lib/walrus/no_parameter_marker.rb +0 -25
- data/lib/walrus/walrus_grammar/assignment_expression.rb +0 -30
- data/lib/walrus/walrus_grammar/block_directive.rb +0 -34
- data/lib/walrus/walrus_grammar/comment.rb +0 -30
- data/lib/walrus/walrus_grammar/def_directive.rb +0 -72
- data/lib/walrus/walrus_grammar/echo_directive.rb +0 -50
- data/lib/walrus/walrus_grammar/escape_sequence.rb +0 -30
- data/lib/walrus/walrus_grammar/import_directive.rb +0 -50
- data/lib/walrus/walrus_grammar/include_directive.rb +0 -33
- data/lib/walrus/walrus_grammar/instance_variable.rb +0 -30
- data/lib/walrus/walrus_grammar/literal.rb +0 -30
- data/lib/walrus/walrus_grammar/message_expression.rb +0 -31
- data/lib/walrus/walrus_grammar/multiline_comment.rb +0 -60
- data/lib/walrus/walrus_grammar/placeholder.rb +0 -46
- data/lib/walrus/walrus_grammar/raw_directive.rb +0 -48
- data/lib/walrus/walrus_grammar/raw_text.rb +0 -51
- data/lib/walrus/walrus_grammar/ruby_directive.rb +0 -35
- data/lib/walrus/walrus_grammar/ruby_expression.rb +0 -37
- data/lib/walrus/walrus_grammar/set_directive.rb +0 -30
- data/lib/walrus/walrus_grammar/silent_directive.rb +0 -50
- data/lib/walrus/walrus_grammar/slurp_directive.rb +0 -31
- data/lib/walrus/walrus_grammar/super_directive.rb +0 -33
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# Copyright 2007 Wincent Colaiuta
|
|
2
|
-
# This program is free software: you can redistribute it and/or modify
|
|
3
|
-
# it under the terms of the GNU General Public License as published by
|
|
4
|
-
# the Free Software Foundation, either version 3 of the License, or
|
|
5
|
-
# (at your option) any later version.
|
|
6
|
-
#
|
|
7
|
-
# This program is distributed in the hope that it will be useful,
|
|
8
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
-
# GNU General Public License for more details.
|
|
11
|
-
#
|
|
12
|
-
# You should have received a copy of the GNU General Public License
|
|
13
|
-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
14
|
-
|
|
15
|
-
require 'walrus'
|
|
16
|
-
|
|
17
|
-
module Walrus
|
|
18
|
-
class Grammar
|
|
19
|
-
|
|
20
|
-
class ParsletOmission < ParsletCombination
|
|
21
|
-
|
|
22
|
-
attr_reader :hash
|
|
23
|
-
|
|
24
|
-
# Raises an ArgumentError if parseable is nil.
|
|
25
|
-
def initialize(parseable)
|
|
26
|
-
raise ArgumentError if parseable.nil?
|
|
27
|
-
@parseable = parseable
|
|
28
|
-
@hash = @parseable.hash + 46 # fixed offset to avoid unwanted collisions with similar classes
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def parse(string, options = {})
|
|
32
|
-
raise ArgumentError if string.nil?
|
|
33
|
-
substring = StringResult.new
|
|
34
|
-
substring.start = [options[:line_start], options[:column_start]]
|
|
35
|
-
substring.end = [options[:line_start], options[:column_start]]
|
|
36
|
-
|
|
37
|
-
# possibly should catch these here as well
|
|
38
|
-
#catch :NotPredicateSuccess do
|
|
39
|
-
#catch :AndPredicateSuccess do
|
|
40
|
-
# one of the fundamental problems is that if a parslet throws such a symbol any info about already skipped material is lost (because the symbols contain nothing)
|
|
41
|
-
# this may be one reason to change these to exceptions...
|
|
42
|
-
catch :ZeroWidthParseSuccess do
|
|
43
|
-
substring = @parseable.memoizing_parse(string, options)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# not enough to just return a ZeroWidthParseSuccess here; that could cause higher levels to stop parsing and in any case there'd be no clean way to embed the scanned substring in the symbol
|
|
47
|
-
raise SkippedSubstringException.new(substring,
|
|
48
|
-
:line_start => options[:line_start], :column_start => options[:column_start],
|
|
49
|
-
:line_end => substring.line_end, :column_end => substring.column_end)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def eql?(other)
|
|
53
|
-
other.instance_of? ParsletOmission and other.parseable.eql? @parseable
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
protected
|
|
57
|
-
|
|
58
|
-
# For determining equality.
|
|
59
|
-
attr_reader :parseable
|
|
60
|
-
|
|
61
|
-
end # class ParsletOmission
|
|
62
|
-
end # class Grammar
|
|
63
|
-
end # module Walrus
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# Copyright 2007 Wincent Colaiuta
|
|
2
|
-
# This program is free software: you can redistribute it and/or modify
|
|
3
|
-
# it under the terms of the GNU General Public License as published by
|
|
4
|
-
# the Free Software Foundation, either version 3 of the License, or
|
|
5
|
-
# (at your option) any later version.
|
|
6
|
-
#
|
|
7
|
-
# This program is distributed in the hope that it will be useful,
|
|
8
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
-
# GNU General Public License for more details.
|
|
11
|
-
#
|
|
12
|
-
# You should have received a copy of the GNU General Public License
|
|
13
|
-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
14
|
-
|
|
15
|
-
require 'walrus'
|
|
16
|
-
|
|
17
|
-
module Walrus
|
|
18
|
-
class Grammar
|
|
19
|
-
|
|
20
|
-
class ParsletRepetition < ParsletCombination
|
|
21
|
-
|
|
22
|
-
attr_reader :hash
|
|
23
|
-
|
|
24
|
-
# Raises an ArgumentError if parseable or min is nil.
|
|
25
|
-
def initialize(parseable, min, max = nil)
|
|
26
|
-
raise ArgumentError if parseable.nil?
|
|
27
|
-
raise ArgumentError if min.nil?
|
|
28
|
-
@parseable = parseable
|
|
29
|
-
self.min = min
|
|
30
|
-
self.max = max
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def parse(string, options = {})
|
|
34
|
-
raise ArgumentError if string.nil?
|
|
35
|
-
state = ParserState.new(string, options)
|
|
36
|
-
catch :ZeroWidthParseSuccess do # a zero-width match is grounds for immediate abort
|
|
37
|
-
while @max.nil? or state.length < @max # try forever if max is nil; otherwise keep trying while match count < max
|
|
38
|
-
begin
|
|
39
|
-
parsed = @parseable.memoizing_parse(state.remainder, state.options)
|
|
40
|
-
state.parsed(parsed)
|
|
41
|
-
rescue SkippedSubstringException => e
|
|
42
|
-
state.skipped(e)
|
|
43
|
-
rescue ParseError => e # failed, will try to skip; save original error in case skipping fails
|
|
44
|
-
if options.has_key?(:skipping_override)
|
|
45
|
-
skipping_parslet = options[:skipping_override]
|
|
46
|
-
elsif options.has_key?(:skipping)
|
|
47
|
-
skipping_parslet = options[:skipping]
|
|
48
|
-
else
|
|
49
|
-
skipping_parslet = nil
|
|
50
|
-
end
|
|
51
|
-
break if skipping_parslet.nil?
|
|
52
|
-
begin
|
|
53
|
-
parsed = skipping_parslet.memoizing_parse(state.remainder, state.options) # guard against self references (possible infinite recursion) here?
|
|
54
|
-
state.skipped(parsed)
|
|
55
|
-
redo # skipping succeeded, try to redo
|
|
56
|
-
rescue ParseError
|
|
57
|
-
break # skipping didn't help either, give up
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# now assess whether our tries met the requirements
|
|
64
|
-
if state.length == 0 and @min == 0 # success (special case)
|
|
65
|
-
throw :ZeroWidthParseSuccess
|
|
66
|
-
elsif state.length < @min # matches < min (failure)
|
|
67
|
-
raise ParseError.new('required %d matches but obtained %d while parsing "%s"' % [@min, state.length, string],
|
|
68
|
-
:line_end => state.options[:line_end], :column_end => state.options[:column_end])
|
|
69
|
-
else # success (general case)
|
|
70
|
-
state.results # returns multiple matches as an array, single matches as a single object
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def eql?(other)
|
|
76
|
-
other.instance_of? ParsletRepetition and @min == other.min and @max == other.max and @parseable.eql? other.parseable
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
protected
|
|
80
|
-
|
|
81
|
-
# For determining equality.
|
|
82
|
-
attr_reader :parseable, :min, :max
|
|
83
|
-
|
|
84
|
-
private
|
|
85
|
-
|
|
86
|
-
def hash_offset
|
|
87
|
-
87
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def update_hash
|
|
91
|
-
@hash = @min.hash + @max.hash + @parseable.hash + hash_offset # fixed offset to minimize risk of collisions
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def min=(min)
|
|
95
|
-
@min = (min.clone rescue min)
|
|
96
|
-
update_hash
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def max=(max)
|
|
100
|
-
@max = (max.clone rescue max)
|
|
101
|
-
update_hash
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
end # class ParsletRepetition
|
|
105
|
-
end # class Grammar
|
|
106
|
-
end # module Walrus
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# Copyright 2007 Wincent Colaiuta
|
|
2
|
-
# This program is free software: you can redistribute it and/or modify
|
|
3
|
-
# it under the terms of the GNU General Public License as published by
|
|
4
|
-
# the Free Software Foundation, either version 3 of the License, or
|
|
5
|
-
# (at your option) any later version.
|
|
6
|
-
#
|
|
7
|
-
# This program is distributed in the hope that it will be useful,
|
|
8
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
-
# GNU General Public License for more details.
|
|
11
|
-
#
|
|
12
|
-
# You should have received a copy of the GNU General Public License
|
|
13
|
-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
14
|
-
|
|
15
|
-
require 'walrus'
|
|
16
|
-
|
|
17
|
-
module Walrus
|
|
18
|
-
class Grammar
|
|
19
|
-
|
|
20
|
-
# ParsletRepetitionDefault is a subclass that modifies the behaviour of its parent, ParsletRepetition, in a very small way. Namely, if the outcome of parsing is a ZeroWidthParse success then it is caught and the default value (defined at initialization time) is returned instead.
|
|
21
|
-
class ParsletRepetitionDefault < ParsletRepetition
|
|
22
|
-
|
|
23
|
-
# Possible re-factoring to consider for the future: roll the functionality of this class in to ParsletRepetition itself.
|
|
24
|
-
# Benefit of keeping it separate is that the ParsletRepetition itself is kept simple.
|
|
25
|
-
def initialize(parseable, min, max = nil, default = nil)
|
|
26
|
-
super(parseable, min, max)
|
|
27
|
-
self.default = default
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def parse(string, options = {})
|
|
31
|
-
catch :ZeroWidthParseSuccess do
|
|
32
|
-
return super(string, options)
|
|
33
|
-
end
|
|
34
|
-
@default.clone rescue @default
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def eql?(other)
|
|
38
|
-
other.instance_of? ParsletRepetitionDefault and @min == other.min and @max == other.max and @parseable.eql? other.parseable and @default == other.default
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
protected
|
|
42
|
-
|
|
43
|
-
# For determining equality.
|
|
44
|
-
attr_reader :default
|
|
45
|
-
|
|
46
|
-
private
|
|
47
|
-
|
|
48
|
-
def hash_offset
|
|
49
|
-
69
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def update_hash
|
|
53
|
-
@hash = super + @default.hash # let super calculate its share of the hash first
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def default=(default)
|
|
57
|
-
@default = (default.clone rescue default)
|
|
58
|
-
@default.extend(LocationTracking)
|
|
59
|
-
update_hash
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
end # class ParsletRepetitionDefault
|
|
63
|
-
end # class Grammar
|
|
64
|
-
end # module Walrus
|
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
# Copyright 2007 Wincent Colaiuta
|
|
2
|
-
# This program is free software: you can redistribute it and/or modify
|
|
3
|
-
# it under the terms of the GNU General Public License as published by
|
|
4
|
-
# the Free Software Foundation, either version 3 of the License, or
|
|
5
|
-
# (at your option) any later version.
|
|
6
|
-
#
|
|
7
|
-
# This program is distributed in the hope that it will be useful,
|
|
8
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
-
# GNU General Public License for more details.
|
|
11
|
-
#
|
|
12
|
-
# You should have received a copy of the GNU General Public License
|
|
13
|
-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
14
|
-
|
|
15
|
-
require 'walrus'
|
|
16
|
-
|
|
17
|
-
module Walrus
|
|
18
|
-
class Grammar
|
|
19
|
-
|
|
20
|
-
class ParsletSequence < ParsletCombination
|
|
21
|
-
|
|
22
|
-
attr_reader :hash
|
|
23
|
-
|
|
24
|
-
# first and second may not be nil.
|
|
25
|
-
def initialize(first, second, *others)
|
|
26
|
-
raise ArgumentError if first.nil?
|
|
27
|
-
raise ArgumentError if second.nil?
|
|
28
|
-
@components = [first, second] + others
|
|
29
|
-
update_hash
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Override so that sequences are appended to an existing sequence:
|
|
33
|
-
# Consider the following example:
|
|
34
|
-
# A & B
|
|
35
|
-
# This constitutes a single sequence:
|
|
36
|
-
# (A & B)
|
|
37
|
-
# If we then make this a three-element sequence:
|
|
38
|
-
# A & B & C
|
|
39
|
-
# We are effectively creating an nested sequence containing the original sequence and an additional element:
|
|
40
|
-
# ((A & B) & C)
|
|
41
|
-
# Although such a nested sequence is correctly parsed it produces unwanted nesting in the results because instead of returning a one-dimensional an array of results:
|
|
42
|
-
# [a, b, c]
|
|
43
|
-
# It returns a nested array:
|
|
44
|
-
# [[a, b], c]
|
|
45
|
-
# The solution to this unwanted nesting is to allowing appending to an existing sequence by using the private "append" method.
|
|
46
|
-
# This ensures that:
|
|
47
|
-
# A & B & C
|
|
48
|
-
# Translates to a single sequence:
|
|
49
|
-
# (A & B & C)
|
|
50
|
-
# And a single, uni-dimensional results array:
|
|
51
|
-
# [a, b, c]
|
|
52
|
-
#
|
|
53
|
-
def &(next_parslet)
|
|
54
|
-
append(next_parslet)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
SKIP_FIRST = true
|
|
58
|
-
NO_SKIP = false
|
|
59
|
-
|
|
60
|
-
def parse(string, options = {})
|
|
61
|
-
parse_common(NO_SKIP, string, options)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def parse_remainder(string, options = {})
|
|
65
|
-
parse_common(SKIP_FIRST, string, options)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def parse_common(skip_first, string, options = {})
|
|
69
|
-
raise ArgumentError if string.nil?
|
|
70
|
-
state = ParserState.new(string, options)
|
|
71
|
-
last_caught = nil # keep track of the last kind of throw to be caught
|
|
72
|
-
left_recursion = false # keep track of whether left recursion was detected
|
|
73
|
-
|
|
74
|
-
@components.each_with_index do |parseable, index|
|
|
75
|
-
|
|
76
|
-
if index == 0 # for first component only
|
|
77
|
-
if skip_first
|
|
78
|
-
next
|
|
79
|
-
end
|
|
80
|
-
begin
|
|
81
|
-
check_left_recursion(parseable, options)
|
|
82
|
-
rescue LeftRecursionException => e
|
|
83
|
-
left_recursion = true
|
|
84
|
-
continuation = nil
|
|
85
|
-
|
|
86
|
-
# Ruby 1.9/2.0 will almost certainly not support continuations so need to come up with an alternative
|
|
87
|
-
# for handling left recursion here
|
|
88
|
-
value = callcc { |c| continuation = c }
|
|
89
|
-
if value == continuation # first time that we're here
|
|
90
|
-
e.continuation = continuation # pack continuation into exception
|
|
91
|
-
raise e # and propagate
|
|
92
|
-
else
|
|
93
|
-
grammar = state.options[:grammar]
|
|
94
|
-
rule_name = state.options[:rule_name]
|
|
95
|
-
state.parsed(grammar.wrap(value, rule_name))
|
|
96
|
-
next
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
catch :ProcessNextComponent do
|
|
102
|
-
catch :NotPredicateSuccess do
|
|
103
|
-
catch :AndPredicateSuccess do
|
|
104
|
-
catch :ZeroWidthParseSuccess do
|
|
105
|
-
begin
|
|
106
|
-
parsed = parseable.memoizing_parse(state.remainder, state.options)
|
|
107
|
-
state.parsed(parsed)
|
|
108
|
-
rescue SkippedSubstringException => e
|
|
109
|
-
state.skipped(e)
|
|
110
|
-
rescue ParseError => e # failed, will try to skip; save original error in case skipping fails
|
|
111
|
-
if options.has_key?(:skipping_override)
|
|
112
|
-
skipping_parslet = options[:skipping_override]
|
|
113
|
-
elsif options.has_key?(:skipping)
|
|
114
|
-
skipping_parslet = options[:skipping]
|
|
115
|
-
else
|
|
116
|
-
skipping_parslet = nil
|
|
117
|
-
end
|
|
118
|
-
raise e if skipping_parslet.nil? # no skipper defined, raise original error
|
|
119
|
-
begin
|
|
120
|
-
parsed = skipping_parslet.memoizing_parse(state.remainder, state.options) # guard against self references (possible infinite recursion) here?
|
|
121
|
-
state.skipped(parsed)
|
|
122
|
-
redo # skipping succeeded, try to redo
|
|
123
|
-
rescue ParseError
|
|
124
|
-
raise e # skipping didn't help either, raise original error
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
last_caught = nil
|
|
128
|
-
throw :ProcessNextComponent # can't use "next" here because it would only break out of innermost "do" rather than continuing the iteration
|
|
129
|
-
end
|
|
130
|
-
last_caught = :ZeroWidthParseSuccess
|
|
131
|
-
throw :ProcessNextComponent
|
|
132
|
-
end
|
|
133
|
-
last_caught = :AndPredicateSuccess
|
|
134
|
-
throw :ProcessNextComponent
|
|
135
|
-
end
|
|
136
|
-
last_caught = :NotPredicateSuccess
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
if left_recursion
|
|
141
|
-
results = recurse(state)
|
|
142
|
-
else
|
|
143
|
-
results = state.results
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
if skip_first
|
|
147
|
-
return results
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
if results.respond_to? :empty? and results.empty? and last_caught
|
|
151
|
-
throw last_caught
|
|
152
|
-
else
|
|
153
|
-
results
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
# Left-recursion helper
|
|
159
|
-
def recurse(state)
|
|
160
|
-
return state.results if state.remainder == '' # further recursion is not possible
|
|
161
|
-
new_state = ParserState.new(state.remainder, state.options)
|
|
162
|
-
last_successful_result = nil
|
|
163
|
-
while state.remainder != ''
|
|
164
|
-
begin
|
|
165
|
-
new_results = parse_remainder(new_state.remainder, new_state.options)
|
|
166
|
-
new_state.parsed(new_results)
|
|
167
|
-
last_successful_result = ArrayResult[last_successful_result || state.results, new_results]
|
|
168
|
-
rescue ParseError
|
|
169
|
-
break
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
last_successful_result || state.results
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
def eql?(other)
|
|
176
|
-
return false if not other.instance_of? ParsletSequence
|
|
177
|
-
other_components = other.components
|
|
178
|
-
return false if @components.length != other_components.length
|
|
179
|
-
for i in 0..(@components.length - 1)
|
|
180
|
-
return false unless @components[i].eql? other_components[i]
|
|
181
|
-
end
|
|
182
|
-
true
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
protected
|
|
186
|
-
|
|
187
|
-
# For determining equality.
|
|
188
|
-
attr_reader :components
|
|
189
|
-
|
|
190
|
-
private
|
|
191
|
-
|
|
192
|
-
def hash_offset
|
|
193
|
-
40
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def update_hash
|
|
197
|
-
@hash = hash_offset # fixed offset to avoid unwanted collisions with similar classes
|
|
198
|
-
@components.each { |parseable| @hash += parseable.hash }
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
# Appends another Parslet, ParsletCombination or Predicate to the receiver and returns the receiver.
|
|
202
|
-
# Raises if next_parslet is nil.
|
|
203
|
-
# Cannot use << as a method name because Ruby cannot parse it without the self, and self is not allowed as en explicit receiver for private messages.
|
|
204
|
-
def append(next_parslet)
|
|
205
|
-
raise ArgumentError if next_parslet.nil?
|
|
206
|
-
@components << next_parslet.to_parseable
|
|
207
|
-
update_hash
|
|
208
|
-
self
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
end # class ParsletSequence
|
|
212
|
-
end # class Grammar
|
|
213
|
-
end # module Walrus
|
|
214
|
-
|