blob: 15f26cea740964418915c336d7dd21a8d109d920 [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 Ronacher0611e492008-04-25 23:44:14 +020017 'macro', 'include', 'from', 'import'])
Armin Ronacher115de2e2008-05-01 22:20:05 +020018_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020019
Armin Ronacher07bc6842008-03-31 14:18:49 +020020
21class Parser(object):
Armin Ronacherd55ab532008-04-09 16:13:39 +020022 """The template parser class.
Armin Ronacher07bc6842008-03-31 14:18:49 +020023
24 Transforms sourcecode into an abstract syntax tree.
25 """
26
27 def __init__(self, environment, source, filename=None):
28 self.environment = environment
Armin Ronacher07bc6842008-03-31 14:18:49 +020029 if isinstance(filename, unicode):
30 filename = filename.encode('utf-8')
Armin Ronacherbf7c4ad2008-04-12 12:02:36 +020031 self.source = unicode(source)
Armin Ronacher07bc6842008-03-31 14:18:49 +020032 self.filename = filename
33 self.closed = False
Armin Ronacher07bc6842008-03-31 14:18:49 +020034 self.stream = environment.lexer.tokenize(source, filename)
Armin Ronacher05530932008-04-20 13:27:49 +020035 self.extensions = {}
36 for extension in environment.extensions:
37 for tag in extension.tags:
38 self.extensions[tag] = extension.parse
Armin Ronacher07bc6842008-03-31 14:18:49 +020039
Armin Ronacher115de2e2008-05-01 22:20:05 +020040 def is_statement_end(self):
41 """Are we at the end of a statement?"""
42 if self.stream.current.type in ('variable_end', 'block_end'):
43 return True
44 return self.stream.current.test('name:in')
45
46 def is_tuple_end(self):
47 """Are we at the end of a tuple?"""
48 return self.stream.current.type is 'rparen' or self.is_statement_end()
49
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020050 def parse_statement(self):
51 """Parse a single statement."""
52 token_type = self.stream.current.type
Armin Ronacher115de2e2008-05-01 22:20:05 +020053 if self.stream.current.type is 'name':
54 if self.stream.current.value in _statement_keywords:
55 return getattr(self, 'parse_' + self.stream.current.value)()
56 elif self.stream.current.value == 'call':
57 return self.parse_call_block()
58 elif self.stream.current.value == 'filter':
59 return self.parse_filter_block()
60 else:
61 ext = self.extensions.get(self.stream.current.value)
62 if ext is not None:
63 return ext(self)
Armin Ronacher157531b2008-04-28 16:14:03 +020064 lineno = self.stream.current.lineno
Armin Ronacher8efc5222008-04-08 14:47:40 +020065 expr = self.parse_tuple()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020066 if self.stream.current.type == 'assign':
Armin Ronachere791c2a2008-04-07 18:39:54 +020067 result = self.parse_assign(expr)
68 else:
69 result = nodes.ExprStmt(expr, lineno=lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +020070 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020071
72 def parse_assign(self, target):
73 """Parse an assign statement."""
74 lineno = self.stream.expect('assign').lineno
75 if not target.can_assign():
76 raise TemplateSyntaxError("can't assign to '%s'" %
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020077 target.__class__.__name__.lower(),
78 target.lineno, self.filename)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020079 expr = self.parse_tuple()
Armin Ronachere791c2a2008-04-07 18:39:54 +020080 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020081 return nodes.Assign(target, expr, lineno=lineno)
82
83 def parse_statements(self, end_tokens, drop_needle=False):
Armin Ronacherf59bac22008-04-20 13:11:43 +020084 """Parse multiple statements into a list until one of the end tokens
85 is reached. This is used to parse the body of statements as it also
86 parses template data if appropriate.
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020087 """
88 # the first token may be a colon for python compatibility
89 if self.stream.current.type is 'colon':
90 self.stream.next()
91
Armin Ronacher2b60fe52008-04-21 08:23:59 +020092 # in the future it would be possible to add whole code sections
93 # by adding some sort of end of statement token and parsing those here.
94 self.stream.expect('block_end')
95 result = self.subparse(end_tokens)
96
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020097 if drop_needle:
98 self.stream.next()
99 return result
100
101 def parse_for(self):
102 """Parse a for loop."""
Armin Ronacher115de2e2008-05-01 22:20:05 +0200103 lineno = self.stream.expect('name:for').lineno
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200104 target = self.parse_tuple(simplified=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200105 if not target.can_assign():
106 raise TemplateSyntaxError("can't assign to '%s'" %
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200107 target.__class__.__name__.lower(),
108 target.lineno, self.filename)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200109 target.set_ctx('store')
Armin Ronacher115de2e2008-05-01 22:20:05 +0200110 self.stream.expect('name:in')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200111 iter = self.parse_tuple(no_condexpr=True)
112 test = None
Armin Ronacher115de2e2008-05-01 22:20:05 +0200113 if self.stream.current.type is 'name:if':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200114 self.stream.next()
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200115 test = self.parse_expression()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200116 body = self.parse_statements(('name:endfor', 'name:else'))
117 if self.stream.next().value == 'endfor':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200118 else_ = []
119 else:
Armin Ronacher115de2e2008-05-01 22:20:05 +0200120 else_ = self.parse_statements(('name:endfor',), drop_needle=True)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200121 return nodes.For(target, iter, body, else_, test, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200122
123 def parse_if(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200124 """Parse an if construct."""
Armin Ronacher115de2e2008-05-01 22:20:05 +0200125 node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200126 while 1:
127 # TODO: exclude conditional expressions here
128 node.test = self.parse_tuple()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200129 node.body = self.parse_statements(('name:elif', 'name:else',
130 'name:endif'))
131 token = self.stream.next()
132 if token.test('name:elif'):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200133 new_node = nodes.If(lineno=self.stream.current.lineno)
134 node.else_ = [new_node]
135 node = new_node
136 continue
Armin Ronacher115de2e2008-05-01 22:20:05 +0200137 elif token.test('name:else'):
138 node.else_ = self.parse_statements(('name:endif',),
Armin Ronachere791c2a2008-04-07 18:39:54 +0200139 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 Ronacher115de2e2008-05-01 22:20:05 +0200146 node = nodes.Block(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200147 node.name = self.stream.expect('name').value
Armin Ronacher115de2e2008-05-01 22:20:05 +0200148 node.body = self.parse_statements(('name:endblock',), drop_needle=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200149 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200150
151 def parse_extends(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200152 node = nodes.Extends(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200153 node.template = self.parse_expression()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200154 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200155
156 def parse_include(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200157 node = nodes.Include(lineno=self.stream.next().lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200158 node.template = self.parse_expression()
159 return node
160
161 def parse_import(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200162 node = nodes.Import(lineno=self.stream.next().lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200163 node.template = self.parse_expression()
164 self.stream.expect('name:as')
165 node.target = self.stream.expect('name').value
166 if not nodes.Name(node.target, 'store').can_assign():
167 raise TemplateSyntaxError('can\'t assign imported template '
168 'to %r' % node.target, node.lineno,
169 self.filename)
170 return node
171
172 def parse_from(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200173 node = nodes.FromImport(lineno=self.stream.next().lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200174 node.template = self.parse_expression()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200175 self.stream.expect('name:import')
Armin Ronacher0611e492008-04-25 23:44:14 +0200176 node.names = []
177 while 1:
178 if node.names:
179 self.stream.expect('comma')
180 if self.stream.current.type is 'name':
181 target = nodes.Name(self.stream.current.value, 'store')
182 if not target.can_assign():
183 raise TemplateSyntaxError('can\'t import object named %r'
184 % target.name, target.lineno,
185 self.filename)
186 elif target.name.startswith('__'):
187 raise TemplateAssertionError('names starting with two '
188 'underscores can not be '
189 'imported', target.lineno,
190 self.filename)
Armin Ronacher0611e492008-04-25 23:44:14 +0200191 self.stream.next()
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200192 if self.stream.current.test('name:as'):
193 self.stream.next()
194 alias = self.stream.expect('name')
195 if not nodes.Name(alias.value, 'store').can_assign():
196 raise TemplateSyntaxError('can\'t name imported '
197 'object %r.' % alias.value,
198 alias.lineno, self.filename)
199 node.names.append((target.name, alias.value))
200 else:
201 node.names.append(target.name)
Armin Ronacher0611e492008-04-25 23:44:14 +0200202 if self.stream.current.type is not 'comma':
203 break
204 else:
205 break
206 if self.stream.current.type is 'comma':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200207 self.stream.next()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200208 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200209
Armin Ronacher71082072008-04-12 14:19:36 +0200210 def parse_signature(self, node):
211 node.args = args = []
212 node.defaults = defaults = []
213 self.stream.expect('lparen')
214 while self.stream.current.type is not 'rparen':
215 if args:
216 self.stream.expect('comma')
217 token = self.stream.expect('name')
218 arg = nodes.Name(token.value, 'param', lineno=token.lineno)
219 if not arg.can_assign():
220 raise TemplateSyntaxError("can't assign to '%s'" %
221 arg.name, arg.lineno,
222 self.filename)
223 if self.stream.current.type is 'assign':
224 self.stream.next()
225 defaults.append(self.parse_expression())
226 args.append(arg)
227 self.stream.expect('rparen')
228
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200229 def parse_call_block(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200230 node = nodes.CallBlock(lineno=self.stream.next().lineno)
Armin Ronacher71082072008-04-12 14:19:36 +0200231 if self.stream.current.type is 'lparen':
232 self.parse_signature(node)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200233 else:
234 node.args = []
235 node.defaults = []
Armin Ronacher71082072008-04-12 14:19:36 +0200236
Armin Ronacher8edbe492008-04-10 20:43:43 +0200237 node.call = self.parse_expression()
238 if not isinstance(node.call, nodes.Call):
239 raise TemplateSyntaxError('expected call', node.lineno,
240 self.filename)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200241 node.body = self.parse_statements(('name:endcall',), drop_needle=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200242 return node
243
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200244 def parse_filter_block(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200245 node = nodes.FilterBlock(lineno=self.stream.next().lineno)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200246 node.filter = self.parse_filter(None, start_inline=True)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200247 node.body = self.parse_statements(('name:endfilter',),
248 drop_needle=True)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200249 return node
250
Armin Ronachere791c2a2008-04-07 18:39:54 +0200251 def parse_macro(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200252 node = nodes.Macro(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200253 node.name = self.stream.expect('name').value
Armin Ronacherf059ec12008-04-11 22:21:00 +0200254 # make sure that assignments to that name are allowed
255 if not nodes.Name(node.name, 'store').can_assign():
256 raise TemplateSyntaxError('can\'t assign macro to %r' %
257 node.target, node.lineno,
258 self.filename)
Armin Ronacher71082072008-04-12 14:19:36 +0200259 self.parse_signature(node)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200260 node.body = self.parse_statements(('name:endmacro',),
261 drop_needle=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200262 return node
263
264 def parse_print(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200265 node = nodes.Output(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200266 node.nodes = []
Armin Ronacher115de2e2008-05-01 22:20:05 +0200267 while not self.is_statement_end():
Armin Ronachere791c2a2008-04-07 18:39:54 +0200268 if node.nodes:
269 self.stream.expect('comma')
270 node.nodes.append(self.parse_expression())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200271 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200272
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200273 def parse_expression(self, no_condexpr=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200274 """Parse an expression."""
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200275 if no_condexpr:
276 return self.parse_or()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200277 return self.parse_condexpr()
278
279 def parse_condexpr(self):
280 lineno = self.stream.current.lineno
281 expr1 = self.parse_or()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200282 while self.stream.current.test('name:if'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200283 self.stream.next()
284 expr2 = self.parse_or()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200285 self.stream.expect('name:else')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200286 expr3 = self.parse_condexpr()
287 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
288 lineno = self.stream.current.lineno
289 return expr1
290
291 def parse_or(self):
292 lineno = self.stream.current.lineno
293 left = self.parse_and()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200294 while self.stream.current.test('name:or'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200295 self.stream.next()
296 right = self.parse_and()
297 left = nodes.Or(left, right, lineno=lineno)
298 lineno = self.stream.current.lineno
299 return left
300
301 def parse_and(self):
302 lineno = self.stream.current.lineno
303 left = self.parse_compare()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200304 while self.stream.current.test('name:and'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200305 self.stream.next()
306 right = self.parse_compare()
307 left = nodes.And(left, right, lineno=lineno)
308 lineno = self.stream.current.lineno
309 return left
310
311 def parse_compare(self):
312 lineno = self.stream.current.lineno
313 expr = self.parse_add()
314 ops = []
315 while 1:
316 token_type = self.stream.current.type
317 if token_type in _compare_operators:
318 self.stream.next()
319 ops.append(nodes.Operand(token_type, self.parse_add()))
Armin Ronacher115de2e2008-05-01 22:20:05 +0200320 elif self.stream.current.test('name:in'):
321 self.stream.next()
322 ops.append(nodes.Operand('in', self.parse_add()))
323 elif self.stream.current.test('name:not') and \
324 self.stream.look().test('name:in'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200325 self.stream.skip(2)
326 ops.append(nodes.Operand('notin', self.parse_add()))
327 else:
328 break
329 lineno = self.stream.current.lineno
330 if not ops:
331 return expr
332 return nodes.Compare(expr, ops, lineno=lineno)
333
334 def parse_add(self):
335 lineno = self.stream.current.lineno
336 left = self.parse_sub()
337 while self.stream.current.type is 'add':
338 self.stream.next()
339 right = self.parse_sub()
340 left = nodes.Add(left, right, lineno=lineno)
341 lineno = self.stream.current.lineno
342 return left
343
344 def parse_sub(self):
345 lineno = self.stream.current.lineno
346 left = self.parse_concat()
347 while self.stream.current.type is 'sub':
348 self.stream.next()
349 right = self.parse_concat()
350 left = nodes.Sub(left, right, lineno=lineno)
351 lineno = self.stream.current.lineno
352 return left
353
354 def parse_concat(self):
355 lineno = self.stream.current.lineno
356 args = [self.parse_mul()]
357 while self.stream.current.type is 'tilde':
358 self.stream.next()
359 args.append(self.parse_mul())
360 if len(args) == 1:
361 return args[0]
362 return nodes.Concat(args, lineno=lineno)
363
364 def parse_mul(self):
365 lineno = self.stream.current.lineno
366 left = self.parse_div()
367 while self.stream.current.type is 'mul':
368 self.stream.next()
369 right = self.parse_div()
370 left = nodes.Mul(left, right, lineno=lineno)
371 lineno = self.stream.current.lineno
372 return left
373
374 def parse_div(self):
375 lineno = self.stream.current.lineno
376 left = self.parse_floordiv()
377 while self.stream.current.type is 'div':
378 self.stream.next()
379 right = self.parse_floordiv()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200380 left = nodes.Div(left, right, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200381 lineno = self.stream.current.lineno
382 return left
383
384 def parse_floordiv(self):
385 lineno = self.stream.current.lineno
386 left = self.parse_mod()
387 while self.stream.current.type is 'floordiv':
388 self.stream.next()
389 right = self.parse_mod()
390 left = nodes.FloorDiv(left, right, lineno=lineno)
391 lineno = self.stream.current.lineno
392 return left
393
394 def parse_mod(self):
395 lineno = self.stream.current.lineno
396 left = self.parse_pow()
397 while self.stream.current.type is 'mod':
398 self.stream.next()
399 right = self.parse_pow()
400 left = nodes.Mod(left, right, lineno=lineno)
401 lineno = self.stream.current.lineno
402 return left
403
404 def parse_pow(self):
405 lineno = self.stream.current.lineno
406 left = self.parse_unary()
407 while self.stream.current.type is 'pow':
408 self.stream.next()
409 right = self.parse_unary()
410 left = nodes.Pow(left, right, lineno=lineno)
411 lineno = self.stream.current.lineno
412 return left
413
414 def parse_unary(self):
415 token_type = self.stream.current.type
416 lineno = self.stream.current.lineno
Armin Ronacher115de2e2008-05-01 22:20:05 +0200417 if token_type is 'name' and self.stream.current.value == 'not':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200418 self.stream.next()
419 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200420 return nodes.Not(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200421 if token_type is 'sub':
422 self.stream.next()
423 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200424 return nodes.Neg(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200425 if token_type is 'add':
426 self.stream.next()
427 node = self.parse_unary()
428 return nodes.Pos(node, lineno=lineno)
429 return self.parse_primary()
430
431 def parse_primary(self, parse_postfix=True):
432 token = self.stream.current
433 if token.type is 'name':
434 if token.value in ('true', 'false'):
435 node = nodes.Const(token.value == 'true', lineno=token.lineno)
436 elif token.value == 'none':
437 node = nodes.Const(None, lineno=token.lineno)
438 else:
439 node = nodes.Name(token.value, 'load', lineno=token.lineno)
440 self.stream.next()
441 elif token.type in ('integer', 'float', 'string'):
442 self.stream.next()
443 node = nodes.Const(token.value, lineno=token.lineno)
444 elif token.type is 'lparen':
445 self.stream.next()
446 node = self.parse_tuple()
447 self.stream.expect('rparen')
448 elif token.type is 'lbracket':
449 node = self.parse_list()
450 elif token.type is 'lbrace':
451 node = self.parse_dict()
452 else:
453 raise TemplateSyntaxError("unexpected token '%s'" %
454 (token,), token.lineno,
455 self.filename)
456 if parse_postfix:
457 node = self.parse_postfix(node)
458 return node
459
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200460 def parse_tuple(self, enforce=False, simplified=False, no_condexpr=False):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200461 """Parse multiple expressions into a tuple. This can also return
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200462 just one expression which is not a tuple. If you want to enforce
463 a tuple, pass it enforce=True (currently unused).
464 """
465 lineno = self.stream.current.lineno
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200466 if simplified:
467 parse = self.parse_primary
468 elif no_condexpr:
469 parse = lambda: self.parse_expression(no_condexpr=True)
470 else:
471 parse = self.parse_expression
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200472 args = []
473 is_tuple = False
474 while 1:
475 if args:
476 self.stream.expect('comma')
Armin Ronacher115de2e2008-05-01 22:20:05 +0200477 if self.is_tuple_end():
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200478 break
479 args.append(parse())
Armin Ronacherb5124e62008-04-25 00:36:14 +0200480 if self.stream.current.type is 'comma':
481 is_tuple = True
482 else:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200483 break
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200484 lineno = self.stream.current.lineno
485 if not is_tuple and args:
486 if enforce:
487 raise TemplateSyntaxError('tuple expected', lineno,
488 self.filename)
489 return args[0]
490 return nodes.Tuple(args, 'load', lineno=lineno)
491
492 def parse_list(self):
493 token = self.stream.expect('lbracket')
494 items = []
495 while self.stream.current.type is not 'rbracket':
496 if items:
497 self.stream.expect('comma')
498 if self.stream.current.type == 'rbracket':
499 break
500 items.append(self.parse_expression())
501 self.stream.expect('rbracket')
502 return nodes.List(items, lineno=token.lineno)
503
504 def parse_dict(self):
505 token = self.stream.expect('lbrace')
506 items = []
507 while self.stream.current.type is not 'rbrace':
508 if items:
509 self.stream.expect('comma')
510 if self.stream.current.type == 'rbrace':
511 break
512 key = self.parse_expression()
513 self.stream.expect('colon')
514 value = self.parse_expression()
515 items.append(nodes.Pair(key, value, lineno=key.lineno))
516 self.stream.expect('rbrace')
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200517 return nodes.Dict(items, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200518
519 def parse_postfix(self, node):
520 while 1:
521 token_type = self.stream.current.type
522 if token_type is 'dot' or token_type is 'lbracket':
523 node = self.parse_subscript(node)
524 elif token_type is 'lparen':
525 node = self.parse_call(node)
526 elif token_type is 'pipe':
527 node = self.parse_filter(node)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200528 elif token_type is 'name' and self.stream.current.value == 'is':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200529 node = self.parse_test(node)
530 else:
531 break
532 return node
533
534 def parse_subscript(self, node):
535 token = self.stream.next()
536 if token.type is 'dot':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200537 attr_token = self.stream.current
538 if attr_token.type not in ('name', 'integer'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200539 raise TemplateSyntaxError('expected name or number',
Armin Ronachere791c2a2008-04-07 18:39:54 +0200540 attr_token.lineno, self.filename)
541 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200542 self.stream.next()
543 elif token.type is 'lbracket':
544 args = []
545 while self.stream.current.type is not 'rbracket':
546 if args:
547 self.stream.expect('comma')
548 args.append(self.parse_subscribed())
549 self.stream.expect('rbracket')
550 if len(args) == 1:
551 arg = args[0]
552 else:
Benjamin Wieganda3152742008-04-28 18:07:52 +0200553 arg = nodes.Tuple(args, self.lineno, self.filename)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200554 else:
555 raise TemplateSyntaxError('expected subscript expression',
556 self.lineno, self.filename)
557 return nodes.Subscript(node, arg, 'load', lineno=token.lineno)
558
559 def parse_subscribed(self):
560 lineno = self.stream.current.lineno
561
562 if self.stream.current.type is 'colon':
563 self.stream.next()
564 args = [None]
565 else:
566 node = self.parse_expression()
567 if self.stream.current.type is not 'colon':
568 return node
569 self.stream.next()
570 args = [node]
571
572 if self.stream.current.type is 'colon':
573 args.append(None)
574 elif self.stream.current.type not in ('rbracket', 'comma'):
575 args.append(self.parse_expression())
576 else:
577 args.append(None)
578
579 if self.stream.current.type is 'colon':
580 self.stream.next()
581 if self.stream.current.type not in ('rbracket', 'comma'):
582 args.append(self.parse_expression())
583 else:
584 args.append(None)
585 else:
586 args.append(None)
587
588 return nodes.Slice(lineno=lineno, *args)
589
590 def parse_call(self, node):
591 token = self.stream.expect('lparen')
592 args = []
593 kwargs = []
594 dyn_args = dyn_kwargs = None
595 require_comma = False
596
597 def ensure(expr):
598 if not expr:
599 raise TemplateSyntaxError('invalid syntax for function '
600 'call expression', token.lineno,
601 self.filename)
602
603 while self.stream.current.type is not 'rparen':
604 if require_comma:
605 self.stream.expect('comma')
606 # support for trailing comma
607 if self.stream.current.type is 'rparen':
608 break
609 if self.stream.current.type is 'mul':
610 ensure(dyn_args is None and dyn_kwargs is None)
611 self.stream.next()
612 dyn_args = self.parse_expression()
613 elif self.stream.current.type is 'pow':
614 ensure(dyn_kwargs is None)
615 self.stream.next()
616 dyn_kwargs = self.parse_expression()
617 else:
618 ensure(dyn_args is None and dyn_kwargs is None)
619 if self.stream.current.type is 'name' and \
620 self.stream.look().type is 'assign':
621 key = self.stream.current.value
622 self.stream.skip(2)
Armin Ronacher0611e492008-04-25 23:44:14 +0200623 value = self.parse_expression()
624 kwargs.append(nodes.Keyword(key, value,
625 lineno=value.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200626 else:
627 ensure(not kwargs)
628 args.append(self.parse_expression())
629
630 require_comma = True
631 self.stream.expect('rparen')
632
633 if node is None:
634 return args, kwargs, dyn_args, dyn_kwargs
635 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
636 lineno=token.lineno)
637
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200638 def parse_filter(self, node, start_inline=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200639 lineno = self.stream.current.type
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200640 while self.stream.current.type == 'pipe' or start_inline:
641 if not start_inline:
642 self.stream.next()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200643 token = self.stream.expect('name')
644 if self.stream.current.type is 'lparen':
645 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
646 else:
647 args = []
648 kwargs = []
649 dyn_args = dyn_kwargs = None
Armin Ronacherd55ab532008-04-09 16:13:39 +0200650 node = nodes.Filter(node, token.value, args, kwargs, dyn_args,
651 dyn_kwargs, lineno=token.lineno)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200652 start_inline = False
Armin Ronacherd55ab532008-04-09 16:13:39 +0200653 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200654
655 def parse_test(self, node):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200656 token = self.stream.next()
657 if self.stream.current.test('name:not'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200658 self.stream.next()
659 negated = True
660 else:
661 negated = False
662 name = self.stream.expect('name').value
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200663 dyn_args = dyn_kwargs = None
664 kwargs = []
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200665 if self.stream.current.type is 'lparen':
666 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
667 elif self.stream.current.type in ('name', 'string', 'integer',
668 'float', 'lparen', 'lbracket',
Armin Ronacher115de2e2008-05-01 22:20:05 +0200669 'lbrace'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200670 args = [self.parse_expression()]
671 else:
672 args = []
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200673 node = nodes.Test(node, name, args, kwargs, dyn_args,
674 dyn_kwargs, lineno=token.lineno)
675 if negated:
Armin Ronacher9a822052008-04-17 18:44:07 +0200676 node = nodes.Not(node, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200677 return node
678
679 def subparse(self, end_tokens=None):
680 body = []
681 data_buffer = []
682 add_data = data_buffer.append
683
684 def flush_data():
685 if data_buffer:
686 lineno = data_buffer[0].lineno
687 body.append(nodes.Output(data_buffer[:], lineno=lineno))
688 del data_buffer[:]
689
690 while self.stream:
691 token = self.stream.current
692 if token.type is 'data':
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200693 if token.value:
694 add_data(nodes.Const(token.value, lineno=token.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200695 self.stream.next()
696 elif token.type is 'variable_begin':
697 self.stream.next()
Armin Ronacherb5124e62008-04-25 00:36:14 +0200698 add_data(self.parse_tuple())
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200699 self.stream.expect('variable_end')
700 elif token.type is 'block_begin':
701 flush_data()
702 self.stream.next()
703 if end_tokens is not None and \
Armin Ronacherf59bac22008-04-20 13:11:43 +0200704 self.stream.current.test_many(end_tokens):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200705 return body
Armin Ronacher32a910f2008-04-26 23:21:03 +0200706 body.append(self.parse_statement())
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200707 self.stream.expect('block_end')
708 else:
709 raise AssertionError('internal parsing error')
710
711 flush_data()
712 return body
713
714 def parse(self):
715 """Parse the whole template into a `Template` node."""
Armin Ronacherd55ab532008-04-09 16:13:39 +0200716 result = nodes.Template(self.subparse(), lineno=1)
717 result.set_environment(self.environment)
718 return result