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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 674b4b6d23d8a81c8dfe49d8b4c55766df7a2b110038f7dd04bb5dbed09095eb
4
- data.tar.gz: 41320e049677ff964e222b3e65eb2aefe5d8513387834bed644d17abefd6caac
3
+ metadata.gz: 31919ddaf84c7eb3ddbf272092cf86714ae8cc82daeaf3d9c1ea07a968a34957
4
+ data.tar.gz: 9e405d3820213e10efe310023576fa4d7fc10c8b132956cb86a1ce454e79568b
5
5
  SHA512:
6
- metadata.gz: a9d8b98237d0c9e1955efd5bf1abf41877159bf590960c223dde2ca6a5a9c09ba6c1ce15d64c7d9beb0a29aa7eef3aa81d1166e45f572308faa249f67d106b73
7
- data.tar.gz: c30fdc12854565c6c733eecf77ae9fce7e3134d67ca1630642ee21b1c5473a6e4fd2296ac82cf95abbe880a9de946a814562ce4913a73bbe5754bbccb571db89
6
+ metadata.gz: fe9835a8158b2f0c4e7e2147dc2055070b271539389715adfa9965eb131e052de3ce146b9677574e9005912e965c5f87f41da6a462ddc3e878e35854198df9cc
7
+ data.tar.gz: 291c349b4c7af42c23814a06bd4b12ddcb8182368bea8662ca0eb1bebafefb9ca2c61c100a255078b43d19e9a197f581a96ed3610f76cb68178be2a1eaffd65b
@@ -0,0 +1,278 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc 'Tests against Twig PHP fixtures.'
4
+
5
+ GIT_LOCATION = "#{__dir__}/../../tmp/twig-php".freeze
6
+ WONT_IMPLEMENT = %w[
7
+ tags/macro/super_globals.test
8
+ tags/for/non_countable.test
9
+ tags/for/generator.test
10
+ tags/for/objects.test
11
+ tags/for/iterator_aggregate.test
12
+ tags/for/objects_countable.test
13
+ expressions/matches_error_compilation.test
14
+ expressions/matches_error_runtime.test
15
+ functions/enum/invalid_dynamic_enum.test
16
+ functions/enum/invalid_enum.test
17
+ functions/enum/invalid_literal_type.test
18
+ functions/enum/valid.test
19
+ functions/enum_cases/invalid_dynamic_enum.test
20
+ functions/enum_cases/invalid_enum.test
21
+ functions/enum_cases/invalid_literal_type.test
22
+ functions/enum_cases/valid.test
23
+ functions/attribute.legacy.test
24
+ functions/attribute_with_wrong_args.legacy.test
25
+ functions/constant.test
26
+ functions/dump.test
27
+ functions/dump_array.test
28
+ tests/defined_for_attribute.legacy.test
29
+ filters/date_default_format_interval.test
30
+ filters/date_interval.test
31
+ filters/date_immutable.test
32
+ filters/date_modify.test
33
+ operators/not_precedence.test
34
+ operators/concat_vs_add_sub.test
35
+ functions/include/sandbox_disabling.test
36
+ functions/include/sandbox.test
37
+ ].freeze
38
+
39
+ class Color
40
+ def self.colorize(text, color_code)
41
+ "\e[#{color_code}m#{text}\e[0m"
42
+ end
43
+
44
+ def self.red(text)
45
+ colorize(text, 31)
46
+ end
47
+
48
+ def self.green(text)
49
+ colorize(text, 32)
50
+ end
51
+ end
52
+
53
+ task :twig_parity, [:file] do |_t, args|
54
+ require 'rspec/support'
55
+ require_relative '../twig_ruby'
56
+ require_relative '../../test/parity'
57
+
58
+ `git clone -b 4.x https://github.com/twigphp/Twig.git #{GIT_LOCATION}`
59
+
60
+ stats = { pass: 0, fail: 0, total: 0 }
61
+
62
+ fixtures = if args[:file]
63
+ [File.join(GIT_LOCATION, 'tests/Fixtures/', args[:file])]
64
+ else
65
+ Dir.glob("#{GIT_LOCATION}/tests/Fixtures/**/*.test")
66
+ end
67
+
68
+ fixtures.each do |fixture|
69
+ base_name = fixture.delete_prefix("#{GIT_LOCATION}/tests/Fixtures/")
70
+
71
+ next if WONT_IMPLEMENT.include?(base_name)
72
+
73
+ TwigFixture.new(fixture, base_name).call.each do |data|
74
+ stats[:total] += 1
75
+
76
+ if data[:status]
77
+ stats[:pass] += 1
78
+ else
79
+ stats[:fail] += 1
80
+ puts '============================='
81
+ puts "FAIL: #{data[:file].delete_prefix("#{GIT_LOCATION}/tests/Fixtures/")}"
82
+ puts data[:error]
83
+ puts "Link: #{data[:file]}:#{data[:lineno]}"
84
+ puts "Rerun: rake twig_parity[#{base_name}]"
85
+ puts "=============================\n\n"
86
+ end
87
+ end
88
+ end
89
+
90
+ puts <<~STATS
91
+
92
+ Stats:
93
+ #{Color.green("#{stats[:pass]} passed")}
94
+ #{Color.red("#{stats[:fail]} failed")}
95
+ correct: #{(stats[:pass] * 100 / stats[:total]).round(2)}%
96
+ STATS
97
+
98
+ if stats[:fail].positive?
99
+ exit 1
100
+ else
101
+ exit 0
102
+ end
103
+ end
104
+
105
+ class TwigFixture
106
+ EXCEPTION_REGEX = /
107
+ --TEST--\s*(.*?)\s*
108
+ (?:--CONDITION--\s*(.*))?\s*
109
+ (?:--DEPRECATION--\s*(.*?))?\s*
110
+ ((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)\s*
111
+ (?:--DATA--\s*(.*))?\s*
112
+ --EXCEPTION--\s*(.*)
113
+ /mx
114
+
115
+ EXPECT_REGEX = /
116
+ --TEST--\s*(.*?)\s*
117
+ (?:--CONDITION--\s*(.*))?\s*
118
+ (?:--DEPRECATION--\s*(.*?))?\s*
119
+ ((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)
120
+ --DATA--.*?
121
+ --EXPECT--.*
122
+ /mx
123
+
124
+ OUTPUTS_REGEX = /
125
+ --DATA--(.*?)(?:--CONFIG--(.*?))?--EXPECT--(.*?)(?=--DATA--|\z)
126
+ /mx
127
+
128
+ def initialize(file, base_name)
129
+ @file = file
130
+ @base_name = base_name
131
+ end
132
+
133
+ def call
134
+ parse
135
+
136
+ require_relative "#{__dir__}/../../test/fixtures/#{@base_name}.rb"
137
+ examples = Data.examples
138
+
139
+ examples.each.with_index.map do |example, i|
140
+ build_and_run(example[:data], example[:config], example[:gsub] || {}, outputs[i][2], i)
141
+ end
142
+ end
143
+
144
+ private
145
+
146
+ attr_accessor :message, :condition, :deprecation, :templates, :exception, :outputs
147
+
148
+ def build_and_run(data, config, replacements, expected, index)
149
+ gsub_templates = templates.transform_values do |template|
150
+ replace(template, replacements[:fixture]) + (' ' * index)
151
+ end
152
+
153
+ loader = ::Twig::Loader::Array.new(gsub_templates)
154
+ environment = ::Twig::Environment.new(loader, {
155
+ cache: false,
156
+ strict_variables: true,
157
+ auto_reload: true,
158
+ **config,
159
+ })
160
+ environment.add_global('global', 'global')
161
+ environment.add_extension(::TwigTestExtension.new)
162
+ environment.add_extension(::Twig::Extension::Debug.new)
163
+ environment.add_extension(::Twig::Extension::StringLoader.new)
164
+ environment.add_runtime_loader(::Twig::Parity::Runtime::Loader.new(environment))
165
+ expected ||= ''
166
+
167
+ # Reset timezone
168
+ ::Time.zone = 'UTC'
169
+
170
+ # Pass current environment if test is lazy
171
+ data = data.call(environment) if data.is_a?(Proc)
172
+
173
+ begin
174
+ output = environment.load('index.twig').render(data).gsub(/\A[\n ]*/, '').gsub(/[\n ]*\z/, '')
175
+ output = replace(output, replacements[:output])
176
+ expected = expected.gsub(/\A[\n ]*/, '').gsub(/[\n ]*\z/, '')
177
+ expected = replace(expected, replacements[:result])
178
+
179
+ if output == expected
180
+ {
181
+ message:,
182
+ file: @file,
183
+ status: true,
184
+ output:,
185
+ }
186
+ else
187
+ {
188
+ message:,
189
+ file: @file,
190
+ status: false,
191
+ error: ::RSpec::Support::Differ.new.diff_as_string(output, expected),
192
+ }
193
+ end
194
+ rescue ::Twig::Error::Base => e
195
+ if exception
196
+ message_only = exception.match(/Twig\\Error\\\w+: (.*)/)&.captures&.[](0)
197
+ message_only = replace(message_only, replacements[:exception]) if message_only
198
+ exception_matches = message_only == e.message
199
+ error = ::RSpec::Support::Differ.new.diff_as_string(e.message, message_only)
200
+ # error = "#{Color.red("- #{message_only}")}\n#{Color.green("+ #{e.message}")}"
201
+ else
202
+ error = Color.red(e.message)
203
+ end
204
+
205
+ {
206
+ message:,
207
+ file: @file,
208
+ status: exception_matches,
209
+ lineno: e.lineno,
210
+ error:,
211
+ }
212
+ rescue Exception => e # rubocop:disable Lint/RescueException
213
+ {
214
+ message:,
215
+ file: @file,
216
+ status: exception_matches,
217
+ lineno: -1,
218
+ error: "Full Error: #{e.message}",
219
+ }
220
+ end
221
+ end
222
+
223
+ def replace(string, replacements)
224
+ return string if replacements.nil?
225
+
226
+ replacements.each do |find, replace|
227
+ string = string.gsub(find, replace)
228
+ end
229
+
230
+ string
231
+ end
232
+
233
+ def contents
234
+ @contents ||= File.read(@file)
235
+ end
236
+
237
+ def parse
238
+ if (matches = contents.match(EXCEPTION_REGEX))
239
+ self.message = matches.captures[0]
240
+ self.condition = matches.captures[1]
241
+ self.deprecation = matches.captures[2]
242
+ self.templates = parse_templates(matches.captures[3])
243
+ self.exception = matches.captures[5]
244
+ self.outputs = [[nil, matches.captures[4], nil, '']]
245
+ elsif (matches = contents.match(EXPECT_REGEX))
246
+ self.message = matches.captures[0]
247
+ self.condition = matches.captures[1]
248
+ self.deprecation = matches.captures[2]
249
+ self.templates = parse_templates(matches.captures[3])
250
+ self.exception = false
251
+ self.outputs = contents.scan(OUTPUTS_REGEX)
252
+ end
253
+ end
254
+
255
+ def parse_templates(test)
256
+ templates = {}
257
+ test.scan(/--TEMPLATE(?:\((.*?)\))?--(.*?)(?=--TEMPLATE|\z)/mx).map do |name, contents|
258
+ templates[name || 'index.twig'] = contents.
259
+ gsub(/[\n]*\z/, '').
260
+ gsub('d/m/Y H:i:s P', '%d/%m/%Y %H:%M:%S %:z'). # Change dates to Ruby format
261
+ gsub('Twig\Tests\TwigTestFoo', 'TwigTestFoo') # Change class name to match Ruby
262
+ end
263
+
264
+ templates
265
+ end
266
+
267
+ def parse_return_value(object)
268
+ if object.is_a?(Array) && object.length == 1
269
+ return parse_return_value(object.first)
270
+ end
271
+
272
+ if object.is_a?(Hash)
273
+ return object.transform_values { |v| parse_return_value(v) }
274
+ end
275
+
276
+ object
277
+ end
278
+ end
@@ -4,7 +4,7 @@ module Twig
4
4
  class AutoHash < Hash
5
5
  def add(*values)
6
6
  values.each do |value|
7
- self[length] = value
7
+ self[next_key] = value
8
8
  end
9
9
 
10
10
  self
@@ -13,5 +13,11 @@ module Twig
13
13
  def <<(*values)
14
14
  add(*values)
15
15
  end
16
+
17
+ private
18
+
19
+ def next_key
20
+ (keys.filter { |key| key.is_a?(Integer) }.max || -1) + 1
21
+ end
16
22
  end
17
23
  end
data/lib/twig/callable.rb CHANGED
@@ -2,13 +2,15 @@
2
2
 
3
3
  module Twig
4
4
  class Callable
5
- attr_reader :name, :dynamic_name, :callable
5
+ attr_reader :callable
6
+ attr_accessor :name, :dynamic_name, :arguments
6
7
 
7
8
  # @param [String] name
8
9
  # @param [Proc|Nil] callable
9
10
  # @param [Hash] options
10
11
  def initialize(name, callable = nil, options = {})
11
12
  @name = @dynamic_name = name
13
+ @arguments = []
12
14
  @callable = callable
13
15
  @options = {
14
16
  needs_environment: false,
@@ -17,5 +19,30 @@ module Twig
17
19
  is_variadic: false,
18
20
  }.merge(options)
19
21
  end
22
+
23
+ def type
24
+ raise NotImplementedError
25
+ end
26
+
27
+ def needs_charset?
28
+ @options[:needs_charset]
29
+ end
30
+
31
+ def needs_environment?
32
+ @options[:needs_environment]
33
+ end
34
+
35
+ def needs_context?
36
+ @options[:needs_context]
37
+ end
38
+
39
+ def with_dynamic_arguments(name, dynamic_name, arguments)
40
+ new = clone
41
+ new.name = name
42
+ new.dynamic_name = dynamic_name
43
+ new.arguments = arguments
44
+
45
+ new
46
+ end
20
47
  end
21
48
  end
data/lib/twig/compiler.rb CHANGED
@@ -6,6 +6,9 @@ module Twig
6
6
  class Compiler
7
7
  attr_reader :source, :environment
8
8
 
9
+ # @return [Hash<Integer, Integer>]
10
+ attr_reader :debug_info
11
+
9
12
  # @param [Environment] environment
10
13
  def initialize(environment)
11
14
  @environment = environment
@@ -47,7 +50,19 @@ module Twig
47
50
  # @param [String] value
48
51
  # @return [Compiler]
49
52
  def string(value)
50
- @source << "%q[#{value}]"
53
+ @source << "%q[#{value.to_s.gsub(/[\[\]\\]/, '[' => '\[', ']' => '\]', '\\' => '\\\\')}]"
54
+
55
+ self
56
+ end
57
+
58
+ # @param [String, Symbol] value
59
+ # @return [Compiler]
60
+ def symbol(value)
61
+ @source << if value.is_a?(Symbol)
62
+ value.inspect
63
+ else
64
+ "%q[#{value}].to_sym"
65
+ end
51
66
 
52
67
  self
53
68
  end
@@ -73,12 +88,28 @@ module Twig
73
88
  raw(Marshal.dump(value).inspect).
74
89
  raw(')')
75
90
  when Symbol
76
- raw(":#{value}")
91
+ symbol(value)
77
92
  else
78
93
  string(value)
79
94
  end
80
95
  end
81
96
 
97
+ # @param [Node::Base] node
98
+ # @return [Compiler]
99
+ def add_debug_info(node)
100
+ if node.lineno != @last_line
101
+ write("# line #{node.lineno}\n")
102
+
103
+ @source_line += @source[@source_offset..].count("\n")
104
+ @source_offset = @source.length
105
+ @debug_info[@source_line] = node.lineno
106
+
107
+ @last_line = node.lineno
108
+ end
109
+
110
+ self
111
+ end
112
+
82
113
  # @param [Integer] step
83
114
  # @return [Compiler]
84
115
  def indent(step = 1)
@@ -97,6 +128,7 @@ module Twig
97
128
 
98
129
  # @return [String]
99
130
  def var_name
131
+ @var_name_salt += 1
100
132
  "_v#{@var_name_salt}"
101
133
  end
102
134
 
@@ -110,7 +142,7 @@ module Twig
110
142
  def reset(indentation = 0)
111
143
  @last_line = nil
112
144
  @source = +''
113
- @debug_info = []
145
+ @debug_info = {}
114
146
  @source_offset = 0
115
147
  # source code starts at 1 (as we then increment it when we encounter new lines)
116
148
  @source_line = 1