blob: 872f174a05d61923d7ccda2cb7efcf4ada30454c [file] [log] [blame]
Jeremy Hylton8b966dc2001-04-09 04:35:35 +00001"""Module symbol-table generator"""
2
3from compiler import ast
Jeremy Hyltonf870c952001-04-09 13:57:32 +00004import types
Jeremy Hylton8b966dc2001-04-09 04:35:35 +00005
Jeremy Hyltonf870c952001-04-09 13:57:32 +00006MANGLE_LEN = 256
Jeremy Hylton8b966dc2001-04-09 04:35:35 +00007
8class Scope:
9 # XXX how much information do I need about each name?
Jeremy Hyltonf870c952001-04-09 13:57:32 +000010 def __init__(self, name, module, klass=None):
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000011 self.name = name
Jeremy Hyltonf870c952001-04-09 13:57:32 +000012 self.module = module
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000013 self.defs = {}
14 self.uses = {}
15 self.globals = {}
16 self.params = {}
Jeremy Hyltonf870c952001-04-09 13:57:32 +000017 self.children = []
18 self.klass = None
19 if klass is not None:
20 for i in range(len(klass)):
21 if klass[i] != '_':
22 self.klass = klass[i:]
23 break
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000024
25 def __repr__(self):
26 return "<%s: %s>" % (self.__class__.__name__, self.name)
27
Jeremy Hyltonf870c952001-04-09 13:57:32 +000028 def mangle(self, name):
29 if self.klass is None:
30 return name
31 if not name.startswith('__'):
32 return name
33 if len(name) + 2 >= MANGLE_LEN:
34 return name
35 if name.endswith('__'):
36 return name
37 return "_%s%s" % (self.klass, name)
38
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000039 def add_def(self, name):
Jeremy Hyltonf870c952001-04-09 13:57:32 +000040 self.defs[self.mangle(name)] = 1
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000041
42 def add_use(self, name):
Jeremy Hyltonf870c952001-04-09 13:57:32 +000043 self.uses[self.mangle(name)] = 1
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000044
45 def add_global(self, name):
Jeremy Hyltonf870c952001-04-09 13:57:32 +000046 name = self.mangle(name)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000047 if self.uses.has_key(name) or self.defs.has_key(name):
48 pass # XXX warn about global following def/use
49 if self.params.has_key(name):
50 raise SyntaxError, "%s in %s is global and parameter" % \
51 (name, self.name)
52 self.globals[name] = 1
Jeremy Hyltonf870c952001-04-09 13:57:32 +000053 self.module.add_def(name)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000054
55 def add_param(self, name):
Jeremy Hyltonf870c952001-04-09 13:57:32 +000056 name = self.mangle(name)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000057 self.defs[name] = 1
58 self.params[name] = 1
59
60 def get_names(self):
61 d = {}
62 d.update(self.defs)
63 d.update(self.uses)
64 return d.keys()
65
Jeremy Hyltonf870c952001-04-09 13:57:32 +000066 def add_child(self, child):
67 self.children.append(child)
68
69 def get_children(self):
70 return self.children
71
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000072class ModuleScope(Scope):
73 __super_init = Scope.__init__
74
75 def __init__(self):
Jeremy Hyltonf870c952001-04-09 13:57:32 +000076 self.__super_init("global", self)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000077
78class LambdaScope(Scope):
79 __super_init = Scope.__init__
80
81 __counter = 1
82
Jeremy Hyltonf870c952001-04-09 13:57:32 +000083 def __init__(self, module, klass=None):
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000084 i = self.__counter
85 self.__counter += 1
Jeremy Hyltonf870c952001-04-09 13:57:32 +000086 self.__super_init("lambda.%d" % i, module, klass)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000087
88class FunctionScope(Scope):
89 pass
90
91class ClassScope(Scope):
Jeremy Hyltonf870c952001-04-09 13:57:32 +000092 __super_init = Scope.__init__
93
94 def __init__(self, name, module):
95 self.__super_init(name, module, name)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000096
97class SymbolVisitor:
98 def __init__(self):
99 self.scopes = {}
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000100 self.klass = None
101
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000102 # node that define new scopes
103
104 def visitModule(self, node):
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000105 scope = self.module = self.scopes[node] = ModuleScope()
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000106 self.visit(node.node, scope)
107
108 def visitFunction(self, node, parent):
109 parent.add_def(node.name)
110 for n in node.defaults:
111 self.visit(n, parent)
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000112 scope = FunctionScope(node.name, self.module, self.klass)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000113 self.scopes[node] = scope
114 for name in node.argnames:
115 scope.add_param(name)
116 self.visit(node.code, scope)
117
118 def visitLambda(self, node, parent):
119 for n in node.defaults:
120 self.visit(n, parent)
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000121 scope = LambdaScope(self.module, self.klass)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000122 self.scopes[node] = scope
123 for name in node.argnames:
124 scope.add_param(name)
125 self.visit(node.code, scope)
126
127 def visitClass(self, node, parent):
128 parent.add_def(node.name)
129 for n in node.bases:
130 self.visit(n, parent)
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000131 scope = ClassScope(node.name, self.module)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000132 self.scopes[node] = scope
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000133 prev = self.klass
134 self.klass = node.name
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000135 self.visit(node.code, scope)
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000136 self.klass = prev
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000137
138 # name can be a def or a use
139
140 # XXX a few calls and nodes expect a third "assign" arg that is
141 # true if the name is being used as an assignment. only
142 # expressions contained within statements may have the assign arg.
143
144 def visitName(self, node, scope, assign=0):
145 if assign:
146 scope.add_def(node.name)
147 else:
148 scope.add_use(node.name)
149
150 # operations that bind new names
151
152 def visitFor(self, node, scope):
153 self.visit(node.assign, scope, 1)
154 self.visit(node.list, scope)
155 self.visit(node.body, scope)
156 if node.else_:
157 self.visit(node.else_, scope)
158
159 def visitFrom(self, node, scope):
160 for name, asname in node.names:
161 if name == "*":
162 continue
163 scope.add_def(asname or name)
164
165 def visitImport(self, node, scope):
166 for name, asname in node.names:
167 i = name.find(".")
168 if i > -1:
169 name = name[:i]
170 scope.add_def(asname or name)
171
172 def visitAssName(self, node, scope, assign=1):
173 scope.add_def(node.name)
174
175 def visitAugAssign(self, node, scope):
176 # basically, the node is referenced and defined by the same expr
177 self.visit(node.node, scope)
178 self.visit(node.node, scope, 1)
179 self.visit(node.expr, scope)
180
181 def visitAssign(self, node, scope):
182 for n in node.nodes:
183 self.visit(n, scope, 1)
184 self.visit(node.expr, scope)
185
186 def visitGlobal(self, node, scope):
187 for name in node.names:
188 scope.add_global(name)
189
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000190 # prune if statements if tests are false
191
192 _const_types = types.StringType, types.IntType, types.FloatType
193
194 def visitIf(self, node, scope):
195 for test, body in node.tests:
196 if isinstance(test, ast.Const):
197 if type(test.value) in self._const_types:
198 if not test.value:
199 continue
200 self.visit(test, scope)
201 self.visit(body, scope)
202 if node.else_:
203 self.visit(node.else_, scope)
204
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000205def sort(l):
206 l = l[:]
207 l.sort()
208 return l
209
210def list_eq(l1, l2):
211 return sort(l1) == sort(l2)
212
213if __name__ == "__main__":
214 import sys
215 from compiler import parseFile, walk
216 import symtable
217
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000218 def get_names(syms):
219 return [s for s in [s.get_name() for s in syms.get_symbols()]
220 if not s.startswith('_[')]
221
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000222 for file in sys.argv[1:]:
223 print file
224 f = open(file)
225 buf = f.read()
226 f.close()
227 syms = symtable.symtable(buf, file, "exec")
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000228 mod_names = get_names(syms)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000229 tree = parseFile(file)
230 s = SymbolVisitor()
231 walk(tree, s)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000232
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000233 # compare module-level symbols
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000234 names2 = s.scopes[tree].get_names()
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000235
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000236 if not list_eq(mod_names, names2):
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000237 print
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000238 print "oops", file
239 print sort(mod_names)
240 print sort(names2)
241 sys.exit(-1)
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000242
243 d = {}
244 d.update(s.scopes)
245 del d[tree]
246 scopes = d.values()
247 del d
248
249 for s in syms.get_symbols():
250 if s.is_namespace():
251 l = [sc for sc in scopes
252 if sc.name == s.get_name()]
253 if len(l) > 1:
254 print "skipping", s.get_name()
255 else:
256 if not list_eq(get_names(s.get_namespace()),
257 l[0].get_names()):
258 print s.get_name()
259 print get_names(s.get_namespace())
260 print l[0].get_names()
261 sys.exit(-1)