twig_ruby 0.0.1 → 0.0.2

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 (167) hide show
  1. checksums.yaml +4 -4
  2. data/lib/tasks/twig_parity.rake +278 -0
  3. data/lib/twig/auto_hash.rb +7 -1
  4. data/lib/twig/callable.rb +28 -1
  5. data/lib/twig/compiler.rb +35 -3
  6. data/lib/twig/environment.rb +198 -41
  7. data/lib/twig/error/base.rb +81 -16
  8. data/lib/twig/error/loader.rb +8 -0
  9. data/lib/twig/error/logic.rb +8 -0
  10. data/lib/twig/error/runtime.rb +8 -0
  11. data/lib/twig/expression_parser/base.rb +30 -0
  12. data/lib/twig/expression_parser/expression_parsers.rb +57 -0
  13. data/lib/twig/expression_parser/infix/arrow.rb +31 -0
  14. data/lib/twig/expression_parser/infix/binary.rb +34 -0
  15. data/lib/twig/expression_parser/infix/conditional_ternary.rb +39 -0
  16. data/lib/twig/expression_parser/infix/dot.rb +72 -0
  17. data/lib/twig/expression_parser/infix/filter.rb +43 -0
  18. data/lib/twig/expression_parser/infix/function.rb +67 -0
  19. data/lib/twig/expression_parser/infix/is.rb +53 -0
  20. data/lib/twig/expression_parser/infix/is_not.rb +19 -0
  21. data/lib/twig/expression_parser/infix/parses_arguments.rb +84 -0
  22. data/lib/twig/expression_parser/infix/square_bracket.rb +66 -0
  23. data/lib/twig/expression_parser/infix_expression_parser.rb +34 -0
  24. data/lib/twig/expression_parser/prefix/grouping.rb +60 -0
  25. data/lib/twig/expression_parser/prefix/literal.rb +244 -0
  26. data/lib/twig/expression_parser/prefix/unary.rb +29 -0
  27. data/lib/twig/expression_parser/prefix_expression_parser.rb +18 -0
  28. data/lib/twig/extension/base.rb +26 -4
  29. data/lib/twig/extension/core.rb +1076 -48
  30. data/lib/twig/extension/debug.rb +25 -0
  31. data/lib/twig/extension/escaper.rb +73 -0
  32. data/lib/twig/extension/rails.rb +10 -57
  33. data/lib/twig/extension/string_loader.rb +19 -0
  34. data/lib/twig/extension_set.rb +117 -20
  35. data/lib/twig/file_extension_escaping_strategy.rb +35 -0
  36. data/lib/twig/lexer.rb +225 -81
  37. data/lib/twig/loader/array.rb +25 -8
  38. data/lib/twig/loader/chain.rb +93 -0
  39. data/lib/twig/loader/filesystem.rb +106 -7
  40. data/lib/twig/node/auto_escape.rb +18 -0
  41. data/lib/twig/node/base.rb +58 -2
  42. data/lib/twig/node/block.rb +2 -0
  43. data/lib/twig/node/block_reference.rb +5 -1
  44. data/lib/twig/node/body.rb +7 -0
  45. data/lib/twig/node/cache.rb +50 -0
  46. data/lib/twig/node/capture.rb +22 -0
  47. data/lib/twig/node/deprecated.rb +53 -0
  48. data/lib/twig/node/do.rb +19 -0
  49. data/lib/twig/node/embed.rb +43 -0
  50. data/lib/twig/node/expression/array.rb +29 -20
  51. data/lib/twig/node/expression/arrow_function.rb +55 -0
  52. data/lib/twig/node/expression/assign_name.rb +1 -1
  53. data/lib/twig/node/expression/binary/and.rb +17 -0
  54. data/lib/twig/node/expression/binary/base.rb +6 -4
  55. data/lib/twig/node/expression/binary/boolean.rb +24 -0
  56. data/lib/twig/node/expression/binary/concat.rb +20 -0
  57. data/lib/twig/node/expression/binary/elvis.rb +35 -0
  58. data/lib/twig/node/expression/binary/ends_with.rb +24 -0
  59. data/lib/twig/node/expression/binary/floor_div.rb +21 -0
  60. data/lib/twig/node/expression/binary/has_every.rb +20 -0
  61. data/lib/twig/node/expression/binary/has_some.rb +20 -0
  62. data/lib/twig/node/expression/binary/in.rb +20 -0
  63. data/lib/twig/node/expression/binary/matches.rb +24 -0
  64. data/lib/twig/node/expression/binary/not_in.rb +20 -0
  65. data/lib/twig/node/expression/binary/null_coalesce.rb +49 -0
  66. data/lib/twig/node/expression/binary/or.rb +15 -0
  67. data/lib/twig/node/expression/binary/starts_with.rb +24 -0
  68. data/lib/twig/node/expression/binary/xor.rb +17 -0
  69. data/lib/twig/node/expression/block_reference.rb +62 -0
  70. data/lib/twig/node/expression/call.rb +126 -6
  71. data/lib/twig/node/expression/constant.rb +3 -1
  72. data/lib/twig/node/expression/filter/default.rb +37 -0
  73. data/lib/twig/node/expression/filter/raw.rb +31 -0
  74. data/lib/twig/node/expression/filter.rb +2 -2
  75. data/lib/twig/node/expression/function.rb +37 -0
  76. data/lib/twig/node/expression/get_attribute.rb +51 -7
  77. data/lib/twig/node/expression/hash.rb +75 -0
  78. data/lib/twig/node/expression/helper_method.rb +6 -18
  79. data/lib/twig/node/expression/macro_reference.rb +43 -0
  80. data/lib/twig/node/expression/name.rb +42 -8
  81. data/lib/twig/node/expression/operator_escape.rb +13 -0
  82. data/lib/twig/node/expression/parent.rb +28 -0
  83. data/lib/twig/node/expression/support_defined_test.rb +23 -0
  84. data/lib/twig/node/expression/ternary.rb +7 -1
  85. data/lib/twig/node/expression/test/base.rb +26 -0
  86. data/lib/twig/node/expression/test/constant.rb +35 -0
  87. data/lib/twig/node/expression/test/defined.rb +33 -0
  88. data/lib/twig/node/expression/test/divisible_by.rb +23 -0
  89. data/lib/twig/node/expression/test/even.rb +21 -0
  90. data/lib/twig/node/expression/test/iterable.rb +21 -0
  91. data/lib/twig/node/expression/test/mapping.rb +21 -0
  92. data/lib/twig/node/expression/test/null.rb +21 -0
  93. data/lib/twig/node/expression/test/odd.rb +21 -0
  94. data/lib/twig/node/expression/test/same_as.rb +23 -0
  95. data/lib/twig/node/expression/test/sequence.rb +21 -0
  96. data/lib/twig/node/expression/unary/base.rb +3 -1
  97. data/lib/twig/node/expression/unary/not.rb +18 -0
  98. data/lib/twig/node/expression/unary/spread.rb +18 -0
  99. data/lib/twig/node/expression/unary/string_cast.rb +18 -0
  100. data/lib/twig/node/expression/variable/assign_template.rb +35 -0
  101. data/lib/twig/node/expression/variable/local.rb +35 -0
  102. data/lib/twig/node/expression/variable/template.rb +54 -0
  103. data/lib/twig/node/for.rb +38 -8
  104. data/lib/twig/node/for_loop.rb +0 -22
  105. data/lib/twig/node/if.rb +4 -1
  106. data/lib/twig/node/import.rb +32 -0
  107. data/lib/twig/node/include.rb +38 -8
  108. data/lib/twig/node/macro.rb +79 -0
  109. data/lib/twig/node/module.rb +278 -23
  110. data/lib/twig/node/output.rb +7 -0
  111. data/lib/twig/node/print.rb +4 -1
  112. data/lib/twig/node/set.rb +72 -0
  113. data/lib/twig/node/text.rb +4 -1
  114. data/lib/twig/node/with.rb +50 -0
  115. data/lib/twig/node/yield.rb +6 -1
  116. data/lib/twig/node_traverser.rb +50 -0
  117. data/lib/twig/node_visitor/base.rb +30 -0
  118. data/lib/twig/node_visitor/escaper.rb +165 -0
  119. data/lib/twig/node_visitor/safe_analysis.rb +127 -0
  120. data/lib/twig/node_visitor/spreader.rb +39 -0
  121. data/lib/twig/output_buffer.rb +14 -12
  122. data/lib/twig/parser.rb +281 -8
  123. data/lib/twig/rails/config.rb +33 -0
  124. data/lib/twig/rails/engine.rb +44 -0
  125. data/lib/twig/rails/renderer.rb +41 -0
  126. data/lib/twig/runtime/argument_spreader.rb +46 -0
  127. data/lib/twig/runtime/context.rb +154 -0
  128. data/lib/twig/runtime/enumerable_hash.rb +51 -0
  129. data/lib/twig/runtime/escaper.rb +155 -0
  130. data/lib/twig/runtime/loop_context.rb +81 -0
  131. data/lib/twig/runtime/loop_iterator.rb +60 -0
  132. data/lib/twig/runtime/spread.rb +21 -0
  133. data/lib/twig/runtime_loader/base.rb +12 -0
  134. data/lib/twig/runtime_loader/factory.rb +23 -0
  135. data/lib/twig/template.rb +267 -14
  136. data/lib/twig/template_wrapper.rb +42 -0
  137. data/lib/twig/token.rb +28 -2
  138. data/lib/twig/token_parser/apply.rb +48 -0
  139. data/lib/twig/token_parser/auto_escape.rb +45 -0
  140. data/lib/twig/token_parser/base.rb +26 -0
  141. data/lib/twig/token_parser/block.rb +4 -4
  142. data/lib/twig/token_parser/cache.rb +31 -0
  143. data/lib/twig/token_parser/deprecated.rb +40 -0
  144. data/lib/twig/token_parser/do.rb +19 -0
  145. data/lib/twig/token_parser/embed.rb +62 -0
  146. data/lib/twig/token_parser/extends.rb +4 -3
  147. data/lib/twig/token_parser/for.rb +14 -9
  148. data/lib/twig/token_parser/from.rb +57 -0
  149. data/lib/twig/token_parser/guard.rb +65 -0
  150. data/lib/twig/token_parser/if.rb +9 -9
  151. data/lib/twig/token_parser/import.rb +29 -0
  152. data/lib/twig/token_parser/include.rb +2 -2
  153. data/lib/twig/token_parser/macro.rb +109 -0
  154. data/lib/twig/token_parser/set.rb +76 -0
  155. data/lib/twig/token_parser/use.rb +54 -0
  156. data/lib/twig/token_parser/with.rb +36 -0
  157. data/lib/twig/token_parser/yield.rb +7 -7
  158. data/lib/twig/token_stream.rb +23 -3
  159. data/lib/twig/twig_filter.rb +20 -0
  160. data/lib/twig/twig_function.rb +37 -0
  161. data/lib/twig/twig_test.rb +31 -0
  162. data/lib/twig/util/callable_arguments_extractor.rb +227 -0
  163. data/lib/twig_ruby.rb +21 -2
  164. metadata +145 -6
  165. data/lib/twig/context.rb +0 -64
  166. data/lib/twig/expression_parser.rb +0 -517
  167. data/lib/twig/railtie.rb +0 -60
@@ -1,517 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Twig
4
- # @!attribute [r] environment
5
- # @return [Environment]
6
- class ExpressionParser
7
- attr_reader :environment
8
-
9
- OPERATOR_LEFT = 1
10
- OPERATOR_RIGHT = 2
11
-
12
- # @param [Parser] parser
13
- # @param [Environment] environment
14
- def initialize(parser, environment)
15
- @parser = parser
16
- @environment = environment
17
- end
18
-
19
- # @return [Node::Expression::Base]
20
- def parse_expression(precedence = 0)
21
- # @todo parse arrow
22
-
23
- expr = primary
24
- token = parser.current_token
25
-
26
- while binary?(token) && binary_operators[token.value.to_sym][:precedence] >= precedence
27
- operator = binary_operators[token.value.to_sym]
28
- parser.stream.next
29
-
30
- next_precedence = operator[:precedence]
31
- next_precedence += 1 if operator[:associativity] == OPERATOR_LEFT
32
-
33
- expr1 = parse_expression(next_precedence)
34
-
35
- # @type [Node::Expression::Binary::Base]
36
- expr = operator[:class].new(expr, expr1, token.lineno)
37
- expr.attributes[:operator] = "binary_#{token.value}"
38
-
39
- token = parser.current_token
40
- end
41
-
42
- return parse_ternary_expression(expr) if precedence.zero?
43
-
44
- expr
45
- end
46
-
47
- # @return [Node::Expression::Base]
48
- def parse_primary_expression
49
- token = parser.current_token
50
-
51
- case token.type
52
- when Token::SYMBOL_TYPE
53
- parser.stream.next
54
-
55
- node = Node::Expression::Constant.new(token.value.to_sym, token.lineno)
56
- when Token::NAME_TYPE
57
- parser.stream.next
58
-
59
- node = case token.value
60
- when 'true', 'TRUE'
61
- Node::Expression::Constant.new(true, token.lineno)
62
- when 'false', 'FALSE'
63
- Node::Expression::Constant.new(false, token.lineno)
64
- when 'null', 'NULL', 'nil'
65
- Node::Expression::Constant.new(nil, token.lineno)
66
- else
67
- if parser.current_token.value == '('
68
- get_function_node(token.value, token.lineno)
69
- else
70
- # @todo should be a context variable
71
- Node::Expression::Name.new(token.value, token.lineno)
72
- end
73
- end
74
- when Token::NUMBER_TYPE
75
- parser.stream.next
76
-
77
- node = Node::Expression::Constant.new(token.value, token.lineno)
78
- when Token::STRING_TYPE, Token::INTERPOLATION_START_TYPE
79
- node = parse_string_expression
80
- when Token::PUNCTUATION_TYPE
81
- case token.value
82
- when '['
83
- node = parse_sequence_expression
84
- when '{'
85
- node = parse_mapping_expression
86
- else
87
- Error::Syntax.new(
88
- "Unexpected token #{token.type} of value #{token.value}",
89
- token.lineno,
90
- parser.stream.source
91
- )
92
- end
93
- else
94
- raise Error::Syntax.new(
95
- "Unexpected token '#{token.type}' of value '#{token.value}'",
96
- token.lineno,
97
- parser.stream.source
98
- )
99
- end
100
-
101
- parse_post_fix_expression(node)
102
- end
103
-
104
- # @param [Node::Expression::Base] node
105
- # @return [Node::Expression::Base]
106
- def parse_post_fix_expression(node)
107
- loop do
108
- token = parser.current_token
109
-
110
- unless token.type == Token::PUNCTUATION_TYPE
111
- break
112
- end
113
-
114
- case token.value
115
- when '.', '['
116
- node = parse_subscript_expression(node)
117
- when '|'
118
- node = parse_filter_expression(node)
119
- else
120
- break
121
- end
122
- end
123
-
124
- node
125
- end
126
-
127
- def parse_subscript_expression(node)
128
- if parser.stream.next.value == '.'
129
- return parse_subscript_expression_dot(node)
130
- end
131
-
132
- parse_subscript_expression_array(node)
133
- end
134
-
135
- def parse_subscript_expression_dot(node)
136
- stream = parser.stream
137
- token = stream.current
138
- lineno = token.lineno
139
- arguments = Node::Expression::Array.new({}, lineno)
140
- token = stream.next
141
-
142
- if token.type == Token::NAME_TYPE
143
- attribute = Node::Expression::Constant.new(token.value, token.lineno)
144
- end
145
-
146
- Node::Expression::GetAttribute.new(node, attribute, arguments, nil, token.lineno)
147
- end
148
-
149
- def parse_sequence_expression
150
- stream = parser.stream
151
- stream.expect(Token::PUNCTUATION_TYPE, '[', 'A sequence element was expected')
152
-
153
- node = Node::Expression::Array.new({}, stream.current.lineno)
154
- first = true
155
-
156
- # raise stream.debug
157
-
158
- until stream.test(Token::PUNCTUATION_TYPE, ']')
159
- unless first
160
- stream.expect(Token::PUNCTUATION_TYPE, ',', 'A sequence element must be followed by a comma')
161
-
162
- # trailing comma
163
- break if stream.test(Token::PUNCTUATION_TYPE, ']')
164
- end
165
-
166
- first = false
167
-
168
- if stream.next_if(Token::SPREAD_TYPE)
169
- expr = parse_expression
170
- expr.attributes[:spread] = true
171
- node.add_element(expr)
172
- else
173
- node.add_element(parse_expression)
174
- end
175
- end
176
-
177
- stream.expect(Token::PUNCTUATION_TYPE, ']', 'An opened sequence is not properly closed')
178
-
179
- node
180
- end
181
-
182
- def parse_mapping_expression
183
- stream = parser.stream
184
- stream.expect(Token::PUNCTUATION_TYPE, '{', 'A mapping element was expected')
185
-
186
- node = Node::Expression::Array.new({}, stream.current.lineno)
187
- first = true
188
-
189
- until stream.test(Token::PUNCTUATION_TYPE, '}')
190
- unless first
191
- stream.expect(Token::PUNCTUATION_TYPE, ',', 'A mapping value must be followed by a comma')
192
-
193
- # trailing comma
194
- if stream.test(Token::PUNCTUATION_TYPE, '}')
195
- break
196
- end
197
- end
198
-
199
- first = false
200
-
201
- if stream.next_if(Token::SPREAD_TYPE)
202
- value = parse_expression
203
- value.attributes[:spread] = true
204
- node.add_element(value)
205
-
206
- next
207
- end
208
-
209
- # a mapping key can be:
210
- #
211
- # * a number -- 12
212
- # * a string -- 'a'
213
- # * a name, which is equivalent to a symbol -- a
214
- # * an expression, which must be enclosed in parentheses -- (1 + 2)
215
- if (token = stream.next_if(Token::NAME_TYPE))
216
- key = Node::Expression::Constant.new(token.value.to_sym, token.lineno)
217
-
218
- # {a} is a shortcut for {a: a}
219
- if stream.test(Token::PUNCTUATION_TYPE, %w[, }])
220
- value = Node::Expression::Variable::Context.new(key.attributes[:value], key.lineno)
221
- node.add_element(value, key.to_sym)
222
-
223
- next
224
- end
225
- elsif (token = stream.next_if(Token::STRING_TYPE)) || (token = stream.next_if(Token::NUMBER_TYPE))
226
- key = Node::Expression::Constant.new(token.value, token.lineno)
227
- elsif stream.test(Token::PUNCTUATION_TYPE, '(')
228
- key = parse_expression
229
- else
230
- current = stream.current
231
-
232
- raise Error::Syntax.new(
233
- 'A mapping key must be a quoted string, number, name, or expression in parentheses ' \
234
- "expected token '#{current.type}' of value '#{current.value}'",
235
- current.lineno,
236
- stream.source
237
- )
238
- end
239
-
240
- stream.expect(Token::PUNCTUATION_TYPE, ':', 'A mapping key must be followed by a colon (:)')
241
- value = parse_expression
242
-
243
- node.add_element(value, key)
244
- end
245
-
246
- stream.expect(Token::PUNCTUATION_TYPE, '}', 'An opened mapping is not properly closed')
247
-
248
- node
249
- end
250
-
251
- def get_function_node(name, line)
252
- # @todo lots of stuff in this method
253
- args = parse_only_arguments
254
-
255
- if environment.helper_method?(name.to_sym)
256
- Node::Expression::HelperMethod.new(name, args, line)
257
- else
258
- raise Error::Syntax.new("Unknown function '#{name}'", line, parser.stream.source)
259
- end
260
- end
261
-
262
- def parse_only_arguments
263
- args = AutoHash.new
264
- stream = parser.stream
265
- stream.expect(Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis')
266
- has_spread = false
267
-
268
- until stream.test(Token::PUNCTUATION_TYPE, ')')
269
- unless args.empty?
270
- stream.expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma')
271
-
272
- # if above was trailing comma, exit early
273
- break if stream.test(Token::PUNCTUATION_TYPE, ')')
274
- end
275
-
276
- if stream.next_if(Token::SPREAD_TYPE)
277
- has_spread = true
278
- value = Node::Expression::Unary::Spread.new(parse_expression, stream.current.lineno)
279
- elsif has_spread
280
- raise Error::Syntax.new(
281
- 'Normal arguments must be placed before argument unpacking.',
282
- stream.current.lineno,
283
- stream.source
284
- )
285
- else
286
- value = parse_expression
287
- end
288
-
289
- name = nil
290
- if (token = stream.next_if(Token::OPERATOR_TYPE, '=')) ||
291
- (token = stream.next_if(Token::PUNCTUATION_TYPE, ':'))
292
- unless value.class <= Node::Expression::Name
293
- raise Error::Syntax.new(
294
- "A parameter name must be a string, #{value.class.name} given.",
295
- token.lineno,
296
- stream.source
297
- )
298
- end
299
-
300
- name = value.attributes[:name]
301
- value = parse_expression
302
- end
303
-
304
- if name.nil?
305
- args.add(value)
306
- else
307
- args[name] = value
308
- end
309
- end
310
-
311
- stream.expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis')
312
-
313
- Node::Nodes.new(args)
314
- end
315
-
316
- # @return [Parser]
317
- attr_reader :parser
318
-
319
- # @return [Node::Expression::Base]
320
- def primary
321
- token = parser.current_token
322
-
323
- if unary?(token)
324
- operator = unary_operators[token.value.to_sym]
325
- parser.stream.next
326
-
327
- expr = parse_expression(operator[:precedence])
328
- expr = operator[:class].new(expr, token.lineno)
329
- expr.attributes[:operator] = "unary_#{token.value}"
330
-
331
- return parse_post_fix_expression(expr)
332
- elsif token.test(Token::PUNCTUATION_TYPE, '(')
333
- parser.stream.next
334
- expr = parse_expression.set_explicit_parentheses
335
-
336
- parser.stream.expect(Token::PUNCTUATION_TYPE, ')', 'Open parenthesis not closed')
337
-
338
- return parse_post_fix_expression(expr)
339
- end
340
-
341
- parse_primary_expression
342
- end
343
-
344
- # @param [Node::Expression] expr
345
- # @return [Node::Expression]
346
- def parse_ternary_expression(expr)
347
- while parser.stream.next_if(Token::PUNCTUATION_TYPE, '?')
348
- expr2 = parse_expression
349
- expr3 = if parser.stream.next_if(Token::PUNCTUATION_TYPE, ':')
350
- parse_expression
351
- else
352
- Node::Expression::Constant.new('', parser.current_token.lineno)
353
- end
354
-
355
- expr = Node::Expression::Ternary.new(expr, expr2, expr3, parser.current_token.lineno)
356
- end
357
-
358
- expr
359
- end
360
-
361
- def parse_filter_expression(node)
362
- parser.stream.next
363
-
364
- parse_filter_expression_raw(node)
365
- end
366
-
367
- def parse_filter_expression_raw(node)
368
- loop do
369
- token = parser.stream.expect(Token::NAME_TYPE)
370
-
371
- arguments = if parser.stream.test(Token::PUNCTUATION_TYPE, '(')
372
- parse_only_arguments
373
- else
374
- Node::Empty.new
375
- end
376
-
377
- filter = get_filter(token.value, token.lineno)
378
- node = filter.node_class.new(node, filter, arguments, token.lineno)
379
-
380
- unless parser.stream.test(Token::PUNCTUATION_TYPE, '|')
381
- break
382
- end
383
-
384
- parser.stream.next
385
- end
386
-
387
- node
388
- end
389
-
390
- def parse_arguments
391
- raise NotImplementedError
392
- end
393
-
394
- # @return [Node::Nodes]
395
- def parse_assignment_expression
396
- stream = parser.stream
397
- targets = AutoHash.new
398
-
399
- loop do
400
- token = parser.current_token
401
-
402
- if stream.test(Token::OPERATOR_TYPE) && token.value.match(Lexer::REGEX_NAME)
403
- # in this context, string operators are variables names
404
- parser.stream.next
405
- else
406
- stream.expect(Token::NAME_TYPE, nil, 'Only variables can be assigned to')
407
- end
408
-
409
- targets << Node::Expression::Variable::AssignContext.new(token.value, token.lineno)
410
-
411
- unless stream.next_if(Token::PUNCTUATION_TYPE, ',')
412
- break
413
- end
414
- end
415
-
416
- Node::Nodes.new(targets)
417
- end
418
-
419
- def parse_string_expression
420
- stream = parser.stream
421
- nodes = []
422
-
423
- # a string cannot be followed by another string in a single expression
424
- next_can_be_string = true
425
-
426
- loop do
427
- if next_can_be_string && (token = stream.next_if(Token::STRING_TYPE))
428
- nodes << Node::Expression::Constant.new(token.value, token.lineno)
429
- next_can_be_string = false
430
- elsif stream.next_if(Token::INTERPOLATION_START_TYPE)
431
- nodes << parse_expression
432
- stream.expect(Token::INTERPOLATION_END_TYPE)
433
- next_can_be_string = true
434
- else
435
- break
436
- end
437
- end
438
-
439
- expr = nodes.shift
440
- nodes.each do |node|
441
- expr = Node::Expression::Binary::Concat(expr, node, node.lineno)
442
- end
443
-
444
- expr
445
- end
446
-
447
- # @param [Node::Base]
448
- # @return [Node::Expression::Base]
449
- def parse_subscript_expression_array(node)
450
- stream = parser.stream
451
- token = stream.current
452
- lineno = token.lineno
453
- arguments = Node::Expression::Array.new({}, lineno)
454
-
455
- slice = false
456
- if stream.test(Token::PUNCTUATION_TYPE, ':')
457
- slice = true
458
- attribute = Node::Expression::Constant.new(0, token.lineno)
459
- else
460
- attribute = parse_expression
461
- end
462
-
463
- if stream.next_if(Token::PUNCTUATION_TYPE, ':')
464
- slice = true
465
- end
466
-
467
- if slice
468
- length = if stream.test(Token::PUNCTUATION_TYPE, ']')
469
- Node::Expression::Constant.new(nil, token.lineno)
470
- else
471
- parse_expression
472
- end
473
-
474
- filter = get_filter('slice', token.lineno)
475
- arguments = Node::Nodes.new(AutoHash.new.add(attribute, length))
476
- filter = filter.node_class.new(node, filter, arguments, token.lineno)
477
-
478
- stream.expect(Token::PUNCTUATION_TYPE, ']')
479
-
480
- return filter
481
- end
482
-
483
- stream.expect(Token::PUNCTUATION_TYPE, ']')
484
-
485
- Node::Expression::GetAttribute.new(node, attribute, arguments, Template::ARRAY_CALL, lineno)
486
- end
487
-
488
- # @return [Hash]
489
- def unary_operators
490
- @unary_operators ||= environment.operators[0]
491
- end
492
-
493
- # @return [Hash]
494
- def binary_operators
495
- @binary_operators ||= environment.operators[1]
496
- end
497
-
498
- # @param [Token] token
499
- def unary?(token)
500
- token.test(Token::OPERATOR_TYPE) && unary_operators.key?(token.value.to_sym)
501
- end
502
-
503
- # @param [Token] token
504
- def binary?(token)
505
- token.test(Token::OPERATOR_TYPE) && binary_operators.key?(token.value.to_sym)
506
- end
507
-
508
- # @return [Filter]
509
- def get_filter(name, lineno)
510
- unless (filter = environment.filter(name))
511
- raise Error::Syntax.new("Unknown '#{name}' filter", lineno, parser.stream.source)
512
- end
513
-
514
- filter
515
- end
516
- end
517
- end
data/lib/twig/railtie.rb DELETED
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Twig
4
- class RailsRenderer
5
- def call(template, source)
6
- <<~TEMPLATE
7
- ::#{self.class.name}.
8
- environment.
9
- load_template("#{template.short_identifier}", call_context: self, output_buffer: @output_buffer).
10
- render(local_assigns)
11
-
12
- @output_buffer
13
- TEMPLATE
14
- end
15
-
16
- def translate_location(spot, backtrace_location, source)
17
- template = backtrace_location.path.delete_prefix(Rails.root.to_s)
18
-
19
- # Attempt to recompile the template to find where the syntax error is
20
- # otherwise just do what would have happened anyway
21
- begin
22
- self.class.environment.render_ruby(template)
23
- rescue ::Twig::Error::Syntax => e
24
- return spot.merge({
25
- first_lineno: e.lineno,
26
- last_lineno: e.lineno + 1,
27
- script_lines: source.lines,
28
- })
29
- rescue StandardError
30
- # Nothing, don't add another exception to the problem
31
- end
32
-
33
- spot
34
- end
35
-
36
- def self.loader
37
- @loader ||= ::Twig::Loader::Filesystem.new(
38
- Rails.root,
39
- %w[/ /app/views]
40
- )
41
- end
42
-
43
- def self.environment
44
- options = {
45
- cache: Rails.root.join('tmp/cache/twig').to_s,
46
- debug: Rails.env.development?,
47
- }
48
-
49
- @environment ||= ::Twig::Environment.new(loader, options).tap do |env|
50
- env.add_extension(::Twig::Extension::Rails.new)
51
- end
52
- end
53
- end
54
-
55
- class Railtie < ::Rails::Railtie
56
- initializer 'twig_ruby.configure_rails_initialization' do
57
- ActionView::Template.register_template_handler :twig, RailsRenderer.new
58
- end
59
- end
60
- end