First extension interface documentation and updates in that interface
--HG--
branch : trunk
diff --git a/jinja2/parser.py b/jinja2/parser.py
index 5298061..91a848b 100644
--- a/jinja2/parser.py
+++ b/jinja2/parser.py
@@ -18,9 +18,8 @@
class Parser(object):
- """The template parser class.
-
- Transforms sourcecode into an abstract syntax tree.
+ """This is the central parsing class Jinja2 uses. It's passed to
+ extensions and can be used to parse expressions or statements.
"""
def __init__(self, environment, source, filename=None):
@@ -30,21 +29,32 @@
self.source = unicode(source)
self.filename = filename
self.closed = False
- self.stream = environment.lexer.tokenize(source, filename)
+ self.stream = environment.lexer.tokenize(self.source, filename)
self.extensions = {}
- for extension in environment.extensions:
+ for extension in environment.extensions.itervalues():
for tag in extension.tags:
self.extensions[tag] = extension.parse
-
- def is_statement_end(self):
- """Are we at the end of a statement?"""
- if self.stream.current.type in ('variable_end', 'block_end'):
- return True
- return self.stream.current.test('name:in')
+ self._last_identifier = 0
def is_tuple_end(self):
"""Are we at the end of a tuple?"""
- return self.stream.current.type is 'rparen' or self.is_statement_end()
+ return self.stream.current.type in ('variable_end', 'block_end',
+ 'rparen') or \
+ self.stream.current.test('name:in')
+
+ def ignore_colon(self):
+ """If there is a colon, skip it and return `True`, else `False`."""
+ if self.stream.current.type is 'colon':
+ self.stream.next()
+ return True
+ return False
+
+ def free_identifier(self, lineno=None):
+ """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
+ self._last_identifier += 1
+ rv = object.__new__(nodes.InternalName)
+ nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno)
+ return rv
def parse_statement(self):
"""Parse a single statement."""
@@ -82,11 +92,15 @@
def parse_statements(self, end_tokens, drop_needle=False):
"""Parse multiple statements into a list until one of the end tokens
is reached. This is used to parse the body of statements as it also
- parses template data if appropriate.
+ parses template data if appropriate. The parser checks first if the
+ current token is a colon and skips it if there is one. Then it checks
+ for the block end and parses until if one of the `end_tokens` is
+ reached. Per default the active token in the stream at the end of
+ the call is the matched end token. If this is not wanted `drop_needle`
+ can be set to `True` and the end token is removed.
"""
# the first token may be a colon for python compatibility
- if self.stream.current.type is 'colon':
- self.stream.next()
+ self.ignore_colon()
# in the future it would be possible to add whole code sections
# by adding some sort of end of statement token and parsing those here.
@@ -284,14 +298,17 @@
def parse_print(self):
node = nodes.Output(lineno=self.stream.next().lineno)
node.nodes = []
- while not self.is_statement_end():
+ while self.stream.current.type is not 'block_end':
if node.nodes:
self.stream.expect('comma')
node.nodes.append(self.parse_expression())
return node
def parse_expression(self, no_condexpr=False):
- """Parse an expression."""
+ """Parse an expression. Per default all expressions are parsed, if
+ the optional `no_condexpr` parameter is set to `True` conditional
+ expressions are not parsed.
+ """
if no_condexpr:
return self.parse_or()
return self.parse_condexpr()
@@ -477,10 +494,15 @@
node = self.parse_postfix(node)
return node
- def parse_tuple(self, enforce=False, simplified=False, no_condexpr=False):
- """Parse multiple expressions into a tuple. This can also return
- just one expression which is not a tuple. If you want to enforce
- a tuple, pass it enforce=True (currently unused).
+ def parse_tuple(self, simplified=False, no_condexpr=False):
+ """Works like `parse_expression` but if multiple expressions are
+ delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
+ This method could also return a regular expression instead of a tuple
+ if no commas where found.
+
+ The default parsing mode is a full tuple. If `simplified` is `True`
+ only names and literals are parsed. The `no_condexpr` parameter is
+ forwarded to :meth:`parse_expression`.
"""
lineno = self.stream.current.lineno
if simplified:
@@ -503,9 +525,6 @@
break
lineno = self.stream.current.lineno
if not is_tuple and args:
- if enforce:
- raise TemplateSyntaxError('tuple expected', lineno,
- self.filename)
return args[0]
return nodes.Tuple(args, 'load', lineno=lineno)
@@ -723,7 +742,11 @@
if end_tokens is not None and \
self.stream.current.test_any(*end_tokens):
return body
- body.append(self.parse_statement())
+ rv = self.parse_statement()
+ if isinstance(rv, list):
+ body.extend(rv)
+ else:
+ body.append(rv)
self.stream.expect('block_end')
else:
raise AssertionError('internal parsing error')