blob: 3ab72f3129892bf866f129a5e61b1e3432306853 [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)
Jeremy Hyltondbdb28e2001-04-09 20:11:59 +000064 d.update(self.globals)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000065 return d.keys()
66
Jeremy Hyltonf870c952001-04-09 13:57:32 +000067 def add_child(self, child):
68 self.children.append(child)
69
70 def get_children(self):
71 return self.children
72
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000073class ModuleScope(Scope):
74 __super_init = Scope.__init__
75
76 def __init__(self):
Jeremy Hyltonf870c952001-04-09 13:57:32 +000077 self.__super_init("global", self)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000078
79class LambdaScope(Scope):
80 __super_init = Scope.__init__
81
82 __counter = 1
83
Jeremy Hyltonf870c952001-04-09 13:57:32 +000084 def __init__(self, module, klass=None):
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000085 i = self.__counter
86 self.__counter += 1
Jeremy Hyltonf870c952001-04-09 13:57:32 +000087 self.__super_init("lambda.%d" % i, module, klass)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000088
89class FunctionScope(Scope):
90 pass
91
92class ClassScope(Scope):
Jeremy Hyltonf870c952001-04-09 13:57:32 +000093 __super_init = Scope.__init__
94
95 def __init__(self, name, module):
96 self.__super_init(name, module, name)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +000097
98class SymbolVisitor:
99 def __init__(self):
100 self.scopes = {}
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000101 self.klass = None
102
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000103 # node that define new scopes
104
105 def visitModule(self, node):
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000106 scope = self.module = self.scopes[node] = ModuleScope()
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000107 self.visit(node.node, scope)
108
109 def visitFunction(self, node, parent):
110 parent.add_def(node.name)
111 for n in node.defaults:
112 self.visit(n, parent)
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000113 scope = FunctionScope(node.name, self.module, self.klass)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000114 self.scopes[node] = scope
Jeremy Hyltondbdb28e2001-04-09 20:11:59 +0000115 self._do_args(scope, node.argnames)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000116 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
Jeremy Hyltondbdb28e2001-04-09 20:11:59 +0000123 self._do_args(scope, node.argnames)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000124 self.visit(node.code, scope)
125
Jeremy Hyltondbdb28e2001-04-09 20:11:59 +0000126 def _do_args(self, scope, args):
127 for name in args:
128 if type(name) == types.TupleType:
129 self._do_args(scope, name)
130 else:
131 scope.add_param(name)
132
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000133 def visitClass(self, node, parent):
134 parent.add_def(node.name)
135 for n in node.bases:
136 self.visit(n, parent)
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000137 scope = ClassScope(node.name, self.module)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000138 self.scopes[node] = scope
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000139 prev = self.klass
140 self.klass = node.name
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000141 self.visit(node.code, scope)
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000142 self.klass = prev
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000143
144 # name can be a def or a use
145
146 # XXX a few calls and nodes expect a third "assign" arg that is
147 # true if the name is being used as an assignment. only
148 # expressions contained within statements may have the assign arg.
149
150 def visitName(self, node, scope, assign=0):
151 if assign:
152 scope.add_def(node.name)
153 else:
154 scope.add_use(node.name)
155
156 # operations that bind new names
157
158 def visitFor(self, node, scope):
159 self.visit(node.assign, scope, 1)
160 self.visit(node.list, scope)
161 self.visit(node.body, scope)
162 if node.else_:
163 self.visit(node.else_, scope)
164
165 def visitFrom(self, node, scope):
166 for name, asname in node.names:
167 if name == "*":
168 continue
169 scope.add_def(asname or name)
170
171 def visitImport(self, node, scope):
172 for name, asname in node.names:
173 i = name.find(".")
174 if i > -1:
175 name = name[:i]
176 scope.add_def(asname or name)
177
178 def visitAssName(self, node, scope, assign=1):
179 scope.add_def(node.name)
180
181 def visitAugAssign(self, node, scope):
182 # basically, the node is referenced and defined by the same expr
183 self.visit(node.node, scope)
184 self.visit(node.node, scope, 1)
185 self.visit(node.expr, scope)
186
187 def visitAssign(self, node, scope):
188 for n in node.nodes:
189 self.visit(n, scope, 1)
190 self.visit(node.expr, scope)
191
192 def visitGlobal(self, node, scope):
193 for name in node.names:
194 scope.add_global(name)
195
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000196 # prune if statements if tests are false
197
198 _const_types = types.StringType, types.IntType, types.FloatType
199
200 def visitIf(self, node, scope):
201 for test, body in node.tests:
202 if isinstance(test, ast.Const):
203 if type(test.value) in self._const_types:
204 if not test.value:
205 continue
206 self.visit(test, scope)
207 self.visit(body, scope)
208 if node.else_:
209 self.visit(node.else_, scope)
210
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000211def sort(l):
212 l = l[:]
213 l.sort()
214 return l
215
216def list_eq(l1, l2):
217 return sort(l1) == sort(l2)
218
219if __name__ == "__main__":
220 import sys
221 from compiler import parseFile, walk
222 import symtable
223
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000224 def get_names(syms):
225 return [s for s in [s.get_name() for s in syms.get_symbols()]
Jeremy Hyltondbdb28e2001-04-09 20:11:59 +0000226 if not (s.startswith('_[') or s.startswith('.'))]
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000227
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000228 for file in sys.argv[1:]:
229 print file
230 f = open(file)
231 buf = f.read()
232 f.close()
233 syms = symtable.symtable(buf, file, "exec")
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000234 mod_names = get_names(syms)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000235 tree = parseFile(file)
236 s = SymbolVisitor()
237 walk(tree, s)
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000238
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000239 # compare module-level symbols
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000240 names2 = s.scopes[tree].get_names()
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000241
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000242 if not list_eq(mod_names, names2):
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000243 print
Jeremy Hylton8b966dc2001-04-09 04:35:35 +0000244 print "oops", file
245 print sort(mod_names)
246 print sort(names2)
247 sys.exit(-1)
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000248
249 d = {}
250 d.update(s.scopes)
251 del d[tree]
252 scopes = d.values()
253 del d
254
255 for s in syms.get_symbols():
256 if s.is_namespace():
257 l = [sc for sc in scopes
258 if sc.name == s.get_name()]
259 if len(l) > 1:
260 print "skipping", s.get_name()
261 else:
262 if not list_eq(get_names(s.get_namespace()),
263 l[0].get_names()):
264 print s.get_name()
Jeremy Hyltondbdb28e2001-04-09 20:11:59 +0000265 print sort(get_names(s.get_namespace()))
266 print sort(l[0].get_names())
Jeremy Hyltonf870c952001-04-09 13:57:32 +0000267 sys.exit(-1)