blob: 8db62deaf0ea1d0479138d1c2e63d810ff387bfb [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 Ronacherf59bac22008-04-20 13:11:43 +020011from operator import itemgetter
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020012from jinja2 import nodes
13from jinja2.exceptions import TemplateSyntaxError
Armin Ronacher07bc6842008-03-31 14:18:49 +020014
15
Armin Ronachere791c2a2008-04-07 18:39:54 +020016_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
Armin Ronacherf59bac22008-04-20 13:11:43 +020017 'macro', 'include'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020018_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq', 'in'])
Armin Ronacher2b60fe52008-04-21 08:23:59 +020019statement_end_tokens = set(['variable_end', 'block_end', 'in'])
Armin Ronacherb5124e62008-04-25 00:36:14 +020020_tuple_edge_tokens = set(['rparen']) | statement_end_tokens
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)
Armin Ronacher05530932008-04-20 13:27:49 +020037 self.extensions = {}
38 for extension in environment.extensions:
39 for tag in extension.tags:
40 self.extensions[tag] = extension.parse
Armin Ronacher07bc6842008-03-31 14:18:49 +020041
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020042 def parse_statement(self):
43 """Parse a single statement."""
44 token_type = self.stream.current.type
45 if token_type in _statement_keywords:
46 return getattr(self, 'parse_' + token_type)()
47 elif token_type is 'call':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020048 return self.parse_call_block()
Armin Ronacherfa865fb2008-04-12 22:11:53 +020049 elif token_type is 'filter':
50 return self.parse_filter_block()
Armin Ronacherf59bac22008-04-20 13:11:43 +020051 elif token_type is 'name':
52 ext = self.extensions.get(self.stream.current.value)
53 if ext is not None:
54 return ext(self)
Armin Ronachere791c2a2008-04-07 18:39:54 +020055 lineno = self.stream.current
Armin Ronacher8efc5222008-04-08 14:47:40 +020056 expr = self.parse_tuple()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020057 if self.stream.current.type == 'assign':
Armin Ronachere791c2a2008-04-07 18:39:54 +020058 result = self.parse_assign(expr)
59 else:
60 result = nodes.ExprStmt(expr, lineno=lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +020061 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020062
63 def parse_assign(self, target):
64 """Parse an assign statement."""
65 lineno = self.stream.expect('assign').lineno
66 if not target.can_assign():
67 raise TemplateSyntaxError("can't assign to '%s'" %
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020068 target.__class__.__name__.lower(),
69 target.lineno, self.filename)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020070 expr = self.parse_tuple()
Armin Ronachere791c2a2008-04-07 18:39:54 +020071 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020072 return nodes.Assign(target, expr, lineno=lineno)
73
74 def parse_statements(self, end_tokens, drop_needle=False):
Armin Ronacherf59bac22008-04-20 13:11:43 +020075 """Parse multiple statements into a list until one of the end tokens
76 is reached. This is used to parse the body of statements as it also
77 parses template data if appropriate.
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020078 """
79 # the first token may be a colon for python compatibility
80 if self.stream.current.type is 'colon':
81 self.stream.next()
82
Armin Ronacher2b60fe52008-04-21 08:23:59 +020083 # in the future it would be possible to add whole code sections
84 # by adding some sort of end of statement token and parsing those here.
85 self.stream.expect('block_end')
86 result = self.subparse(end_tokens)
87
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020088 if drop_needle:
89 self.stream.next()
90 return result
91
92 def parse_for(self):
93 """Parse a for loop."""
94 lineno = self.stream.expect('for').lineno
95 target = self.parse_tuple(simplified=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +020096 if not target.can_assign():
97 raise TemplateSyntaxError("can't assign to '%s'" %
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020098 target.__class__.__name__.lower(),
99 target.lineno, self.filename)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200100 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200101 self.stream.expect('in')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200102 iter = self.parse_tuple(no_condexpr=True)
103 test = None
104 if self.stream.current.type is 'if':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200105 self.stream.next()
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200106 test = self.parse_expression()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200107 body = self.parse_statements(('endfor', 'else'))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200108 if self.stream.next().type is 'endfor':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200109 else_ = []
110 else:
111 else_ = self.parse_statements(('endfor',), drop_needle=True)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200112 return nodes.For(target, iter, body, else_, test, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200113
114 def parse_if(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200115 """Parse an if construct."""
116 node = result = nodes.If(lineno=self.stream.expect('if').lineno)
117 while 1:
118 # TODO: exclude conditional expressions here
119 node.test = self.parse_tuple()
120 node.body = self.parse_statements(('elif', 'else', 'endif'))
121 token_type = self.stream.next().type
122 if token_type is 'elif':
123 new_node = nodes.If(lineno=self.stream.current.lineno)
124 node.else_ = [new_node]
125 node = new_node
126 continue
127 elif token_type is 'else':
128 node.else_ = self.parse_statements(('endif',),
129 drop_needle=True)
130 else:
131 node.else_ = []
132 break
133 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200134
135 def parse_block(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200136 node = nodes.Block(lineno=self.stream.expect('block').lineno)
137 node.name = self.stream.expect('name').value
138 node.body = self.parse_statements(('endblock',), drop_needle=True)
139 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200140
141 def parse_extends(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200142 node = nodes.Extends(lineno=self.stream.expect('extends').lineno)
143 node.template = self.parse_expression()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200144 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200145
146 def parse_include(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200147 node = nodes.Include(lineno=self.stream.expect('include').lineno)
148 expr = self.parse_expression()
149 if self.stream.current.type is 'assign':
150 self.stream.next()
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200151 if not isinstance(expr, nodes.Name):
152 raise TemplateSyntaxError('must assign imported template to '
153 'variable or current scope',
154 expr.lineno, self.filename)
155 if not expr.can_assign():
Armin Ronachere791c2a2008-04-07 18:39:54 +0200156 raise TemplateSyntaxError('can\'t assign imported template '
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200157 'to %r' % expr, expr.lineno,
Armin Ronachere791c2a2008-04-07 18:39:54 +0200158 self.filename)
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200159 node.target = expr.name
Armin Ronachere791c2a2008-04-07 18:39:54 +0200160 node.template = self.parse_expression()
161 else:
162 node.target = None
163 node.template = expr
Armin Ronachere791c2a2008-04-07 18:39:54 +0200164 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200165
Armin Ronacher71082072008-04-12 14:19:36 +0200166 def parse_signature(self, node):
167 node.args = args = []
168 node.defaults = defaults = []
169 self.stream.expect('lparen')
170 while self.stream.current.type is not 'rparen':
171 if args:
172 self.stream.expect('comma')
173 token = self.stream.expect('name')
174 arg = nodes.Name(token.value, 'param', lineno=token.lineno)
175 if not arg.can_assign():
176 raise TemplateSyntaxError("can't assign to '%s'" %
177 arg.name, arg.lineno,
178 self.filename)
179 if self.stream.current.type is 'assign':
180 self.stream.next()
181 defaults.append(self.parse_expression())
182 args.append(arg)
183 self.stream.expect('rparen')
184
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200185 def parse_call_block(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200186 node = nodes.CallBlock(lineno=self.stream.expect('call').lineno)
Armin Ronacher71082072008-04-12 14:19:36 +0200187 if self.stream.current.type is 'lparen':
188 self.parse_signature(node)
189
Armin Ronacher8edbe492008-04-10 20:43:43 +0200190 node.call = self.parse_expression()
191 if not isinstance(node.call, nodes.Call):
192 raise TemplateSyntaxError('expected call', node.lineno,
193 self.filename)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200194 node.body = self.parse_statements(('endcall',), drop_needle=True)
195 return node
196
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200197 def parse_filter_block(self):
198 node = nodes.FilterBlock(lineno=self.stream.expect('filter').lineno)
199 node.filter = self.parse_filter(None, start_inline=True)
200 node.body = self.parse_statements(('endfilter',), drop_needle=True)
201 return node
202
Armin Ronachere791c2a2008-04-07 18:39:54 +0200203 def parse_macro(self):
204 node = nodes.Macro(lineno=self.stream.expect('macro').lineno)
205 node.name = self.stream.expect('name').value
Armin Ronacherf059ec12008-04-11 22:21:00 +0200206 # make sure that assignments to that name are allowed
207 if not nodes.Name(node.name, 'store').can_assign():
208 raise TemplateSyntaxError('can\'t assign macro to %r' %
209 node.target, node.lineno,
210 self.filename)
Armin Ronacher71082072008-04-12 14:19:36 +0200211 self.parse_signature(node)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200212 node.body = self.parse_statements(('endmacro',), drop_needle=True)
213 return node
214
215 def parse_print(self):
216 node = nodes.Output(lineno=self.stream.expect('print').lineno)
217 node.nodes = []
Armin Ronacherf59bac22008-04-20 13:11:43 +0200218 while self.stream.current.type not in statement_end_tokens:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200219 if node.nodes:
220 self.stream.expect('comma')
221 node.nodes.append(self.parse_expression())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200222 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200223
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200224 def parse_expression(self, no_condexpr=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200225 """Parse an expression."""
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200226 if no_condexpr:
227 return self.parse_or()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200228 return self.parse_condexpr()
229
230 def parse_condexpr(self):
231 lineno = self.stream.current.lineno
232 expr1 = self.parse_or()
233 while self.stream.current.type is 'if':
234 self.stream.next()
235 expr2 = self.parse_or()
236 self.stream.expect('else')
237 expr3 = self.parse_condexpr()
238 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
239 lineno = self.stream.current.lineno
240 return expr1
241
242 def parse_or(self):
243 lineno = self.stream.current.lineno
244 left = self.parse_and()
245 while self.stream.current.type is 'or':
246 self.stream.next()
247 right = self.parse_and()
248 left = nodes.Or(left, right, lineno=lineno)
249 lineno = self.stream.current.lineno
250 return left
251
252 def parse_and(self):
253 lineno = self.stream.current.lineno
254 left = self.parse_compare()
255 while self.stream.current.type is 'and':
256 self.stream.next()
257 right = self.parse_compare()
258 left = nodes.And(left, right, lineno=lineno)
259 lineno = self.stream.current.lineno
260 return left
261
262 def parse_compare(self):
263 lineno = self.stream.current.lineno
264 expr = self.parse_add()
265 ops = []
266 while 1:
267 token_type = self.stream.current.type
268 if token_type in _compare_operators:
269 self.stream.next()
270 ops.append(nodes.Operand(token_type, self.parse_add()))
271 elif token_type is 'not' and self.stream.look().type is 'in':
272 self.stream.skip(2)
273 ops.append(nodes.Operand('notin', self.parse_add()))
274 else:
275 break
276 lineno = self.stream.current.lineno
277 if not ops:
278 return expr
279 return nodes.Compare(expr, ops, lineno=lineno)
280
281 def parse_add(self):
282 lineno = self.stream.current.lineno
283 left = self.parse_sub()
284 while self.stream.current.type is 'add':
285 self.stream.next()
286 right = self.parse_sub()
287 left = nodes.Add(left, right, lineno=lineno)
288 lineno = self.stream.current.lineno
289 return left
290
291 def parse_sub(self):
292 lineno = self.stream.current.lineno
293 left = self.parse_concat()
294 while self.stream.current.type is 'sub':
295 self.stream.next()
296 right = self.parse_concat()
297 left = nodes.Sub(left, right, lineno=lineno)
298 lineno = self.stream.current.lineno
299 return left
300
301 def parse_concat(self):
302 lineno = self.stream.current.lineno
303 args = [self.parse_mul()]
304 while self.stream.current.type is 'tilde':
305 self.stream.next()
306 args.append(self.parse_mul())
307 if len(args) == 1:
308 return args[0]
309 return nodes.Concat(args, lineno=lineno)
310
311 def parse_mul(self):
312 lineno = self.stream.current.lineno
313 left = self.parse_div()
314 while self.stream.current.type is 'mul':
315 self.stream.next()
316 right = self.parse_div()
317 left = nodes.Mul(left, right, lineno=lineno)
318 lineno = self.stream.current.lineno
319 return left
320
321 def parse_div(self):
322 lineno = self.stream.current.lineno
323 left = self.parse_floordiv()
324 while self.stream.current.type is 'div':
325 self.stream.next()
326 right = self.parse_floordiv()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200327 left = nodes.Div(left, right, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200328 lineno = self.stream.current.lineno
329 return left
330
331 def parse_floordiv(self):
332 lineno = self.stream.current.lineno
333 left = self.parse_mod()
334 while self.stream.current.type is 'floordiv':
335 self.stream.next()
336 right = self.parse_mod()
337 left = nodes.FloorDiv(left, right, lineno=lineno)
338 lineno = self.stream.current.lineno
339 return left
340
341 def parse_mod(self):
342 lineno = self.stream.current.lineno
343 left = self.parse_pow()
344 while self.stream.current.type is 'mod':
345 self.stream.next()
346 right = self.parse_pow()
347 left = nodes.Mod(left, right, lineno=lineno)
348 lineno = self.stream.current.lineno
349 return left
350
351 def parse_pow(self):
352 lineno = self.stream.current.lineno
353 left = self.parse_unary()
354 while self.stream.current.type is 'pow':
355 self.stream.next()
356 right = self.parse_unary()
357 left = nodes.Pow(left, right, lineno=lineno)
358 lineno = self.stream.current.lineno
359 return left
360
361 def parse_unary(self):
362 token_type = self.stream.current.type
363 lineno = self.stream.current.lineno
364 if token_type is 'not':
365 self.stream.next()
366 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200367 return nodes.Not(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200368 if token_type is 'sub':
369 self.stream.next()
370 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200371 return nodes.Neg(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200372 if token_type is 'add':
373 self.stream.next()
374 node = self.parse_unary()
375 return nodes.Pos(node, lineno=lineno)
376 return self.parse_primary()
377
378 def parse_primary(self, parse_postfix=True):
379 token = self.stream.current
380 if token.type is 'name':
381 if token.value in ('true', 'false'):
382 node = nodes.Const(token.value == 'true', lineno=token.lineno)
383 elif token.value == 'none':
384 node = nodes.Const(None, lineno=token.lineno)
385 else:
386 node = nodes.Name(token.value, 'load', lineno=token.lineno)
387 self.stream.next()
388 elif token.type in ('integer', 'float', 'string'):
389 self.stream.next()
390 node = nodes.Const(token.value, lineno=token.lineno)
391 elif token.type is 'lparen':
392 self.stream.next()
393 node = self.parse_tuple()
394 self.stream.expect('rparen')
395 elif token.type is 'lbracket':
396 node = self.parse_list()
397 elif token.type is 'lbrace':
398 node = self.parse_dict()
399 else:
400 raise TemplateSyntaxError("unexpected token '%s'" %
401 (token,), token.lineno,
402 self.filename)
403 if parse_postfix:
404 node = self.parse_postfix(node)
405 return node
406
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200407 def parse_tuple(self, enforce=False, simplified=False, no_condexpr=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200408 """
409 Parse multiple expressions into a tuple. This can also return
410 just one expression which is not a tuple. If you want to enforce
411 a tuple, pass it enforce=True (currently unused).
412 """
413 lineno = self.stream.current.lineno
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200414 if simplified:
415 parse = self.parse_primary
416 elif no_condexpr:
417 parse = lambda: self.parse_expression(no_condexpr=True)
418 else:
419 parse = self.parse_expression
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200420 args = []
421 is_tuple = False
422 while 1:
423 if args:
424 self.stream.expect('comma')
Armin Ronacherb5124e62008-04-25 00:36:14 +0200425 if self.stream.current.type in _tuple_edge_tokens:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200426 break
427 args.append(parse())
Armin Ronacherb5124e62008-04-25 00:36:14 +0200428 if self.stream.current.type is 'comma':
429 is_tuple = True
430 else:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200431 break
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200432 lineno = self.stream.current.lineno
433 if not is_tuple and args:
434 if enforce:
435 raise TemplateSyntaxError('tuple expected', lineno,
436 self.filename)
437 return args[0]
438 return nodes.Tuple(args, 'load', lineno=lineno)
439
440 def parse_list(self):
441 token = self.stream.expect('lbracket')
442 items = []
443 while self.stream.current.type is not 'rbracket':
444 if items:
445 self.stream.expect('comma')
446 if self.stream.current.type == 'rbracket':
447 break
448 items.append(self.parse_expression())
449 self.stream.expect('rbracket')
450 return nodes.List(items, lineno=token.lineno)
451
452 def parse_dict(self):
453 token = self.stream.expect('lbrace')
454 items = []
455 while self.stream.current.type is not 'rbrace':
456 if items:
457 self.stream.expect('comma')
458 if self.stream.current.type == 'rbrace':
459 break
460 key = self.parse_expression()
461 self.stream.expect('colon')
462 value = self.parse_expression()
463 items.append(nodes.Pair(key, value, lineno=key.lineno))
464 self.stream.expect('rbrace')
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200465 return nodes.Dict(items, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200466
467 def parse_postfix(self, node):
468 while 1:
469 token_type = self.stream.current.type
470 if token_type is 'dot' or token_type is 'lbracket':
471 node = self.parse_subscript(node)
472 elif token_type is 'lparen':
473 node = self.parse_call(node)
474 elif token_type is 'pipe':
475 node = self.parse_filter(node)
476 elif token_type is 'is':
477 node = self.parse_test(node)
478 else:
479 break
480 return node
481
482 def parse_subscript(self, node):
483 token = self.stream.next()
484 if token.type is 'dot':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200485 attr_token = self.stream.current
486 if attr_token.type not in ('name', 'integer'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200487 raise TemplateSyntaxError('expected name or number',
Armin Ronachere791c2a2008-04-07 18:39:54 +0200488 attr_token.lineno, self.filename)
489 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200490 self.stream.next()
491 elif token.type is 'lbracket':
492 args = []
493 while self.stream.current.type is not 'rbracket':
494 if args:
495 self.stream.expect('comma')
496 args.append(self.parse_subscribed())
497 self.stream.expect('rbracket')
498 if len(args) == 1:
499 arg = args[0]
500 else:
501 arg = nodes.Tuple(args, lineno, self.filename)
502 else:
503 raise TemplateSyntaxError('expected subscript expression',
504 self.lineno, self.filename)
505 return nodes.Subscript(node, arg, 'load', lineno=token.lineno)
506
507 def parse_subscribed(self):
508 lineno = self.stream.current.lineno
509
510 if self.stream.current.type is 'colon':
511 self.stream.next()
512 args = [None]
513 else:
514 node = self.parse_expression()
515 if self.stream.current.type is not 'colon':
516 return node
517 self.stream.next()
518 args = [node]
519
520 if self.stream.current.type is 'colon':
521 args.append(None)
522 elif self.stream.current.type not in ('rbracket', 'comma'):
523 args.append(self.parse_expression())
524 else:
525 args.append(None)
526
527 if self.stream.current.type is 'colon':
528 self.stream.next()
529 if self.stream.current.type not in ('rbracket', 'comma'):
530 args.append(self.parse_expression())
531 else:
532 args.append(None)
533 else:
534 args.append(None)
535
536 return nodes.Slice(lineno=lineno, *args)
537
538 def parse_call(self, node):
539 token = self.stream.expect('lparen')
540 args = []
541 kwargs = []
542 dyn_args = dyn_kwargs = None
543 require_comma = False
544
545 def ensure(expr):
546 if not expr:
547 raise TemplateSyntaxError('invalid syntax for function '
548 'call expression', token.lineno,
549 self.filename)
550
551 while self.stream.current.type is not 'rparen':
552 if require_comma:
553 self.stream.expect('comma')
554 # support for trailing comma
555 if self.stream.current.type is 'rparen':
556 break
557 if self.stream.current.type is 'mul':
558 ensure(dyn_args is None and dyn_kwargs is None)
559 self.stream.next()
560 dyn_args = self.parse_expression()
561 elif self.stream.current.type is 'pow':
562 ensure(dyn_kwargs is None)
563 self.stream.next()
564 dyn_kwargs = self.parse_expression()
565 else:
566 ensure(dyn_args is None and dyn_kwargs is None)
567 if self.stream.current.type is 'name' and \
568 self.stream.look().type is 'assign':
569 key = self.stream.current.value
570 self.stream.skip(2)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200571 kwargs.append(nodes.Keyword(key, self.parse_expression(),
572 lineno=key.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200573 else:
574 ensure(not kwargs)
575 args.append(self.parse_expression())
576
577 require_comma = True
578 self.stream.expect('rparen')
579
580 if node is None:
581 return args, kwargs, dyn_args, dyn_kwargs
582 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
583 lineno=token.lineno)
584
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200585 def parse_filter(self, node, start_inline=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200586 lineno = self.stream.current.type
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200587 while self.stream.current.type == 'pipe' or start_inline:
588 if not start_inline:
589 self.stream.next()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200590 token = self.stream.expect('name')
591 if self.stream.current.type is 'lparen':
592 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
593 else:
594 args = []
595 kwargs = []
596 dyn_args = dyn_kwargs = None
Armin Ronacherd55ab532008-04-09 16:13:39 +0200597 node = nodes.Filter(node, token.value, args, kwargs, dyn_args,
598 dyn_kwargs, lineno=token.lineno)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200599 start_inline = False
Armin Ronacherd55ab532008-04-09 16:13:39 +0200600 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200601
602 def parse_test(self, node):
603 token = self.stream.expect('is')
604 if self.stream.current.type is 'not':
605 self.stream.next()
606 negated = True
607 else:
608 negated = False
609 name = self.stream.expect('name').value
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200610 dyn_args = dyn_kwargs = None
611 kwargs = []
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200612 if self.stream.current.type is 'lparen':
613 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
614 elif self.stream.current.type in ('name', 'string', 'integer',
615 'float', 'lparen', 'lbracket',
616 'lbrace', 'regex'):
617 args = [self.parse_expression()]
618 else:
619 args = []
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200620 node = nodes.Test(node, name, args, kwargs, dyn_args,
621 dyn_kwargs, lineno=token.lineno)
622 if negated:
Armin Ronacher9a822052008-04-17 18:44:07 +0200623 node = nodes.Not(node, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200624 return node
625
626 def subparse(self, end_tokens=None):
627 body = []
628 data_buffer = []
629 add_data = data_buffer.append
630
631 def flush_data():
632 if data_buffer:
633 lineno = data_buffer[0].lineno
634 body.append(nodes.Output(data_buffer[:], lineno=lineno))
635 del data_buffer[:]
636
637 while self.stream:
638 token = self.stream.current
639 if token.type is 'data':
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200640 if token.value:
641 add_data(nodes.Const(token.value, lineno=token.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200642 self.stream.next()
643 elif token.type is 'variable_begin':
644 self.stream.next()
Armin Ronacherb5124e62008-04-25 00:36:14 +0200645 add_data(self.parse_tuple())
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200646 self.stream.expect('variable_end')
647 elif token.type is 'block_begin':
648 flush_data()
649 self.stream.next()
650 if end_tokens is not None and \
Armin Ronacherf59bac22008-04-20 13:11:43 +0200651 self.stream.current.test_many(end_tokens):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200652 return body
653 while self.stream.current.type is not 'block_end':
654 body.append(self.parse_statement())
655 self.stream.expect('block_end')
656 else:
657 raise AssertionError('internal parsing error')
658
659 flush_data()
660 return body
661
662 def parse(self):
663 """Parse the whole template into a `Template` node."""
Armin Ronacherd55ab532008-04-09 16:13:39 +0200664 result = nodes.Template(self.subparse(), lineno=1)
665 result.set_environment(self.environment)
666 return result