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.
- checksums.yaml +4 -4
- data/lib/tasks/twig_parity.rake +278 -0
- data/lib/twig/auto_hash.rb +7 -1
- data/lib/twig/callable.rb +28 -1
- data/lib/twig/compiler.rb +35 -3
- data/lib/twig/environment.rb +198 -41
- data/lib/twig/error/base.rb +81 -16
- data/lib/twig/error/loader.rb +8 -0
- data/lib/twig/error/logic.rb +8 -0
- data/lib/twig/error/runtime.rb +8 -0
- data/lib/twig/expression_parser/base.rb +30 -0
- data/lib/twig/expression_parser/expression_parsers.rb +57 -0
- data/lib/twig/expression_parser/infix/arrow.rb +31 -0
- data/lib/twig/expression_parser/infix/binary.rb +34 -0
- data/lib/twig/expression_parser/infix/conditional_ternary.rb +39 -0
- data/lib/twig/expression_parser/infix/dot.rb +72 -0
- data/lib/twig/expression_parser/infix/filter.rb +43 -0
- data/lib/twig/expression_parser/infix/function.rb +67 -0
- data/lib/twig/expression_parser/infix/is.rb +53 -0
- data/lib/twig/expression_parser/infix/is_not.rb +19 -0
- data/lib/twig/expression_parser/infix/parses_arguments.rb +84 -0
- data/lib/twig/expression_parser/infix/square_bracket.rb +66 -0
- data/lib/twig/expression_parser/infix_expression_parser.rb +34 -0
- data/lib/twig/expression_parser/prefix/grouping.rb +60 -0
- data/lib/twig/expression_parser/prefix/literal.rb +244 -0
- data/lib/twig/expression_parser/prefix/unary.rb +29 -0
- data/lib/twig/expression_parser/prefix_expression_parser.rb +18 -0
- data/lib/twig/extension/base.rb +26 -4
- data/lib/twig/extension/core.rb +1076 -48
- data/lib/twig/extension/debug.rb +25 -0
- data/lib/twig/extension/escaper.rb +73 -0
- data/lib/twig/extension/rails.rb +10 -57
- data/lib/twig/extension/string_loader.rb +19 -0
- data/lib/twig/extension_set.rb +117 -20
- data/lib/twig/file_extension_escaping_strategy.rb +35 -0
- data/lib/twig/lexer.rb +225 -81
- data/lib/twig/loader/array.rb +25 -8
- data/lib/twig/loader/chain.rb +93 -0
- data/lib/twig/loader/filesystem.rb +106 -7
- data/lib/twig/node/auto_escape.rb +18 -0
- data/lib/twig/node/base.rb +58 -2
- data/lib/twig/node/block.rb +2 -0
- data/lib/twig/node/block_reference.rb +5 -1
- data/lib/twig/node/body.rb +7 -0
- data/lib/twig/node/cache.rb +50 -0
- data/lib/twig/node/capture.rb +22 -0
- data/lib/twig/node/deprecated.rb +53 -0
- data/lib/twig/node/do.rb +19 -0
- data/lib/twig/node/embed.rb +43 -0
- data/lib/twig/node/expression/array.rb +29 -20
- data/lib/twig/node/expression/arrow_function.rb +55 -0
- data/lib/twig/node/expression/assign_name.rb +1 -1
- data/lib/twig/node/expression/binary/and.rb +17 -0
- data/lib/twig/node/expression/binary/base.rb +6 -4
- data/lib/twig/node/expression/binary/boolean.rb +24 -0
- data/lib/twig/node/expression/binary/concat.rb +20 -0
- data/lib/twig/node/expression/binary/elvis.rb +35 -0
- data/lib/twig/node/expression/binary/ends_with.rb +24 -0
- data/lib/twig/node/expression/binary/floor_div.rb +21 -0
- data/lib/twig/node/expression/binary/has_every.rb +20 -0
- data/lib/twig/node/expression/binary/has_some.rb +20 -0
- data/lib/twig/node/expression/binary/in.rb +20 -0
- data/lib/twig/node/expression/binary/matches.rb +24 -0
- data/lib/twig/node/expression/binary/not_in.rb +20 -0
- data/lib/twig/node/expression/binary/null_coalesce.rb +49 -0
- data/lib/twig/node/expression/binary/or.rb +15 -0
- data/lib/twig/node/expression/binary/starts_with.rb +24 -0
- data/lib/twig/node/expression/binary/xor.rb +17 -0
- data/lib/twig/node/expression/block_reference.rb +62 -0
- data/lib/twig/node/expression/call.rb +126 -6
- data/lib/twig/node/expression/constant.rb +3 -1
- data/lib/twig/node/expression/filter/default.rb +37 -0
- data/lib/twig/node/expression/filter/raw.rb +31 -0
- data/lib/twig/node/expression/filter.rb +2 -2
- data/lib/twig/node/expression/function.rb +37 -0
- data/lib/twig/node/expression/get_attribute.rb +51 -7
- data/lib/twig/node/expression/hash.rb +75 -0
- data/lib/twig/node/expression/helper_method.rb +6 -18
- data/lib/twig/node/expression/macro_reference.rb +43 -0
- data/lib/twig/node/expression/name.rb +42 -8
- data/lib/twig/node/expression/operator_escape.rb +13 -0
- data/lib/twig/node/expression/parent.rb +28 -0
- data/lib/twig/node/expression/support_defined_test.rb +23 -0
- data/lib/twig/node/expression/ternary.rb +7 -1
- data/lib/twig/node/expression/test/base.rb +26 -0
- data/lib/twig/node/expression/test/constant.rb +35 -0
- data/lib/twig/node/expression/test/defined.rb +33 -0
- data/lib/twig/node/expression/test/divisible_by.rb +23 -0
- data/lib/twig/node/expression/test/even.rb +21 -0
- data/lib/twig/node/expression/test/iterable.rb +21 -0
- data/lib/twig/node/expression/test/mapping.rb +21 -0
- data/lib/twig/node/expression/test/null.rb +21 -0
- data/lib/twig/node/expression/test/odd.rb +21 -0
- data/lib/twig/node/expression/test/same_as.rb +23 -0
- data/lib/twig/node/expression/test/sequence.rb +21 -0
- data/lib/twig/node/expression/unary/base.rb +3 -1
- data/lib/twig/node/expression/unary/not.rb +18 -0
- data/lib/twig/node/expression/unary/spread.rb +18 -0
- data/lib/twig/node/expression/unary/string_cast.rb +18 -0
- data/lib/twig/node/expression/variable/assign_template.rb +35 -0
- data/lib/twig/node/expression/variable/local.rb +35 -0
- data/lib/twig/node/expression/variable/template.rb +54 -0
- data/lib/twig/node/for.rb +38 -8
- data/lib/twig/node/for_loop.rb +0 -22
- data/lib/twig/node/if.rb +4 -1
- data/lib/twig/node/import.rb +32 -0
- data/lib/twig/node/include.rb +38 -8
- data/lib/twig/node/macro.rb +79 -0
- data/lib/twig/node/module.rb +278 -23
- data/lib/twig/node/output.rb +7 -0
- data/lib/twig/node/print.rb +4 -1
- data/lib/twig/node/set.rb +72 -0
- data/lib/twig/node/text.rb +4 -1
- data/lib/twig/node/with.rb +50 -0
- data/lib/twig/node/yield.rb +6 -1
- data/lib/twig/node_traverser.rb +50 -0
- data/lib/twig/node_visitor/base.rb +30 -0
- data/lib/twig/node_visitor/escaper.rb +165 -0
- data/lib/twig/node_visitor/safe_analysis.rb +127 -0
- data/lib/twig/node_visitor/spreader.rb +39 -0
- data/lib/twig/output_buffer.rb +14 -12
- data/lib/twig/parser.rb +281 -8
- data/lib/twig/rails/config.rb +33 -0
- data/lib/twig/rails/engine.rb +44 -0
- data/lib/twig/rails/renderer.rb +41 -0
- data/lib/twig/runtime/argument_spreader.rb +46 -0
- data/lib/twig/runtime/context.rb +154 -0
- data/lib/twig/runtime/enumerable_hash.rb +51 -0
- data/lib/twig/runtime/escaper.rb +155 -0
- data/lib/twig/runtime/loop_context.rb +81 -0
- data/lib/twig/runtime/loop_iterator.rb +60 -0
- data/lib/twig/runtime/spread.rb +21 -0
- data/lib/twig/runtime_loader/base.rb +12 -0
- data/lib/twig/runtime_loader/factory.rb +23 -0
- data/lib/twig/template.rb +267 -14
- data/lib/twig/template_wrapper.rb +42 -0
- data/lib/twig/token.rb +28 -2
- data/lib/twig/token_parser/apply.rb +48 -0
- data/lib/twig/token_parser/auto_escape.rb +45 -0
- data/lib/twig/token_parser/base.rb +26 -0
- data/lib/twig/token_parser/block.rb +4 -4
- data/lib/twig/token_parser/cache.rb +31 -0
- data/lib/twig/token_parser/deprecated.rb +40 -0
- data/lib/twig/token_parser/do.rb +19 -0
- data/lib/twig/token_parser/embed.rb +62 -0
- data/lib/twig/token_parser/extends.rb +4 -3
- data/lib/twig/token_parser/for.rb +14 -9
- data/lib/twig/token_parser/from.rb +57 -0
- data/lib/twig/token_parser/guard.rb +65 -0
- data/lib/twig/token_parser/if.rb +9 -9
- data/lib/twig/token_parser/import.rb +29 -0
- data/lib/twig/token_parser/include.rb +2 -2
- data/lib/twig/token_parser/macro.rb +109 -0
- data/lib/twig/token_parser/set.rb +76 -0
- data/lib/twig/token_parser/use.rb +54 -0
- data/lib/twig/token_parser/with.rb +36 -0
- data/lib/twig/token_parser/yield.rb +7 -7
- data/lib/twig/token_stream.rb +23 -3
- data/lib/twig/twig_filter.rb +20 -0
- data/lib/twig/twig_function.rb +37 -0
- data/lib/twig/twig_test.rb +31 -0
- data/lib/twig/util/callable_arguments_extractor.rb +227 -0
- data/lib/twig_ruby.rb +21 -2
- metadata +145 -6
- data/lib/twig/context.rb +0 -64
- data/lib/twig/expression_parser.rb +0 -517
- data/lib/twig/railtie.rb +0 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31919ddaf84c7eb3ddbf272092cf86714ae8cc82daeaf3d9c1ea07a968a34957
|
4
|
+
data.tar.gz: 9e405d3820213e10efe310023576fa4d7fc10c8b132956cb86a1ce454e79568b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/twig/auto_hash.rb
CHANGED
@@ -4,7 +4,7 @@ module Twig
|
|
4
4
|
class AutoHash < Hash
|
5
5
|
def add(*values)
|
6
6
|
values.each do |value|
|
7
|
-
self[
|
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 :
|
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
|
-
|
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
|