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