added first working pieces of compiler
--HG--
branch : trunk
diff --git a/jinja2/parser.py b/jinja2/parser.py
index 0c4edfc..96ba0a0 100644
--- a/jinja2/parser.py
+++ b/jinja2/parser.py
@@ -14,13 +14,12 @@
__all__ = ['Parser']
-_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'include'])
+_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
+ 'macro', 'include'])
_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq', 'in'])
-_tuple_edge_tokens = set(['rparen', 'block_end', 'variable_end', 'in',
- 'semicolon', 'recursive'])
_statement_end_tokens = set(['elif', 'else', 'endblock', 'endfilter',
- 'endfor', 'endif', 'endmacro',
- 'endcall', 'block_end'])
+ 'endfor', 'endif', 'endmacro', 'variable_end',
+ 'in', 'recursive', 'endcall', 'block_end'])
class Parser(object):
@@ -39,7 +38,6 @@
self.source = source
self.filename = filename
self.closed = False
- self.blocks = set()
self.no_variable_block = self.environment.lexer.no_variable_block
self.stream = environment.lexer.tokenize(source, filename)
@@ -60,12 +58,14 @@
elif token_type is 'call':
self.stream.next()
return self.parse_call_block()
- lineno = self.stream.current.lineno
+ lineno = self.stream.current
expr = self.parse_expression()
if self.stream.current.type == 'assign':
- return self.parse_assign(expr)
+ result = self.parse_assign(expr)
+ else:
+ result = nodes.ExprStmt(expr, lineno=lineno)
self.end_statement()
- return nodes.ExprStmt(expr, lineno=lineno)
+ return result
def parse_assign(self, target):
"""Parse an assign statement."""
@@ -76,7 +76,7 @@
self.filename)
expr = self.parse_tuple()
self.end_statement()
- nodes.set_ctx(target, 'store')
+ target.set_ctx('store')
return nodes.Assign(target, expr, lineno=lineno)
def parse_statements(self, end_tokens, drop_needle=False):
@@ -95,6 +95,10 @@
else:
result = []
while self.stream.current.type not in end_tokens:
+ if self.stream.current.type is 'block_end':
+ self.stream.next()
+ result.extend(self.subparse(end_tokens))
+ break
result.append(self.parse_statement())
if drop_needle:
self.stream.next()
@@ -104,7 +108,11 @@
"""Parse a for loop."""
lineno = self.stream.expect('for').lineno
target = self.parse_tuple(simplified=True)
- nodes.set_ctx(target, 'store')
+ if not target.can_assign():
+ raise TemplateSyntaxError("can't assign to '%s'" %
+ target, target.lineno,
+ self.filename)
+ target.set_ctx('store')
self.stream.expect('in')
iter = self.parse_tuple()
if self.stream.current.type is 'recursive':
@@ -113,28 +121,100 @@
else:
recursive = False
body = self.parse_statements(('endfor', 'else'))
- token_type = self.stream.current.type
- self.stream.next()
- if token_type is 'endfor':
+ if self.stream.next().type is 'endfor':
else_ = []
else:
else_ = self.parse_statements(('endfor',), drop_needle=True)
return nodes.For(target, iter, body, else_, False, lineno=lineno)
def parse_if(self):
- pass
+ """Parse an if construct."""
+ node = result = nodes.If(lineno=self.stream.expect('if').lineno)
+ while 1:
+ # TODO: exclude conditional expressions here
+ node.test = self.parse_tuple()
+ node.body = self.parse_statements(('elif', 'else', 'endif'))
+ token_type = self.stream.next().type
+ if token_type is 'elif':
+ new_node = nodes.If(lineno=self.stream.current.lineno)
+ node.else_ = [new_node]
+ node = new_node
+ continue
+ elif token_type is 'else':
+ node.else_ = self.parse_statements(('endif',),
+ drop_needle=True)
+ else:
+ node.else_ = []
+ break
+ return result
def parse_block(self):
- pass
+ node = nodes.Block(lineno=self.stream.expect('block').lineno)
+ node.name = self.stream.expect('name').value
+ node.body = self.parse_statements(('endblock',), drop_needle=True)
+ return node
def parse_extends(self):
- pass
+ node = nodes.Extends(lineno=self.stream.expect('extends').lineno)
+ node.template = self.parse_expression()
+ self.end_statement()
+ return node
def parse_include(self):
- pass
+ node = nodes.Include(lineno=self.stream.expect('include').lineno)
+ expr = self.parse_expression()
+ if self.stream.current.type is 'assign':
+ self.stream.next()
+ if not expr.can_assign():
+ raise TemplateSyntaxError('can\'t assign imported template '
+ 'to this expression.', expr.lineno,
+ self.filename)
+ expr.set_ctx('store')
+ node.target = expr
+ node.template = self.parse_expression()
+ else:
+ node.target = None
+ node.template = expr
+ self.end_statement()
+ return node
def parse_call_block(self):
- pass
+ node = nodes.CallBlock(lineno=self.stream.expect('call').lineno)
+ node.call = self.parse_call()
+ node.body = self.parse_statements(('endcall',), drop_needle=True)
+ return node
+
+ def parse_macro(self):
+ node = nodes.Macro(lineno=self.stream.expect('macro').lineno)
+ node.name = self.stream.expect('name').value
+ self.stream.expect('lparen')
+ node.args = args = []
+ node.defaults = defaults = []
+ while self.stream.current.type is not 'rparen':
+ if args:
+ self.stream.expect('comma')
+ token = self.stream.expect('name')
+ arg = nodes.Name(token.value, 'param', lineno=token.lineno)
+ if not arg.can_assign():
+ raise TemplateSyntaxError("can't assign to '%s'" %
+ arg.name, arg.lineno,
+ self.filename)
+ if self.stream.current.type is 'assign':
+ self.stream.next()
+ defaults.append(self.parse_expression())
+ self.stream.expect('rparen')
+ node.body = self.parse_statements(('endmacro',), drop_needle=True)
+ return node
+
+ def parse_print(self):
+ node = nodes.Output(lineno=self.stream.expect('print').lineno)
+ node.nodes = []
+ while self.stream.current.type not in _statement_end_tokens:
+ if node.nodes:
+ self.stream.expect('comma')
+ node.nodes.append(self.parse_expression())
+ self.end_statement()
+ return node
def parse_expression(self):
"""Parse an expression."""
@@ -237,7 +317,7 @@
while self.stream.current.type is 'div':
self.stream.next()
right = self.parse_floordiv()
- left = nodes.Floor(left, right, lineno=lineno)
+ left = nodes.Div(left, right, lineno=lineno)
lineno = self.stream.current.lineno
return left
@@ -277,11 +357,11 @@
if token_type is 'not':
self.stream.next()
node = self.parse_unary()
- return nodes.Neg(node, lineno=lineno)
+ return nodes.Not(node, lineno=lineno)
if token_type is 'sub':
self.stream.next()
node = self.parse_unary()
- return nodes.Sub(node, lineno=lineno)
+ return nodes.Neg(node, lineno=lineno)
if token_type is 'add':
self.stream.next()
node = self.parse_unary()
@@ -330,7 +410,7 @@
while 1:
if args:
self.stream.expect('comma')
- if self.stream.current.type in _tuple_edge_tokens:
+ if self.stream.current.type in _statement_end_tokens:
break
args.append(parse())
if self.stream.current.type is not 'comma':
@@ -389,10 +469,11 @@
def parse_subscript(self, node):
token = self.stream.next()
if token.type is 'dot':
- if token.type not in ('name', 'integer'):
+ attr_token = self.stream.current
+ if attr_token.type not in ('name', 'integer'):
raise TemplateSyntaxError('expected name or number',
- token.lineno, self.filename)
- arg = nodes.Const(token.value, lineno=token.lineno)
+ attr_token.lineno, self.filename)
+ arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
self.stream.next()
elif token.type is 'lbracket':
args = []
@@ -547,7 +628,12 @@
self.stream.next()
elif token.type is 'variable_begin':
self.stream.next()
- add_data(self.parse_tuple())
+ want_comma = False
+ while not self.stream.current.type in _statement_end_tokens:
+ if want_comma:
+ self.stream.expect('comma')
+ add_data(self.parse_expression())
+ want_comma = True
self.stream.expect('variable_end')
elif token.type is 'block_begin':
flush_data()