blob: 427cb058dac323e082fd9c0efaa5c1bb834ffd1f [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 Ronacher0611e492008-04-25 23:44:14 +020016 'macro', 'include', 'from', 'import'])
Armin Ronacher115de2e2008-05-01 22:20:05 +020017_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020018
Armin Ronacher07bc6842008-03-31 14:18:49 +020019
20class Parser(object):
Armin Ronacher023b5e92008-05-08 11:03:10 +020021 """This is the central parsing class Jinja2 uses. It's passed to
22 extensions and can be used to parse expressions or statements.
Armin Ronacher07bc6842008-03-31 14:18:49 +020023 """
24
25 def __init__(self, environment, source, filename=None):
26 self.environment = environment
Armin Ronacher07bc6842008-03-31 14:18:49 +020027 if isinstance(filename, unicode):
28 filename = filename.encode('utf-8')
Armin Ronacherbf7c4ad2008-04-12 12:02:36 +020029 self.source = unicode(source)
Armin Ronacher07bc6842008-03-31 14:18:49 +020030 self.filename = filename
31 self.closed = False
Armin Ronacher023b5e92008-05-08 11:03:10 +020032 self.stream = environment.lexer.tokenize(self.source, filename)
Armin Ronacher05530932008-04-20 13:27:49 +020033 self.extensions = {}
Armin Ronacher023b5e92008-05-08 11:03:10 +020034 for extension in environment.extensions.itervalues():
Armin Ronacher05530932008-04-20 13:27:49 +020035 for tag in extension.tags:
36 self.extensions[tag] = extension.parse
Armin Ronacher023b5e92008-05-08 11:03:10 +020037 self._last_identifier = 0
Armin Ronacher115de2e2008-05-01 22:20:05 +020038
39 def is_tuple_end(self):
40 """Are we at the end of a tuple?"""
Armin Ronacher023b5e92008-05-08 11:03:10 +020041 return self.stream.current.type in ('variable_end', 'block_end',
42 'rparen') or \
43 self.stream.current.test('name:in')
44
Armin Ronacher762079c2008-05-08 23:57:56 +020045 def skip_colon(self):
Armin Ronacher023b5e92008-05-08 11:03:10 +020046 """If there is a colon, skip it and return `True`, else `False`."""
47 if self.stream.current.type is 'colon':
48 self.stream.next()
49 return True
50 return False
51
Armin Ronacher762079c2008-05-08 23:57:56 +020052 def skip_comma(self):
53 """If there is a comma, skip it and return `True`, else `False`."""
54 if self.stream.current.type is 'comma':
55 self.stream.next()
56 return True
57 return False
58
Armin Ronacher023b5e92008-05-08 11:03:10 +020059 def free_identifier(self, lineno=None):
60 """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
61 self._last_identifier += 1
62 rv = object.__new__(nodes.InternalName)
63 nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno)
64 return rv
Armin Ronacher115de2e2008-05-01 22:20:05 +020065
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020066 def parse_statement(self):
67 """Parse a single statement."""
68 token_type = self.stream.current.type
Armin Ronacher115de2e2008-05-01 22:20:05 +020069 if self.stream.current.type is 'name':
70 if self.stream.current.value in _statement_keywords:
71 return getattr(self, 'parse_' + self.stream.current.value)()
72 elif self.stream.current.value == 'call':
73 return self.parse_call_block()
74 elif self.stream.current.value == 'filter':
75 return self.parse_filter_block()
76 else:
77 ext = self.extensions.get(self.stream.current.value)
78 if ext is not None:
79 return ext(self)
Armin Ronacher157531b2008-04-28 16:14:03 +020080 lineno = self.stream.current.lineno
Armin Ronacher8efc5222008-04-08 14:47:40 +020081 expr = self.parse_tuple()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020082 if self.stream.current.type == 'assign':
Armin Ronachere791c2a2008-04-07 18:39:54 +020083 result = self.parse_assign(expr)
84 else:
85 result = nodes.ExprStmt(expr, lineno=lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +020086 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020087
88 def parse_assign(self, target):
89 """Parse an assign statement."""
90 lineno = self.stream.expect('assign').lineno
91 if not target.can_assign():
92 raise TemplateSyntaxError("can't assign to '%s'" %
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020093 target.__class__.__name__.lower(),
94 target.lineno, self.filename)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020095 expr = self.parse_tuple()
Armin Ronachere791c2a2008-04-07 18:39:54 +020096 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020097 return nodes.Assign(target, expr, lineno=lineno)
98
99 def parse_statements(self, end_tokens, drop_needle=False):
Armin Ronacherf59bac22008-04-20 13:11:43 +0200100 """Parse multiple statements into a list until one of the end tokens
101 is reached. This is used to parse the body of statements as it also
Armin Ronacher023b5e92008-05-08 11:03:10 +0200102 parses template data if appropriate. The parser checks first if the
103 current token is a colon and skips it if there is one. Then it checks
104 for the block end and parses until if one of the `end_tokens` is
105 reached. Per default the active token in the stream at the end of
106 the call is the matched end token. If this is not wanted `drop_needle`
107 can be set to `True` and the end token is removed.
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200108 """
109 # the first token may be a colon for python compatibility
Armin Ronacher762079c2008-05-08 23:57:56 +0200110 self.skip_colon()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200111
Armin Ronacher2b60fe52008-04-21 08:23:59 +0200112 # in the future it would be possible to add whole code sections
113 # by adding some sort of end of statement token and parsing those here.
114 self.stream.expect('block_end')
115 result = self.subparse(end_tokens)
116
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200117 if drop_needle:
118 self.stream.next()
119 return result
120
121 def parse_for(self):
122 """Parse a for loop."""
Armin Ronacher115de2e2008-05-01 22:20:05 +0200123 lineno = self.stream.expect('name:for').lineno
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200124 target = self.parse_tuple(simplified=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200125 if not target.can_assign():
126 raise TemplateSyntaxError("can't assign to '%s'" %
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200127 target.__class__.__name__.lower(),
128 target.lineno, self.filename)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200129 target.set_ctx('store')
Armin Ronacher115de2e2008-05-01 22:20:05 +0200130 self.stream.expect('name:in')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200131 iter = self.parse_tuple(no_condexpr=True)
132 test = None
Armin Ronacherc0725642008-05-04 22:43:19 +0200133 if self.stream.current.test('name:if'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200134 self.stream.next()
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200135 test = self.parse_expression()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200136 body = self.parse_statements(('name:endfor', 'name:else'))
137 if self.stream.next().value == 'endfor':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200138 else_ = []
139 else:
Armin Ronacher115de2e2008-05-01 22:20:05 +0200140 else_ = self.parse_statements(('name:endfor',), drop_needle=True)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200141 return nodes.For(target, iter, body, else_, test, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200142
143 def parse_if(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200144 """Parse an if construct."""
Armin Ronacher115de2e2008-05-01 22:20:05 +0200145 node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200146 while 1:
Armin Ronacherea847c52008-05-02 20:04:32 +0200147 node.test = self.parse_tuple(no_condexpr=True)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200148 node.body = self.parse_statements(('name:elif', 'name:else',
149 'name:endif'))
150 token = self.stream.next()
151 if token.test('name:elif'):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200152 new_node = nodes.If(lineno=self.stream.current.lineno)
153 node.else_ = [new_node]
154 node = new_node
155 continue
Armin Ronacher115de2e2008-05-01 22:20:05 +0200156 elif token.test('name:else'):
157 node.else_ = self.parse_statements(('name:endif',),
Armin Ronachere791c2a2008-04-07 18:39:54 +0200158 drop_needle=True)
159 else:
160 node.else_ = []
161 break
162 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200163
164 def parse_block(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200165 node = nodes.Block(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200166 node.name = self.stream.expect('name').value
Armin Ronacher115de2e2008-05-01 22:20:05 +0200167 node.body = self.parse_statements(('name:endblock',), drop_needle=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200168 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200169
170 def parse_extends(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200171 node = nodes.Extends(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200172 node.template = self.parse_expression()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200173 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200174
Armin Ronacherea847c52008-05-02 20:04:32 +0200175 def parse_import_context(self, node, default):
Armin Ronachercda43df2008-05-03 17:10:05 +0200176 if self.stream.current.test_any('name:with', 'name:without') and \
Armin Ronacherea847c52008-05-02 20:04:32 +0200177 self.stream.look().test('name:context'):
178 node.with_context = self.stream.next().value == 'with'
179 self.stream.skip()
180 else:
181 node.with_context = default
182 return node
183
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200184 def parse_include(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200185 node = nodes.Include(lineno=self.stream.next().lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200186 node.template = self.parse_expression()
Armin Ronacherea847c52008-05-02 20:04:32 +0200187 return self.parse_import_context(node, True)
Armin Ronacher0611e492008-04-25 23:44:14 +0200188
189 def parse_import(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200190 node = nodes.Import(lineno=self.stream.next().lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200191 node.template = self.parse_expression()
192 self.stream.expect('name:as')
193 node.target = self.stream.expect('name').value
194 if not nodes.Name(node.target, 'store').can_assign():
195 raise TemplateSyntaxError('can\'t assign imported template '
196 'to %r' % node.target, node.lineno,
197 self.filename)
Armin Ronacherea847c52008-05-02 20:04:32 +0200198 return self.parse_import_context(node, False)
Armin Ronacher0611e492008-04-25 23:44:14 +0200199
200 def parse_from(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200201 node = nodes.FromImport(lineno=self.stream.next().lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200202 node.template = self.parse_expression()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200203 self.stream.expect('name:import')
Armin Ronacher0611e492008-04-25 23:44:14 +0200204 node.names = []
Armin Ronacherea847c52008-05-02 20:04:32 +0200205
206 def parse_context():
207 if self.stream.current.value in ('with', 'without') and \
208 self.stream.look().test('name:context'):
209 node.with_context = self.stream.next().value == 'with'
210 self.stream.skip()
211 return True
212 return False
213
Armin Ronacher0611e492008-04-25 23:44:14 +0200214 while 1:
215 if node.names:
216 self.stream.expect('comma')
217 if self.stream.current.type is 'name':
Armin Ronacherea847c52008-05-02 20:04:32 +0200218 if parse_context():
219 break
Armin Ronacher0611e492008-04-25 23:44:14 +0200220 target = nodes.Name(self.stream.current.value, 'store')
221 if not target.can_assign():
222 raise TemplateSyntaxError('can\'t import object named %r'
223 % target.name, target.lineno,
224 self.filename)
225 elif target.name.startswith('__'):
226 raise TemplateAssertionError('names starting with two '
227 'underscores can not be '
228 'imported', target.lineno,
229 self.filename)
Armin Ronacher0611e492008-04-25 23:44:14 +0200230 self.stream.next()
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200231 if self.stream.current.test('name:as'):
232 self.stream.next()
233 alias = self.stream.expect('name')
234 if not nodes.Name(alias.value, 'store').can_assign():
235 raise TemplateSyntaxError('can\'t name imported '
236 'object %r.' % alias.value,
237 alias.lineno, self.filename)
238 node.names.append((target.name, alias.value))
239 else:
240 node.names.append(target.name)
Armin Ronacherea847c52008-05-02 20:04:32 +0200241 if parse_context() or self.stream.current.type is not 'comma':
Armin Ronacher0611e492008-04-25 23:44:14 +0200242 break
243 else:
244 break
Armin Ronacherea847c52008-05-02 20:04:32 +0200245 if not hasattr(node, 'with_context'):
246 node.with_context = False
247 if self.stream.current.type is 'comma':
248 self.stream.next()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200249 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200250
Armin Ronacher71082072008-04-12 14:19:36 +0200251 def parse_signature(self, node):
252 node.args = args = []
253 node.defaults = defaults = []
254 self.stream.expect('lparen')
255 while self.stream.current.type is not 'rparen':
256 if args:
257 self.stream.expect('comma')
258 token = self.stream.expect('name')
259 arg = nodes.Name(token.value, 'param', lineno=token.lineno)
260 if not arg.can_assign():
261 raise TemplateSyntaxError("can't assign to '%s'" %
262 arg.name, arg.lineno,
263 self.filename)
264 if self.stream.current.type is 'assign':
265 self.stream.next()
266 defaults.append(self.parse_expression())
267 args.append(arg)
268 self.stream.expect('rparen')
269
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200270 def parse_call_block(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200271 node = nodes.CallBlock(lineno=self.stream.next().lineno)
Armin Ronacher71082072008-04-12 14:19:36 +0200272 if self.stream.current.type is 'lparen':
273 self.parse_signature(node)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200274 else:
275 node.args = []
276 node.defaults = []
Armin Ronacher71082072008-04-12 14:19:36 +0200277
Armin Ronacher8edbe492008-04-10 20:43:43 +0200278 node.call = self.parse_expression()
279 if not isinstance(node.call, nodes.Call):
280 raise TemplateSyntaxError('expected call', node.lineno,
281 self.filename)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200282 node.body = self.parse_statements(('name:endcall',), drop_needle=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200283 return node
284
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200285 def parse_filter_block(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200286 node = nodes.FilterBlock(lineno=self.stream.next().lineno)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200287 node.filter = self.parse_filter(None, start_inline=True)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200288 node.body = self.parse_statements(('name:endfilter',),
289 drop_needle=True)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200290 return node
291
Armin Ronachere791c2a2008-04-07 18:39:54 +0200292 def parse_macro(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200293 node = nodes.Macro(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200294 node.name = self.stream.expect('name').value
Armin Ronacherf059ec12008-04-11 22:21:00 +0200295 # make sure that assignments to that name are allowed
296 if not nodes.Name(node.name, 'store').can_assign():
297 raise TemplateSyntaxError('can\'t assign macro to %r' %
298 node.target, node.lineno,
299 self.filename)
Armin Ronacher71082072008-04-12 14:19:36 +0200300 self.parse_signature(node)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200301 node.body = self.parse_statements(('name:endmacro',),
302 drop_needle=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200303 return node
304
305 def parse_print(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200306 node = nodes.Output(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200307 node.nodes = []
Armin Ronacher023b5e92008-05-08 11:03:10 +0200308 while self.stream.current.type is not 'block_end':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200309 if node.nodes:
310 self.stream.expect('comma')
311 node.nodes.append(self.parse_expression())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200312 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200313
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200314 def parse_expression(self, no_condexpr=False):
Armin Ronacher023b5e92008-05-08 11:03:10 +0200315 """Parse an expression. Per default all expressions are parsed, if
316 the optional `no_condexpr` parameter is set to `True` conditional
317 expressions are not parsed.
318 """
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200319 if no_condexpr:
320 return self.parse_or()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200321 return self.parse_condexpr()
322
323 def parse_condexpr(self):
324 lineno = self.stream.current.lineno
325 expr1 = self.parse_or()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200326 while self.stream.current.test('name:if'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200327 self.stream.next()
328 expr2 = self.parse_or()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200329 self.stream.expect('name:else')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200330 expr3 = self.parse_condexpr()
331 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
332 lineno = self.stream.current.lineno
333 return expr1
334
335 def parse_or(self):
336 lineno = self.stream.current.lineno
337 left = self.parse_and()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200338 while self.stream.current.test('name:or'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200339 self.stream.next()
340 right = self.parse_and()
341 left = nodes.Or(left, right, lineno=lineno)
342 lineno = self.stream.current.lineno
343 return left
344
345 def parse_and(self):
346 lineno = self.stream.current.lineno
347 left = self.parse_compare()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200348 while self.stream.current.test('name:and'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200349 self.stream.next()
350 right = self.parse_compare()
351 left = nodes.And(left, right, lineno=lineno)
352 lineno = self.stream.current.lineno
353 return left
354
355 def parse_compare(self):
356 lineno = self.stream.current.lineno
357 expr = self.parse_add()
358 ops = []
359 while 1:
360 token_type = self.stream.current.type
361 if token_type in _compare_operators:
362 self.stream.next()
363 ops.append(nodes.Operand(token_type, self.parse_add()))
Armin Ronacher115de2e2008-05-01 22:20:05 +0200364 elif self.stream.current.test('name:in'):
365 self.stream.next()
366 ops.append(nodes.Operand('in', self.parse_add()))
367 elif self.stream.current.test('name:not') and \
368 self.stream.look().test('name:in'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200369 self.stream.skip(2)
370 ops.append(nodes.Operand('notin', self.parse_add()))
371 else:
372 break
373 lineno = self.stream.current.lineno
374 if not ops:
375 return expr
376 return nodes.Compare(expr, ops, lineno=lineno)
377
378 def parse_add(self):
379 lineno = self.stream.current.lineno
380 left = self.parse_sub()
381 while self.stream.current.type is 'add':
382 self.stream.next()
383 right = self.parse_sub()
384 left = nodes.Add(left, right, lineno=lineno)
385 lineno = self.stream.current.lineno
386 return left
387
388 def parse_sub(self):
389 lineno = self.stream.current.lineno
390 left = self.parse_concat()
391 while self.stream.current.type is 'sub':
392 self.stream.next()
393 right = self.parse_concat()
394 left = nodes.Sub(left, right, lineno=lineno)
395 lineno = self.stream.current.lineno
396 return left
397
398 def parse_concat(self):
399 lineno = self.stream.current.lineno
400 args = [self.parse_mul()]
401 while self.stream.current.type is 'tilde':
402 self.stream.next()
403 args.append(self.parse_mul())
404 if len(args) == 1:
405 return args[0]
406 return nodes.Concat(args, lineno=lineno)
407
408 def parse_mul(self):
409 lineno = self.stream.current.lineno
410 left = self.parse_div()
411 while self.stream.current.type is 'mul':
412 self.stream.next()
413 right = self.parse_div()
414 left = nodes.Mul(left, right, lineno=lineno)
415 lineno = self.stream.current.lineno
416 return left
417
418 def parse_div(self):
419 lineno = self.stream.current.lineno
420 left = self.parse_floordiv()
421 while self.stream.current.type is 'div':
422 self.stream.next()
423 right = self.parse_floordiv()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200424 left = nodes.Div(left, right, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200425 lineno = self.stream.current.lineno
426 return left
427
428 def parse_floordiv(self):
429 lineno = self.stream.current.lineno
430 left = self.parse_mod()
431 while self.stream.current.type is 'floordiv':
432 self.stream.next()
433 right = self.parse_mod()
434 left = nodes.FloorDiv(left, right, lineno=lineno)
435 lineno = self.stream.current.lineno
436 return left
437
438 def parse_mod(self):
439 lineno = self.stream.current.lineno
440 left = self.parse_pow()
441 while self.stream.current.type is 'mod':
442 self.stream.next()
443 right = self.parse_pow()
444 left = nodes.Mod(left, right, lineno=lineno)
445 lineno = self.stream.current.lineno
446 return left
447
448 def parse_pow(self):
449 lineno = self.stream.current.lineno
450 left = self.parse_unary()
451 while self.stream.current.type is 'pow':
452 self.stream.next()
453 right = self.parse_unary()
454 left = nodes.Pow(left, right, lineno=lineno)
455 lineno = self.stream.current.lineno
456 return left
457
458 def parse_unary(self):
459 token_type = self.stream.current.type
460 lineno = self.stream.current.lineno
Armin Ronacher115de2e2008-05-01 22:20:05 +0200461 if token_type is 'name' and self.stream.current.value == 'not':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200462 self.stream.next()
463 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200464 return nodes.Not(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200465 if token_type is 'sub':
466 self.stream.next()
467 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200468 return nodes.Neg(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200469 if token_type is 'add':
470 self.stream.next()
471 node = self.parse_unary()
472 return nodes.Pos(node, lineno=lineno)
473 return self.parse_primary()
474
475 def parse_primary(self, parse_postfix=True):
476 token = self.stream.current
477 if token.type is 'name':
478 if token.value in ('true', 'false'):
479 node = nodes.Const(token.value == 'true', lineno=token.lineno)
480 elif token.value == 'none':
481 node = nodes.Const(None, lineno=token.lineno)
482 else:
483 node = nodes.Name(token.value, 'load', lineno=token.lineno)
484 self.stream.next()
485 elif token.type in ('integer', 'float', 'string'):
486 self.stream.next()
487 node = nodes.Const(token.value, lineno=token.lineno)
488 elif token.type is 'lparen':
489 self.stream.next()
490 node = self.parse_tuple()
491 self.stream.expect('rparen')
492 elif token.type is 'lbracket':
493 node = self.parse_list()
494 elif token.type is 'lbrace':
495 node = self.parse_dict()
496 else:
497 raise TemplateSyntaxError("unexpected token '%s'" %
498 (token,), token.lineno,
499 self.filename)
500 if parse_postfix:
501 node = self.parse_postfix(node)
502 return node
503
Armin Ronacher023b5e92008-05-08 11:03:10 +0200504 def parse_tuple(self, simplified=False, no_condexpr=False):
505 """Works like `parse_expression` but if multiple expressions are
506 delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
507 This method could also return a regular expression instead of a tuple
508 if no commas where found.
509
510 The default parsing mode is a full tuple. If `simplified` is `True`
511 only names and literals are parsed. The `no_condexpr` parameter is
512 forwarded to :meth:`parse_expression`.
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200513 """
514 lineno = self.stream.current.lineno
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200515 if simplified:
516 parse = self.parse_primary
517 elif no_condexpr:
518 parse = lambda: self.parse_expression(no_condexpr=True)
519 else:
520 parse = self.parse_expression
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200521 args = []
522 is_tuple = False
523 while 1:
524 if args:
525 self.stream.expect('comma')
Armin Ronacher115de2e2008-05-01 22:20:05 +0200526 if self.is_tuple_end():
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200527 break
528 args.append(parse())
Armin Ronacherb5124e62008-04-25 00:36:14 +0200529 if self.stream.current.type is 'comma':
530 is_tuple = True
531 else:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200532 break
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200533 lineno = self.stream.current.lineno
534 if not is_tuple and args:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200535 return args[0]
536 return nodes.Tuple(args, 'load', lineno=lineno)
537
538 def parse_list(self):
539 token = self.stream.expect('lbracket')
540 items = []
541 while self.stream.current.type is not 'rbracket':
542 if items:
543 self.stream.expect('comma')
544 if self.stream.current.type == 'rbracket':
545 break
546 items.append(self.parse_expression())
547 self.stream.expect('rbracket')
548 return nodes.List(items, lineno=token.lineno)
549
550 def parse_dict(self):
551 token = self.stream.expect('lbrace')
552 items = []
553 while self.stream.current.type is not 'rbrace':
554 if items:
555 self.stream.expect('comma')
556 if self.stream.current.type == 'rbrace':
557 break
558 key = self.parse_expression()
559 self.stream.expect('colon')
560 value = self.parse_expression()
561 items.append(nodes.Pair(key, value, lineno=key.lineno))
562 self.stream.expect('rbrace')
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200563 return nodes.Dict(items, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200564
565 def parse_postfix(self, node):
566 while 1:
567 token_type = self.stream.current.type
568 if token_type is 'dot' or token_type is 'lbracket':
569 node = self.parse_subscript(node)
570 elif token_type is 'lparen':
571 node = self.parse_call(node)
572 elif token_type is 'pipe':
573 node = self.parse_filter(node)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200574 elif token_type is 'name' and self.stream.current.value == 'is':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200575 node = self.parse_test(node)
576 else:
577 break
578 return node
579
580 def parse_subscript(self, node):
581 token = self.stream.next()
582 if token.type is 'dot':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200583 attr_token = self.stream.current
584 if attr_token.type not in ('name', 'integer'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200585 raise TemplateSyntaxError('expected name or number',
Armin Ronachere791c2a2008-04-07 18:39:54 +0200586 attr_token.lineno, self.filename)
587 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200588 self.stream.next()
589 elif token.type is 'lbracket':
590 args = []
591 while self.stream.current.type is not 'rbracket':
592 if args:
593 self.stream.expect('comma')
594 args.append(self.parse_subscribed())
595 self.stream.expect('rbracket')
596 if len(args) == 1:
597 arg = args[0]
598 else:
Benjamin Wieganda3152742008-04-28 18:07:52 +0200599 arg = nodes.Tuple(args, self.lineno, self.filename)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200600 else:
601 raise TemplateSyntaxError('expected subscript expression',
602 self.lineno, self.filename)
603 return nodes.Subscript(node, arg, 'load', lineno=token.lineno)
604
605 def parse_subscribed(self):
606 lineno = self.stream.current.lineno
607
608 if self.stream.current.type is 'colon':
609 self.stream.next()
610 args = [None]
611 else:
612 node = self.parse_expression()
613 if self.stream.current.type is not 'colon':
614 return node
615 self.stream.next()
616 args = [node]
617
618 if self.stream.current.type is 'colon':
619 args.append(None)
620 elif self.stream.current.type not in ('rbracket', 'comma'):
621 args.append(self.parse_expression())
622 else:
623 args.append(None)
624
625 if self.stream.current.type is 'colon':
626 self.stream.next()
627 if self.stream.current.type not in ('rbracket', 'comma'):
628 args.append(self.parse_expression())
629 else:
630 args.append(None)
631 else:
632 args.append(None)
633
634 return nodes.Slice(lineno=lineno, *args)
635
636 def parse_call(self, node):
637 token = self.stream.expect('lparen')
638 args = []
639 kwargs = []
640 dyn_args = dyn_kwargs = None
641 require_comma = False
642
643 def ensure(expr):
644 if not expr:
645 raise TemplateSyntaxError('invalid syntax for function '
646 'call expression', token.lineno,
647 self.filename)
648
649 while self.stream.current.type is not 'rparen':
650 if require_comma:
651 self.stream.expect('comma')
652 # support for trailing comma
653 if self.stream.current.type is 'rparen':
654 break
655 if self.stream.current.type is 'mul':
656 ensure(dyn_args is None and dyn_kwargs is None)
657 self.stream.next()
658 dyn_args = self.parse_expression()
659 elif self.stream.current.type is 'pow':
660 ensure(dyn_kwargs is None)
661 self.stream.next()
662 dyn_kwargs = self.parse_expression()
663 else:
664 ensure(dyn_args is None and dyn_kwargs is None)
665 if self.stream.current.type is 'name' and \
666 self.stream.look().type is 'assign':
667 key = self.stream.current.value
668 self.stream.skip(2)
Armin Ronacher0611e492008-04-25 23:44:14 +0200669 value = self.parse_expression()
670 kwargs.append(nodes.Keyword(key, value,
671 lineno=value.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200672 else:
673 ensure(not kwargs)
674 args.append(self.parse_expression())
675
676 require_comma = True
677 self.stream.expect('rparen')
678
679 if node is None:
680 return args, kwargs, dyn_args, dyn_kwargs
681 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
682 lineno=token.lineno)
683
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200684 def parse_filter(self, node, start_inline=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200685 lineno = self.stream.current.type
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200686 while self.stream.current.type == 'pipe' or start_inline:
687 if not start_inline:
688 self.stream.next()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200689 token = self.stream.expect('name')
690 if self.stream.current.type is 'lparen':
691 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
692 else:
693 args = []
694 kwargs = []
695 dyn_args = dyn_kwargs = None
Armin Ronacherd55ab532008-04-09 16:13:39 +0200696 node = nodes.Filter(node, token.value, args, kwargs, dyn_args,
697 dyn_kwargs, lineno=token.lineno)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200698 start_inline = False
Armin Ronacherd55ab532008-04-09 16:13:39 +0200699 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200700
701 def parse_test(self, node):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200702 token = self.stream.next()
703 if self.stream.current.test('name:not'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200704 self.stream.next()
705 negated = True
706 else:
707 negated = False
708 name = self.stream.expect('name').value
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200709 dyn_args = dyn_kwargs = None
710 kwargs = []
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200711 if self.stream.current.type is 'lparen':
712 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
713 elif self.stream.current.type in ('name', 'string', 'integer',
714 'float', 'lparen', 'lbracket',
Armin Ronacher115de2e2008-05-01 22:20:05 +0200715 'lbrace'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200716 args = [self.parse_expression()]
717 else:
718 args = []
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200719 node = nodes.Test(node, name, args, kwargs, dyn_args,
720 dyn_kwargs, lineno=token.lineno)
721 if negated:
Armin Ronacher9a822052008-04-17 18:44:07 +0200722 node = nodes.Not(node, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200723 return node
724
725 def subparse(self, end_tokens=None):
726 body = []
727 data_buffer = []
728 add_data = data_buffer.append
729
730 def flush_data():
731 if data_buffer:
732 lineno = data_buffer[0].lineno
733 body.append(nodes.Output(data_buffer[:], lineno=lineno))
734 del data_buffer[:]
735
736 while self.stream:
737 token = self.stream.current
738 if token.type is 'data':
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200739 if token.value:
740 add_data(nodes.Const(token.value, lineno=token.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200741 self.stream.next()
742 elif token.type is 'variable_begin':
743 self.stream.next()
Armin Ronacherb5124e62008-04-25 00:36:14 +0200744 add_data(self.parse_tuple())
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200745 self.stream.expect('variable_end')
746 elif token.type is 'block_begin':
747 flush_data()
748 self.stream.next()
749 if end_tokens is not None and \
Armin Ronachercda43df2008-05-03 17:10:05 +0200750 self.stream.current.test_any(*end_tokens):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200751 return body
Armin Ronacher023b5e92008-05-08 11:03:10 +0200752 rv = self.parse_statement()
753 if isinstance(rv, list):
754 body.extend(rv)
755 else:
756 body.append(rv)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200757 self.stream.expect('block_end')
758 else:
759 raise AssertionError('internal parsing error')
760
761 flush_data()
762 return body
763
764 def parse(self):
765 """Parse the whole template into a `Template` node."""
Armin Ronacherd55ab532008-04-09 16:13:39 +0200766 result = nodes.Template(self.subparse(), lineno=1)
767 result.set_environment(self.environment)
768 return result