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
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Twig
|
|
4
|
+
class TemplateWrapper
|
|
5
|
+
def initialize(environment, template)
|
|
6
|
+
@environment = environment
|
|
7
|
+
@template = template
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def render(context = {}, call_context: nil, output_buffer: nil)
|
|
11
|
+
context = Runtime::Context.from(context, call_context:, output_buffer:)
|
|
12
|
+
|
|
13
|
+
template.render(context).to_s
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render_block(name, context = {}, call_context: nil, output_buffer: nil)
|
|
17
|
+
context = Runtime::Context.from(context, call_context:, output_buffer:)
|
|
18
|
+
context.merge!(environment.globals)
|
|
19
|
+
|
|
20
|
+
template.render_block(name, context).to_s
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def block?(name, context = {})
|
|
24
|
+
context = Runtime::Context.from(context)
|
|
25
|
+
|
|
26
|
+
template.block?(name, context)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @return [Template]
|
|
30
|
+
def unwrap
|
|
31
|
+
template
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
# @return [Environment]
|
|
37
|
+
attr_reader :environment
|
|
38
|
+
|
|
39
|
+
# @return [Twig::Template]
|
|
40
|
+
attr_accessor :template
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/twig/token.rb
CHANGED
|
@@ -10,14 +10,30 @@ module Twig
|
|
|
10
10
|
VAR_END_TYPE = :var_end
|
|
11
11
|
NAME_TYPE = :name
|
|
12
12
|
SYMBOL_TYPE = :symbol
|
|
13
|
+
CLASS_VAR_TYPE = :class_var
|
|
13
14
|
NUMBER_TYPE = :number
|
|
14
15
|
STRING_TYPE = :string
|
|
15
16
|
OPERATOR_TYPE = :operator
|
|
16
17
|
PUNCTUATION_TYPE = :punctuation
|
|
17
18
|
INTERPOLATION_START_TYPE = :interpolation_start
|
|
18
19
|
INTERPOLATION_END_TYPE = :interpolation_end
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
TOKEN_TO_ENGLISH = {
|
|
22
|
+
EOF_TYPE => 'end of template',
|
|
23
|
+
TEXT_TYPE => 'text',
|
|
24
|
+
BLOCK_START_TYPE => 'begin of statement block',
|
|
25
|
+
VAR_START_TYPE => 'begin of print statement',
|
|
26
|
+
BLOCK_END_TYPE => 'end of statement block',
|
|
27
|
+
VAR_END_TYPE => 'end of print statement',
|
|
28
|
+
NAME_TYPE => 'name',
|
|
29
|
+
NUMBER_TYPE => 'number',
|
|
30
|
+
STRING_TYPE => 'string',
|
|
31
|
+
OPERATOR_TYPE => 'operator',
|
|
32
|
+
PUNCTUATION_TYPE => 'punctuation',
|
|
33
|
+
INTERPOLATION_START_TYPE => 'begin of string interpolation',
|
|
34
|
+
INTERPOLATION_END_TYPE => 'end of string interpolation',
|
|
35
|
+
SYMBOL_TYPE => 'symbol',
|
|
36
|
+
}.freeze
|
|
21
37
|
|
|
22
38
|
attr_reader :type, :value, :lineno
|
|
23
39
|
|
|
@@ -44,5 +60,15 @@ module Twig
|
|
|
44
60
|
def debug
|
|
45
61
|
[type, value]
|
|
46
62
|
end
|
|
63
|
+
|
|
64
|
+
def to_english
|
|
65
|
+
self.class.type_to_english(@type)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.type_to_english(type)
|
|
69
|
+
TOKEN_TO_ENGLISH.fetch(type) do
|
|
70
|
+
raise ArgumentError, "Token of type \"#{type}\" does not exist."
|
|
71
|
+
end
|
|
72
|
+
end
|
|
47
73
|
end
|
|
48
74
|
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Twig
|
|
4
|
+
module TokenParser
|
|
5
|
+
# Applies filters on a section of a template.
|
|
6
|
+
#
|
|
7
|
+
# {% apply upper %}
|
|
8
|
+
# This text becomes uppercase
|
|
9
|
+
# {% endapply %}
|
|
10
|
+
class Apply < Base
|
|
11
|
+
def parse(token)
|
|
12
|
+
lineno = token.lineno
|
|
13
|
+
ref = Node::Expression::Variable::Local.new(nil, lineno)
|
|
14
|
+
filter = ref
|
|
15
|
+
ep = parser.environment.expression_parsers.by_class(ExpressionParser::Infix::Filter.name)
|
|
16
|
+
|
|
17
|
+
loop do
|
|
18
|
+
filter = ep.parse(parser, filter, parser.current_token)
|
|
19
|
+
|
|
20
|
+
unless parser.stream.test(Token::OPERATOR_TYPE, '|')
|
|
21
|
+
break
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
parser.stream.next
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
parser.stream.expect(Token::BLOCK_END_TYPE)
|
|
28
|
+
body = parser.subparse(method(:decide_apply_end), drop_needle: true)
|
|
29
|
+
parser.stream.expect(Token::BLOCK_END_TYPE)
|
|
30
|
+
|
|
31
|
+
Node::Nodes.new({
|
|
32
|
+
0 => Node::Set.new(true, ref, body, lineno),
|
|
33
|
+
1 => Node::Print.new(filter, lineno),
|
|
34
|
+
}, lineno)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def tag
|
|
38
|
+
'apply'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def decide_apply_end(token)
|
|
44
|
+
token.test('endapply')
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Twig
|
|
4
|
+
module TokenParser
|
|
5
|
+
class AutoEscape < TokenParser::Base
|
|
6
|
+
def parse(token)
|
|
7
|
+
lineno = token.lineno
|
|
8
|
+
stream = parser.stream
|
|
9
|
+
|
|
10
|
+
if stream.test(Token::BLOCK_END_TYPE)
|
|
11
|
+
value = :html
|
|
12
|
+
else
|
|
13
|
+
expr = parser.parse_expression
|
|
14
|
+
|
|
15
|
+
unless expr.is_a?(Node::Expression::Constant)
|
|
16
|
+
raise Error::Syntax.new(
|
|
17
|
+
'An escaping strategy must be a string or false.',
|
|
18
|
+
stream.current.lineno,
|
|
19
|
+
stream.source
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
value = expr.attributes[:value]
|
|
24
|
+
value = value.to_sym if value.is_a?(String)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
stream.expect(Token::BLOCK_END_TYPE)
|
|
28
|
+
body = parser.subparse(method(:decide_block_end), drop_needle: true)
|
|
29
|
+
stream.expect(Token::BLOCK_END_TYPE)
|
|
30
|
+
|
|
31
|
+
Node::AutoEscape.new(value, body, lineno)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def tag
|
|
35
|
+
'autoescape'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def decide_block_end(token)
|
|
41
|
+
token.test('endautoescape')
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -15,6 +15,32 @@ module Twig
|
|
|
15
15
|
def tag
|
|
16
16
|
raise 'tag is not implemented'
|
|
17
17
|
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def parse_assignment_expression
|
|
22
|
+
stream = parser.stream
|
|
23
|
+
targets = AutoHash.new
|
|
24
|
+
|
|
25
|
+
loop do
|
|
26
|
+
token = parser.current_token
|
|
27
|
+
|
|
28
|
+
if stream.test(Token::OPERATOR_TYPE) && token.value.match(Lexer::REGEX_NAME)
|
|
29
|
+
# in this context, string operators are variables names
|
|
30
|
+
parser.stream.next
|
|
31
|
+
else
|
|
32
|
+
stream.expect(Token::NAME_TYPE, nil, 'Only variables can be assigned to')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
targets << Node::Expression::Variable::AssignContext.new(token.value, token.lineno)
|
|
36
|
+
|
|
37
|
+
unless stream.next_if(Token::PUNCTUATION_TYPE, ',')
|
|
38
|
+
break
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
Node::Nodes.new(targets)
|
|
43
|
+
end
|
|
18
44
|
end
|
|
19
45
|
end
|
|
20
46
|
end
|
|
@@ -20,14 +20,14 @@ module Twig
|
|
|
20
20
|
parser.push_block_stack(name)
|
|
21
21
|
|
|
22
22
|
if stream.next_if(Token::BLOCK_END_TYPE)
|
|
23
|
-
body = parser.subparse(decide_block_end, drop_needle: true)
|
|
23
|
+
body = parser.subparse(method(:decide_block_end), drop_needle: true)
|
|
24
24
|
|
|
25
25
|
if (token = stream.next_if(Token::NAME_TYPE)) && token.value != name
|
|
26
26
|
raise "Expected end block for #{name}, given #{token.value}"
|
|
27
27
|
end
|
|
28
28
|
else
|
|
29
29
|
body = Node::Nodes.new({
|
|
30
|
-
0 => Node::Print.new(parser.
|
|
30
|
+
0 => Node::Print.new(parser.parse_expression, lineno),
|
|
31
31
|
})
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -46,8 +46,8 @@ module Twig
|
|
|
46
46
|
|
|
47
47
|
private
|
|
48
48
|
|
|
49
|
-
def decide_block_end
|
|
50
|
-
|
|
49
|
+
def decide_block_end(token)
|
|
50
|
+
token.test('endblock')
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Twig
|
|
4
|
+
module TokenParser
|
|
5
|
+
class Cache < TokenParser::Base
|
|
6
|
+
include Twig::ExpressionParser::ParsesArguments
|
|
7
|
+
|
|
8
|
+
def parse(token)
|
|
9
|
+
stream = parser.stream
|
|
10
|
+
lineno = token.lineno
|
|
11
|
+
arguments = parse_named_arguments(parser)
|
|
12
|
+
|
|
13
|
+
stream.expect(Token::BLOCK_END_TYPE)
|
|
14
|
+
body = parser.subparse(method(:decide_cache_end), drop_needle: true)
|
|
15
|
+
stream.expect(Token::BLOCK_END_TYPE)
|
|
16
|
+
|
|
17
|
+
Node::Cache.new(arguments, body, lineno)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def tag
|
|
21
|
+
'cache'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def decide_cache_end(token)
|
|
27
|
+
token.test('endcache')
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Twig
|
|
4
|
+
module TokenParser
|
|
5
|
+
class Deprecated < Base
|
|
6
|
+
def parse(token)
|
|
7
|
+
stream = parser.stream
|
|
8
|
+
expr = parser.parse_expression
|
|
9
|
+
node = Node::Deprecated.new(expr, token.lineno)
|
|
10
|
+
|
|
11
|
+
while stream.test(Token::NAME_TYPE)
|
|
12
|
+
k = stream.current.value
|
|
13
|
+
stream.next
|
|
14
|
+
stream.expect(Token::OPERATOR_TYPE, '=')
|
|
15
|
+
|
|
16
|
+
case k
|
|
17
|
+
when 'package'
|
|
18
|
+
node.nodes[:package] = parser.parse_expression
|
|
19
|
+
when 'version'
|
|
20
|
+
node.nodes[:version] = parser.parse_expression
|
|
21
|
+
else
|
|
22
|
+
raise Error::Syntax.new(
|
|
23
|
+
"Unknown \"#{k}\" option.",
|
|
24
|
+
stream.current.lineno,
|
|
25
|
+
stream.source_context
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
stream.expect(Token::BLOCK_END_TYPE)
|
|
31
|
+
|
|
32
|
+
node
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def tag
|
|
36
|
+
'deprecated'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Twig
|
|
4
|
+
module TokenParser
|
|
5
|
+
class Do < TokenParser::Base
|
|
6
|
+
def parse(token)
|
|
7
|
+
expr = parser.parse_expression
|
|
8
|
+
|
|
9
|
+
parser.stream.expect(Token::BLOCK_END_TYPE)
|
|
10
|
+
|
|
11
|
+
Node::Do.new(expr, token.lineno)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def tag
|
|
15
|
+
'do'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'include'
|
|
4
|
+
|
|
5
|
+
module Twig
|
|
6
|
+
module TokenParser
|
|
7
|
+
class Embed < Include
|
|
8
|
+
def parse(token)
|
|
9
|
+
stream = parser.stream
|
|
10
|
+
parent = parser.parse_expression
|
|
11
|
+
|
|
12
|
+
variables, only, ignore_missing = parse_arguments
|
|
13
|
+
|
|
14
|
+
parent_token = fake_parent_token = Token.new(Token::STRING_TYPE, '__parent__', token.lineno)
|
|
15
|
+
|
|
16
|
+
if parent.is_a?(Node::Expression::Constant)
|
|
17
|
+
parent_token = Token.new(Token::STRING_TYPE, parent.attributes[:value], token.lineno)
|
|
18
|
+
elsif parent.is_a?(Node::Expression::Variable::Context)
|
|
19
|
+
parent_token = Token.new(Token::NAME_TYPE, parent.attributes[:name], token.lineno)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# inject a fake parent to make the parent() function work
|
|
23
|
+
stream.inject([
|
|
24
|
+
Token.new(Token::BLOCK_START_TYPE, '', token.lineno),
|
|
25
|
+
Token.new(Token::NAME_TYPE, 'extends', token.lineno),
|
|
26
|
+
parent_token,
|
|
27
|
+
Token.new(Token::BLOCK_END_TYPE, '', token.lineno),
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
node = parser.parse(stream, method(:decide_block_end), drop_needle: true)
|
|
31
|
+
|
|
32
|
+
# override the parent with the correct one
|
|
33
|
+
if fake_parent_token == parent_token
|
|
34
|
+
node.nodes[:parent] = parent
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
parser.embed_template(node)
|
|
38
|
+
|
|
39
|
+
stream.expect(Token::BLOCK_END_TYPE)
|
|
40
|
+
|
|
41
|
+
Node::Embed.new(
|
|
42
|
+
node.template_name,
|
|
43
|
+
node.attributes[:index],
|
|
44
|
+
variables,
|
|
45
|
+
only,
|
|
46
|
+
ignore_missing,
|
|
47
|
+
token.lineno
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def tag
|
|
52
|
+
'embed'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def decide_block_end(token)
|
|
58
|
+
token.test('endembed')
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -7,11 +7,12 @@ module Twig
|
|
|
7
7
|
stream = parser.stream
|
|
8
8
|
|
|
9
9
|
if parser.peek_block_stack
|
|
10
|
-
raise Error::Syntax.new('Cannot
|
|
11
|
-
|
|
10
|
+
raise Error::Syntax.new('Cannot use "extend" in a block.', token.lineno, stream.source)
|
|
11
|
+
elsif !parser.main_scope?
|
|
12
|
+
raise Error::Syntax.new('Cannot use "extend" in a macro.', token.lineno, stream.source)
|
|
12
13
|
end
|
|
13
14
|
|
|
14
|
-
parser.parent = parser.
|
|
15
|
+
parser.parent = parser.parse_expression
|
|
15
16
|
stream.expect(Token::BLOCK_END_TYPE)
|
|
16
17
|
|
|
17
18
|
Node::Empty.new(token.lineno)
|
|
@@ -10,16 +10,21 @@ module Twig
|
|
|
10
10
|
lineno = token.lineno
|
|
11
11
|
stream = parser.stream
|
|
12
12
|
|
|
13
|
-
targets =
|
|
13
|
+
targets = parse_assignment_expression
|
|
14
14
|
stream.expect(Token::OPERATOR_TYPE, 'in')
|
|
15
|
-
seq = parser.
|
|
15
|
+
seq = parser.parse_expression
|
|
16
|
+
|
|
17
|
+
if_expr = nil
|
|
18
|
+
if stream.next_if(Token::NAME_TYPE, 'if')
|
|
19
|
+
if_expr = parser.parse_expression
|
|
20
|
+
end
|
|
16
21
|
|
|
17
22
|
stream.expect(Token::BLOCK_END_TYPE)
|
|
18
|
-
body = parser.subparse(decide_for_fork)
|
|
23
|
+
body = parser.subparse(method(:decide_for_fork))
|
|
19
24
|
|
|
20
25
|
if stream.next.value == 'else'
|
|
21
26
|
stream.expect(Token::BLOCK_END_TYPE)
|
|
22
|
-
else_expr = parser.subparse(decide_for_end, drop_needle: true)
|
|
27
|
+
else_expr = parser.subparse(method(:decide_for_end), drop_needle: true)
|
|
23
28
|
else
|
|
24
29
|
else_expr = nil
|
|
25
30
|
end
|
|
@@ -43,7 +48,7 @@ module Twig
|
|
|
43
48
|
value_target.lineno
|
|
44
49
|
)
|
|
45
50
|
|
|
46
|
-
Node::For.new(key_target, value_target, seq,
|
|
51
|
+
Node::For.new(key_target, value_target, seq, if_expr, body, else_expr, lineno)
|
|
47
52
|
end
|
|
48
53
|
|
|
49
54
|
def tag
|
|
@@ -52,12 +57,12 @@ module Twig
|
|
|
52
57
|
|
|
53
58
|
private
|
|
54
59
|
|
|
55
|
-
def decide_for_fork
|
|
56
|
-
|
|
60
|
+
def decide_for_fork(token)
|
|
61
|
+
token.test(%w[else endfor])
|
|
57
62
|
end
|
|
58
63
|
|
|
59
|
-
def decide_for_end
|
|
60
|
-
|
|
64
|
+
def decide_for_end(token)
|
|
65
|
+
token.test(%w[endfor])
|
|
61
66
|
end
|
|
62
67
|
end
|
|
63
68
|
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Twig
|
|
4
|
+
module TokenParser
|
|
5
|
+
# Imports macros.
|
|
6
|
+
#
|
|
7
|
+
# {% from 'forms.html.twig' import forms %}
|
|
8
|
+
#
|
|
9
|
+
class From < Base
|
|
10
|
+
# @param [Token] token
|
|
11
|
+
def parse(token)
|
|
12
|
+
macro = parser.parse_expression
|
|
13
|
+
stream = parser.stream
|
|
14
|
+
stream.expect(Token::NAME_TYPE, 'import')
|
|
15
|
+
|
|
16
|
+
targets = {}
|
|
17
|
+
loop do
|
|
18
|
+
name = stream.expect(Token::NAME_TYPE).value
|
|
19
|
+
|
|
20
|
+
aliased = if stream.next_if('as')
|
|
21
|
+
Node::Expression::Variable::AssignContext.new(
|
|
22
|
+
stream.expect(Token::NAME_TYPE).value,
|
|
23
|
+
token.lineno
|
|
24
|
+
)
|
|
25
|
+
else
|
|
26
|
+
Node::Expression::Variable::AssignContext.new(name, token.lineno)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
targets[name] = aliased
|
|
30
|
+
|
|
31
|
+
unless stream.next_if(Token::PUNCTUATION_TYPE, ',')
|
|
32
|
+
break
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
stream.expect(Token::BLOCK_END_TYPE)
|
|
37
|
+
|
|
38
|
+
internal_ref = Node::Expression::Variable::AssignTemplate.new(
|
|
39
|
+
Node::Expression::Variable::Template.new(nil, token.lineno),
|
|
40
|
+
global: parser.main_scope?
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
node = Node::Import.new(macro, internal_ref, token.lineno)
|
|
44
|
+
|
|
45
|
+
targets.each do |name, aliased|
|
|
46
|
+
parser.add_imported_symbol(:function, aliased.attributes[:name], "macro_#{name}", internal_ref)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
node
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def tag
|
|
53
|
+
'from'
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Twig
|
|
4
|
+
module TokenParser
|
|
5
|
+
# Prevents compilation of block if function/filter/test does not exist.
|
|
6
|
+
# Since this is at compilation time, this DOES NOT work with Rails helper functions
|
|
7
|
+
# as those can't be determined fully at compile time as of now.
|
|
8
|
+
class Guard < Base
|
|
9
|
+
def parse(token)
|
|
10
|
+
stream = parser.stream
|
|
11
|
+
type_token = stream.expect(Token::NAME_TYPE)
|
|
12
|
+
|
|
13
|
+
unless %w[function filter test].include?(type_token.value)
|
|
14
|
+
raise Error::Syntax.new(
|
|
15
|
+
"Supported guard types are function, filter, and test, \"#{token.value}\" given.",
|
|
16
|
+
type_token.lineno,
|
|
17
|
+
stream.source
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
name_token = stream.expect(Token::NAME_TYPE)
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
exists = !parser.environment.send(type_token.value, name_token.value).nil?
|
|
25
|
+
rescue Error::Syntax
|
|
26
|
+
exists = false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
stream.expect(Token::BLOCK_END_TYPE)
|
|
30
|
+
|
|
31
|
+
if exists
|
|
32
|
+
body = parser.subparse(method(:decide_guard_fork))
|
|
33
|
+
else
|
|
34
|
+
body = Node::Empty.new
|
|
35
|
+
parser.subparse_ignore_unknown_twig_callables(method(:decide_guard_fork))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
else_node = Node::Empty.new
|
|
39
|
+
|
|
40
|
+
if stream.next.value == 'else'
|
|
41
|
+
stream.expect(Token::BLOCK_END_TYPE)
|
|
42
|
+
else_node = parser.subparse(method(:decide_guard_end), drop_needle: true)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
stream.expect(Token::BLOCK_END_TYPE)
|
|
46
|
+
|
|
47
|
+
Node::Nodes.new(AutoHash.new.add(exists ? body : else_node))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def tag
|
|
51
|
+
'guard'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def decide_guard_fork(token)
|
|
57
|
+
token.test(%w[else endguard])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def decide_guard_end(token)
|
|
61
|
+
token.test(['endguard'])
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/twig/token_parser/if.rb
CHANGED
|
@@ -12,10 +12,10 @@ module Twig
|
|
|
12
12
|
class If < Base
|
|
13
13
|
def parse(token)
|
|
14
14
|
lineno = token.lineno
|
|
15
|
-
expr = parser.
|
|
15
|
+
expr = parser.parse_expression
|
|
16
16
|
stream = parser.stream
|
|
17
17
|
stream.expect(Token::BLOCK_END_TYPE)
|
|
18
|
-
body = parser.subparse(decide_if_fork)
|
|
18
|
+
body = parser.subparse(method(:decide_if_fork))
|
|
19
19
|
tests = [expr, body]
|
|
20
20
|
else_node = nil
|
|
21
21
|
|
|
@@ -24,11 +24,11 @@ module Twig
|
|
|
24
24
|
case stream.next.value
|
|
25
25
|
when 'else'
|
|
26
26
|
stream.expect(Token::BLOCK_END_TYPE)
|
|
27
|
-
else_node = parser.subparse(decide_if_end)
|
|
27
|
+
else_node = parser.subparse(method(:decide_if_end))
|
|
28
28
|
when 'elsif', 'elseif'
|
|
29
|
-
expr = parser.
|
|
29
|
+
expr = parser.parse_expression
|
|
30
30
|
stream.expect(Token::BLOCK_END_TYPE)
|
|
31
|
-
body = parser.subparse(decide_if_fork)
|
|
31
|
+
body = parser.subparse(method(:decide_if_fork))
|
|
32
32
|
tests.push(expr, body)
|
|
33
33
|
when 'endif'
|
|
34
34
|
if_ended = true
|
|
@@ -52,12 +52,12 @@ module Twig
|
|
|
52
52
|
|
|
53
53
|
private
|
|
54
54
|
|
|
55
|
-
def decide_if_end
|
|
56
|
-
|
|
55
|
+
def decide_if_end(token)
|
|
56
|
+
token.test(%w[endif])
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
def decide_if_fork
|
|
60
|
-
|
|
59
|
+
def decide_if_fork(token)
|
|
60
|
+
token.test(%w[elseif elsif else endif])
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Twig
|
|
4
|
+
module TokenParser
|
|
5
|
+
# Imports macros.
|
|
6
|
+
#
|
|
7
|
+
# {% import 'forms.html.twig' as forms %}
|
|
8
|
+
#
|
|
9
|
+
class Import < Base
|
|
10
|
+
def parse(token)
|
|
11
|
+
macro = parser.parse_expression
|
|
12
|
+
parser.stream.expect(Token::NAME_TYPE, 'as')
|
|
13
|
+
name = parser.stream.expect(Token::NAME_TYPE).value
|
|
14
|
+
var = Node::Expression::Variable::AssignTemplate.new(
|
|
15
|
+
Node::Expression::Variable::Template.new(name, token.lineno),
|
|
16
|
+
global: parser.main_scope?
|
|
17
|
+
)
|
|
18
|
+
parser.stream.expect(Token::BLOCK_END_TYPE)
|
|
19
|
+
parser.add_imported_symbol(:template, name)
|
|
20
|
+
|
|
21
|
+
Node::Import.new(macro, var, token.lineno)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def tag
|
|
25
|
+
'import'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -4,7 +4,7 @@ module Twig
|
|
|
4
4
|
module TokenParser
|
|
5
5
|
class Include < TokenParser::Base
|
|
6
6
|
def parse(token)
|
|
7
|
-
expr = parser.
|
|
7
|
+
expr = parser.parse_expression
|
|
8
8
|
variables, only, ignore_missing = parse_arguments
|
|
9
9
|
|
|
10
10
|
Node::Include.new(
|
|
@@ -34,7 +34,7 @@ module Twig
|
|
|
34
34
|
|
|
35
35
|
variables = nil
|
|
36
36
|
if stream.next_if(Token::NAME_TYPE, 'with')
|
|
37
|
-
variables = parser.
|
|
37
|
+
variables = parser.parse_expression
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
only = false
|