blob: 3133633affce4512520f5c7105756b373697fa60 [file] [log] [blame]
Jeremy Hylton816e1492001-03-22 23:32:22 +00001"""Interface to the compiler's internal symbol tables"""
2from __future__ import nested_scopes
3
4import _symtable
5from _symtable import USE, DEF_GLOBAL, DEF_LOCAL, DEF_PARAM, \
6 DEF_STAR, DEF_DOUBLESTAR, DEF_INTUPLE, DEF_FREE, \
7 DEF_FREE_GLOBAL, DEF_FREE_CLASS, DEF_IMPORT, DEF_BOUND
8
9import weakref
10
11__all__ = ["symtable", "SymbolTable", "newSymbolTable"]
12
13def symtable(code, filename, compile_type):
14 raw = _symtable.symtable(code, filename, compile_type)
15 return newSymbolTable(raw[0], filename)
16
17class SymbolTableFactory:
18 def __init__(self):
19 self.__memo = weakref.WeakValueDictionary()
20
21 def new(self, table, filename):
22 if table.type == _symtable.TYPE_FUNCTION:
23 return Function(table, filename)
24 if table.type == _symtable.TYPE_CLASS:
25 return Class(table, filename)
26 return SymbolTable(table, filename)
27
28 def __call__(self, table, filename):
29 key = table, filename
30 obj = self.__memo.get(key, None)
31 if obj is None:
32 obj = self.__memo[key] = self.new(table, filename)
33 return obj
34
35newSymbolTable = SymbolTableFactory()
36
37def bool(x):
38 """Helper to force boolean result to 1 or 0"""
39 if x:
40 return 1
41 return 0
42
43def is_free(flags):
44 if (flags & (USE | DEF_FREE)) \
45 and (flags & (DEF_LOCAL | DEF_PARAM | DEF_GLOBAL)):
46 return 1
47 if flags & DEF_FREE_CLASS:
48 return 1
49 return 0
50
51class SymbolTable:
52 def __init__(self, raw_table, filename):
53 self._table = raw_table
54 self._filename = filename
55 self._symbols = {}
56
57 def __repr__(self):
58 if self.__class__ == SymbolTable:
59 kind = ""
60 else:
61 kind = "%s " % self.__class__.__name__
62
63 if self._table.name == "global":
64 return "<%sSymbolTable for module %s>" % (kind, self._filename)
65 else:
66 return "<%sSymbolTable for %s in %s>" % (kind, self._table.name,
67 self._filename)
68
69 def get_type(self):
70 if self._table.type == _symtable.TYPE_MODULE:
71 return "module"
72 if self._table.type == _symtable.TYPE_FUNCTION:
73 return "function"
74 if self._table.type == _symtable.TYPE_CLASS:
75 return "class"
76 assert self._table.type in (1, 2, 3), \
77 "unexpected type: %s" % self._table.type
78
79 def get_id(self):
80 return self._table.id
81
82 def get_name(self):
83 return self._table.name
84
85 def get_lineno(self):
86 return self._table.lineno
87
88 def is_optimized(self):
89 return bool(self._table.type == _symtable.TYPE_FUNCTION
90 and not self._table.optimized)
91
92 def is_nested(self):
93 return bool(self._table.nested)
94
95 def has_children(self):
96 return bool(self._table.children)
97
98 def has_exec(self):
99 return bool()
100
101 def get_identifiers(self):
102 return self._table.symbols.keys()
103
104 def lookup(self, name):
105 sym = self._symbols.get(name)
106 if sym is None:
107 flags = self._table.symbols[name]
108 namespaces = self.__check_children(name)
109 sym = self._symbols[name] = Symbol(name, flags, namespaces)
110 return sym
111
112 def get_symbols(self):
113 return [self.lookup(ident) for ident in self.get_identifiers()]
114
115 def __check_children(self, name):
116 return [newSymbolTable(st, self._filename)
117 for st in self._table.children
118 if st.name == name]
119
120class Function(SymbolTable):
121
122 # Default values for instance variables
123 __params = None
124 __locals = None
125 __frees = None
126 __globals = None
127
128 def __idents_matching(self, test_func):
129 return tuple([ident for ident in self.get_identifiers()
130 if test_func(self._table.symbols[ident])])
131
132 def get_parameters(self):
133 if self.__params is None:
134 self.__params = self.__idents_matching(lambda x:x & DEF_PARAM)
135 return self.__params
136
137 def get_locals(self):
138 if self.__locals is None:
139 self.__locals = self.__idents_matching(lambda x:x & DEF_BOUND)
140 return self.__locals
141
142 def get_globals(self):
143 if self.__globals is None:
144 glob = DEF_GLOBAL | DEF_FREE_GLOBAL
145 self.__globals = self.__idents_matching(lambda x:x & glob)
146 return self.__globals
147
148 def get_frees(self):
149 if self.__frees is None:
150 self.__frees = self.__idents_matching(is_free)
151 return self.__frees
152
153class Class(SymbolTable):
154
155 __methods = None
156
157 def get_methods(self):
158 if self.__methods is None:
159 d = {}
160 for st in self._table.children:
161 d[st.name] = 1
162 self.__methods = tuple(d.keys())
163 return self.__methods
164
165class Symbol:
166 def __init__(self, name, flags, namespaces=None):
167 self.__name = name
168 self.__flags = flags
169 self.__namespaces = namespaces or ()
170
171 def __repr__(self):
172 return "<symbol '%s'>" % self.__name
173
174 def get_name(self):
175 return self.__name
176
177 def is_referenced(self):
178 return bool(self.__flags & _symtable.USE)
179
180 def is_parameter(self):
181 return bool(self.__flags & DEF_PARAM)
182
183 def is_global(self):
184 return bool((self.__flags & DEF_GLOBAL)
185 or (self.__flags & DEF_FREE_GLOBAL))
186
187 def is_vararg(self):
188 return bool(self.flag & DEF_STAR)
189
190 def is_keywordarg(self):
191 return bool(self.__flags & DEF_STARSTAR)
192
193 def is_local(self):
194 return bool(self.__flags & DEF_BOUND)
195
196 def is_free(self):
197 if (self.__flags & (USE | DEF_FREE)) \
198 and (self.__flags & (DEF_LOCAL | DEF_PARAM | DEF_GLOBAL)):
199 return 1
200 if self.__flags & DEF_FREE_CLASS:
201 return 1
202 return 0
203
204 def is_imported(self):
205 return bool(self.__flags & DEF_IMPORT)
206
207 def is_assigned(self):
208 return bool(self.__flags & DEF_LOCAL)
209
210 def is_in_tuple(self):
211 return bool(self.__flags & DEF_INTUPLE)
212
213 def is_namespace(self):
214 """Returns true if name binding introduces new namespace.
215
216 If the name is used as the target of a function or class
217 statement, this will be true.
218
219 Note that a single name can be bound to multiple objects. If
220 is_namespace() is true, the name may also be bound to other
221 objects, like an int or list, that does not introduce a new
222 namespace.
223 """
224 return bool(self.__namespaces)
225
226 def get_namespaces(self):
227 """Return a list of namespaces bound to this name"""
228 return self.__namespaces
229
230 def get_namespace(self):
231 """Returns the single namespace bound to this name.
232
233 Raises ValueError if the name is bound to multiple namespaces.
234 """
235 if len(self.__namespaces) != 1:
236 raise ValueError, "name is bound to multiple namespaces"
237 return self.__namespaces[0]
238
239if __debug__:
240 class Foo:
241 version = 1
242
243 class Foo:
244 version = 2
245
246 class Foo:
247 version = 3
248
249 def execfunc(x):
250 exec x in y
251
252if __name__ == "__main__":
253 import os, sys
254 src = open(sys.argv[0]).read()
255 mod = symtable(src, os.path.split(sys.argv[0])[1], "exec")
256 for ident in mod.get_identifiers():
257 info = mod.lookup(ident)
258 print info, info.is_local(), info.is_namespace()