walrus 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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,34 +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
|
-
class Parslet
|
20
|
-
|
21
|
-
include Walrus::Grammar::ParsletCombining
|
22
|
-
include Walrus::Grammar::Memoizing
|
23
|
-
|
24
|
-
def to_parseable
|
25
|
-
self
|
26
|
-
end
|
27
|
-
|
28
|
-
def parse(string, options = {})
|
29
|
-
raise NotImplementedError # subclass responsibility
|
30
|
-
end
|
31
|
-
|
32
|
-
end # class Parslet
|
33
|
-
end # class Grammar
|
34
|
-
end # module Walrus
|
@@ -1,128 +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 ParsletChoice < ParsletCombination
|
21
|
-
|
22
|
-
attr_reader :hash
|
23
|
-
|
24
|
-
# Either parameter may be a Parslet or a ParsletCombination.
|
25
|
-
# Neither parmeter may be nil.
|
26
|
-
def initialize(left, right, *others)
|
27
|
-
raise ArgumentError if left.nil?
|
28
|
-
raise ArgumentError if right.nil?
|
29
|
-
@alternatives = [left, right] + others
|
30
|
-
update_hash
|
31
|
-
end
|
32
|
-
|
33
|
-
# Override so that alternatives are appended to an existing sequence:
|
34
|
-
# Consider the following example:
|
35
|
-
# A | B
|
36
|
-
# This constitutes a single choice:
|
37
|
-
# (A | B)
|
38
|
-
# If we then make this a three-element sequence:
|
39
|
-
# A | B | C
|
40
|
-
# We are effectively creating an nested sequence containing the original sequence and an additional element:
|
41
|
-
# ((A | B) | C)
|
42
|
-
# Although such a nested sequence is correctly parsed it is not as architecturally clean as a single sequence without nesting:
|
43
|
-
# (A | B | C)
|
44
|
-
# This method allows us to use the architecturally cleaner format.
|
45
|
-
#
|
46
|
-
def |(next_parslet)
|
47
|
-
append(next_parslet)
|
48
|
-
end
|
49
|
-
|
50
|
-
# First tries to parse the left option, falling back and trying the right option and then the any subsequent options in the others instance variable on failure. If no options successfully complete parsing then an ParseError is raised. Any zero-width parse successes thrown by alternative parsers will flow on to a higher level.
|
51
|
-
def parse(string, options = {})
|
52
|
-
raise ArgumentError if string.nil?
|
53
|
-
error = nil # for error reporting purposes will track which parseable gets farthest to the right before failing
|
54
|
-
left_recursion = nil # will also track any left recursion that we detect
|
55
|
-
@alternatives.each do |parseable|
|
56
|
-
begin
|
57
|
-
result = parseable.memoizing_parse(string, options) # successful parse
|
58
|
-
if left_recursion and left_recursion.continuation # and we have a continuation
|
59
|
-
continuation = left_recursion.continuation # continuations are once-only, one-way tickets
|
60
|
-
left_recursion = nil # set this to nil so as not to call it again without meaning to
|
61
|
-
continuation.call(result) # so jump back to where we were before
|
62
|
-
end
|
63
|
-
return result
|
64
|
-
rescue LeftRecursionException => e
|
65
|
-
left_recursion = e
|
66
|
-
|
67
|
-
# TODO:
|
68
|
-
# it's not enough to just catch this kind of exception and remember the last one
|
69
|
-
# may need to accumulate these in an array
|
70
|
-
# consider the example rule:
|
71
|
-
# :a, :a & :b | :a & :c | :a & :d | :b
|
72
|
-
# the first option will raise a LeftRecursionException
|
73
|
-
# the next option will raise for the same reason
|
74
|
-
# the third likewise
|
75
|
-
# finally we get to the fourth option, the first which might succeed
|
76
|
-
# at that point we should have three continuations
|
77
|
-
# we should try the first, falling back to the second and third if necessary
|
78
|
-
# on successfully retrying, need to start all over again and try all the options again, just in case further recursion is possible
|
79
|
-
# so it is quite complicated
|
80
|
-
# the question is, is it more complicated than the other ways of getting right-associativity into Walrus-generated parsers?
|
81
|
-
|
82
|
-
rescue ParseError => e
|
83
|
-
if error.nil?
|
84
|
-
error = e
|
85
|
-
else
|
86
|
-
error = e unless error.rightmost?(e)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
raise ParseError.new('no valid alternatives while parsing "%s" (%s)' % [string, error.to_s],
|
91
|
-
:line_end => error.line_end, :column_end => error.column_end) # should generally report the rightmost error
|
92
|
-
end
|
93
|
-
|
94
|
-
def eql?(other)
|
95
|
-
return false if not other.instance_of? ParsletChoice
|
96
|
-
other_alternatives = other.alternatives
|
97
|
-
return false if @alternatives.length != other_alternatives.length
|
98
|
-
for i in 0..(@alternatives.length - 1)
|
99
|
-
return false unless @alternatives[i].eql? other_alternatives[i]
|
100
|
-
end
|
101
|
-
true
|
102
|
-
end
|
103
|
-
|
104
|
-
protected
|
105
|
-
|
106
|
-
# For determining equality.
|
107
|
-
attr_reader :alternatives
|
108
|
-
|
109
|
-
private
|
110
|
-
|
111
|
-
def update_hash
|
112
|
-
@hash = 30 # fixed offset to avoid unwanted collisions with similar classes
|
113
|
-
@alternatives.each { |parseable| @hash += parseable.hash }
|
114
|
-
end
|
115
|
-
|
116
|
-
# Appends another Parslet (or ParsletCombination) to the receiver and returns the receiver.
|
117
|
-
# Raises if parslet is nil.
|
118
|
-
# 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.
|
119
|
-
def append(next_parslet)
|
120
|
-
raise ArgumentError if next_parslet.nil?
|
121
|
-
@alternatives << next_parslet.to_parseable
|
122
|
-
update_hash
|
123
|
-
self
|
124
|
-
end
|
125
|
-
|
126
|
-
end # class ParsletChoice
|
127
|
-
end # class Grammar
|
128
|
-
end # module Walrus
|
@@ -1,32 +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 ParsletCombination
|
21
|
-
|
22
|
-
include Walrus::Grammar::ParsletCombining
|
23
|
-
include Walrus::Grammar::Memoizing
|
24
|
-
|
25
|
-
def to_parseable
|
26
|
-
self
|
27
|
-
end
|
28
|
-
|
29
|
-
end # module ParsletCombination
|
30
|
-
|
31
|
-
end # class Grammar
|
32
|
-
end # module Walrus
|
@@ -1,160 +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
|
-
# The ParsletCombining module, together with the ParsletCombination class and its subclasses, provides simple container classes for encapsulating relationships among Parslets. By storing this information outside of the Parslet objects themselves their design is kept clean and they can become immutable objects which are much more easily copied and shared among multiple rules in a Grammar.
|
21
|
-
module ParsletCombining
|
22
|
-
|
23
|
-
# Convenience method.
|
24
|
-
def memoizing_parse(string, options = {})
|
25
|
-
self.to_parseable.memoizing_parse(string, options)
|
26
|
-
end
|
27
|
-
|
28
|
-
# Convenience method.
|
29
|
-
def parse(string, options = {})
|
30
|
-
self.to_parseable.parse(string, options)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Defines a sequence of Parslets (or ParsletCombinations).
|
34
|
-
# Returns a ParsletSequence instance.
|
35
|
-
def sequence(first, second, *others)
|
36
|
-
Walrus::Grammar::ParsletSequence.new(first.to_parseable, second.to_parseable, *others)
|
37
|
-
end
|
38
|
-
|
39
|
-
# Shorthand for ParsletCombining.sequence(first, second).
|
40
|
-
def &(next_parslet)
|
41
|
-
self.sequence(self, next_parslet)
|
42
|
-
end
|
43
|
-
|
44
|
-
# Defines a sequence of Parslets similar to the sequence method but with the difference that the contents of array results from the component parslets will be merged into a single array rather than being added as arrays. To illustrate:
|
45
|
-
#
|
46
|
-
# 'foo' & 'bar'.one_or_more # returns results like ['foo', ['bar', 'bar', 'bar']]
|
47
|
-
# 'foo' >> 'bar'.one_or_more # returns results like ['foo', 'bar', 'bar', 'bar']
|
48
|
-
#
|
49
|
-
def merge(first, second, *others)
|
50
|
-
Walrus::Grammar::ParsletMerge.new(first.to_parseable, second.to_parseable, *others)
|
51
|
-
end
|
52
|
-
|
53
|
-
# Shorthand for ParsletCombining.sequence(first, second)
|
54
|
-
def >>(next_parslet)
|
55
|
-
self.merge(self, next_parslet)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Defines a choice of Parslets (or ParsletCombinations).
|
59
|
-
# Returns a ParsletChoice instance.
|
60
|
-
def choice(left, right, *others)
|
61
|
-
Walrus::Grammar::ParsletChoice.new(left.to_parseable, right.to_parseable, *others)
|
62
|
-
end
|
63
|
-
|
64
|
-
# Shorthand for ParsletCombining.choice(left, right)
|
65
|
-
def |(alternative_parslet)
|
66
|
-
self.choice(self, alternative_parslet)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Defines a repetition of the supplied Parslet (or ParsletCombination).
|
70
|
-
# Returns a ParsletRepetition instance.
|
71
|
-
def repetition(parslet, min, max)
|
72
|
-
Walrus::Grammar::ParsletRepetition.new(parslet.to_parseable, min, max)
|
73
|
-
end
|
74
|
-
|
75
|
-
# Shorthand for ParsletCombining.repetition.
|
76
|
-
def repeat(min = nil, max = nil)
|
77
|
-
self.repetition(self, min, max)
|
78
|
-
end
|
79
|
-
|
80
|
-
def repetition_with_default(parslet, min, max, default)
|
81
|
-
Walrus::Grammar::ParsletRepetitionDefault.new(parslet.to_parseable, min, max, default)
|
82
|
-
end
|
83
|
-
|
84
|
-
def repeat_with_default(min = nil, max = nil, default = nil)
|
85
|
-
self.repetition_with_default(self, min, max, default)
|
86
|
-
end
|
87
|
-
|
88
|
-
# Shorthand for ParsletCombining.repetition(0, 1).
|
89
|
-
# This method optionally takes a single parameter specifying what object should be returned as a placeholder when there are no matches; this is useful for packing into ASTs where it may be better to parse an empty Array rather than nil. The specified object is cloned and returned in the event that there are no matches. As a convenience, the specified object is automatically extended using the LocationTracking module (this is a convenience so that you can specify empty Arrays, "[]", rather than explicitly passing an "ArrayResult.new")
|
90
|
-
def optional(default_return_value = NoParameterMarker.instance)
|
91
|
-
if default_return_value == NoParameterMarker.instance
|
92
|
-
self.repeat(0, 1) # default behaviour
|
93
|
-
else
|
94
|
-
self.repeat_with_default(0, 1, default_return_value)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
# Alternative to optional.
|
99
|
-
def zero_or_one
|
100
|
-
self.optional
|
101
|
-
end
|
102
|
-
|
103
|
-
# possible synonym "star"
|
104
|
-
def zero_or_more(default_return_value = NoParameterMarker.instance)
|
105
|
-
if default_return_value == NoParameterMarker.instance
|
106
|
-
self.repeat(0) # default behaviour
|
107
|
-
else
|
108
|
-
self.repeat_with_default(0, nil, default_return_value)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# possible synonym "plus"
|
113
|
-
def one_or_more
|
114
|
-
self.repeat(1)
|
115
|
-
end
|
116
|
-
|
117
|
-
# Parsing Expression Grammar support.
|
118
|
-
# Succeeds if parslet succeeds but consumes no input (throws an :AndPredicateSuccess symbol).
|
119
|
-
def and_predicate(parslet)
|
120
|
-
Walrus::Grammar::AndPredicate.new(parslet.to_parseable)
|
121
|
-
end
|
122
|
-
|
123
|
-
# Shorthand for and_predicate
|
124
|
-
# Strictly speaking, this shorthand breaks with established Ruby practice that "?" at the end of a method name should indicate a method that returns true or false.
|
125
|
-
def and?
|
126
|
-
self.and_predicate(self)
|
127
|
-
end
|
128
|
-
|
129
|
-
# Parsing Expression Grammar support.
|
130
|
-
# Succeeds if parslet fails (throws a :NotPredicateSuccess symbol).
|
131
|
-
# Fails if parslet succeeds (raise a ParseError).
|
132
|
-
# Consumes no output.
|
133
|
-
# This method will almost invariably be used in conjuntion with the & operator, like this:
|
134
|
-
# rule :foo, :p1 & :p2.not_predicate
|
135
|
-
# rule :foo, :p1 & :p2.not!
|
136
|
-
def not_predicate(parslet)
|
137
|
-
Walrus::Grammar::NotPredicate.new(parslet.to_parseable)
|
138
|
-
end
|
139
|
-
|
140
|
-
# Shorthand for not_predicate.
|
141
|
-
# Strictly speaking, this shorthand breaks with established Ruby practice that "!" at the end of a method name should indicate a destructive behaviour on (mutation of) the receiver.
|
142
|
-
def not!
|
143
|
-
self.not_predicate(self)
|
144
|
-
end
|
145
|
-
|
146
|
-
# Succeeds if parsing succeeds, consuming the output, but doesn't actually return anything.
|
147
|
-
# This is for elements which are required but which shouldn't appear in the final AST.
|
148
|
-
def omission(parslet)
|
149
|
-
Walrus::Grammar::ParsletOmission.new(parslet.to_parseable)
|
150
|
-
end
|
151
|
-
|
152
|
-
# Shorthand for ParsletCombining.omission
|
153
|
-
def skip
|
154
|
-
self.omission(self)
|
155
|
-
end
|
156
|
-
|
157
|
-
end # module ParsletCombining
|
158
|
-
|
159
|
-
end # class Grammar
|
160
|
-
end # module Walrus
|
@@ -1,94 +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 ParsletMerge < ParsletSequence
|
21
|
-
|
22
|
-
def parse(string, options = {})
|
23
|
-
raise ArgumentError if string.nil?
|
24
|
-
state = ParserState.new(string, options)
|
25
|
-
last_caught = nil # keep track of the last kind of throw to be caught
|
26
|
-
@components.each do |parseable|
|
27
|
-
catch :ProcessNextComponent do
|
28
|
-
catch :NotPredicateSuccess do
|
29
|
-
catch :AndPredicateSuccess do
|
30
|
-
catch :ZeroWidthParseSuccess do
|
31
|
-
begin
|
32
|
-
parsed = parseable.memoizing_parse(state.remainder, state.options)
|
33
|
-
if parsed.respond_to? :each
|
34
|
-
parsed.each { |element| state.parsed(element) }
|
35
|
-
else
|
36
|
-
state.parsed(parsed)
|
37
|
-
end
|
38
|
-
rescue SkippedSubstringException => e
|
39
|
-
state.skipped(e)
|
40
|
-
# rescue ParseError => e # failed, will try to skip; save original error in case skipping fails
|
41
|
-
# if options.has_key?(:skipping_override) : skipping_parslet = options[:skipping_override]
|
42
|
-
# elsif options.has_key?(:skipping) : skipping_parslet = options[:skipping]
|
43
|
-
# else skipping_parslet = nil
|
44
|
-
# end
|
45
|
-
# raise e if skipping_parslet.nil? # no skipper defined, raise original error
|
46
|
-
# begin
|
47
|
-
# parsed = skipping_parslet.memoizing_parse(state.remainder, state.options) # guard against self references (possible infinite recursion) here?
|
48
|
-
# state.skipped(parsed)
|
49
|
-
# redo # skipping succeeded, try to redo
|
50
|
-
# rescue ParseError
|
51
|
-
# raise e # skipping didn't help either, raise original error
|
52
|
-
# end
|
53
|
-
end
|
54
|
-
last_caught = nil
|
55
|
-
throw :ProcessNextComponent # can't use "next" here because it will only break out of innermost "do"
|
56
|
-
end
|
57
|
-
last_caught = :ZeroWidthParseSuccess
|
58
|
-
throw :ProcessNextComponent
|
59
|
-
end
|
60
|
-
last_caught = :AndPredicateSuccess
|
61
|
-
throw :ProcessNextComponent
|
62
|
-
end
|
63
|
-
last_caught = :NotPredicateSuccess
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
if state.results.respond_to? :empty? and state.results.empty? and
|
68
|
-
throw last_caught
|
69
|
-
else
|
70
|
-
state.results
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
def eql?(other)
|
76
|
-
return false if not other.instance_of? ParsletMerge
|
77
|
-
other_components = other.components
|
78
|
-
return false if @components.length != other_components.length
|
79
|
-
for i in 0..(@components.length - 1)
|
80
|
-
return false unless @components[i].eql? other_components[i]
|
81
|
-
end
|
82
|
-
true
|
83
|
-
end
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
def hash_offset
|
88
|
-
53
|
89
|
-
end
|
90
|
-
|
91
|
-
end # class ParsletMerge
|
92
|
-
end # class Grammar
|
93
|
-
end # module Walrus
|
94
|
-
|