racc 1.4.14-java → 1.4.15-java

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.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/Manifest.txt +50 -0
  3. data/ext/racc/com/headius/racc/Cparse.java +66 -23
  4. data/ext/racc/cparse.c +1 -1
  5. data/ext/racc/depend +1 -1
  6. data/lib/racc/cparse-jruby.jar +0 -0
  7. data/lib/racc/info.rb +2 -2
  8. data/test/assets/bibtex.y +141 -0
  9. data/test/assets/cadenza.y +170 -0
  10. data/test/assets/cast.y +926 -0
  11. data/test/assets/csspool.y +729 -0
  12. data/test/assets/edtf.y +583 -0
  13. data/test/assets/huia.y +318 -0
  14. data/test/assets/journey.y +47 -0
  15. data/test/assets/liquor.y +313 -0
  16. data/test/assets/machete.y +423 -0
  17. data/test/assets/macruby.y +2197 -0
  18. data/test/assets/mediacloth.y +599 -0
  19. data/test/assets/mof.y +649 -0
  20. data/test/assets/namae.y +302 -0
  21. data/test/assets/nasl.y +626 -0
  22. data/test/assets/nokogiri-css.y +255 -0
  23. data/test/assets/opal.y +1807 -0
  24. data/test/assets/php_serialization.y +98 -0
  25. data/test/assets/rdblockparser.y +576 -0
  26. data/test/assets/rdinlineparser.y +561 -0
  27. data/test/assets/riml.y +665 -0
  28. data/test/assets/ruby18.y +1943 -0
  29. data/test/assets/ruby19.y +2174 -0
  30. data/test/assets/ruby20.y +2350 -0
  31. data/test/assets/ruby21.y +2359 -0
  32. data/test/assets/ruby22.y +2381 -0
  33. data/test/assets/tp_plus.y +622 -0
  34. data/test/assets/twowaysql.y +278 -0
  35. data/test/helper.rb +50 -34
  36. data/test/regress/bibtex +474 -0
  37. data/test/regress/cadenza +796 -0
  38. data/test/regress/cast +3425 -0
  39. data/test/regress/csspool +2318 -0
  40. data/test/regress/edtf +1794 -0
  41. data/test/regress/huia +1392 -0
  42. data/test/regress/journey +222 -0
  43. data/test/regress/liquor +885 -0
  44. data/test/regress/machete +833 -0
  45. data/test/regress/mediacloth +1463 -0
  46. data/test/regress/mof +1368 -0
  47. data/test/regress/namae +634 -0
  48. data/test/regress/nasl +2058 -0
  49. data/test/regress/nokogiri-css +836 -0
  50. data/test/regress/opal +6429 -0
  51. data/test/regress/php_serialization +336 -0
  52. data/test/regress/rdblockparser +1061 -0
  53. data/test/regress/rdinlineparser +1243 -0
  54. data/test/regress/riml +3297 -0
  55. data/test/regress/ruby18 +6351 -0
  56. data/test/regress/ruby22 +7456 -0
  57. data/test/regress/tp_plus +1933 -0
  58. data/test/regress/twowaysql +556 -0
  59. data/test/test_racc_command.rb +177 -0
  60. metadata +80 -25
  61. data/.gemtest +0 -0
@@ -0,0 +1,318 @@
1
+ # Copyright (c) 2014 James Harton
2
+ #
3
+ # MIT License
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ class Huia::Parser
25
+
26
+ token
27
+ IDENTIFIER EQUAL PLUS MINUS ASTERISK FWD_SLASH COLON FLOAT INTEGER STRING
28
+ EXPO INDENT OUTDENT OPAREN CPAREN DOT SIGNATURE NL EOF PIPE COMMA NIL TRUE
29
+ FALSE EQUALITY CALL SELF CONSTANT CHAR DOUBLE_TICK_STRING
30
+ DOUBLE_TICK_STRING_END INTERPOLATE_START INTERPOLATE_END BOX LSQUARE
31
+ RSQUARE FACES LFACE RFACE BANG TILDE RETURN NOT_EQUALITY OR AND GT LT
32
+ GTE LTE AT
33
+
34
+ prechigh
35
+ left EXPO
36
+ left BANG TILDE
37
+ left ASTERISK FWD_SLASH PERCENT
38
+ left PLUS MINUS
39
+
40
+ right EQUAL
41
+ preclow
42
+
43
+ rule
44
+ statements: statement
45
+ | statements statement { return scope }
46
+
47
+ statement: expr eol { return scope.append val[0] }
48
+ | expr { return scope.append val[0] }
49
+ | eol { return scope }
50
+
51
+ eol: NL | EOF
52
+ nlq: NL |
53
+
54
+ expr: literal
55
+ | grouped_expr
56
+ | binary_op
57
+ | unary_op
58
+ | method_call
59
+ | constant
60
+ | variable
61
+ | array
62
+ | hash
63
+ | return
64
+
65
+ return: return_expr
66
+ | return_nil
67
+ return_expr: RETURN expr { return n(:Return, val[1]) }
68
+ return_nil: RETURN { return n(:Return, n(:Nil)) }
69
+
70
+ array: empty_array
71
+ | array_list
72
+
73
+ empty_array: BOX { return n :Array }
74
+
75
+ array_list: LSQUARE array_items RSQUARE { return val[1] }
76
+ array_items: expr { return n :Array, [val[0]] }
77
+ | array_items COMMA expr { val[0].append(val[2]); return val[0] }
78
+
79
+ hash: empty_hash
80
+ | hash_list
81
+ empty_hash: FACES { return n :Hash }
82
+ hash_list: LFACE hash_items RFACE { return val[1] }
83
+ hash_items: hash_item { return n :Hash, val[0] }
84
+ | hash_items COMMA hash_item { val[0].append(val[2]); return val[0] }
85
+ hash_item: expr COLON expr { return n :HashItem, val[0], val[2] }
86
+
87
+ constant: CONSTANT { return constant val[0] }
88
+
89
+ indented: indented_w_stmts
90
+ | indented_w_expr
91
+ | indented_wo_stmts
92
+ indented_w_stmts: indent statements outdent { return val[0] }
93
+ indented_w_expr: indent expr outdent { return val[0].append(val[1]) }
94
+ indented_wo_stmts: indent outdent { return val[0] }
95
+ outdent: OUTDENT { return pop_scope }
96
+
97
+
98
+ indent_w_args: indent_pipe indent_args PIPE nlq INDENT { return val[0] }
99
+ indent_pipe: PIPE { return push_scope }
100
+ indent_wo_args: INDENT { return push_scope }
101
+ indent: indent_w_args
102
+ | indent_wo_args
103
+
104
+ indent_args: indent_arg
105
+ | indent_args COMMA indent_arg
106
+ indent_arg: arg_var { return scope.add_argument val[0] }
107
+ | arg_var EQUAL expr { return n :Assignment, val[0], val[2] }
108
+ arg_var: IDENTIFIER { return n :Variable, val[0] }
109
+
110
+ method_call: method_call_on_object
111
+ | method_call_on_self
112
+ | method_call_on_closure
113
+ method_call_on_object: expr DOT call_signature { return n :MethodCall, val[0], val[2] }
114
+ | expr DOT IDENTIFIER { return n :MethodCall, val[0], n(:CallSignature, val[2]) }
115
+ method_call_on_self: call_signature { return n :MethodCall, scope_instance, val[0] }
116
+
117
+ method_call_on_closure: AT call_signature { return n :MethodCall, this_closure, val[1] }
118
+ | AT IDENTIFIER { return n :MethodCall, this_closure, n(:CallSignature, val[1]) }
119
+
120
+ call_signature: call_arguments
121
+ | call_simple_name
122
+ call_simple_name: CALL { return n :CallSignature, val[0] }
123
+ call_argument: SIGNATURE call_passed_arg { return n :CallSignature, val[0], [val[1]] }
124
+ call_passed_arg: call_passed_simple
125
+ | call_passed_indented
126
+ call_passed_simple: expr
127
+ | expr NL
128
+ call_passed_indented: indented
129
+ | indented NL
130
+ call_arguments: call_argument { return val[0] }
131
+ | call_arguments call_argument { return val[0].concat_signature val[1] }
132
+
133
+ grouped_expr: OPAREN expr CPAREN { return n :Expression, val[1] }
134
+
135
+ variable: IDENTIFIER { return allocate_local val[0] }
136
+
137
+ binary_op: assignment
138
+ | addition
139
+ | subtraction
140
+ | multiplication
141
+ | division
142
+ | exponentiation
143
+ | modulo
144
+ | equality
145
+ | not_equality
146
+ | logical_or
147
+ | logical_and
148
+ | greater_than
149
+ | less_than
150
+ | greater_or_eq
151
+ | less_or_eq
152
+
153
+ assignment: IDENTIFIER EQUAL expr { return allocate_local_assignment val[0], val[2] }
154
+ addition: expr PLUS expr { return binary val[0], val[2], 'plus:' }
155
+ subtraction: expr MINUS expr { return binary val[0], val[2], 'minus:' }
156
+ multiplication: expr ASTERISK expr { return binary val[0], val[2], 'multiplyBy:' }
157
+ division: expr FWD_SLASH expr { return binary val[0], val[2], 'divideBy:' }
158
+ exponentiation: expr EXPO expr { return binary val[0], val[2], 'toThePowerOf:' }
159
+ modulo: expr PERCENT expr { return binary val[0], val[2], 'moduloOf:' }
160
+ equality: expr EQUALITY expr { return binary val[0], val[2], 'isEqualTo:' }
161
+ not_equality: expr NOT_EQUALITY expr { return binary val[0], val[2], 'isNotEqualTo:' }
162
+ logical_or: expr OR expr { return binary val[0], val[2], 'logicalOr:' }
163
+ logical_and: expr AND expr { return binary val[0], val[2], 'logicalAnd:' }
164
+ greater_than: expr GT expr { return binary val[0], val[2], 'isGreaterThan:' }
165
+ less_than: expr LT expr { return binary val[0], val[2], 'isLessThan:' }
166
+ greater_or_eq: expr GTE expr { return binary val[0], val[2], 'isGreaterOrEqualTo:' }
167
+ less_or_eq: expr LTE expr { return binary val[0], val[2], 'isLessOrEqualTo:' }
168
+
169
+ unary_op: unary_not
170
+ | unary_plus
171
+ | unary_minus
172
+ | unary_complement
173
+
174
+ unary_not: BANG expr { return unary val[1], 'unaryNot' }
175
+ unary_plus: PLUS expr { return unary val[1], 'unaryPlus' }
176
+ unary_minus: MINUS expr { return unary val[1], 'unaryMinus' }
177
+ unary_complement: TILDE expr { return unary val[1], 'unaryComplement' }
178
+
179
+ literal: integer
180
+ | float
181
+ | string
182
+ | nil
183
+ | true
184
+ | false
185
+ | self
186
+
187
+ float: FLOAT { return n :Float, val[0] }
188
+ integer: INTEGER { return n :Integer, val[0] }
189
+ nil: NIL { return n :Nil }
190
+ true: TRUE { return n :True }
191
+ false: FALSE { return n :False }
192
+ self: SELF { return n :Self }
193
+
194
+ string: STRING { return n :String, val[0] }
195
+ | interpolated_string
196
+ | empty_string
197
+
198
+ interpolated_string: DOUBLE_TICK_STRING interpolated_string_contents DOUBLE_TICK_STRING_END { return val[1] }
199
+ interpolation: INTERPOLATE_START expr INTERPOLATE_END { return val[1] }
200
+ interpolated_string_contents: interpolated_string_chunk { return n :InterpolatedString, val[0] }
201
+ | interpolated_string_contents interpolated_string_chunk { val[0].append(val[1]); return val[0] }
202
+ interpolated_string_chunk: chars { return val[0] }
203
+ | interpolation { return to_string(val[0]) }
204
+ empty_string: DOUBLE_TICK_STRING DOUBLE_TICK_STRING_END { return n :String, '' }
205
+
206
+ chars: CHAR { return n :String, val[0] }
207
+ | chars CHAR { val[0].append(val[1]); return val[0] }
208
+ end
209
+
210
+ ---- inner
211
+
212
+ attr_accessor :lexer, :scopes, :state
213
+
214
+ def initialize lexer
215
+ @lexer = lexer
216
+ @state = []
217
+ @scopes = []
218
+ push_scope
219
+ end
220
+
221
+ def ast
222
+ @ast ||= do_parse
223
+ @scopes.first
224
+ end
225
+
226
+ def on_error t, val, vstack
227
+ line = lexer.line
228
+ col = lexer.column
229
+ message = "Unexpected #{token_to_str t} at #{lexer.filename} line #{line}:#{col}:\n\n"
230
+
231
+ start = line - 5 > 0 ? line - 5 : 0
232
+ i_size = line.to_s.size
233
+ (start..(start + 5)).each do |i|
234
+ message << sprintf("\t%#{i_size}d: %s\n", i, lexer.get_line(i))
235
+ message << "\t#{' ' * i_size} #{'-' * (col - 1)}^\n" if i == line
236
+ end
237
+
238
+ raise SyntaxError, message
239
+ end
240
+
241
+ def next_token
242
+ nt = lexer.next_computed_token
243
+ # just use a state stack for now, we'll have to do something
244
+ # more sophisticated soon.
245
+ if nt && nt.first == :state
246
+ if nt.last
247
+ state.push << nt.last
248
+ else
249
+ state.pop
250
+ end
251
+ next_token
252
+ else
253
+ nt
254
+ end
255
+ end
256
+
257
+ def push_scope
258
+ new_scope = Huia::AST::Scope.new scope
259
+ new_scope.file = lexer.filename
260
+ new_scope.line = lexer.line
261
+ new_scope.column = lexer.column
262
+ scopes.push new_scope
263
+ new_scope
264
+ end
265
+
266
+ def pop_scope
267
+ scopes.pop
268
+ end
269
+
270
+ def scope
271
+ scopes.last
272
+ end
273
+
274
+ def binary left, right, method
275
+ node(:MethodCall, left, node(:CallSignature, method, [right]))
276
+ end
277
+
278
+ def unary left, method
279
+ node(:MethodCall, left, node(:CallSignature, method))
280
+ end
281
+
282
+ def node type, *args
283
+ Huia::AST.const_get(type).new(*args).tap do |n|
284
+ n.file = lexer.filename
285
+ n.line = lexer.line
286
+ n.column = lexer.column
287
+ end
288
+ end
289
+ alias n node
290
+
291
+ def allocate_local name
292
+ node(:Variable, name).tap do |n|
293
+ scope.allocate_local n
294
+ end
295
+ end
296
+
297
+ def allocate_local_assignment name, value
298
+ node(:Assignment, name, value).tap do |n|
299
+ scope.allocate_local n
300
+ end
301
+ end
302
+
303
+ def this_closure
304
+ allocate_local('@')
305
+ end
306
+
307
+ def scope_instance
308
+ node(:ScopeInstance, scope)
309
+ end
310
+
311
+ def constant name
312
+ return scope_instance if name == 'self'
313
+ node(:Constant, name)
314
+ end
315
+
316
+ def to_string expr
317
+ node(:MethodCall, expr, node(:CallSignature, 'toString'))
318
+ end
@@ -0,0 +1,47 @@
1
+ class Journey::Parser
2
+
3
+ token SLASH LITERAL SYMBOL LPAREN RPAREN DOT STAR OR
4
+
5
+ rule
6
+ expressions
7
+ : expressions expression { result = Cat.new(val.first, val.last) }
8
+ | expression { result = val.first }
9
+ | or
10
+ ;
11
+ expression
12
+ : terminal
13
+ | group
14
+ | star
15
+ ;
16
+ group
17
+ : LPAREN expressions RPAREN { result = Group.new(val[1]) }
18
+ ;
19
+ or
20
+ : expressions OR expression { result = Or.new([val.first, val.last]) }
21
+ ;
22
+ star
23
+ : STAR { result = Star.new(Symbol.new(val.last)) }
24
+ ;
25
+ terminal
26
+ : symbol
27
+ | literal
28
+ | slash
29
+ | dot
30
+ ;
31
+ slash
32
+ : SLASH { result = Slash.new('/') }
33
+ ;
34
+ symbol
35
+ : SYMBOL { result = Symbol.new(val.first) }
36
+ ;
37
+ literal
38
+ : LITERAL { result = Literal.new(val.first) }
39
+ dot
40
+ : DOT { result = Dot.new(val.first) }
41
+ ;
42
+
43
+ end
44
+
45
+ ---- header
46
+
47
+ require 'journey/parser_extras'
@@ -0,0 +1,313 @@
1
+ # Copyright (c) 2012-2013 Peter Zotov <whitequark@whitequark.org>
2
+ # 2012 Yaroslav Markin <yaroslav@markin.net>
3
+ # 2012 Nate Gadgibalaev <nat@xnsv.ru>
4
+ #
5
+ # MIT License
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ class Liquor::Parser
27
+ token comma dot endtag ident integer keyword lblock lblock2 lbracket
28
+ linterp lparen op_div op_eq op_gt op_geq op_lt op_leq op_minus
29
+ op_mod op_mul op_neq op_not op_plus pipe plaintext rblock
30
+ rbracket rinterp rparen string tag_ident
31
+
32
+ prechigh
33
+ left dot
34
+ nonassoc op_uminus op_not
35
+ left op_mul op_div op_mod
36
+ left op_plus op_minus
37
+ left op_eq op_neq op_lt op_leq op_gt op_geq
38
+ left op_and
39
+ left op_or
40
+ preclow
41
+
42
+ expect 15
43
+
44
+ start block
45
+
46
+ rule
47
+ block: /* empty */
48
+ { result = [] }
49
+ | plaintext block
50
+ { result = [ val[0], *val[1] ] }
51
+ | interp block
52
+ { result = [ val[0], *val[1] ] }
53
+ | tag block
54
+ { result = [ val[0], *val[1] ] }
55
+
56
+ interp:
57
+ linterp expr rinterp
58
+ { result = [ :interp, retag(val), val[1] ] }
59
+ | linterp filter_chain rinterp
60
+ { result = [ :interp, retag(val), val[1] ] }
61
+
62
+ primary_expr:
63
+ ident
64
+ | lparen expr rparen
65
+ { result = [ val[1][0], retag(val), *val[1][2..-1] ] }
66
+
67
+ expr:
68
+ integer
69
+ | string
70
+ | tuple
71
+ | ident function_args
72
+ { result = [ :call, retag(val), val[0], val[1] ] }
73
+ | expr lbracket expr rbracket
74
+ { result = [ :index, retag(val), val[0], val[2] ] }
75
+ | expr dot ident function_args
76
+ { result = [ :external, retag(val), val[0], val[2], val[3] ] }
77
+ | expr dot ident
78
+ { result = [ :external, retag(val), val[0], val[2], nil ] }
79
+ | op_minus expr =op_uminus
80
+ { result = [ :uminus, retag(val), val[1] ] }
81
+ | op_not expr
82
+ { result = [ :not, retag(val), val[1] ] }
83
+ | expr op_mul expr
84
+ { result = [ :mul, retag(val), val[0], val[2] ] }
85
+ | expr op_div expr
86
+ { result = [ :div, retag(val), val[0], val[2] ] }
87
+ | expr op_mod expr
88
+ { result = [ :mod, retag(val), val[0], val[2] ] }
89
+ | expr op_plus expr
90
+ { result = [ :plus, retag(val), val[0], val[2] ] }
91
+ | expr op_minus expr
92
+ { result = [ :minus, retag(val), val[0], val[2] ] }
93
+ | expr op_eq expr
94
+ { result = [ :eq, retag(val), val[0], val[2] ] }
95
+ | expr op_neq expr
96
+ { result = [ :neq, retag(val), val[0], val[2] ] }
97
+ | expr op_lt expr
98
+ { result = [ :lt, retag(val), val[0], val[2] ] }
99
+ | expr op_leq expr
100
+ { result = [ :leq, retag(val), val[0], val[2] ] }
101
+ | expr op_gt expr
102
+ { result = [ :gt, retag(val), val[0], val[2] ] }
103
+ | expr op_geq expr
104
+ { result = [ :geq, retag(val), val[0], val[2] ] }
105
+ | expr op_and expr
106
+ { result = [ :and, retag(val), val[0], val[2] ] }
107
+ | expr op_or expr
108
+ { result = [ :or, retag(val), val[0], val[2] ] }
109
+ | primary_expr
110
+
111
+ tuple:
112
+ lbracket tuple_content rbracket
113
+ { result = [ :tuple, retag(val), val[1].compact ] }
114
+
115
+ tuple_content:
116
+ expr comma tuple_content
117
+ { result = [ val[0], *val[2] ] }
118
+ | expr
119
+ { result = [ val[0] ] }
120
+ | /* empty */
121
+ { result = [ ] }
122
+
123
+ function_args:
124
+ lparen function_args_inside rparen
125
+ { result = [ :args, retag(val), *val[1] ] }
126
+
127
+ function_args_inside:
128
+ expr function_keywords
129
+ { result = [ val[0], val[1][2] ] }
130
+ | function_keywords
131
+ { result = [ nil, val[0][2] ] }
132
+
133
+ function_keywords:
134
+ keyword expr function_keywords
135
+ { name = val[0][2].to_sym
136
+ tail = val[2][2]
137
+ loc = retag([ val[0], val[1] ])
138
+
139
+ if tail.include? name
140
+ @errors << SyntaxError.new("duplicate keyword argument `#{val[0][2]}'",
141
+ tail[name][1])
142
+ end
143
+
144
+ hash = {
145
+ name => [ val[1][0], loc, *val[1][2..-1] ]
146
+ }.merge(tail)
147
+
148
+ result = [ :keywords, retag([ loc, val[2] ]), hash ]
149
+ }
150
+ | /* empty */
151
+ { result = [ :keywords, nil, {} ] }
152
+
153
+ filter_chain:
154
+ expr pipe filter_chain_cont
155
+ { result = [ val[0], *val[2] ].
156
+ reduce { |tree, node| node[3][2] = tree; node }
157
+ }
158
+
159
+ filter_chain_cont:
160
+ filter_call pipe filter_chain_cont
161
+ { result = [ val[0], *val[2] ] }
162
+ | filter_call
163
+ { result = [ val[0] ] }
164
+
165
+ filter_call:
166
+ ident function_keywords
167
+ { ident_loc = val[0][1]
168
+ empty_args_loc = { line: ident_loc[:line],
169
+ start: ident_loc[:end] + 1,
170
+ end: ident_loc[:end] + 1, }
171
+ result = [ :call, val[0][1], val[0],
172
+ [ :args, val[1][1] || empty_args_loc, nil, val[1][2] ] ]
173
+ }
174
+
175
+ tag:
176
+ lblock ident expr tag_first_cont
177
+ { result = [ :tag, retag(val), val[1], val[2], *reduce_tag_args(val[3][2]) ] }
178
+ | lblock ident tag_first_cont
179
+ { result = [ :tag, retag(val), val[1], nil, *reduce_tag_args(val[2][2]) ] }
180
+
181
+ # Racc cannot do lookahead across rules. I had to add states
182
+ # explicitly to avoid S/R conflicts. You are not expected to
183
+ # understand this.
184
+
185
+ tag_first_cont:
186
+ rblock
187
+ { result = [ :cont, retag(val), [] ] }
188
+ | keyword tag_first_cont2
189
+ { result = [ :cont, retag(val), [ val[0], *val[1][2] ] ] }
190
+
191
+ tag_first_cont2:
192
+ rblock block lblock2 tag_next_cont
193
+ { result = [ :cont2, val[0][1], [ [:block, val[0][1], val[1] ], *val[3] ] ] }
194
+ | expr tag_first_cont
195
+ { result = [ :cont2, retag(val), [ val[0], *val[1][2] ] ] }
196
+
197
+ tag_next_cont:
198
+ endtag rblock
199
+ { result = [] }
200
+ | keyword tag_next_cont2
201
+ { result = [ val[0], *val[1] ] }
202
+
203
+ tag_next_cont2:
204
+ rblock block lblock2 tag_next_cont
205
+ { result = [ [:block, val[0][1], val[1] ], *val[3] ] }
206
+ | expr keyword tag_next_cont3
207
+ { result = [ val[0], val[1], *val[2] ] }
208
+
209
+ tag_next_cont3:
210
+ rblock block lblock2 tag_next_cont
211
+ { result = [ [:block, val[0][1], val[1] ], *val[3] ] }
212
+ | expr tag_next_cont
213
+ { result = [ val[0], *val[1] ] }
214
+
215
+ ---- inner
216
+ attr_reader :errors, :ast
217
+
218
+ def initialize(tags={})
219
+ super()
220
+
221
+ @errors = []
222
+ @ast = nil
223
+ @tags = tags
224
+ end
225
+
226
+ def success?
227
+ @errors.empty?
228
+ end
229
+
230
+ def parse(string, name='(code)')
231
+ @errors.clear
232
+ @name = name
233
+ @ast = nil
234
+
235
+ begin
236
+ @stream = Lexer.lex(string, @name, @tags)
237
+ @ast = do_parse
238
+ rescue Liquor::SyntaxError => e
239
+ @errors << e
240
+ end
241
+
242
+ success?
243
+ end
244
+
245
+ def next_token
246
+ tok = @stream.shift
247
+ [ tok[0], tok ] if tok
248
+ end
249
+
250
+ TOKEN_NAME_MAP = {
251
+ :comma => ',',
252
+ :dot => '.',
253
+ :lblock => '{%',
254
+ :rblock => '%}',
255
+ :linterp => '{{',
256
+ :rinterp => '}}',
257
+ :lbracket => '[',
258
+ :rbracket => ']',
259
+ :lparen => '(',
260
+ :rparen => ')',
261
+ :pipe => '|',
262
+ :op_not => '!',
263
+ :op_mul => '*',
264
+ :op_div => '/',
265
+ :op_mod => '%',
266
+ :op_plus => '+',
267
+ :op_minus => '-',
268
+ :op_eq => '==',
269
+ :op_neq => '!=',
270
+ :op_lt => '<',
271
+ :op_leq => '<=',
272
+ :op_gt => '>',
273
+ :op_geq => '>=',
274
+ :keyword => 'keyword argument name',
275
+ :kwarg => 'keyword argument',
276
+ :ident => 'identifier',
277
+ }
278
+
279
+ def on_error(error_token_id, error_token, value_stack)
280
+ if token_to_str(error_token_id) == "$end"
281
+ raise Liquor::SyntaxError.new("unexpected end of program", {
282
+ file: @name
283
+ })
284
+ else
285
+ type, (loc, value) = error_token
286
+ type = TOKEN_NAME_MAP[type] || type
287
+
288
+ raise Liquor::SyntaxError.new("unexpected token `#{type}'", loc)
289
+ end
290
+ end
291
+
292
+ def retag(nodes)
293
+ loc = nodes.map { |node| node[1] }.compact
294
+ first, *, last = loc
295
+ return first if last.nil?
296
+
297
+ {
298
+ file: first[:file],
299
+ line: first[:line],
300
+ start: first[:start],
301
+ end: last[:end],
302
+ }
303
+ end
304
+
305
+ def reduce_tag_args(list)
306
+ list.each_slice(2).reduce([]) { |args, (k, v)|
307
+ if v[0] == :block
308
+ args << [ :blockarg, retag([ k, v ]), k, v[2] || [] ]
309
+ else
310
+ args << [ :kwarg, retag([ k, v ]), k, v ]
311
+ end
312
+ }
313
+ end