[Cython] Some small phase refactorings

Dag Sverre Seljebotn dagss at student.matnat.uio.no
Fri May 2 13:22:35 CEST 2008


This isn't much, but it is a start.

Phase refactoring seems like it will become a bit difficult and I think 
it is better left for dev1 if possible; I need to work with someone who 
know the source more intimately. It appears that some parts must almost 
be "rebuilt" or at least control flow heavily altered between 
statements, it is all about keeping the changes to a minimum... the good 
news is that a certain amount of spaghetti will disappear through the 
process (you know -- the things that break in a different part of the 
code when you change another part in the code; while not necesarrily the 
root of all evil, it does make it harder to get to know a codebase).

But I have patch for some trivial stuff at the "top" of the call chain 
that at least is a good point for discussion (if somebody else cares how 
this is done). It can be found here:

http://wiki.cython.org/DagSverreSeljebotn/patches?action=AttachFile&do=get&target=phaserefactoring1.diff

and succeeds the testcases; I consider it ready for inclusion.

Basically, what it does is turn this (psuedo-code describing overall 
structure, you won't find this anywhere):

for each function in module:
  construct_function_scope
  analyse_control_flow
  analyse_declarations
  analyse_expressions
  generate_code

into

for each function in module: construct_function_scope
for each function in module: analyse_control_flow
for each function in module: analyse_declarations
for each function in module: analyse_expressions
for each function in module: generate_code

However, each of these are in turn run as recursive calls just like 
before. In particular, analyse_expressions is still one phase and not 
easily seperateable (it should be split in three I think: analyse types, 
coercion, allocate temps).

Also, these are all "function-level"; on module-level things happen like 
before, i.e. analyse_declarations in the module level is run at another 
time (and for the nearest future it should remain this way; just 
consider module-level and function-level analysis different phases).

The new code is implemented using visitor transforms. One could argue 
about this, but I do think it leads to pretty neat code. The third 
for-loop in the psuedo-code above is this implemented like this:

Cython/Compiler/Transforms/Analysis.py [is this source structure ok?]:

class AnalyseFunctionBodyDeclarations(VisitorTransform):
    def pre_FuncDefNode(self, node):
        node.body.analyse_declarations(node.scope)
        return False # do not recurse beyond first function level

Cython/Compiler/ModuleNode.py:

class ModuleNode:
    def generate_c_code(self, env, options, result):
        ...
        import Cython.Compiler.Transforms.Analysis as Analysis
        ...
        Analysis.AnalyseFunctionBodyDeclarations()(self, env=env)
        ...
        self.body.generate_function_definitions(env, code, 
options.transforms)
        ...


The only non-trivial part is that the function scope is constructed by a 
tree transform that adds the "scope" and "scopenode" attributes in the 
following way: The FuncDefNodes and ModuleNodes gets their "scope" 
attribute set to the object that was previously passed around everywhere 
as the "env" parameter (this is then read back in order to pass the 
"env" correctly). All nodes also have their "scopenode" attribute set to 
the node that caused creation of the enclosing scope. I.e., rather than 
relying on a passed in env passed recursively from the function/module, 
it is possible for nodes to look up self.scopenode.scope. This will aid 
seperation of phases.

-- 
Dag Sverre



More information about the Cython-dev mailing list