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