walrus 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/bin/walrus +19 -12
  2. data/lib/walrus.rb +27 -70
  3. data/lib/walrus/additions/string.rb +21 -35
  4. data/lib/walrus/compile_error.rb +19 -16
  5. data/lib/walrus/compiler.rb +66 -55
  6. data/lib/walrus/contrib/spec/walruscloth_spec.rb +21 -17
  7. data/lib/walrus/contrib/walruscloth.rb +19 -11
  8. data/lib/walrus/document.rb +41 -33
  9. data/lib/walrus/grammar.rb +474 -162
  10. data/lib/walrus/grammar/assignment_expression.rb +33 -0
  11. data/lib/walrus/grammar/block_directive.rb +37 -0
  12. data/lib/walrus/grammar/comment.rb +33 -0
  13. data/lib/walrus/grammar/def_directive.rb +80 -0
  14. data/lib/walrus/grammar/echo_directive.rb +56 -0
  15. data/lib/walrus/grammar/escape_sequence.rb +33 -0
  16. data/lib/walrus/grammar/import_directive.rb +54 -0
  17. data/lib/walrus/grammar/include_directive.rb +36 -0
  18. data/lib/walrus/grammar/instance_variable.rb +33 -0
  19. data/lib/walrus/grammar/literal.rb +33 -0
  20. data/lib/walrus/grammar/message_expression.rb +34 -0
  21. data/lib/walrus/grammar/multiline_comment.rb +70 -0
  22. data/lib/walrus/grammar/placeholder.rb +47 -0
  23. data/lib/walrus/grammar/raw_directive.rb +50 -0
  24. data/lib/walrus/grammar/raw_text.rb +56 -0
  25. data/lib/walrus/grammar/ruby_directive.rb +41 -0
  26. data/lib/walrus/grammar/ruby_expression.rb +42 -0
  27. data/lib/walrus/grammar/set_directive.rb +34 -0
  28. data/lib/walrus/grammar/silent_directive.rb +51 -0
  29. data/lib/walrus/grammar/slurp_directive.rb +36 -0
  30. data/lib/walrus/grammar/super_directive.rb +34 -0
  31. data/lib/walrus/parser.rb +26 -408
  32. data/lib/walrus/runner.rb +37 -20
  33. data/lib/walrus/template.rb +34 -25
  34. data/lib/walrus/version.rb +24 -1
  35. metadata +57 -71
  36. data/ext/extconf.rb +0 -16
  37. data/ext/jindex.c +0 -92
  38. data/lib/walrus/diff.rb +0 -95
  39. data/lib/walrus/grammar/additions/proc.rb +0 -26
  40. data/lib/walrus/grammar/additions/regexp.rb +0 -27
  41. data/lib/walrus/grammar/additions/string.rb +0 -58
  42. data/lib/walrus/grammar/additions/symbol.rb +0 -49
  43. data/lib/walrus/grammar/and_predicate.rb +0 -46
  44. data/lib/walrus/grammar/array_result.rb +0 -25
  45. data/lib/walrus/grammar/continuation_wrapper_exception.rb +0 -34
  46. data/lib/walrus/grammar/left_recursion_exception.rb +0 -33
  47. data/lib/walrus/grammar/location_tracking.rb +0 -115
  48. data/lib/walrus/grammar/match_data_wrapper.rb +0 -71
  49. data/lib/walrus/grammar/memoizing.rb +0 -47
  50. data/lib/walrus/grammar/memoizing_cache.rb +0 -103
  51. data/lib/walrus/grammar/node.rb +0 -66
  52. data/lib/walrus/grammar/not_predicate.rb +0 -46
  53. data/lib/walrus/grammar/parse_error.rb +0 -45
  54. data/lib/walrus/grammar/parser_state.rb +0 -187
  55. data/lib/walrus/grammar/parslet.rb +0 -34
  56. data/lib/walrus/grammar/parslet_choice.rb +0 -128
  57. data/lib/walrus/grammar/parslet_combination.rb +0 -32
  58. data/lib/walrus/grammar/parslet_combining.rb +0 -160
  59. data/lib/walrus/grammar/parslet_merge.rb +0 -94
  60. data/lib/walrus/grammar/parslet_omission.rb +0 -63
  61. data/lib/walrus/grammar/parslet_repetition.rb +0 -106
  62. data/lib/walrus/grammar/parslet_repetition_default.rb +0 -64
  63. data/lib/walrus/grammar/parslet_sequence.rb +0 -214
  64. data/lib/walrus/grammar/predicate.rb +0 -63
  65. data/lib/walrus/grammar/proc_parslet.rb +0 -58
  66. data/lib/walrus/grammar/regexp_parslet.rb +0 -79
  67. data/lib/walrus/grammar/skipped_substring_exception.rb +0 -42
  68. data/lib/walrus/grammar/string_enumerator.rb +0 -53
  69. data/lib/walrus/grammar/string_parslet.rb +0 -81
  70. data/lib/walrus/grammar/string_result.rb +0 -30
  71. data/lib/walrus/grammar/symbol_parslet.rb +0 -69
  72. data/lib/walrus/no_parameter_marker.rb +0 -25
  73. data/lib/walrus/walrus_grammar/assignment_expression.rb +0 -30
  74. data/lib/walrus/walrus_grammar/block_directive.rb +0 -34
  75. data/lib/walrus/walrus_grammar/comment.rb +0 -30
  76. data/lib/walrus/walrus_grammar/def_directive.rb +0 -72
  77. data/lib/walrus/walrus_grammar/echo_directive.rb +0 -50
  78. data/lib/walrus/walrus_grammar/escape_sequence.rb +0 -30
  79. data/lib/walrus/walrus_grammar/import_directive.rb +0 -50
  80. data/lib/walrus/walrus_grammar/include_directive.rb +0 -33
  81. data/lib/walrus/walrus_grammar/instance_variable.rb +0 -30
  82. data/lib/walrus/walrus_grammar/literal.rb +0 -30
  83. data/lib/walrus/walrus_grammar/message_expression.rb +0 -31
  84. data/lib/walrus/walrus_grammar/multiline_comment.rb +0 -60
  85. data/lib/walrus/walrus_grammar/placeholder.rb +0 -46
  86. data/lib/walrus/walrus_grammar/raw_directive.rb +0 -48
  87. data/lib/walrus/walrus_grammar/raw_text.rb +0 -51
  88. data/lib/walrus/walrus_grammar/ruby_directive.rb +0 -35
  89. data/lib/walrus/walrus_grammar/ruby_expression.rb +0 -37
  90. data/lib/walrus/walrus_grammar/set_directive.rb +0 -30
  91. data/lib/walrus/walrus_grammar/silent_directive.rb +0 -50
  92. data/lib/walrus/walrus_grammar/slurp_directive.rb +0 -31
  93. 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
-