| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 1 | """Interface to the compiler's internal symbol tables""" | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 2 |  | 
 | 3 | import _symtable | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 4 | from _symtable import (USE, DEF_GLOBAL, DEF_LOCAL, DEF_PARAM, | 
| Benjamin Peterson | 78565b2 | 2009-06-28 19:19:51 +0000 | [diff] [blame] | 5 |      DEF_IMPORT, DEF_BOUND, OPT_IMPORT_STAR, SCOPE_OFF, SCOPE_MASK, FREE, | 
| Benjamin Peterson | 500c6ef | 2009-06-28 19:30:36 +0000 | [diff] [blame] | 6 |      LOCAL, GLOBAL_IMPLICIT, GLOBAL_EXPLICIT, CELL) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 7 |  | 
 | 8 | import weakref | 
 | 9 |  | 
| Benjamin Peterson | 3938a90 | 2008-08-20 02:33:00 +0000 | [diff] [blame] | 10 | __all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"] | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 11 |  | 
 | 12 | def symtable(code, filename, compile_type): | 
| Benjamin Peterson | 89d8cd9 | 2013-10-26 13:13:51 -0400 | [diff] [blame] | 13 |     top = _symtable.symtable(code, filename, compile_type) | 
| Benjamin Peterson | 3938a90 | 2008-08-20 02:33:00 +0000 | [diff] [blame] | 14 |     return _newSymbolTable(top, filename) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 15 |  | 
 | 16 | class SymbolTableFactory: | 
 | 17 |     def __init__(self): | 
 | 18 |         self.__memo = weakref.WeakValueDictionary() | 
 | 19 |  | 
 | 20 |     def new(self, table, filename): | 
 | 21 |         if table.type == _symtable.TYPE_FUNCTION: | 
 | 22 |             return Function(table, filename) | 
 | 23 |         if table.type == _symtable.TYPE_CLASS: | 
 | 24 |             return Class(table, filename) | 
 | 25 |         return SymbolTable(table, filename) | 
 | 26 |  | 
 | 27 |     def __call__(self, table, filename): | 
 | 28 |         key = table, filename | 
 | 29 |         obj = self.__memo.get(key, None) | 
 | 30 |         if obj is None: | 
 | 31 |             obj = self.__memo[key] = self.new(table, filename) | 
 | 32 |         return obj | 
 | 33 |  | 
| Benjamin Peterson | 3938a90 | 2008-08-20 02:33:00 +0000 | [diff] [blame] | 34 | _newSymbolTable = SymbolTableFactory() | 
| Tim Peters | a19a168 | 2001-03-29 04:36:09 +0000 | [diff] [blame] | 35 |  | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 36 |  | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 37 | class SymbolTable(object): | 
 | 38 |  | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 39 |     def __init__(self, raw_table, filename): | 
 | 40 |         self._table = raw_table | 
 | 41 |         self._filename = filename | 
 | 42 |         self._symbols = {} | 
 | 43 |  | 
 | 44 |     def __repr__(self): | 
 | 45 |         if self.__class__ == SymbolTable: | 
 | 46 |             kind = "" | 
 | 47 |         else: | 
 | 48 |             kind = "%s " % self.__class__.__name__ | 
| Tim Peters | a19a168 | 2001-03-29 04:36:09 +0000 | [diff] [blame] | 49 |  | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 50 |         if self._table.name == "global": | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 51 |             return "<{0}SymbolTable for module {1}>".format(kind, self._filename) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 52 |         else: | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 53 |             return "<{0}SymbolTable for {1} in {2}>".format(kind, | 
 | 54 |                                                             self._table.name, | 
 | 55 |                                                             self._filename) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 56 |  | 
 | 57 |     def get_type(self): | 
 | 58 |         if self._table.type == _symtable.TYPE_MODULE: | 
 | 59 |             return "module" | 
 | 60 |         if self._table.type == _symtable.TYPE_FUNCTION: | 
 | 61 |             return "function" | 
 | 62 |         if self._table.type == _symtable.TYPE_CLASS: | 
 | 63 |             return "class" | 
 | 64 |         assert self._table.type in (1, 2, 3), \ | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 65 |                "unexpected type: {0}".format(self._table.type) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 66 |  | 
 | 67 |     def get_id(self): | 
 | 68 |         return self._table.id | 
 | 69 |  | 
 | 70 |     def get_name(self): | 
 | 71 |         return self._table.name | 
 | 72 |  | 
 | 73 |     def get_lineno(self): | 
 | 74 |         return self._table.lineno | 
 | 75 |  | 
 | 76 |     def is_optimized(self): | 
 | 77 |         return bool(self._table.type == _symtable.TYPE_FUNCTION | 
 | 78 |                     and not self._table.optimized) | 
 | 79 |  | 
 | 80 |     def is_nested(self): | 
 | 81 |         return bool(self._table.nested) | 
 | 82 |  | 
 | 83 |     def has_children(self): | 
 | 84 |         return bool(self._table.children) | 
 | 85 |  | 
 | 86 |     def has_exec(self): | 
| Guido van Rossum | 2def557 | 2006-09-06 22:37:15 +0000 | [diff] [blame] | 87 |         """Return true if the scope uses exec.  Deprecated method.""" | 
 | 88 |         return False | 
| Jeremy Hylton | 5030cf1 | 2001-04-16 18:43:18 +0000 | [diff] [blame] | 89 |  | 
 | 90 |     def has_import_star(self): | 
 | 91 |         """Return true if the scope uses import *""" | 
 | 92 |         return bool(self._table.optimized & OPT_IMPORT_STAR) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 93 |  | 
 | 94 |     def get_identifiers(self): | 
 | 95 |         return self._table.symbols.keys() | 
 | 96 |  | 
 | 97 |     def lookup(self, name): | 
 | 98 |         sym = self._symbols.get(name) | 
 | 99 |         if sym is None: | 
 | 100 |             flags = self._table.symbols[name] | 
 | 101 |             namespaces = self.__check_children(name) | 
 | 102 |             sym = self._symbols[name] = Symbol(name, flags, namespaces) | 
 | 103 |         return sym | 
 | 104 |  | 
 | 105 |     def get_symbols(self): | 
 | 106 |         return [self.lookup(ident) for ident in self.get_identifiers()] | 
 | 107 |  | 
 | 108 |     def __check_children(self, name): | 
| Benjamin Peterson | 3938a90 | 2008-08-20 02:33:00 +0000 | [diff] [blame] | 109 |         return [_newSymbolTable(st, self._filename) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 110 |                 for st in self._table.children | 
 | 111 |                 if st.name == name] | 
 | 112 |  | 
| Jeremy Hylton | 101651c | 2001-03-23 15:41:14 +0000 | [diff] [blame] | 113 |     def get_children(self): | 
| Benjamin Peterson | 3938a90 | 2008-08-20 02:33:00 +0000 | [diff] [blame] | 114 |         return [_newSymbolTable(st, self._filename) | 
| Jeremy Hylton | 101651c | 2001-03-23 15:41:14 +0000 | [diff] [blame] | 115 |                 for st in self._table.children] | 
 | 116 |  | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 117 |  | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 118 | class Function(SymbolTable): | 
 | 119 |  | 
 | 120 |     # Default values for instance variables | 
 | 121 |     __params = None | 
 | 122 |     __locals = None | 
 | 123 |     __frees = None | 
 | 124 |     __globals = None | 
 | 125 |  | 
 | 126 |     def __idents_matching(self, test_func): | 
| Benjamin Peterson | b71caf1 | 2008-08-20 12:55:31 +0000 | [diff] [blame] | 127 |         return tuple([ident for ident in self.get_identifiers() | 
 | 128 |                       if test_func(self._table.symbols[ident])]) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 129 |  | 
 | 130 |     def get_parameters(self): | 
 | 131 |         if self.__params is None: | 
 | 132 |             self.__params = self.__idents_matching(lambda x:x & DEF_PARAM) | 
 | 133 |         return self.__params | 
 | 134 |  | 
 | 135 |     def get_locals(self): | 
 | 136 |         if self.__locals is None: | 
| Benjamin Peterson | 500c6ef | 2009-06-28 19:30:36 +0000 | [diff] [blame] | 137 |             locs = (LOCAL, CELL) | 
 | 138 |             test = lambda x: ((x >> SCOPE_OFF) & SCOPE_MASK) in locs | 
| Benjamin Peterson | 78565b2 | 2009-06-28 19:19:51 +0000 | [diff] [blame] | 139 |             self.__locals = self.__idents_matching(test) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 140 |         return self.__locals | 
| Tim Peters | a19a168 | 2001-03-29 04:36:09 +0000 | [diff] [blame] | 141 |  | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 142 |     def get_globals(self): | 
 | 143 |         if self.__globals is None: | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 144 |             glob = (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT) | 
 | 145 |             test = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) in glob | 
 | 146 |             self.__globals = self.__idents_matching(test) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 147 |         return self.__globals | 
 | 148 |  | 
 | 149 |     def get_frees(self): | 
 | 150 |         if self.__frees is None: | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 151 |             is_free = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) == FREE | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 152 |             self.__frees = self.__idents_matching(is_free) | 
 | 153 |         return self.__frees | 
 | 154 |  | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 155 |  | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 156 | class Class(SymbolTable): | 
 | 157 |  | 
 | 158 |     __methods = None | 
 | 159 |  | 
 | 160 |     def get_methods(self): | 
 | 161 |         if self.__methods is None: | 
 | 162 |             d = {} | 
 | 163 |             for st in self._table.children: | 
 | 164 |                 d[st.name] = 1 | 
| Benjamin Peterson | b71caf1 | 2008-08-20 12:55:31 +0000 | [diff] [blame] | 165 |             self.__methods = tuple(d) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 166 |         return self.__methods | 
 | 167 |  | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 168 |  | 
 | 169 | class Symbol(object): | 
 | 170 |  | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 171 |     def __init__(self, name, flags, namespaces=None): | 
 | 172 |         self.__name = name | 
 | 173 |         self.__flags = flags | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 174 |         self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope() | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 175 |         self.__namespaces = namespaces or () | 
 | 176 |  | 
 | 177 |     def __repr__(self): | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 178 |         return "<symbol {0!r}>".format(self.__name) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 179 |  | 
 | 180 |     def get_name(self): | 
 | 181 |         return self.__name | 
 | 182 |  | 
 | 183 |     def is_referenced(self): | 
 | 184 |         return bool(self.__flags & _symtable.USE) | 
 | 185 |  | 
 | 186 |     def is_parameter(self): | 
 | 187 |         return bool(self.__flags & DEF_PARAM) | 
 | 188 |  | 
 | 189 |     def is_global(self): | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 190 |         return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 191 |  | 
| Jeremy Hylton | f37708e | 2009-03-31 15:26:37 +0000 | [diff] [blame] | 192 |     def is_declared_global(self): | 
 | 193 |         return bool(self.__scope == GLOBAL_EXPLICIT) | 
 | 194 |  | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 195 |     def is_local(self): | 
 | 196 |         return bool(self.__flags & DEF_BOUND) | 
 | 197 |  | 
 | 198 |     def is_free(self): | 
| Benjamin Peterson | 55e00f2 | 2008-08-17 18:02:44 +0000 | [diff] [blame] | 199 |         return bool(self.__scope == FREE) | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 200 |  | 
 | 201 |     def is_imported(self): | 
 | 202 |         return bool(self.__flags & DEF_IMPORT) | 
 | 203 |  | 
 | 204 |     def is_assigned(self): | 
 | 205 |         return bool(self.__flags & DEF_LOCAL) | 
 | 206 |  | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 207 |     def is_namespace(self): | 
 | 208 |         """Returns true if name binding introduces new namespace. | 
 | 209 |  | 
 | 210 |         If the name is used as the target of a function or class | 
 | 211 |         statement, this will be true. | 
 | 212 |  | 
 | 213 |         Note that a single name can be bound to multiple objects.  If | 
 | 214 |         is_namespace() is true, the name may also be bound to other | 
 | 215 |         objects, like an int or list, that does not introduce a new | 
 | 216 |         namespace. | 
 | 217 |         """ | 
 | 218 |         return bool(self.__namespaces) | 
 | 219 |  | 
 | 220 |     def get_namespaces(self): | 
 | 221 |         """Return a list of namespaces bound to this name""" | 
 | 222 |         return self.__namespaces | 
 | 223 |  | 
 | 224 |     def get_namespace(self): | 
 | 225 |         """Returns the single namespace bound to this name. | 
 | 226 |  | 
 | 227 |         Raises ValueError if the name is bound to multiple namespaces. | 
 | 228 |         """ | 
 | 229 |         if len(self.__namespaces) != 1: | 
| Collin Winter | ce36ad8 | 2007-08-30 01:19:48 +0000 | [diff] [blame] | 230 |             raise ValueError("name is bound to multiple namespaces") | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 231 |         return self.__namespaces[0] | 
 | 232 |  | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 233 | if __name__ == "__main__": | 
 | 234 |     import os, sys | 
| Giampaolo Rodola' | 2f50aaf | 2013-02-12 02:04:27 +0100 | [diff] [blame] | 235 |     with open(sys.argv[0]) as f: | 
 | 236 |         src = f.read() | 
| Jeremy Hylton | 816e149 | 2001-03-22 23:32:22 +0000 | [diff] [blame] | 237 |     mod = symtable(src, os.path.split(sys.argv[0])[1], "exec") | 
 | 238 |     for ident in mod.get_identifiers(): | 
 | 239 |         info = mod.lookup(ident) | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 240 |         print(info, info.is_local(), info.is_namespace()) |