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