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
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Unary
7
+ class Not < Unary::Base
8
+ def compile(compiler)
9
+ compiler.
10
+ raw('!::Twig::Extension::Core.bool(').
11
+ subcompile(nodes[:node]).
12
+ raw(')')
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Unary
7
+ class Spread < Unary::Base
8
+ def compile(compiler)
9
+ compiler.
10
+ raw('::Twig::Runtime::Spread.new(').
11
+ subcompile(nodes[:node]).
12
+ raw(')')
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Unary
7
+ class StringCast < Unary::Base
8
+ def compile(compiler)
9
+ compiler.
10
+ raw('(').
11
+ subcompile(nodes[:node]).
12
+ raw('.to_s)')
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Variable
7
+ class AssignTemplate < Expression::Base
8
+ # @param [Variable::Template] var
9
+ # @param [Boolean] global
10
+ def initialize(var, global: true)
11
+ super({ var: }, { global: }, var.lineno)
12
+ end
13
+
14
+ def compile(compiler)
15
+ # @var [Template] var
16
+ var = nodes[:var]
17
+
18
+ compiler.
19
+ add_debug_info(self).
20
+ write('macros[').
21
+ string(var.name(compiler)).
22
+ raw('] = ')
23
+
24
+ if attributes[:global]
25
+ compiler.
26
+ raw('@macros[').
27
+ string(var.name(compiler)).
28
+ raw('] = ')
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Variable
7
+ class Local < Expression::Base
8
+ RESERVED_NAMES = %w[varargs context macros blocks self].freeze
9
+
10
+ # @param [String|Integer|nil]
11
+ def initialize(name, lineno)
12
+ if %w[true false none null nil].include?(name.to_s.downcase)
13
+ raise Error::Syntax.new("You cannot assign a value to \"#{name}\".", lineno)
14
+ end
15
+
16
+ # Convert to integer if name is an integer or consists of digits only
17
+ if !name.nil? && (name.is_a?(Integer) || name == name.to_i.to_s)
18
+ name = name.to_i
19
+ elsif RESERVED_NAMES.include?(name)
20
+ name = "\u035C#{name}"
21
+ end
22
+
23
+ super({}, { name: }, lineno)
24
+ end
25
+
26
+ def compile(compiler)
27
+ attributes[:name] = compiler.var_name if attributes[:name].nil?
28
+
29
+ compiler.raw(attributes[:name].to_s)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ module Expression
6
+ module Variable
7
+ class Template < Expression::Base
8
+ RESERVED_NAMES = %w[varargs context macros blocks self].freeze
9
+
10
+ # @param [String, Integer, nil] name
11
+ # @param [Integer] lineno
12
+ def initialize(name, lineno)
13
+ if name && %w[true false none null nil].include?(name.to_s.downcase)
14
+ raise Error::Syntax.new("You cannot assign a value to \"#{name}\".", lineno)
15
+ end
16
+
17
+ # Convert to integer if name is an integer or consists of digits only
18
+ if !name.nil? && (name.is_a?(Integer) || name == name.to_i.to_s)
19
+ name = name.to_i
20
+ elsif RESERVED_NAMES.include?(name)
21
+ name = "\u035C#{name}"
22
+ end
23
+
24
+ super({}, { name: }, lineno)
25
+ end
26
+
27
+ # @param [Compiler] compiler
28
+ # @return [String]
29
+ def name(compiler)
30
+ if attributes[:name].nil?
31
+ attributes[:name] = compiler.var_name
32
+ end
33
+
34
+ attributes[:name]
35
+ end
36
+
37
+ # @param [Compiler] compiler
38
+ def compile(compiler)
39
+ name_value = name(compiler)
40
+
41
+ if name_value == '_self'
42
+ compiler.raw('self')
43
+ else
44
+ compiler.
45
+ raw('macros[').
46
+ string(name_value).
47
+ raw(']')
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
data/lib/twig/node/for.rb CHANGED
@@ -4,6 +4,10 @@ module Twig
4
4
  module Node
5
5
  class For < Node::Base
6
6
  def initialize(key_target, value_target, seq, if_expr, body, else_expr, lineno)
7
+ unless if_expr.nil?
8
+ body = If.new(Nodes.new(AutoHash.new.add(if_expr, body)), nil, lineno)
9
+ end
10
+
7
11
  loop = ForLoop.new(lineno)
8
12
  body = Nodes.new(AutoHash.new.add(body, loop))
9
13
 
@@ -22,27 +26,40 @@ module Twig
22
26
  end
23
27
 
24
28
  def compile(compiler)
29
+ iteration_var = compiler.var_name
30
+ function_var = compiler.var_name
31
+
25
32
  compiler.
26
- write("context.push_stack\n").
27
- write('context[:_seq] = ::Twig::Extension::Core.ensure_hash(').
33
+ add_debug_info(self).
34
+ write("#{iteration_var} = ::Twig::Runtime::LoopIterator.new(").
28
35
  subcompile(nodes[:seq]).
29
- raw(")\n")
36
+ raw(")\n").
37
+ write("#{function_var} = lambda do |iterator, context, blocks, recurse, depth|\n").
38
+ indent.
39
+ write("parent = context.dup\n")
30
40
 
31
- # @todo Missing some more loops stuff here
41
+ if attributes[:with_loop]
42
+ compiler.
43
+ write('context[:loop] = ::Twig::Runtime::LoopContext.new(').
44
+ raw("iterator, parent, blocks, recurse, depth)\n")
45
+ end
32
46
 
33
47
  if nodes.key?(:else_expr)
34
48
  compiler.write("context[:_iterated] = false\n")
35
49
  end
36
50
 
51
+ key_var = compiler.var_name
52
+ value_var = compiler.var_name
53
+
37
54
  compiler.
38
- write("context[:_seq].each do |k, v|\n").
55
+ write("iterator.each do |#{key_var}, #{value_var}|\n").
39
56
  indent.
40
57
  write('').
41
58
  subcompile(nodes[:key_target]).
42
- raw(" = k\n").
59
+ raw(" = #{key_var}\n").
43
60
  write('').
44
61
  subcompile(nodes[:value_target]).
45
- raw(" = v\n\n").
62
+ raw(" = #{value_var}\n").
46
63
  subcompile(nodes[:body]).
47
64
  outdent.
48
65
  write("end\n")
@@ -57,7 +74,20 @@ module Twig
57
74
  end
58
75
 
59
76
  compiler.
60
- write("context.pop_stack\n")
77
+ write("context.remove!(:#{nodes[:key_target].attributes[:name]}, ").
78
+ raw(":#{nodes[:value_target].attributes[:name]}")
79
+
80
+ if attributes[:with_loop]
81
+ compiler.raw(', :loop')
82
+ end
83
+
84
+ compiler.
85
+ raw(")\n").
86
+ write("context.keep!(parent.keys)\n").
87
+ write("context.merge!(parent, overwrite: false)\n").
88
+ outdent.
89
+ write("end\n").
90
+ write("#{function_var}.call(#{iteration_var}, context, blocks, #{function_var}, 0)\n")
61
91
  end
62
92
  end
63
93
  end
@@ -11,28 +11,6 @@ module Twig
11
11
  if attributes.key?(:else_expr)
12
12
  compiler.write("context[:_iterated] = true\n")
13
13
  end
14
-
15
- # @todo if with loop
16
- compiler.
17
- write("context[:loop] = {\n").
18
- write(" index0: 0,\n").
19
- write(" index: 1,\n").
20
- write(" first: true,\n").
21
- write("}\n")
22
-
23
- if attributes.key?(:with_loop)
24
- compiler.
25
- write("context[:loop][:index0] += 1\n").
26
- write("context[:loop][:index] += 1\n").
27
- write("context[:loop][:first] = false\n").
28
- write("if context[:loop].key?(:revindex0) && context[:loop].key?(:revindex)\n").
29
- indent.
30
- write("context[:loop][:revindex0] -= 1\n").
31
- write("context[:loop][:revindex] -= 1\n").
32
- write("context[:loop][:last] = (context[:loop][:revindex0] == 0)\n").
33
- outdent.
34
- write("end\n")
35
- end
36
14
  end
37
15
  end
38
16
  end
data/lib/twig/node/if.rb CHANGED
@@ -11,6 +11,8 @@ module Twig
11
11
  end
12
12
 
13
13
  def compile(compiler)
14
+ compiler.add_debug_info(self)
15
+
14
16
  (0...nodes[:tests].nodes.length).step(2).each do |i|
15
17
  if i.zero?
16
18
  compiler.
@@ -23,8 +25,9 @@ module Twig
23
25
  end
24
26
 
25
27
  compiler.
28
+ raw('::Twig::Extension::Core.bool(').
26
29
  subcompile(nodes[:tests].nodes[i]).
27
- raw(")\n").
30
+ raw("))\n").
28
31
  indent
29
32
 
30
33
  if nodes[:tests].nodes.key?(i + 1)
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ class Import < Node::Base
6
+ # @param [Expression::Base] expr
7
+ # @param [Expression::Base, Expression::Variable::AssignTemplate] var
8
+ # @param [Integer] lineno
9
+ def initialize(expr, var, lineno)
10
+ super({ expr:, var: }, {}, lineno)
11
+ end
12
+
13
+ # @param [Compiler] compiler
14
+ def compile(compiler)
15
+ compiler.subcompile(nodes[:var])
16
+
17
+ if nodes[:expr].is_a?(Expression::Variable::Context) && nodes[:expr].attributes[:name] == '_self'
18
+ compiler.raw('self')
19
+ else
20
+ compiler.
21
+ raw('load(').
22
+ subcompile(nodes[:expr]).
23
+ raw(', ').
24
+ repr(lineno).
25
+ raw(').unwrap')
26
+ end
27
+
28
+ compiler.raw("\n")
29
+ end
30
+ end
31
+ end
32
+ end
@@ -3,6 +3,8 @@
3
3
  module Twig
4
4
  module Node
5
5
  class Include < Node::Base
6
+ include Output
7
+
6
8
  # @param [Expression::Base] expr
7
9
  # @param [Expression::Base, nil] variables
8
10
  # @param [Boolean] only
@@ -19,9 +21,37 @@ module Twig
19
21
  end
20
22
 
21
23
  def compile(compiler)
24
+ compiler.add_debug_info(self)
25
+
22
26
  if attributes[:ignore_missing]
23
- # @todo
24
- raise 'not implemented yet'
27
+ template = compiler.var_name
28
+
29
+ compiler.
30
+ write("begin\n").
31
+ indent.
32
+ write("#{template} = ")
33
+
34
+ add_get_template(compiler, template)
35
+
36
+ compiler.
37
+ raw("\n").
38
+ outdent.
39
+ write("rescue ::Twig::Error::Loader => e\n").
40
+ indent.
41
+ write("# ignore missing template\n").
42
+ write("#{template} = nil\n").
43
+ outdent.
44
+ write("end\n").
45
+ write("if #{template}\n").
46
+ indent.
47
+ write("#{template}.render_with_blocks(")
48
+
49
+ add_template_arguments(compiler)
50
+
51
+ compiler.
52
+ raw(");\n").
53
+ outdent.
54
+ write("end\n")
25
55
  else
26
56
  compiler.
27
57
  write('')
@@ -41,13 +71,11 @@ module Twig
41
71
  private
42
72
 
43
73
  # @param [Compiler] compiler
44
- def add_get_template(compiler)
74
+ def add_get_template(compiler, template = '')
45
75
  compiler.
46
- raw('load_template(').
76
+ raw('load(').
47
77
  subcompile(nodes[:expr]).
48
78
  raw(', ').
49
- repr(template_name).
50
- raw(', ').
51
79
  repr(lineno).
52
80
  raw(')')
53
81
  end
@@ -55,7 +83,7 @@ module Twig
55
83
  # @param [Compiler] compiler
56
84
  def add_template_arguments(compiler)
57
85
  if !nodes.key?(:variables)
58
- compiler.raw(attributes[:only] == false ? 'context' : '{}')
86
+ compiler.raw(attributes[:only] == false ? 'context' : 'context.only({})')
59
87
  elsif attributes[:only] == false
60
88
  compiler.
61
89
  raw('context.merge(').
@@ -63,7 +91,9 @@ module Twig
63
91
  raw(')')
64
92
  else
65
93
  compiler.
66
- subcompile(nodes[:variables])
94
+ raw('context.only(').
95
+ subcompile(nodes[:variables]).
96
+ raw(')')
67
97
  end
68
98
  end
69
99
  end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twig
4
+ module Node
5
+ # Represents a macro node.
6
+ class Macro < Node::Base
7
+ VARARGS_NAME = 'varargs'
8
+
9
+ # @param [String] name
10
+ # @param [Body] body
11
+ # @param [Expression::Hash] arguments
12
+ # @param [Integer] lineno
13
+ def initialize(name, body, arguments, lineno)
14
+ arguments.key_value_pairs.each do |key, value|
15
+ next unless "\u035C#{VARARGS_NAME}" == key.attributes[:name]
16
+
17
+ raise Error::Syntax.new(
18
+ "The argument \"#{VARARGS_NAME}\" in macro \"#{name}\" cannot be defined because the variable " \
19
+ "\"#{VARARGS_NAME}\" is reserved for arbitrary arguments.",
20
+ value.lineno,
21
+ value.source_context
22
+ )
23
+ end
24
+
25
+ super({ body: body, arguments: arguments }, { name: name }, lineno)
26
+ end
27
+
28
+ # @param [Compiler] compiler
29
+ def compile(compiler)
30
+ compiler.
31
+ add_debug_info(self).
32
+ write("def macro_#{attributes[:name]}(call_context, ")
33
+
34
+ arguments = nodes[:arguments]
35
+ arguments.key_value_pairs.each do |key, value|
36
+ compiler.
37
+ subcompile(key).
38
+ raw(': ').
39
+ subcompile(value).
40
+ raw(', ')
41
+ end
42
+
43
+ compiler.
44
+ raw('**varargs').
45
+ raw(")\n").
46
+ write(" macros = @macros\n").
47
+ write(" context = ::Twig::Runtime::Context.new({\n")
48
+
49
+ arguments.key_value_pairs.each do |pair|
50
+ key = pair[0]
51
+ var = key.attributes[:name]
52
+ if var.start_with?("\u035C")
53
+ var = var[("\u035C".length)..]
54
+ end
55
+ compiler.
56
+ write(' ').
57
+ string(var).
58
+ raw(' => ').
59
+ subcompile(key).
60
+ raw(",\n")
61
+ end
62
+
63
+ capture_node = Node::Capture.new(nodes[:body], nodes[:body].lineno)
64
+
65
+ compiler.
66
+ write(' ').
67
+ string(VARARGS_NAME).
68
+ raw(' => ').
69
+ raw("varargs,\n").
70
+ write(" }.merge(env.globals), call_context:)\n\n").
71
+ write(" blocks = {}\n\n").
72
+ write(' ').
73
+ subcompile(capture_node).
74
+ raw("\n").
75
+ write("end\n\n")
76
+ end
77
+ end
78
+ end
79
+ end