added first working pieces of compiler
--HG--
branch : trunk
diff --git a/jinja2/nodes.py b/jinja2/nodes.py
index 974b4ed..e6e68a1 100644
--- a/jinja2/nodes.py
+++ b/jinja2/nodes.py
@@ -35,19 +35,6 @@
}
-def set_ctx(node, ctx):
- """
- Reset the context of a node and all child nodes. Per default the parser
- will all generate nodes that have a 'load' context as it's the most common
- one. This method is used in the parser to set assignment targets and
- other nodes to a store context.
- """
- todo = deque([node])
- while todo:
- node = todo.popleft()
- if 'ctx' in node._fields:
- node.ctx = ctx
- todo.extend(node.iter_child_nodes())
class Impossible(Exception):
@@ -59,7 +46,7 @@
class NodeType(type):
def __new__(cls, name, bases, d):
- for attr in '_fields', '_attributes':
+ for attr in 'fields', 'attributes':
storage = []
for base in bases:
storage.extend(getattr(base, attr, ()))
@@ -71,39 +58,41 @@
class Node(object):
"""
- Base jinja node.
+ Baseclass for all Jinja nodes.
"""
__metaclass__ = NodeType
- _fields = ()
- _attributes = ('lineno',)
+ fields = ()
+ attributes = ('lineno',)
def __init__(self, *args, **kw):
if args:
- if len(args) != len(self._fields):
- if not self._fields:
+ if len(args) != len(self.fields):
+ if not self.fields:
raise TypeError('%r takes 0 arguments' %
self.__class__.__name__)
raise TypeError('%r takes 0 or %d argument%s' % (
self.__class__.__name__,
- len(self._fields),
- len(self._fields) != 1 and 's' or ''
+ len(self.fields),
+ len(self.fields) != 1 and 's' or ''
))
- for name, arg in izip(self._fields, args):
+ for name, arg in izip(self.fields, args):
setattr(self, name, arg)
- for attr in self._attributes:
+ for attr in self.attributes:
setattr(self, attr, kw.pop(attr, None))
if kw:
raise TypeError('unknown keyword argument %r' %
iter(kw).next())
def iter_fields(self):
- for name in self._fields:
+ """Iterate over all fields."""
+ for name in self.fields:
try:
yield name, getattr(self, name)
except AttributeError:
pass
def iter_child_nodes(self):
+ """Iterate over all child nodes."""
for field, item in self.iter_fields():
if isinstance(item, list):
for n in item:
@@ -112,11 +101,38 @@
elif isinstance(item, Node):
yield item
+ def find(self, node_type):
+ """Find the first node of a given type."""
+ for result in self.find_all(node_type):
+ return result
+
+ def find_all(self, node_type):
+ """Find all the nodes of a given type."""
+ for child in self.iter_child_nodes():
+ if isinstance(child, node_type):
+ yield child
+ for result in child.find_all(node_type):
+ yield result
+
+ def set_ctx(self, ctx):
+ """
+ Reset the context of a node and all child nodes. Per default the parser
+ will all generate nodes that have a 'load' context as it's the most common
+ one. This method is used in the parser to set assignment targets and
+ other nodes to a store context.
+ """
+ todo = deque([self])
+ while todo:
+ node = todo.popleft()
+ if 'ctx' in node.fields:
+ node.ctx = ctx
+ todo.extend(node.iter_child_nodes())
+
def __repr__(self):
return '%s(%s)' % (
self.__class__.__name__,
', '.join('%s=%r' % (arg, getattr(self, arg, None)) for
- arg in self._fields)
+ arg in self.fields)
)
@@ -136,7 +152,7 @@
"""
Node that represents a template.
"""
- _fields = ('body',)
+ fields = ('body',)
class Output(Stmt):
@@ -144,91 +160,106 @@
A node that holds multiple expressions which are then printed out. This
is used both for the `print` statement and the regular template data.
"""
- _fields = ('nodes',)
+ fields = ('nodes',)
+
+ def optimized_nodes(self):
+ """Try to optimize the nodes."""
+ buffer = []
+ for node in self.nodes:
+ try:
+ const = unicode(node.as_const())
+ except:
+ buffer.append(node)
+ else:
+ if buffer and isinstance(buffer[-1], unicode):
+ buffer[-1] += const
+ else:
+ buffer.append(const)
+ return buffer
class Extends(Stmt):
"""
Represents an extends statement.
"""
- _fields = ('extends',)
+ fields = ('template',)
class For(Stmt):
"""
A node that represents a for loop
"""
- _fields = ('target', 'iter', 'body', 'else_', 'recursive')
+ fields = ('target', 'iter', 'body', 'else_', 'recursive')
class If(Stmt):
"""
A node that represents an if condition.
"""
- _fields = ('test', 'body', 'else_')
+ fields = ('test', 'body', 'else_')
class Macro(Stmt):
"""
A node that represents a macro.
"""
- _fields = ('name', 'arguments', 'body')
+ fields = ('name', 'args', 'defaults', 'dyn_args', 'dyn_kwargs', 'body')
class CallBlock(Stmt):
"""
A node that represents am extended macro call.
"""
- _fields = ('expr', 'body')
+ fields = ('call', 'body')
class Set(Stmt):
"""
Allows defining own variables.
"""
- _fields = ('name', 'expr')
+ fields = ('name', 'expr')
class FilterBlock(Stmt):
"""
Node for filter sections.
"""
- _fields = ('body', 'filters')
+ fields = ('body', 'filters')
class Block(Stmt):
"""
A node that represents a block.
"""
- _fields = ('name', 'body')
+ fields = ('name', 'body')
class Include(Stmt):
"""
A node that represents the include tag.
"""
- _fields = ('template',)
+ fields = ('template', 'target')
class Trans(Stmt):
"""
A node for translatable sections.
"""
- _fields = ('singular', 'plural', 'indicator', 'replacements')
+ fields = ('singular', 'plural', 'indicator', 'replacements')
class ExprStmt(Stmt):
"""
A statement that evaluates an expression to None.
"""
- _fields = ('node',)
+ fields = ('node',)
class Assign(Stmt):
"""
Assigns an expression to a target.
"""
- _fields = ('target', 'node')
+ fields = ('target', 'node')
class Expr(Node):
@@ -254,7 +285,7 @@
"""
Baseclass for all binary expressions.
"""
- _fields = ('left', 'right')
+ fields = ('left', 'right')
operator = None
def as_const(self):
@@ -270,7 +301,7 @@
"""
Baseclass for all unary expressions.
"""
- _fields = ('node',)
+ fields = ('node',)
operator = None
def as_const(self):
@@ -285,10 +316,10 @@
"""
any name such as {{ foo }}
"""
- _fields = ('name', 'ctx')
+ fields = ('name', 'ctx')
def can_assign(self):
- return True
+ return self.name not in ('true', 'false', 'none')
class Literal(Expr):
@@ -301,7 +332,7 @@
"""
any constat such as {{ "foo" }}
"""
- _fields = ('value',)
+ fields = ('value',)
def as_const(self):
return self.value
@@ -312,7 +343,7 @@
For loop unpacking and some other things like multiple arguments
for subscripts.
"""
- _fields = ('items', 'ctx')
+ fields = ('items', 'ctx')
def as_const(self):
return tuple(x.as_const() for x in self.items)
@@ -328,7 +359,7 @@
"""
any list literal such as {{ [1, 2, 3] }}
"""
- _fields = ('items',)
+ fields = ('items',)
def as_const(self):
return [x.as_const() for x in self.items]
@@ -338,7 +369,7 @@
"""
any dict literal such as {{ {1: 2, 3: 4} }}
"""
- _fields = ('items',)
+ fields = ('items',)
def as_const(self):
return dict(x.as_const() for x in self.items)
@@ -348,7 +379,7 @@
"""
A key, value pair for dicts.
"""
- _fields = ('key', 'value')
+ fields = ('key', 'value')
def as_const(self):
return self.key.as_const(), self.value.as_const()
@@ -358,7 +389,7 @@
"""
{{ foo if bar else baz }}
"""
- _fields = ('test', 'expr1', 'expr2')
+ fields = ('test', 'expr1', 'expr2')
def as_const(self):
if self.test.as_const():
@@ -370,35 +401,35 @@
"""
{{ foo|bar|baz }}
"""
- _fields = ('node', 'filters')
+ fields = ('node', 'filters')
class FilterCall(Expr):
"""
{{ |bar() }}
"""
- _fields = ('name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
+ fields = ('name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
class Test(Expr):
"""
{{ foo is lower }}
"""
- _fields = ('name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
+ fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
class Call(Expr):
"""
{{ foo(bar) }}
"""
- _fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
+ fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
class Subscript(Expr):
"""
{{ foo.bar }} and {{ foo['bar'] }} etc.
"""
- _fields = ('node', 'arg', 'ctx')
+ fields = ('node', 'arg', 'ctx')
def as_const(self):
try:
@@ -414,14 +445,14 @@
"""
1:2:3 etc.
"""
- _fields = ('start', 'stop', 'step')
+ fields = ('start', 'stop', 'step')
class Concat(Expr):
"""
For {{ foo ~ bar }}. Concatenates strings.
"""
- _fields = ('nodes',)
+ fields = ('nodes',)
def as_const(self):
return ''.join(unicode(x.as_const()) for x in self.nodes)
@@ -431,14 +462,14 @@
"""
{{ foo == bar }}, {{ foo >= bar }} etc.
"""
- _fields = ('expr', 'ops')
+ fields = ('expr', 'ops')
class Operand(Helper):
"""
Operator + expression.
"""
- _fields = ('op', 'expr')
+ fields = ('op', 'expr')
class Mul(BinExpr):
@@ -517,14 +548,14 @@
operator = 'not'
-class NegExpr(UnaryExpr):
+class Neg(UnaryExpr):
"""
{{ -foo }}
"""
operator = '-'
-class PosExpr(UnaryExpr):
+class Pos(UnaryExpr):
"""
{{ +foo }}
"""