diff -r 3c924a0594ba Cython/Compiler/ModuleNode.py
--- a/Cython/Compiler/ModuleNode.py	Fri May 02 10:22:20 2008 +0200
+++ b/Cython/Compiler/ModuleNode.py	Fri May 02 11:42:17 2008 +0200
@@ -33,7 +33,7 @@ class ModuleNode(Nodes.Node, Nodes.Block
     #  module_temp_cname    string
     #  full_module_name     string
 
-    children_attrs = ["body"]
+    child_attrs = ["body"]
     
     def analyse_declarations(self, env):
         if Options.embed_pos_in_docstring:
@@ -45,6 +45,7 @@ class ModuleNode(Nodes.Node, Nodes.Block
         self.body.analyse_declarations(env)
     
     def process_implementation(self, env, options, result):
+        options.transforms.run('after_parse', self, global_env=env)
         self.analyse_declarations(env)
         env.check_c_classes()
         self.body.analyse_expressions(env)
@@ -224,6 +225,14 @@ class ModuleNode(Nodes.Node, Nodes.Block
         self.generate_interned_name_decls(env, code)
         self.generate_py_string_decls(env, code)
         self.generate_cached_builtins_decls(env, code)
+
+        import Cython.Compiler.Transforms.Analysis as Analysis
+        Analysis.CreateFunctionScope()(self, env=env)
+        Analysis.AnalyseControlFlow()(self, env=env)
+        Analysis.AnalyseFunctionBodyDeclarations()(self, env=env)
+        Analysis.AnalyseFunctionBodyExpressions()(self, env=env)
+        options.transforms.run('after_function_analysis', self, global_env=env)
+
         self.body.generate_function_definitions(env, code, options.transforms)
         code.mark_pos(None)
         self.generate_interned_name_table(env, code)
diff -r 3c924a0594ba Cython/Compiler/Nodes.py
--- a/Cython/Compiler/Nodes.py	Fri May 02 10:22:20 2008 +0200
+++ b/Cython/Compiler/Nodes.py	Fri May 02 11:42:17 2008 +0200
@@ -863,20 +863,13 @@ class FuncDefNode(StatNode, BlockNode):
                 
     def generate_function_definitions(self, env, code, transforms):
         code.mark_pos(self.pos)
+        code.init_labels()
+
         # Generate C code for header and body of function
-        genv = env.global_scope()
-        lenv = LocalScope(name = self.entry.name, outer_scope = genv)
-        lenv.return_type = self.return_type
-        code.init_labels()
-        self.declare_arguments(lenv)
-        transforms.run('before_analyse_function', self, env=env, lenv=lenv, genv=genv)
-        self.body.analyse_control_flow(lenv)
-        self.body.analyse_declarations(lenv)
-        self.body.analyse_expressions(lenv)
-        transforms.run('after_analyse_function', self, env=env, lenv=lenv, genv=genv)
-        # Code for nested function definitions would go here
-        # if we supported them, which we probably won't.
+        lenv = self.scope
+
         # ----- Top-level constants used by this function
+
         self.generate_interned_num_decls(lenv, code)
         self.generate_interned_name_decls(lenv, code)
         self.generate_py_string_decls(lenv, code)
diff -r 3c924a0594ba Cython/Compiler/Transform.py
--- a/Cython/Compiler/Transform.py	Fri May 02 10:22:20 2008 +0200
+++ b/Cython/Compiler/Transform.py	Fri May 02 11:42:17 2008 +0200
@@ -3,11 +3,15 @@
 #
 import Nodes
 import ExprNodes
+import inspect
 
 class Transform(object):
-    #  parent_stack [Node]       A stack providing information about where in the tree
-    #                            we currently are. Nodes here should be considered
-    #                            read-only.
+    # parent_stack [Node]       A stack providing information about where in the tree
+    #                           we currently are. Nodes here should be considered
+    #                           read-only.
+    #
+    # Additionally, any keyword arguments to __call__ will be set as fields while in
+    # a transformation.
 
     # Transforms for the parse tree should usually extend this class for convenience.
     # The caller of a transform will only first call initialize and then process_node on
@@ -18,12 +22,6 @@ class Transform(object):
     # return the input node untouched. Returning None will remove the node from the
     # parent.
     
-    def __init__(self):
-        self.parent_stack = []
-    
-    def initialize(self, phase, **options):
-        pass
-
     def process_children(self, node):
         """For all children of node, either process_list (if isinstance(node, list))
         or process_node (otherwise) is called."""
@@ -45,12 +43,11 @@ class Transform(object):
     def process_list(self, l, name):
         """Calls process_node on all the items in l, using the name one gets when appending
         [idx] to the name. Each item in l is transformed in-place by the item process_node
-        returns, then l is returned."""
-        # Comment: If moving to a copying strategy, it might makes sense to return a
-        # new list instead.
+        returns, then l is returned. If process_node returns None, the item is removed
+        from the list."""
         for idx in xrange(len(l)):
             l[idx] = self.process_node(l[idx], "%s[%d]" % (name, idx))
-        return l
+        return [x for x in l if x is not None]
 
     def process_node(self, node, name):
         """Override this method to process nodes. name specifies which kind of relation the
@@ -58,6 +55,67 @@ class Transform(object):
         should use for this relation, which can either be the same node, None to remove
         the node, or a different node."""
         raise NotImplementedError("Not implemented")
+
+    def __call__(self, root, **params):
+        self.parent_stack = []
+        for key, value in params.iteritems():
+            setattr(self, key, value)
+        self.process_node(root, "(root)")
+        for key, value in params.iteritems():
+            delattr(self, key)
+        del self.parent_stack
+
+
+class VisitorTransform(Transform):
+
+    # Note: If needed, this can be replaced with a more efficient metaclass
+    # approach, resolving the jump table at module load time.
+    
+    def __init__(self):
+        super(VisitorTransform, self).__init__()
+        self.visitmethods = {'process_' : {}, 'pre_' : {}, 'post_' : {}}
+        self.attrname = ""
+     
+    def get_visitfunc(self, prefix, cls):
+        mname = prefix + cls.__name__
+        m = self.visitmethods[prefix].get(mname)
+        if m is None:
+            # Must resolve, try entire hierarchy
+            for cls in inspect.getmro(cls):
+                m = getattr(self, prefix + cls.__name__, None)
+                if m is not None:
+                    break
+            if m is None: raise RuntimeError("Not a Node descendant: " + node)
+            self.visitmethods[prefix][mname] = m
+        return m
+
+    def process_node(self, node, name):
+        # Pass on to calls registered in self.visitmethods
+        self.attrname = name
+        if node is None:
+            return None
+        return self.get_visitfunc("process_", node.__class__)(node)
+    
+    def process_Node(self, node):
+        descend = self.get_visitfunc("pre_", node.__class__)(node)
+        if descend:
+            self.process_children(node)
+            self.get_visitfunc("post_", node.__class__)(node)
+        return node
+
+    def pre_Node(self, node):
+        return True
+
+    def post_Node(self, node):
+        pass
+
+
+# Utils
+def ensure_statlist(node):
+    if not isinstance(node, Nodes.StatListNode):
+        node = Nodes.StatListNode(pos=node.pos, stats=[node])
+    return node
+
 
 class PrintTree(Transform):
     """Prints a representation of the tree to standard output.
@@ -72,8 +130,9 @@ class PrintTree(Transform):
     def unindent(self):
         self._indent = self._indent[:-2]
 
-    def initialize(self, phase, **options):
+    def __call__(self, tree, phase, **params):
         print("Parse tree dump at phase '%s'" % phase)
+        super(PrintTree, self).__call__(tree, phase=phase, **params)
 
     # Don't do anything about process_list, the defaults gives
     # nice-looking name[idx] nodes which will visually appear
@@ -92,15 +151,20 @@ class PrintTree(Transform):
             return "(none)"
         else:
             result = node.__class__.__name__
-            if isinstance(node, ExprNodes.ExprNode):
+            if isinstance(node, ExprNodes.NameNode):
+                result += "(type=%s, name=\"%s\")" % (repr(node.type), node.name)
+            elif isinstance(node, Nodes.DefNode):
+                result += "(name=\"%s\")" % node.name
+            elif isinstance(node, ExprNodes.ExprNode):
                 t = node.type
                 result += "(type=%s)" % repr(t)
+                
             return result
 
 
 PHASES = [
-    'before_analyse_function', # run in FuncDefNode.generate_function_definitions
-    'after_analyse_function'   # run in FuncDefNode.generate_function_definitions
+    'after_parse'              # run in ModuleNode.process_implementation
+    'after_function_analysis'  # run in ModuleNode.generate_c_code
 ]
 
 class TransformSet(dict):
@@ -110,7 +174,6 @@ class TransformSet(dict):
     def run(self, name, node, **options):
         assert name in self
         for transform in self[name]:
-            transform.initialize(phase=name, **options)
-            transform.process_node(node, "(root)")
+            transform(node, phase=name, **options)
 
 
diff -r 3c924a0594ba Cython/Compiler/Transforms/Analysis.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Cython/Compiler/Transforms/Analysis.py	Fri May 02 11:42:17 2008 +0200
@@ -0,0 +1,51 @@
+from Cython.Compiler.Transform import VisitorTransform
+from Cython.Compiler.Symtab import ModuleScope, LocalScope, \
+    StructOrUnionScope, PyClassScope, CClassScope
+
+from Cython.Compiler.Nodes import *
+
+class CreateFunctionScope(VisitorTransform):
+    def initialize(self, phase, env):
+        self.env = env
+    
+    def pre_ModuleNode(self, node):
+        self.current_scopenode = node
+        node.scope = self.env
+        return True
+
+    def pre_FuncDefNode(self, node):
+        genv = self.env.global_scope()
+        lenv = LocalScope(name = node.entry.name, outer_scope = genv)
+        lenv.return_type = node.return_type
+        node.declare_arguments(lenv)
+        node.scope = lenv
+        node.scopenode = self.current_scopenode
+        self.current_scopenode = node
+        return True
+     
+    def post_FuncDefNode(self, node):
+        self.current_scopenode = node.scopenode
+
+    def pre_Node(self, node):
+        node.scopenode = self.current_scopenode
+        return True
+
+# Calls analyse_declarations within function body level
+class AnalyseControlFlow(VisitorTransform):
+    def pre_FuncDefNode(self, node):
+        node.body.analyse_control_flow(node.scope)
+        return False # do not recurse beyond first function level
+
+# Calls analyse_declarations within function body level
+class AnalyseFunctionBodyDeclarations(VisitorTransform):
+    def pre_FuncDefNode(self, node):
+        node.body.analyse_declarations(node.scope)
+        return False # do not recurse beyond first function level
+
+# Calls analyse_expressions within function body level
+class AnalyseFunctionBodyExpressions(VisitorTransform):
+    def pre_FuncDefNode(self, node):
+        node.body.analyse_expressions(node.scope)
+        return False # do not recurse beyond first function level
+
+
diff -r 3c924a0594ba Cython/Compiler/Transforms/__init__.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Cython/Compiler/Transforms/__init__.py	Fri May 02 11:42:17 2008 +0200
@@ -0,0 +1,1 @@
+#empty
