blob: eedea7a03a9c5ef2c22df6f227fe00c665ac18d5 [file] [log] [blame]
IRIS YANG31213572020-08-18 13:17:02 +00001"""Parse tokens from the lexer into nodes for the compiler."""
2from . import nodes
3from .exceptions import TemplateAssertionError
4from .exceptions import TemplateSyntaxError
5from .lexer import describe_token
6from .lexer import describe_token_expr
7
8_statement_keywords = frozenset(
9 [
10 "for",
11 "if",
12 "block",
13 "extends",
14 "print",
15 "macro",
16 "include",
17 "from",
18 "import",
19 "set",
20 "with",
21 "autoescape",
22 ]
23)
24_compare_operators = frozenset(["eq", "ne", "lt", "lteq", "gt", "gteq"])
25
26_math_nodes = {
27 "add": nodes.Add,
28 "sub": nodes.Sub,
29 "mul": nodes.Mul,
30 "div": nodes.Div,
31 "floordiv": nodes.FloorDiv,
32 "mod": nodes.Mod,
33}
34
35
36class Parser:
37 """This is the central parsing class Jinja uses. It's passed to
38 extensions and can be used to parse expressions or statements.
39 """
40
41 def __init__(self, environment, source, name=None, filename=None, state=None):
42 self.environment = environment
43 self.stream = environment._tokenize(source, name, filename, state)
44 self.name = name
45 self.filename = filename
46 self.closed = False
47 self.extensions = {}
48 for extension in environment.iter_extensions():
49 for tag in extension.tags:
50 self.extensions[tag] = extension.parse
51 self._last_identifier = 0
52 self._tag_stack = []
53 self._end_token_stack = []
54
55 def fail(self, msg, lineno=None, exc=TemplateSyntaxError):
56 """Convenience method that raises `exc` with the message, passed
57 line number or last line number as well as the current name and
58 filename.
59 """
60 if lineno is None:
61 lineno = self.stream.current.lineno
62 raise exc(msg, lineno, self.name, self.filename)
63
64 def _fail_ut_eof(self, name, end_token_stack, lineno):
65 expected = []
66 for exprs in end_token_stack:
67 expected.extend(map(describe_token_expr, exprs))
68 if end_token_stack:
69 currently_looking = " or ".join(
70 map(repr, map(describe_token_expr, end_token_stack[-1]))
71 )
72 else:
73 currently_looking = None
74
75 if name is None:
76 message = ["Unexpected end of template."]
77 else:
78 message = [f"Encountered unknown tag {name!r}."]
79
80 if currently_looking:
81 if name is not None and name in expected:
82 message.append(
83 "You probably made a nesting mistake. Jinja is expecting this tag,"
84 f" but currently looking for {currently_looking}."
85 )
86 else:
87 message.append(
88 f"Jinja was looking for the following tags: {currently_looking}."
89 )
90
91 if self._tag_stack:
92 message.append(
93 "The innermost block that needs to be closed is"
94 f" {self._tag_stack[-1]!r}."
95 )
96
97 self.fail(" ".join(message), lineno)
98
99 def fail_unknown_tag(self, name, lineno=None):
100 """Called if the parser encounters an unknown tag. Tries to fail
101 with a human readable error message that could help to identify
102 the problem.
103 """
104 return self._fail_ut_eof(name, self._end_token_stack, lineno)
105
106 def fail_eof(self, end_tokens=None, lineno=None):
107 """Like fail_unknown_tag but for end of template situations."""
108 stack = list(self._end_token_stack)
109 if end_tokens is not None:
110 stack.append(end_tokens)
111 return self._fail_ut_eof(None, stack, lineno)
112
113 def is_tuple_end(self, extra_end_rules=None):
114 """Are we at the end of a tuple?"""
115 if self.stream.current.type in ("variable_end", "block_end", "rparen"):
116 return True
117 elif extra_end_rules is not None:
118 return self.stream.current.test_any(extra_end_rules)
119 return False
120
121 def free_identifier(self, lineno=None):
122 """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
123 self._last_identifier += 1
124 rv = object.__new__(nodes.InternalName)
125 nodes.Node.__init__(rv, f"fi{self._last_identifier}", lineno=lineno)
126 return rv
127
128 def parse_statement(self):
129 """Parse a single statement."""
130 token = self.stream.current
131 if token.type != "name":
132 self.fail("tag name expected", token.lineno)
133 self._tag_stack.append(token.value)
134 pop_tag = True
135 try:
136 if token.value in _statement_keywords:
137 return getattr(self, "parse_" + self.stream.current.value)()
138 if token.value == "call":
139 return self.parse_call_block()
140 if token.value == "filter":
141 return self.parse_filter_block()
142 ext = self.extensions.get(token.value)
143 if ext is not None:
144 return ext(self)
145
146 # did not work out, remove the token we pushed by accident
147 # from the stack so that the unknown tag fail function can
148 # produce a proper error message.
149 self._tag_stack.pop()
150 pop_tag = False
151 self.fail_unknown_tag(token.value, token.lineno)
152 finally:
153 if pop_tag:
154 self._tag_stack.pop()
155
156 def parse_statements(self, end_tokens, drop_needle=False):
157 """Parse multiple statements into a list until one of the end tokens
158 is reached. This is used to parse the body of statements as it also
159 parses template data if appropriate. The parser checks first if the
160 current token is a colon and skips it if there is one. Then it checks
161 for the block end and parses until if one of the `end_tokens` is
162 reached. Per default the active token in the stream at the end of
163 the call is the matched end token. If this is not wanted `drop_needle`
164 can be set to `True` and the end token is removed.
165 """
166 # the first token may be a colon for python compatibility
167 self.stream.skip_if("colon")
168
169 # in the future it would be possible to add whole code sections
170 # by adding some sort of end of statement token and parsing those here.
171 self.stream.expect("block_end")
172 result = self.subparse(end_tokens)
173
174 # we reached the end of the template too early, the subparser
175 # does not check for this, so we do that now
176 if self.stream.current.type == "eof":
177 self.fail_eof(end_tokens)
178
179 if drop_needle:
180 next(self.stream)
181 return result
182
183 def parse_set(self):
184 """Parse an assign statement."""
185 lineno = next(self.stream).lineno
186 target = self.parse_assign_target(with_namespace=True)
187 if self.stream.skip_if("assign"):
188 expr = self.parse_tuple()
189 return nodes.Assign(target, expr, lineno=lineno)
190 filter_node = self.parse_filter(None)
191 body = self.parse_statements(("name:endset",), drop_needle=True)
192 return nodes.AssignBlock(target, filter_node, body, lineno=lineno)
193
194 def parse_for(self):
195 """Parse a for loop."""
196 lineno = self.stream.expect("name:for").lineno
197 target = self.parse_assign_target(extra_end_rules=("name:in",))
198 self.stream.expect("name:in")
199 iter = self.parse_tuple(
200 with_condexpr=False, extra_end_rules=("name:recursive",)
201 )
202 test = None
203 if self.stream.skip_if("name:if"):
204 test = self.parse_expression()
205 recursive = self.stream.skip_if("name:recursive")
206 body = self.parse_statements(("name:endfor", "name:else"))
207 if next(self.stream).value == "endfor":
208 else_ = []
209 else:
210 else_ = self.parse_statements(("name:endfor",), drop_needle=True)
211 return nodes.For(target, iter, body, else_, test, recursive, lineno=lineno)
212
213 def parse_if(self):
214 """Parse an if construct."""
215 node = result = nodes.If(lineno=self.stream.expect("name:if").lineno)
216 while 1:
217 node.test = self.parse_tuple(with_condexpr=False)
218 node.body = self.parse_statements(("name:elif", "name:else", "name:endif"))
219 node.elif_ = []
220 node.else_ = []
221 token = next(self.stream)
222 if token.test("name:elif"):
223 node = nodes.If(lineno=self.stream.current.lineno)
224 result.elif_.append(node)
225 continue
226 elif token.test("name:else"):
227 result.else_ = self.parse_statements(("name:endif",), drop_needle=True)
228 break
229 return result
230
231 def parse_with(self):
232 node = nodes.With(lineno=next(self.stream).lineno)
233 targets = []
234 values = []
235 while self.stream.current.type != "block_end":
236 if targets:
237 self.stream.expect("comma")
238 target = self.parse_assign_target()
239 target.set_ctx("param")
240 targets.append(target)
241 self.stream.expect("assign")
242 values.append(self.parse_expression())
243 node.targets = targets
244 node.values = values
245 node.body = self.parse_statements(("name:endwith",), drop_needle=True)
246 return node
247
248 def parse_autoescape(self):
249 node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno)
250 node.options = [nodes.Keyword("autoescape", self.parse_expression())]
251 node.body = self.parse_statements(("name:endautoescape",), drop_needle=True)
252 return nodes.Scope([node])
253
254 def parse_block(self):
255 node = nodes.Block(lineno=next(self.stream).lineno)
256 node.name = self.stream.expect("name").value
257 node.scoped = self.stream.skip_if("name:scoped")
258
259 # common problem people encounter when switching from django
260 # to jinja. we do not support hyphens in block names, so let's
261 # raise a nicer error message in that case.
262 if self.stream.current.type == "sub":
263 self.fail(
264 "Block names in Jinja have to be valid Python identifiers and may not"
265 " contain hyphens, use an underscore instead."
266 )
267
268 node.body = self.parse_statements(("name:endblock",), drop_needle=True)
269 self.stream.skip_if("name:" + node.name)
270 return node
271
272 def parse_extends(self):
273 node = nodes.Extends(lineno=next(self.stream).lineno)
274 node.template = self.parse_expression()
275 return node
276
277 def parse_import_context(self, node, default):
278 if self.stream.current.test_any(
279 "name:with", "name:without"
280 ) and self.stream.look().test("name:context"):
281 node.with_context = next(self.stream).value == "with"
282 self.stream.skip()
283 else:
284 node.with_context = default
285 return node
286
287 def parse_include(self):
288 node = nodes.Include(lineno=next(self.stream).lineno)
289 node.template = self.parse_expression()
290 if self.stream.current.test("name:ignore") and self.stream.look().test(
291 "name:missing"
292 ):
293 node.ignore_missing = True
294 self.stream.skip(2)
295 else:
296 node.ignore_missing = False
297 return self.parse_import_context(node, True)
298
299 def parse_import(self):
300 node = nodes.Import(lineno=next(self.stream).lineno)
301 node.template = self.parse_expression()
302 self.stream.expect("name:as")
303 node.target = self.parse_assign_target(name_only=True).name
304 return self.parse_import_context(node, False)
305
306 def parse_from(self):
307 node = nodes.FromImport(lineno=next(self.stream).lineno)
308 node.template = self.parse_expression()
309 self.stream.expect("name:import")
310 node.names = []
311
312 def parse_context():
313 if self.stream.current.value in (
314 "with",
315 "without",
316 ) and self.stream.look().test("name:context"):
317 node.with_context = next(self.stream).value == "with"
318 self.stream.skip()
319 return True
320 return False
321
322 while 1:
323 if node.names:
324 self.stream.expect("comma")
325 if self.stream.current.type == "name":
326 if parse_context():
327 break
328 target = self.parse_assign_target(name_only=True)
329 if target.name.startswith("_"):
330 self.fail(
331 "names starting with an underline can not be imported",
332 target.lineno,
333 exc=TemplateAssertionError,
334 )
335 if self.stream.skip_if("name:as"):
336 alias = self.parse_assign_target(name_only=True)
337 node.names.append((target.name, alias.name))
338 else:
339 node.names.append(target.name)
340 if parse_context() or self.stream.current.type != "comma":
341 break
342 else:
343 self.stream.expect("name")
344 if not hasattr(node, "with_context"):
345 node.with_context = False
346 return node
347
348 def parse_signature(self, node):
349 node.args = args = []
350 node.defaults = defaults = []
351 self.stream.expect("lparen")
352 while self.stream.current.type != "rparen":
353 if args:
354 self.stream.expect("comma")
355 arg = self.parse_assign_target(name_only=True)
356 arg.set_ctx("param")
357 if self.stream.skip_if("assign"):
358 defaults.append(self.parse_expression())
359 elif defaults:
360 self.fail("non-default argument follows default argument")
361 args.append(arg)
362 self.stream.expect("rparen")
363
364 def parse_call_block(self):
365 node = nodes.CallBlock(lineno=next(self.stream).lineno)
366 if self.stream.current.type == "lparen":
367 self.parse_signature(node)
368 else:
369 node.args = []
370 node.defaults = []
371
372 node.call = self.parse_expression()
373 if not isinstance(node.call, nodes.Call):
374 self.fail("expected call", node.lineno)
375 node.body = self.parse_statements(("name:endcall",), drop_needle=True)
376 return node
377
378 def parse_filter_block(self):
379 node = nodes.FilterBlock(lineno=next(self.stream).lineno)
380 node.filter = self.parse_filter(None, start_inline=True)
381 node.body = self.parse_statements(("name:endfilter",), drop_needle=True)
382 return node
383
384 def parse_macro(self):
385 node = nodes.Macro(lineno=next(self.stream).lineno)
386 node.name = self.parse_assign_target(name_only=True).name
387 self.parse_signature(node)
388 node.body = self.parse_statements(("name:endmacro",), drop_needle=True)
389 return node
390
391 def parse_print(self):
392 node = nodes.Output(lineno=next(self.stream).lineno)
393 node.nodes = []
394 while self.stream.current.type != "block_end":
395 if node.nodes:
396 self.stream.expect("comma")
397 node.nodes.append(self.parse_expression())
398 return node
399
400 def parse_assign_target(
401 self,
402 with_tuple=True,
403 name_only=False,
404 extra_end_rules=None,
405 with_namespace=False,
406 ):
407 """Parse an assignment target. As Jinja allows assignments to
408 tuples, this function can parse all allowed assignment targets. Per
409 default assignments to tuples are parsed, that can be disable however
410 by setting `with_tuple` to `False`. If only assignments to names are
411 wanted `name_only` can be set to `True`. The `extra_end_rules`
412 parameter is forwarded to the tuple parsing function. If
413 `with_namespace` is enabled, a namespace assignment may be parsed.
414 """
415 if with_namespace and self.stream.look().type == "dot":
416 token = self.stream.expect("name")
417 next(self.stream) # dot
418 attr = self.stream.expect("name")
419 target = nodes.NSRef(token.value, attr.value, lineno=token.lineno)
420 elif name_only:
421 token = self.stream.expect("name")
422 target = nodes.Name(token.value, "store", lineno=token.lineno)
423 else:
424 if with_tuple:
425 target = self.parse_tuple(
426 simplified=True, extra_end_rules=extra_end_rules
427 )
428 else:
429 target = self.parse_primary()
430 target.set_ctx("store")
431 if not target.can_assign():
432 self.fail(
433 f"can't assign to {target.__class__.__name__.lower()!r}", target.lineno
434 )
435 return target
436
437 def parse_expression(self, with_condexpr=True):
438 """Parse an expression. Per default all expressions are parsed, if
439 the optional `with_condexpr` parameter is set to `False` conditional
440 expressions are not parsed.
441 """
442 if with_condexpr:
443 return self.parse_condexpr()
444 return self.parse_or()
445
446 def parse_condexpr(self):
447 lineno = self.stream.current.lineno
448 expr1 = self.parse_or()
449 while self.stream.skip_if("name:if"):
450 expr2 = self.parse_or()
451 if self.stream.skip_if("name:else"):
452 expr3 = self.parse_condexpr()
453 else:
454 expr3 = None
455 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
456 lineno = self.stream.current.lineno
457 return expr1
458
459 def parse_or(self):
460 lineno = self.stream.current.lineno
461 left = self.parse_and()
462 while self.stream.skip_if("name:or"):
463 right = self.parse_and()
464 left = nodes.Or(left, right, lineno=lineno)
465 lineno = self.stream.current.lineno
466 return left
467
468 def parse_and(self):
469 lineno = self.stream.current.lineno
470 left = self.parse_not()
471 while self.stream.skip_if("name:and"):
472 right = self.parse_not()
473 left = nodes.And(left, right, lineno=lineno)
474 lineno = self.stream.current.lineno
475 return left
476
477 def parse_not(self):
478 if self.stream.current.test("name:not"):
479 lineno = next(self.stream).lineno
480 return nodes.Not(self.parse_not(), lineno=lineno)
481 return self.parse_compare()
482
483 def parse_compare(self):
484 lineno = self.stream.current.lineno
485 expr = self.parse_math1()
486 ops = []
487 while 1:
488 token_type = self.stream.current.type
489 if token_type in _compare_operators:
490 next(self.stream)
491 ops.append(nodes.Operand(token_type, self.parse_math1()))
492 elif self.stream.skip_if("name:in"):
493 ops.append(nodes.Operand("in", self.parse_math1()))
494 elif self.stream.current.test("name:not") and self.stream.look().test(
495 "name:in"
496 ):
497 self.stream.skip(2)
498 ops.append(nodes.Operand("notin", self.parse_math1()))
499 else:
500 break
501 lineno = self.stream.current.lineno
502 if not ops:
503 return expr
504 return nodes.Compare(expr, ops, lineno=lineno)
505
506 def parse_math1(self):
507 lineno = self.stream.current.lineno
508 left = self.parse_concat()
509 while self.stream.current.type in ("add", "sub"):
510 cls = _math_nodes[self.stream.current.type]
511 next(self.stream)
512 right = self.parse_concat()
513 left = cls(left, right, lineno=lineno)
514 lineno = self.stream.current.lineno
515 return left
516
517 def parse_concat(self):
518 lineno = self.stream.current.lineno
519 args = [self.parse_math2()]
520 while self.stream.current.type == "tilde":
521 next(self.stream)
522 args.append(self.parse_math2())
523 if len(args) == 1:
524 return args[0]
525 return nodes.Concat(args, lineno=lineno)
526
527 def parse_math2(self):
528 lineno = self.stream.current.lineno
529 left = self.parse_pow()
530 while self.stream.current.type in ("mul", "div", "floordiv", "mod"):
531 cls = _math_nodes[self.stream.current.type]
532 next(self.stream)
533 right = self.parse_pow()
534 left = cls(left, right, lineno=lineno)
535 lineno = self.stream.current.lineno
536 return left
537
538 def parse_pow(self):
539 lineno = self.stream.current.lineno
540 left = self.parse_unary()
541 while self.stream.current.type == "pow":
542 next(self.stream)
543 right = self.parse_unary()
544 left = nodes.Pow(left, right, lineno=lineno)
545 lineno = self.stream.current.lineno
546 return left
547
548 def parse_unary(self, with_filter=True):
549 token_type = self.stream.current.type
550 lineno = self.stream.current.lineno
551 if token_type == "sub":
552 next(self.stream)
553 node = nodes.Neg(self.parse_unary(False), lineno=lineno)
554 elif token_type == "add":
555 next(self.stream)
556 node = nodes.Pos(self.parse_unary(False), lineno=lineno)
557 else:
558 node = self.parse_primary()
559 node = self.parse_postfix(node)
560 if with_filter:
561 node = self.parse_filter_expr(node)
562 return node
563
564 def parse_primary(self):
565 token = self.stream.current
566 if token.type == "name":
567 if token.value in ("true", "false", "True", "False"):
568 node = nodes.Const(token.value in ("true", "True"), lineno=token.lineno)
569 elif token.value in ("none", "None"):
570 node = nodes.Const(None, lineno=token.lineno)
571 else:
572 node = nodes.Name(token.value, "load", lineno=token.lineno)
573 next(self.stream)
574 elif token.type == "string":
575 next(self.stream)
576 buf = [token.value]
577 lineno = token.lineno
578 while self.stream.current.type == "string":
579 buf.append(self.stream.current.value)
580 next(self.stream)
581 node = nodes.Const("".join(buf), lineno=lineno)
582 elif token.type in ("integer", "float"):
583 next(self.stream)
584 node = nodes.Const(token.value, lineno=token.lineno)
585 elif token.type == "lparen":
586 next(self.stream)
587 node = self.parse_tuple(explicit_parentheses=True)
588 self.stream.expect("rparen")
589 elif token.type == "lbracket":
590 node = self.parse_list()
591 elif token.type == "lbrace":
592 node = self.parse_dict()
593 else:
594 self.fail(f"unexpected {describe_token(token)!r}", token.lineno)
595 return node
596
597 def parse_tuple(
598 self,
599 simplified=False,
600 with_condexpr=True,
601 extra_end_rules=None,
602 explicit_parentheses=False,
603 ):
604 """Works like `parse_expression` but if multiple expressions are
605 delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
606 This method could also return a regular expression instead of a tuple
607 if no commas where found.
608
609 The default parsing mode is a full tuple. If `simplified` is `True`
610 only names and literals are parsed. The `no_condexpr` parameter is
611 forwarded to :meth:`parse_expression`.
612
613 Because tuples do not require delimiters and may end in a bogus comma
614 an extra hint is needed that marks the end of a tuple. For example
615 for loops support tuples between `for` and `in`. In that case the
616 `extra_end_rules` is set to ``['name:in']``.
617
618 `explicit_parentheses` is true if the parsing was triggered by an
619 expression in parentheses. This is used to figure out if an empty
620 tuple is a valid expression or not.
621 """
622 lineno = self.stream.current.lineno
623 if simplified:
624 parse = self.parse_primary
625 elif with_condexpr:
626 parse = self.parse_expression
627 else:
628
629 def parse():
630 return self.parse_expression(with_condexpr=False)
631
632 args = []
633 is_tuple = False
634 while 1:
635 if args:
636 self.stream.expect("comma")
637 if self.is_tuple_end(extra_end_rules):
638 break
639 args.append(parse())
640 if self.stream.current.type == "comma":
641 is_tuple = True
642 else:
643 break
644 lineno = self.stream.current.lineno
645
646 if not is_tuple:
647 if args:
648 return args[0]
649
650 # if we don't have explicit parentheses, an empty tuple is
651 # not a valid expression. This would mean nothing (literally
652 # nothing) in the spot of an expression would be an empty
653 # tuple.
654 if not explicit_parentheses:
655 self.fail(
656 "Expected an expression,"
657 f" got {describe_token(self.stream.current)!r}"
658 )
659
660 return nodes.Tuple(args, "load", lineno=lineno)
661
662 def parse_list(self):
663 token = self.stream.expect("lbracket")
664 items = []
665 while self.stream.current.type != "rbracket":
666 if items:
667 self.stream.expect("comma")
668 if self.stream.current.type == "rbracket":
669 break
670 items.append(self.parse_expression())
671 self.stream.expect("rbracket")
672 return nodes.List(items, lineno=token.lineno)
673
674 def parse_dict(self):
675 token = self.stream.expect("lbrace")
676 items = []
677 while self.stream.current.type != "rbrace":
678 if items:
679 self.stream.expect("comma")
680 if self.stream.current.type == "rbrace":
681 break
682 key = self.parse_expression()
683 self.stream.expect("colon")
684 value = self.parse_expression()
685 items.append(nodes.Pair(key, value, lineno=key.lineno))
686 self.stream.expect("rbrace")
687 return nodes.Dict(items, lineno=token.lineno)
688
689 def parse_postfix(self, node):
690 while 1:
691 token_type = self.stream.current.type
692 if token_type == "dot" or token_type == "lbracket":
693 node = self.parse_subscript(node)
694 # calls are valid both after postfix expressions (getattr
695 # and getitem) as well as filters and tests
696 elif token_type == "lparen":
697 node = self.parse_call(node)
698 else:
699 break
700 return node
701
702 def parse_filter_expr(self, node):
703 while 1:
704 token_type = self.stream.current.type
705 if token_type == "pipe":
706 node = self.parse_filter(node)
707 elif token_type == "name" and self.stream.current.value == "is":
708 node = self.parse_test(node)
709 # calls are valid both after postfix expressions (getattr
710 # and getitem) as well as filters and tests
711 elif token_type == "lparen":
712 node = self.parse_call(node)
713 else:
714 break
715 return node
716
717 def parse_subscript(self, node):
718 token = next(self.stream)
719 if token.type == "dot":
720 attr_token = self.stream.current
721 next(self.stream)
722 if attr_token.type == "name":
723 return nodes.Getattr(
724 node, attr_token.value, "load", lineno=token.lineno
725 )
726 elif attr_token.type != "integer":
727 self.fail("expected name or number", attr_token.lineno)
728 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
729 return nodes.Getitem(node, arg, "load", lineno=token.lineno)
730 if token.type == "lbracket":
731 args = []
732 while self.stream.current.type != "rbracket":
733 if args:
734 self.stream.expect("comma")
735 args.append(self.parse_subscribed())
736 self.stream.expect("rbracket")
737 if len(args) == 1:
738 arg = args[0]
739 else:
740 arg = nodes.Tuple(args, "load", lineno=token.lineno)
741 return nodes.Getitem(node, arg, "load", lineno=token.lineno)
742 self.fail("expected subscript expression", token.lineno)
743
744 def parse_subscribed(self):
745 lineno = self.stream.current.lineno
746
747 if self.stream.current.type == "colon":
748 next(self.stream)
749 args = [None]
750 else:
751 node = self.parse_expression()
752 if self.stream.current.type != "colon":
753 return node
754 next(self.stream)
755 args = [node]
756
757 if self.stream.current.type == "colon":
758 args.append(None)
759 elif self.stream.current.type not in ("rbracket", "comma"):
760 args.append(self.parse_expression())
761 else:
762 args.append(None)
763
764 if self.stream.current.type == "colon":
765 next(self.stream)
766 if self.stream.current.type not in ("rbracket", "comma"):
767 args.append(self.parse_expression())
768 else:
769 args.append(None)
770 else:
771 args.append(None)
772
773 return nodes.Slice(lineno=lineno, *args)
774
775 def parse_call(self, node):
776 token = self.stream.expect("lparen")
777 args = []
778 kwargs = []
779 dyn_args = dyn_kwargs = None
780 require_comma = False
781
782 def ensure(expr):
783 if not expr:
784 self.fail("invalid syntax for function call expression", token.lineno)
785
786 while self.stream.current.type != "rparen":
787 if require_comma:
788 self.stream.expect("comma")
789 # support for trailing comma
790 if self.stream.current.type == "rparen":
791 break
792 if self.stream.current.type == "mul":
793 ensure(dyn_args is None and dyn_kwargs is None)
794 next(self.stream)
795 dyn_args = self.parse_expression()
796 elif self.stream.current.type == "pow":
797 ensure(dyn_kwargs is None)
798 next(self.stream)
799 dyn_kwargs = self.parse_expression()
800 else:
801 if (
802 self.stream.current.type == "name"
803 and self.stream.look().type == "assign"
804 ):
805 # Parsing a kwarg
806 ensure(dyn_kwargs is None)
807 key = self.stream.current.value
808 self.stream.skip(2)
809 value = self.parse_expression()
810 kwargs.append(nodes.Keyword(key, value, lineno=value.lineno))
811 else:
812 # Parsing an arg
813 ensure(dyn_args is None and dyn_kwargs is None and not kwargs)
814 args.append(self.parse_expression())
815
816 require_comma = True
817 self.stream.expect("rparen")
818
819 if node is None:
820 return args, kwargs, dyn_args, dyn_kwargs
821 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno)
822
823 def parse_filter(self, node, start_inline=False):
824 while self.stream.current.type == "pipe" or start_inline:
825 if not start_inline:
826 next(self.stream)
827 token = self.stream.expect("name")
828 name = token.value
829 while self.stream.current.type == "dot":
830 next(self.stream)
831 name += "." + self.stream.expect("name").value
832 if self.stream.current.type == "lparen":
833 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
834 else:
835 args = []
836 kwargs = []
837 dyn_args = dyn_kwargs = None
838 node = nodes.Filter(
839 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno
840 )
841 start_inline = False
842 return node
843
844 def parse_test(self, node):
845 token = next(self.stream)
846 if self.stream.current.test("name:not"):
847 next(self.stream)
848 negated = True
849 else:
850 negated = False
851 name = self.stream.expect("name").value
852 while self.stream.current.type == "dot":
853 next(self.stream)
854 name += "." + self.stream.expect("name").value
855 dyn_args = dyn_kwargs = None
856 kwargs = []
857 if self.stream.current.type == "lparen":
858 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
859 elif self.stream.current.type in (
860 "name",
861 "string",
862 "integer",
863 "float",
864 "lparen",
865 "lbracket",
866 "lbrace",
867 ) and not self.stream.current.test_any("name:else", "name:or", "name:and"):
868 if self.stream.current.test("name:is"):
869 self.fail("You cannot chain multiple tests with is")
870 arg_node = self.parse_primary()
871 arg_node = self.parse_postfix(arg_node)
872 args = [arg_node]
873 else:
874 args = []
875 node = nodes.Test(
876 node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno
877 )
878 if negated:
879 node = nodes.Not(node, lineno=token.lineno)
880 return node
881
882 def subparse(self, end_tokens=None):
883 body = []
884 data_buffer = []
885 add_data = data_buffer.append
886
887 if end_tokens is not None:
888 self._end_token_stack.append(end_tokens)
889
890 def flush_data():
891 if data_buffer:
892 lineno = data_buffer[0].lineno
893 body.append(nodes.Output(data_buffer[:], lineno=lineno))
894 del data_buffer[:]
895
896 try:
897 while self.stream:
898 token = self.stream.current
899 if token.type == "data":
900 if token.value:
901 add_data(nodes.TemplateData(token.value, lineno=token.lineno))
902 next(self.stream)
903 elif token.type == "variable_begin":
904 next(self.stream)
905 add_data(self.parse_tuple(with_condexpr=True))
906 self.stream.expect("variable_end")
907 elif token.type == "block_begin":
908 flush_data()
909 next(self.stream)
910 if end_tokens is not None and self.stream.current.test_any(
911 *end_tokens
912 ):
913 return body
914 rv = self.parse_statement()
915 if isinstance(rv, list):
916 body.extend(rv)
917 else:
918 body.append(rv)
919 self.stream.expect("block_end")
920 else:
921 raise AssertionError("internal parsing error")
922
923 flush_data()
924 finally:
925 if end_tokens is not None:
926 self._end_token_stack.pop()
927
928 return body
929
930 def parse(self):
931 """Parse the whole template into a `Template` node."""
932 result = nodes.Template(self.subparse(), lineno=1)
933 result.set_environment(self.environment)
934 return result