This is to answer the question: How to solve the scoping problem using a simple transform? (This might not be the proper solution, just investigating. I know too little about why the current variable scoping is broken.)
(Btw there's some rationale for doing this: It would closely emulate how the Python parser works when interpreting the module scope -- almost simulate running the module-level code (though with an eye only for the namespace handling). This kind of stuff (emulating the simple Python parser) is imnsho much easier to do with a visitor than in a recursive process.)
What one does: Transform every variable assignment, and reference, but the last one, to a temporary. I.e:
1 import math
2
3 f = 3
4 f = 34 + math.sin(f)
5
6 class f:
7 x = f
8 x = 2*f
9
10 x = f()
11
12 def f(y):
13 return x(y)
14
15 def x(z):
16 return 4
By a single pass (keeping track of names assigned to in AssignmentNodes and references to them in NameNode in dicts and lists) one can without much pain (= ~50 lines of "interesting but not really challenging code") transform this to (where each tmpx variable is a TempNode that doesn't name-clash with variables named tmpx, of course. Also the tmpx variables does not enter the module dict):
1 tmp1 = 3
2 tmp2 = 34 + math.sin(tmp1)
3
4 class tmp3:
5 tmp4 = tmp2
6 x = tmp4
7
8 tmp5 = tmp3()
9
10 def f(y):
11 return x(y) # Python semantics is to keep x here now, and not tmp4 which is x at definition time? Transform can work either way.
12
13 def tmp6(z):
14 return 4
This will in turn cause static linking in the correct way everywhere.
In addition, some fudging of the function names stored etc. is needed (add an "should_appear_as" attribute to TempNode and use it in code generation of a function), but that appears to be it?
The same transform could easily raise an error when a global name is used before it is defined too.
