1"""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 935