logo

oasis-root

Compiled tree of Oasis Linux based on own branch at <https://hacktivis.me/git/oasis/> git clone https://anongit.hacktivis.me/git/oasis-root.git

symtable.py (10198B)


  1. """Interface to the compiler's internal symbol tables"""
  2. import _symtable
  3. from _symtable import (USE, DEF_GLOBAL, DEF_NONLOCAL, DEF_LOCAL, DEF_PARAM,
  4. DEF_IMPORT, DEF_BOUND, DEF_ANNOT, SCOPE_OFF, SCOPE_MASK, FREE,
  5. LOCAL, GLOBAL_IMPLICIT, GLOBAL_EXPLICIT, CELL)
  6. import weakref
  7. __all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"]
  8. def symtable(code, filename, compile_type):
  9. """ Return the toplevel *SymbolTable* for the source code.
  10. *filename* is the name of the file with the code
  11. and *compile_type* is the *compile()* mode argument.
  12. """
  13. top = _symtable.symtable(code, filename, compile_type)
  14. return _newSymbolTable(top, filename)
  15. class SymbolTableFactory:
  16. def __init__(self):
  17. self.__memo = weakref.WeakValueDictionary()
  18. def new(self, table, filename):
  19. if table.type == _symtable.TYPE_FUNCTION:
  20. return Function(table, filename)
  21. if table.type == _symtable.TYPE_CLASS:
  22. return Class(table, filename)
  23. return SymbolTable(table, filename)
  24. def __call__(self, table, filename):
  25. key = table, filename
  26. obj = self.__memo.get(key, None)
  27. if obj is None:
  28. obj = self.__memo[key] = self.new(table, filename)
  29. return obj
  30. _newSymbolTable = SymbolTableFactory()
  31. class SymbolTable:
  32. def __init__(self, raw_table, filename):
  33. self._table = raw_table
  34. self._filename = filename
  35. self._symbols = {}
  36. def __repr__(self):
  37. if self.__class__ == SymbolTable:
  38. kind = ""
  39. else:
  40. kind = "%s " % self.__class__.__name__
  41. if self._table.name == "top":
  42. return "<{0}SymbolTable for module {1}>".format(kind, self._filename)
  43. else:
  44. return "<{0}SymbolTable for {1} in {2}>".format(kind,
  45. self._table.name,
  46. self._filename)
  47. def get_type(self):
  48. """Return the type of the symbol table.
  49. The values retuned are 'class', 'module' and
  50. 'function'.
  51. """
  52. if self._table.type == _symtable.TYPE_MODULE:
  53. return "module"
  54. if self._table.type == _symtable.TYPE_FUNCTION:
  55. return "function"
  56. if self._table.type == _symtable.TYPE_CLASS:
  57. return "class"
  58. assert self._table.type in (1, 2, 3), \
  59. "unexpected type: {0}".format(self._table.type)
  60. def get_id(self):
  61. """Return an identifier for the table.
  62. """
  63. return self._table.id
  64. def get_name(self):
  65. """Return the table's name.
  66. This corresponds to the name of the class, function
  67. or 'top' if the table is for a class, function or
  68. global respectively.
  69. """
  70. return self._table.name
  71. def get_lineno(self):
  72. """Return the number of the first line in the
  73. block for the table.
  74. """
  75. return self._table.lineno
  76. def is_optimized(self):
  77. """Return *True* if the locals in the table
  78. are optimizable.
  79. """
  80. return bool(self._table.type == _symtable.TYPE_FUNCTION)
  81. def is_nested(self):
  82. """Return *True* if the block is a nested class
  83. or function."""
  84. return bool(self._table.nested)
  85. def has_children(self):
  86. """Return *True* if the block has nested namespaces.
  87. """
  88. return bool(self._table.children)
  89. def get_identifiers(self):
  90. """Return a list of names of symbols in the table.
  91. """
  92. return self._table.symbols.keys()
  93. def lookup(self, name):
  94. """Lookup a *name* in the table.
  95. Returns a *Symbol* instance.
  96. """
  97. sym = self._symbols.get(name)
  98. if sym is None:
  99. flags = self._table.symbols[name]
  100. namespaces = self.__check_children(name)
  101. module_scope = (self._table.name == "top")
  102. sym = self._symbols[name] = Symbol(name, flags, namespaces,
  103. module_scope=module_scope)
  104. return sym
  105. def get_symbols(self):
  106. """Return a list of *Symbol* instances for
  107. names in the table.
  108. """
  109. return [self.lookup(ident) for ident in self.get_identifiers()]
  110. def __check_children(self, name):
  111. return [_newSymbolTable(st, self._filename)
  112. for st in self._table.children
  113. if st.name == name]
  114. def get_children(self):
  115. """Return a list of the nested symbol tables.
  116. """
  117. return [_newSymbolTable(st, self._filename)
  118. for st in self._table.children]
  119. class Function(SymbolTable):
  120. # Default values for instance variables
  121. __params = None
  122. __locals = None
  123. __frees = None
  124. __globals = None
  125. __nonlocals = None
  126. def __idents_matching(self, test_func):
  127. return tuple(ident for ident in self.get_identifiers()
  128. if test_func(self._table.symbols[ident]))
  129. def get_parameters(self):
  130. """Return a tuple of parameters to the function.
  131. """
  132. if self.__params is None:
  133. self.__params = self.__idents_matching(lambda x:x & DEF_PARAM)
  134. return self.__params
  135. def get_locals(self):
  136. """Return a tuple of locals in the function.
  137. """
  138. if self.__locals is None:
  139. locs = (LOCAL, CELL)
  140. test = lambda x: ((x >> SCOPE_OFF) & SCOPE_MASK) in locs
  141. self.__locals = self.__idents_matching(test)
  142. return self.__locals
  143. def get_globals(self):
  144. """Return a tuple of globals in the function.
  145. """
  146. if self.__globals is None:
  147. glob = (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)
  148. test = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) in glob
  149. self.__globals = self.__idents_matching(test)
  150. return self.__globals
  151. def get_nonlocals(self):
  152. """Return a tuple of nonlocals in the function.
  153. """
  154. if self.__nonlocals is None:
  155. self.__nonlocals = self.__idents_matching(lambda x:x & DEF_NONLOCAL)
  156. return self.__nonlocals
  157. def get_frees(self):
  158. """Return a tuple of free variables in the function.
  159. """
  160. if self.__frees is None:
  161. is_free = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) == FREE
  162. self.__frees = self.__idents_matching(is_free)
  163. return self.__frees
  164. class Class(SymbolTable):
  165. __methods = None
  166. def get_methods(self):
  167. """Return a tuple of methods declared in the class.
  168. """
  169. if self.__methods is None:
  170. d = {}
  171. for st in self._table.children:
  172. d[st.name] = 1
  173. self.__methods = tuple(d)
  174. return self.__methods
  175. class Symbol:
  176. def __init__(self, name, flags, namespaces=None, *, module_scope=False):
  177. self.__name = name
  178. self.__flags = flags
  179. self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope()
  180. self.__namespaces = namespaces or ()
  181. self.__module_scope = module_scope
  182. def __repr__(self):
  183. return "<symbol {0!r}>".format(self.__name)
  184. def get_name(self):
  185. """Return a name of a symbol.
  186. """
  187. return self.__name
  188. def is_referenced(self):
  189. """Return *True* if the symbol is used in
  190. its block.
  191. """
  192. return bool(self.__flags & _symtable.USE)
  193. def is_parameter(self):
  194. """Return *True* if the symbol is a parameter.
  195. """
  196. return bool(self.__flags & DEF_PARAM)
  197. def is_global(self):
  198. """Return *True* if the sysmbol is global.
  199. """
  200. return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)
  201. or (self.__module_scope and self.__flags & DEF_BOUND))
  202. def is_nonlocal(self):
  203. """Return *True* if the symbol is nonlocal."""
  204. return bool(self.__flags & DEF_NONLOCAL)
  205. def is_declared_global(self):
  206. """Return *True* if the symbol is declared global
  207. with a global statement."""
  208. return bool(self.__scope == GLOBAL_EXPLICIT)
  209. def is_local(self):
  210. """Return *True* if the symbol is local.
  211. """
  212. return bool(self.__scope in (LOCAL, CELL)
  213. or (self.__module_scope and self.__flags & DEF_BOUND))
  214. def is_annotated(self):
  215. """Return *True* if the symbol is annotated.
  216. """
  217. return bool(self.__flags & DEF_ANNOT)
  218. def is_free(self):
  219. """Return *True* if a referenced symbol is
  220. not assigned to.
  221. """
  222. return bool(self.__scope == FREE)
  223. def is_imported(self):
  224. """Return *True* if the symbol is created from
  225. an import statement.
  226. """
  227. return bool(self.__flags & DEF_IMPORT)
  228. def is_assigned(self):
  229. """Return *True* if a symbol is assigned to."""
  230. return bool(self.__flags & DEF_LOCAL)
  231. def is_namespace(self):
  232. """Returns *True* if name binding introduces new namespace.
  233. If the name is used as the target of a function or class
  234. statement, this will be true.
  235. Note that a single name can be bound to multiple objects. If
  236. is_namespace() is true, the name may also be bound to other
  237. objects, like an int or list, that does not introduce a new
  238. namespace.
  239. """
  240. return bool(self.__namespaces)
  241. def get_namespaces(self):
  242. """Return a list of namespaces bound to this name"""
  243. return self.__namespaces
  244. def get_namespace(self):
  245. """Return the single namespace bound to this name.
  246. Raises ValueError if the name is bound to multiple namespaces.
  247. """
  248. if len(self.__namespaces) != 1:
  249. raise ValueError("name is bound to multiple namespaces")
  250. return self.__namespaces[0]
  251. if __name__ == "__main__":
  252. import os, sys
  253. with open(sys.argv[0]) as f:
  254. src = f.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())