improved variable exporting, added TODO file

--HG--
branch : trunk
diff --git a/jinja2/nodes.py b/jinja2/nodes.py
index 67f7559..0db061a 100644
--- a/jinja2/nodes.py
+++ b/jinja2/nodes.py
@@ -90,13 +90,20 @@
     two attributes: `lineno` (the line number of the node) and `environment`.
     The `environment` attribute is set at the end of the parsing process for
     all nodes automatically.
+
+    Nodes can be frozen which makes them hashable.  The compiler freezes the
+    nodes automatically.  Modifications on frozen nodes are possible but not
+    allowed.
     """
     __metaclass__ = NodeType
     fields = ()
     attributes = ('lineno', 'environment')
     abstract = True
+    frozen = False
 
     def __init__(self, *fields, **attributes):
+        if self.abstract:
+            raise TypeError('abstract nodes are not instanciable')
         if fields:
             if len(fields) != len(self.fields):
                 if not self.fields:
@@ -213,6 +220,30 @@
             todo.extend(node.iter_child_nodes())
         return self
 
+    def freeze(self):
+        """Freeze the complete node tree which makes them hashable.
+        This happens automatically on compilation.  Frozen nodes must not be
+        modified any further.  Extensions may not freeze nodes that appear
+        in the final node tree (ie: nodes that are returned from the extension
+        parse method).
+        """
+        todo = deque([self])
+        while todo:
+            node = todo.popleft()
+            node.frozen = True
+            todo.extend(node.iter_child_nodes())
+
+    def __eq__(self, other):
+        return type(self) is type(other) and self.__dict__ == other.__dict__
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __hash__(self):
+        if not self.frozen:
+            raise TypeError('unfrozen nodes are unhashable')
+        return hash(tuple(self.__dict__.items()))
+
     def __repr__(self):
         return '%s(%s)' % (
             self.__class__.__name__,
@@ -374,6 +405,7 @@
     """Baseclass for all binary expressions."""
     fields = ('left', 'right')
     operator = None
+    abstract = True
 
     def as_const(self):
         f = _binop_to_func[self.operator]
@@ -387,6 +419,7 @@
     """Baseclass for all unary expressions."""
     fields = ('node',)
     operator = None
+    abstract = True
 
     def as_const(self):
         f = _uaop_to_func[self.operator]
@@ -412,6 +445,7 @@
 
 class Literal(Expr):
     """Baseclass for literals."""
+    abstract = True
 
 
 class Const(Literal):