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