racc 1.4.14 → 1.4.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) 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/info.rb +2 -2
  7. data/test/assets/bibtex.y +141 -0
  8. data/test/assets/cadenza.y +170 -0
  9. data/test/assets/cast.y +926 -0
  10. data/test/assets/csspool.y +729 -0
  11. data/test/assets/edtf.y +583 -0
  12. data/test/assets/huia.y +318 -0
  13. data/test/assets/journey.y +47 -0
  14. data/test/assets/liquor.y +313 -0
  15. data/test/assets/machete.y +423 -0
  16. data/test/assets/macruby.y +2197 -0
  17. data/test/assets/mediacloth.y +599 -0
  18. data/test/assets/mof.y +649 -0
  19. data/test/assets/namae.y +302 -0
  20. data/test/assets/nasl.y +626 -0
  21. data/test/assets/nokogiri-css.y +255 -0
  22. data/test/assets/opal.y +1807 -0
  23. data/test/assets/php_serialization.y +98 -0
  24. data/test/assets/rdblockparser.y +576 -0
  25. data/test/assets/rdinlineparser.y +561 -0
  26. data/test/assets/riml.y +665 -0
  27. data/test/assets/ruby18.y +1943 -0
  28. data/test/assets/ruby19.y +2174 -0
  29. data/test/assets/ruby20.y +2350 -0
  30. data/test/assets/ruby21.y +2359 -0
  31. data/test/assets/ruby22.y +2381 -0
  32. data/test/assets/tp_plus.y +622 -0
  33. data/test/assets/twowaysql.y +278 -0
  34. data/test/helper.rb +31 -15
  35. data/test/regress/bibtex +474 -0
  36. data/test/regress/cadenza +796 -0
  37. data/test/regress/cast +3425 -0
  38. data/test/regress/csspool +2318 -0
  39. data/test/regress/edtf +1794 -0
  40. data/test/regress/huia +1392 -0
  41. data/test/regress/journey +222 -0
  42. data/test/regress/liquor +885 -0
  43. data/test/regress/machete +833 -0
  44. data/test/regress/mediacloth +1463 -0
  45. data/test/regress/mof +1368 -0
  46. data/test/regress/namae +634 -0
  47. data/test/regress/nasl +2058 -0
  48. data/test/regress/nokogiri-css +836 -0
  49. data/test/regress/opal +6429 -0
  50. data/test/regress/php_serialization +336 -0
  51. data/test/regress/rdblockparser +1061 -0
  52. data/test/regress/rdinlineparser +1243 -0
  53. data/test/regress/riml +3297 -0
  54. data/test/regress/ruby18 +6351 -0
  55. data/test/regress/ruby22 +7456 -0
  56. data/test/regress/tp_plus +1933 -0
  57. data/test/regress/twowaysql +556 -0
  58. data/test/test_racc_command.rb +177 -0
  59. metadata +75 -20
@@ -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