blob: 84a317c66de7bc11a8ee10f52b34d04595d191ca [file] [log] [blame]
Armin Ronacher07bc6842008-03-31 14:18:49 +02001# -*- coding: utf-8 -*-
2"""
3 jinja2.parser
4 ~~~~~~~~~~~~~
5
6 Implements the template parser.
7
8 :copyright: 2008 by Armin Ronacher.
9 :license: BSD, see LICENSE for more details.
10"""
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020011from jinja2 import nodes
12from jinja2.exceptions import TemplateSyntaxError
Armin Ronacher07bc6842008-03-31 14:18:49 +020013
14
Armin Ronachere791c2a2008-04-07 18:39:54 +020015_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
Armin Ronacher2e9396b2008-04-16 14:21:57 +020016 'macro', 'include', 'trans'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020017_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq', 'in'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020018_statement_end_tokens = set(['elif', 'else', 'endblock', 'endfilter',
Armin Ronachere791c2a2008-04-07 18:39:54 +020019 'endfor', 'endif', 'endmacro', 'variable_end',
20 'in', 'recursive', 'endcall', 'block_end'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020021
Armin Ronacher07bc6842008-03-31 14:18:49 +020022
23class Parser(object):
Armin Ronacherd55ab532008-04-09 16:13:39 +020024 """The template parser class.
Armin Ronacher07bc6842008-03-31 14:18:49 +020025
26 Transforms sourcecode into an abstract syntax tree.
27 """
28
29 def __init__(self, environment, source, filename=None):
30 self.environment = environment
Armin Ronacher07bc6842008-03-31 14:18:49 +020031 if isinstance(filename, unicode):
32 filename = filename.encode('utf-8')
Armin Ronacherbf7c4ad2008-04-12 12:02:36 +020033 self.source = unicode(source)
Armin Ronacher07bc6842008-03-31 14:18:49 +020034 self.filename = filename
35 self.closed = False
Armin Ronacher07bc6842008-03-31 14:18:49 +020036 self.stream = environment.lexer.tokenize(source, filename)
37
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020038 def end_statement(self):
39 """Make sure that the statement ends properly."""
40 if self.stream.current.type is 'semicolon':
41 self.stream.next()
42 elif self.stream.current.type not in _statement_end_tokens:
43 raise TemplateSyntaxError('ambigous end of statement',
44 self.stream.current.lineno,
45 self.filename)
46
47 def parse_statement(self):
48 """Parse a single statement."""
49 token_type = self.stream.current.type
50 if token_type in _statement_keywords:
51 return getattr(self, 'parse_' + token_type)()
52 elif token_type is 'call':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020053 return self.parse_call_block()
Armin Ronacherfa865fb2008-04-12 22:11:53 +020054 elif token_type is 'filter':
55 return self.parse_filter_block()
Armin Ronachere791c2a2008-04-07 18:39:54 +020056 lineno = self.stream.current
Armin Ronacher8efc5222008-04-08 14:47:40 +020057 expr = self.parse_tuple()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020058 if self.stream.current.type == 'assign':
Armin Ronachere791c2a2008-04-07 18:39:54 +020059 result = self.parse_assign(expr)
60 else:
61 result = nodes.ExprStmt(expr, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020062 self.end_statement()
Armin Ronachere791c2a2008-04-07 18:39:54 +020063 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020064
65 def parse_assign(self, target):
66 """Parse an assign statement."""
67 lineno = self.stream.expect('assign').lineno
68 if not target.can_assign():
69 raise TemplateSyntaxError("can't assign to '%s'" %
70 target, target.lineno,
71 self.filename)
72 expr = self.parse_tuple()
73 self.end_statement()
Armin Ronachere791c2a2008-04-07 18:39:54 +020074 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020075 return nodes.Assign(target, expr, lineno=lineno)
76
77 def parse_statements(self, end_tokens, drop_needle=False):
78 """
79 Parse multiple statements into a list until one of the end tokens
80 is reached. This is used to parse the body of statements as it
81 also parses template data if appropriate.
82 """
83 # the first token may be a colon for python compatibility
84 if self.stream.current.type is 'colon':
85 self.stream.next()
86
87 if self.stream.current.type is 'block_end':
88 self.stream.next()
89 result = self.subparse(end_tokens)
90 else:
91 result = []
92 while self.stream.current.type not in end_tokens:
Armin Ronachere791c2a2008-04-07 18:39:54 +020093 if self.stream.current.type is 'block_end':
94 self.stream.next()
95 result.extend(self.subparse(end_tokens))
96 break
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020097 result.append(self.parse_statement())
98 if drop_needle:
99 self.stream.next()
100 return result
101
102 def parse_for(self):
103 """Parse a for loop."""
104 lineno = self.stream.expect('for').lineno
105 target = self.parse_tuple(simplified=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200106 if not target.can_assign():
107 raise TemplateSyntaxError("can't assign to '%s'" %
108 target, target.lineno,
109 self.filename)
110 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200111 self.stream.expect('in')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200112 iter = self.parse_tuple(no_condexpr=True)
113 test = None
114 if self.stream.current.type is 'if':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200115 self.stream.next()
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200116 test = self.parse_expression()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200117 body = self.parse_statements(('endfor', 'else'))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200118 if self.stream.next().type is 'endfor':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200119 else_ = []
120 else:
121 else_ = self.parse_statements(('endfor',), drop_needle=True)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200122 return nodes.For(target, iter, body, else_, test, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200123
124 def parse_if(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200125 """Parse an if construct."""
126 node = result = nodes.If(lineno=self.stream.expect('if').lineno)
127 while 1:
128 # TODO: exclude conditional expressions here
129 node.test = self.parse_tuple()
130 node.body = self.parse_statements(('elif', 'else', 'endif'))
131 token_type = self.stream.next().type
132 if token_type is 'elif':
133 new_node = nodes.If(lineno=self.stream.current.lineno)
134 node.else_ = [new_node]
135 node = new_node
136 continue
137 elif token_type is 'else':
138 node.else_ = self.parse_statements(('endif',),
139 drop_needle=True)
140 else:
141 node.else_ = []
142 break
143 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200144
145 def parse_block(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200146 node = nodes.Block(lineno=self.stream.expect('block').lineno)
147 node.name = self.stream.expect('name').value
148 node.body = self.parse_statements(('endblock',), drop_needle=True)
149 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200150
151 def parse_extends(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200152 node = nodes.Extends(lineno=self.stream.expect('extends').lineno)
153 node.template = self.parse_expression()
154 self.end_statement()
155 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200156
157 def parse_include(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200158 node = nodes.Include(lineno=self.stream.expect('include').lineno)
159 expr = self.parse_expression()
160 if self.stream.current.type is 'assign':
161 self.stream.next()
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200162 if not isinstance(expr, nodes.Name):
163 raise TemplateSyntaxError('must assign imported template to '
164 'variable or current scope',
165 expr.lineno, self.filename)
166 if not expr.can_assign():
Armin Ronachere791c2a2008-04-07 18:39:54 +0200167 raise TemplateSyntaxError('can\'t assign imported template '
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200168 'to %r' % expr, expr.lineno,
Armin Ronachere791c2a2008-04-07 18:39:54 +0200169 self.filename)
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200170 node.target = expr.name
Armin Ronachere791c2a2008-04-07 18:39:54 +0200171 node.template = self.parse_expression()
172 else:
173 node.target = None
174 node.template = expr
175 self.end_statement()
176 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200177
Armin Ronacher71082072008-04-12 14:19:36 +0200178 def parse_signature(self, node):
179 node.args = args = []
180 node.defaults = defaults = []
181 self.stream.expect('lparen')
182 while self.stream.current.type is not 'rparen':
183 if args:
184 self.stream.expect('comma')
185 token = self.stream.expect('name')
186 arg = nodes.Name(token.value, 'param', lineno=token.lineno)
187 if not arg.can_assign():
188 raise TemplateSyntaxError("can't assign to '%s'" %
189 arg.name, arg.lineno,
190 self.filename)
191 if self.stream.current.type is 'assign':
192 self.stream.next()
193 defaults.append(self.parse_expression())
194 args.append(arg)
195 self.stream.expect('rparen')
196
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200197 def parse_call_block(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200198 node = nodes.CallBlock(lineno=self.stream.expect('call').lineno)
Armin Ronacher71082072008-04-12 14:19:36 +0200199 if self.stream.current.type is 'lparen':
200 self.parse_signature(node)
201
Armin Ronacher8edbe492008-04-10 20:43:43 +0200202 node.call = self.parse_expression()
203 if not isinstance(node.call, nodes.Call):
204 raise TemplateSyntaxError('expected call', node.lineno,
205 self.filename)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200206 node.body = self.parse_statements(('endcall',), drop_needle=True)
207 return node
208
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200209 def parse_filter_block(self):
210 node = nodes.FilterBlock(lineno=self.stream.expect('filter').lineno)
211 node.filter = self.parse_filter(None, start_inline=True)
212 node.body = self.parse_statements(('endfilter',), drop_needle=True)
213 return node
214
Armin Ronachere791c2a2008-04-07 18:39:54 +0200215 def parse_macro(self):
216 node = nodes.Macro(lineno=self.stream.expect('macro').lineno)
217 node.name = self.stream.expect('name').value
Armin Ronacherf059ec12008-04-11 22:21:00 +0200218 # make sure that assignments to that name are allowed
219 if not nodes.Name(node.name, 'store').can_assign():
220 raise TemplateSyntaxError('can\'t assign macro to %r' %
221 node.target, node.lineno,
222 self.filename)
Armin Ronacher71082072008-04-12 14:19:36 +0200223 self.parse_signature(node)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200224 node.body = self.parse_statements(('endmacro',), drop_needle=True)
225 return node
226
227 def parse_print(self):
228 node = nodes.Output(lineno=self.stream.expect('print').lineno)
229 node.nodes = []
230 while self.stream.current.type not in _statement_end_tokens:
231 if node.nodes:
232 self.stream.expect('comma')
233 node.nodes.append(self.parse_expression())
234 self.end_statement()
235 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200236
Armin Ronacher2e9396b2008-04-16 14:21:57 +0200237 def parse_trans(self):
238 """Parse a translatable section."""
239 # lazily imported because we don't want the i18n overhead
240 # if it's not used. (Even though the overhead is low)
241 from jinja2.i18n import parse_trans
242 return parse_trans(self)
243
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200244 def parse_expression(self, no_condexpr=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200245 """Parse an expression."""
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200246 if no_condexpr:
247 return self.parse_or()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200248 return self.parse_condexpr()
249
250 def parse_condexpr(self):
251 lineno = self.stream.current.lineno
252 expr1 = self.parse_or()
253 while self.stream.current.type is 'if':
254 self.stream.next()
255 expr2 = self.parse_or()
256 self.stream.expect('else')
257 expr3 = self.parse_condexpr()
258 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
259 lineno = self.stream.current.lineno
260 return expr1
261
262 def parse_or(self):
263 lineno = self.stream.current.lineno
264 left = self.parse_and()
265 while self.stream.current.type is 'or':
266 self.stream.next()
267 right = self.parse_and()
268 left = nodes.Or(left, right, lineno=lineno)
269 lineno = self.stream.current.lineno
270 return left
271
272 def parse_and(self):
273 lineno = self.stream.current.lineno
274 left = self.parse_compare()
275 while self.stream.current.type is 'and':
276 self.stream.next()
277 right = self.parse_compare()
278 left = nodes.And(left, right, lineno=lineno)
279 lineno = self.stream.current.lineno
280 return left
281
282 def parse_compare(self):
283 lineno = self.stream.current.lineno
284 expr = self.parse_add()
285 ops = []
286 while 1:
287 token_type = self.stream.current.type
288 if token_type in _compare_operators:
289 self.stream.next()
290 ops.append(nodes.Operand(token_type, self.parse_add()))
291 elif token_type is 'not' and self.stream.look().type is 'in':
292 self.stream.skip(2)
293 ops.append(nodes.Operand('notin', self.parse_add()))
294 else:
295 break
296 lineno = self.stream.current.lineno
297 if not ops:
298 return expr
299 return nodes.Compare(expr, ops, lineno=lineno)
300
301 def parse_add(self):
302 lineno = self.stream.current.lineno
303 left = self.parse_sub()
304 while self.stream.current.type is 'add':
305 self.stream.next()
306 right = self.parse_sub()
307 left = nodes.Add(left, right, lineno=lineno)
308 lineno = self.stream.current.lineno
309 return left
310
311 def parse_sub(self):
312 lineno = self.stream.current.lineno
313 left = self.parse_concat()
314 while self.stream.current.type is 'sub':
315 self.stream.next()
316 right = self.parse_concat()
317 left = nodes.Sub(left, right, lineno=lineno)
318 lineno = self.stream.current.lineno
319 return left
320
321 def parse_concat(self):
322 lineno = self.stream.current.lineno
323 args = [self.parse_mul()]
324 while self.stream.current.type is 'tilde':
325 self.stream.next()
326 args.append(self.parse_mul())
327 if len(args) == 1:
328 return args[0]
329 return nodes.Concat(args, lineno=lineno)
330
331 def parse_mul(self):
332 lineno = self.stream.current.lineno
333 left = self.parse_div()
334 while self.stream.current.type is 'mul':
335 self.stream.next()
336 right = self.parse_div()
337 left = nodes.Mul(left, right, lineno=lineno)
338 lineno = self.stream.current.lineno
339 return left
340
341 def parse_div(self):
342 lineno = self.stream.current.lineno
343 left = self.parse_floordiv()
344 while self.stream.current.type is 'div':
345 self.stream.next()
346 right = self.parse_floordiv()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200347 left = nodes.Div(left, right, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200348 lineno = self.stream.current.lineno
349 return left
350
351 def parse_floordiv(self):
352 lineno = self.stream.current.lineno
353 left = self.parse_mod()
354 while self.stream.current.type is 'floordiv':
355 self.stream.next()
356 right = self.parse_mod()
357 left = nodes.FloorDiv(left, right, lineno=lineno)
358 lineno = self.stream.current.lineno
359 return left
360
361 def parse_mod(self):
362 lineno = self.stream.current.lineno
363 left = self.parse_pow()
364 while self.stream.current.type is 'mod':
365 self.stream.next()
366 right = self.parse_pow()
367 left = nodes.Mod(left, right, lineno=lineno)
368 lineno = self.stream.current.lineno
369 return left
370
371 def parse_pow(self):
372 lineno = self.stream.current.lineno
373 left = self.parse_unary()
374 while self.stream.current.type is 'pow':
375 self.stream.next()
376 right = self.parse_unary()
377 left = nodes.Pow(left, right, lineno=lineno)
378 lineno = self.stream.current.lineno
379 return left
380
381 def parse_unary(self):
382 token_type = self.stream.current.type
383 lineno = self.stream.current.lineno
384 if token_type is 'not':
385 self.stream.next()
386 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200387 return nodes.Not(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200388 if token_type is 'sub':
389 self.stream.next()
390 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200391 return nodes.Neg(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200392 if token_type is 'add':
393 self.stream.next()
394 node = self.parse_unary()
395 return nodes.Pos(node, lineno=lineno)
396 return self.parse_primary()
397
398 def parse_primary(self, parse_postfix=True):
399 token = self.stream.current
400 if token.type is 'name':
401 if token.value in ('true', 'false'):
402 node = nodes.Const(token.value == 'true', lineno=token.lineno)
403 elif token.value == 'none':
404 node = nodes.Const(None, lineno=token.lineno)
405 else:
406 node = nodes.Name(token.value, 'load', lineno=token.lineno)
407 self.stream.next()
408 elif token.type in ('integer', 'float', 'string'):
409 self.stream.next()
410 node = nodes.Const(token.value, lineno=token.lineno)
411 elif token.type is 'lparen':
412 self.stream.next()
413 node = self.parse_tuple()
414 self.stream.expect('rparen')
415 elif token.type is 'lbracket':
416 node = self.parse_list()
417 elif token.type is 'lbrace':
418 node = self.parse_dict()
419 else:
420 raise TemplateSyntaxError("unexpected token '%s'" %
421 (token,), token.lineno,
422 self.filename)
423 if parse_postfix:
424 node = self.parse_postfix(node)
425 return node
426
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200427 def parse_tuple(self, enforce=False, simplified=False, no_condexpr=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200428 """
429 Parse multiple expressions into a tuple. This can also return
430 just one expression which is not a tuple. If you want to enforce
431 a tuple, pass it enforce=True (currently unused).
432 """
433 lineno = self.stream.current.lineno
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200434 if simplified:
435 parse = self.parse_primary
436 elif no_condexpr:
437 parse = lambda: self.parse_expression(no_condexpr=True)
438 else:
439 parse = self.parse_expression
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200440 args = []
441 is_tuple = False
442 while 1:
443 if args:
444 self.stream.expect('comma')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200445 if self.stream.current.type in _statement_end_tokens:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200446 break
447 args.append(parse())
448 if self.stream.current.type is not 'comma':
449 break
450 is_tuple = True
451 lineno = self.stream.current.lineno
452 if not is_tuple and args:
453 if enforce:
454 raise TemplateSyntaxError('tuple expected', lineno,
455 self.filename)
456 return args[0]
457 return nodes.Tuple(args, 'load', lineno=lineno)
458
459 def parse_list(self):
460 token = self.stream.expect('lbracket')
461 items = []
462 while self.stream.current.type is not 'rbracket':
463 if items:
464 self.stream.expect('comma')
465 if self.stream.current.type == 'rbracket':
466 break
467 items.append(self.parse_expression())
468 self.stream.expect('rbracket')
469 return nodes.List(items, lineno=token.lineno)
470
471 def parse_dict(self):
472 token = self.stream.expect('lbrace')
473 items = []
474 while self.stream.current.type is not 'rbrace':
475 if items:
476 self.stream.expect('comma')
477 if self.stream.current.type == 'rbrace':
478 break
479 key = self.parse_expression()
480 self.stream.expect('colon')
481 value = self.parse_expression()
482 items.append(nodes.Pair(key, value, lineno=key.lineno))
483 self.stream.expect('rbrace')
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200484 return nodes.Dict(items, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200485
486 def parse_postfix(self, node):
487 while 1:
488 token_type = self.stream.current.type
489 if token_type is 'dot' or token_type is 'lbracket':
490 node = self.parse_subscript(node)
491 elif token_type is 'lparen':
492 node = self.parse_call(node)
493 elif token_type is 'pipe':
494 node = self.parse_filter(node)
495 elif token_type is 'is':
496 node = self.parse_test(node)
497 else:
498 break
499 return node
500
501 def parse_subscript(self, node):
502 token = self.stream.next()
503 if token.type is 'dot':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200504 attr_token = self.stream.current
505 if attr_token.type not in ('name', 'integer'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200506 raise TemplateSyntaxError('expected name or number',
Armin Ronachere791c2a2008-04-07 18:39:54 +0200507 attr_token.lineno, self.filename)
508 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200509 self.stream.next()
510 elif token.type is 'lbracket':
511 args = []
512 while self.stream.current.type is not 'rbracket':
513 if args:
514 self.stream.expect('comma')
515 args.append(self.parse_subscribed())
516 self.stream.expect('rbracket')
517 if len(args) == 1:
518 arg = args[0]
519 else:
520 arg = nodes.Tuple(args, lineno, self.filename)
521 else:
522 raise TemplateSyntaxError('expected subscript expression',
523 self.lineno, self.filename)
524 return nodes.Subscript(node, arg, 'load', lineno=token.lineno)
525
526 def parse_subscribed(self):
527 lineno = self.stream.current.lineno
528
529 if self.stream.current.type is 'colon':
530 self.stream.next()
531 args = [None]
532 else:
533 node = self.parse_expression()
534 if self.stream.current.type is not 'colon':
535 return node
536 self.stream.next()
537 args = [node]
538
539 if self.stream.current.type is 'colon':
540 args.append(None)
541 elif self.stream.current.type not in ('rbracket', 'comma'):
542 args.append(self.parse_expression())
543 else:
544 args.append(None)
545
546 if self.stream.current.type is 'colon':
547 self.stream.next()
548 if self.stream.current.type not in ('rbracket', 'comma'):
549 args.append(self.parse_expression())
550 else:
551 args.append(None)
552 else:
553 args.append(None)
554
555 return nodes.Slice(lineno=lineno, *args)
556
557 def parse_call(self, node):
558 token = self.stream.expect('lparen')
559 args = []
560 kwargs = []
561 dyn_args = dyn_kwargs = None
562 require_comma = False
563
564 def ensure(expr):
565 if not expr:
566 raise TemplateSyntaxError('invalid syntax for function '
567 'call expression', token.lineno,
568 self.filename)
569
570 while self.stream.current.type is not 'rparen':
571 if require_comma:
572 self.stream.expect('comma')
573 # support for trailing comma
574 if self.stream.current.type is 'rparen':
575 break
576 if self.stream.current.type is 'mul':
577 ensure(dyn_args is None and dyn_kwargs is None)
578 self.stream.next()
579 dyn_args = self.parse_expression()
580 elif self.stream.current.type is 'pow':
581 ensure(dyn_kwargs is None)
582 self.stream.next()
583 dyn_kwargs = self.parse_expression()
584 else:
585 ensure(dyn_args is None and dyn_kwargs is None)
586 if self.stream.current.type is 'name' and \
587 self.stream.look().type is 'assign':
588 key = self.stream.current.value
589 self.stream.skip(2)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200590 kwargs.append(nodes.Keyword(key, self.parse_expression(),
591 lineno=key.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200592 else:
593 ensure(not kwargs)
594 args.append(self.parse_expression())
595
596 require_comma = True
597 self.stream.expect('rparen')
598
599 if node is None:
600 return args, kwargs, dyn_args, dyn_kwargs
601 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
602 lineno=token.lineno)
603
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200604 def parse_filter(self, node, start_inline=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200605 lineno = self.stream.current.type
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200606 while self.stream.current.type == 'pipe' or start_inline:
607 if not start_inline:
608 self.stream.next()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200609 token = self.stream.expect('name')
610 if self.stream.current.type is 'lparen':
611 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
612 else:
613 args = []
614 kwargs = []
615 dyn_args = dyn_kwargs = None
Armin Ronacherd55ab532008-04-09 16:13:39 +0200616 node = nodes.Filter(node, token.value, args, kwargs, dyn_args,
617 dyn_kwargs, lineno=token.lineno)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200618 start_inline = False
Armin Ronacherd55ab532008-04-09 16:13:39 +0200619 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200620
621 def parse_test(self, node):
622 token = self.stream.expect('is')
623 if self.stream.current.type is 'not':
624 self.stream.next()
625 negated = True
626 else:
627 negated = False
628 name = self.stream.expect('name').value
629 if self.stream.current.type is 'lparen':
630 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
631 elif self.stream.current.type in ('name', 'string', 'integer',
632 'float', 'lparen', 'lbracket',
633 'lbrace', 'regex'):
634 args = [self.parse_expression()]
635 else:
636 args = []
637 kwargs = []
638 dyn_args = dyn_kwargs = None
639 node = nodes.Test(node, name, args, kwargs, dyn_args,
640 dyn_kwargs, lineno=token.lineno)
641 if negated:
Armin Ronacher9a822052008-04-17 18:44:07 +0200642 node = nodes.Not(node, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200643 return node
644
645 def subparse(self, end_tokens=None):
646 body = []
647 data_buffer = []
648 add_data = data_buffer.append
649
650 def flush_data():
651 if data_buffer:
652 lineno = data_buffer[0].lineno
653 body.append(nodes.Output(data_buffer[:], lineno=lineno))
654 del data_buffer[:]
655
656 while self.stream:
657 token = self.stream.current
658 if token.type is 'data':
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200659 if token.value:
660 add_data(nodes.Const(token.value, lineno=token.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200661 self.stream.next()
662 elif token.type is 'variable_begin':
663 self.stream.next()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200664 want_comma = False
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200665 while self.stream.current.type not in _statement_end_tokens:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200666 if want_comma:
667 self.stream.expect('comma')
668 add_data(self.parse_expression())
669 want_comma = True
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200670 self.stream.expect('variable_end')
671 elif token.type is 'block_begin':
672 flush_data()
673 self.stream.next()
674 if end_tokens is not None and \
675 self.stream.current.type in end_tokens:
676 return body
677 while self.stream.current.type is not 'block_end':
678 body.append(self.parse_statement())
679 self.stream.expect('block_end')
680 else:
681 raise AssertionError('internal parsing error')
682
683 flush_data()
684 return body
685
686 def parse(self):
687 """Parse the whole template into a `Template` node."""
Armin Ronacherd55ab532008-04-09 16:13:39 +0200688 result = nodes.Template(self.subparse(), lineno=1)
689 result.set_environment(self.environment)
690 return result