1 #!/usr/bin/env python 2 3 """ 4 Simplified AST nodes for easier type propagation and analysis. 5 6 Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk> 7 8 This software is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License as 10 published by the Free Software Foundation; either version 2 of 11 the License, or (at your option) any later version. 12 13 This software is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public 19 License along with this library; see the file LICENCE.txt 20 If not, write to the Free Software Foundation, Inc., 21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 22 """ 23 24 from compiler.visitor import ASTVisitor 25 import sys 26 27 # Unique name registration. 28 29 class Naming: 30 31 "Maintain records of unique names for each simple name." 32 33 def __init__(self): 34 self.obj_to_name = {} 35 self.names = {} 36 37 def get(self, obj): 38 return self.obj_to_name[obj] 39 40 def set(self, obj, name): 41 if self.obj_to_name.has_key(obj): 42 return 43 if not self.names.has_key(name): 44 self.names[name] = 0 45 n = self.names[name] + 1 46 self.names[name] = n 47 self.obj_to_name[obj] = "%s#%d" % (name, n) 48 49 naming = Naming() 50 51 def name(obj, name): 52 naming.set(obj, name) 53 return naming.get(obj) 54 55 # Elementary visitor support. 56 57 class Visitor(ASTVisitor): 58 59 "A visitor base class." 60 61 def __init__(self): 62 ASTVisitor.__init__(self) 63 64 def default(self, node, *args): 65 raise ValueError, node.__class__ 66 67 def dispatch(self, node, *args): 68 return ASTVisitor.dispatch(self, node, *args) 69 70 def dispatches(self, nodes, *args): 71 results = [] 72 for node in nodes: 73 results.append(self.dispatch(node, *args)) 74 return results 75 76 # Simplified program nodes. 77 78 class Node: 79 80 """ 81 A result node with common attributes: 82 83 original The original node from which this node was created. 84 name Any name involved (variable or attribute). 85 index Any index involved (temporary variable name). 86 value Any constant value. 87 ref Any reference to (for example) subprograms. 88 nstype Any indication of the namespace type involved in a name access. 89 90 Expression-related attributes: 91 92 expr Any contributing expression. 93 lvalue Any target expression. 94 test Any test expression in a conditional instruction. 95 96 Invocation and subprogram attributes: 97 98 args Any collection of argument nodes. 99 params Any collection of parameter nodes and defaults. 100 101 Statement-grouping attributes: 102 103 body Any conditional code depending on the success of a test. 104 else_ Any conditional code depending on the failure of a test. 105 handler Any exception handler code. 106 finally_ Any code which will be executed regardless. 107 code Any unconditional code. 108 choices Any choices which may be included in the final program. 109 """ 110 111 def __init__(self, original=None, **kw): 112 113 """ 114 Initialise a program node with an optional link to an 'original' AST 115 node. 116 """ 117 118 self.original = original 119 if self.original is not None: 120 self.original._node = self 121 for name, value in kw.items(): 122 setattr(self, name, value) 123 124 def __repr__(self): 125 if hasattr(self, "full_name"): 126 s = "%s '%s'" % (self.__class__.__name__, self.full_name) 127 elif hasattr(self, "name"): 128 s = "%s '%s'" % (self.__class__.__name__, self.name) 129 elif hasattr(self, "index"): 130 s = "%s (%s)" % (self.__class__.__name__, self.index) 131 elif hasattr(self, "value"): 132 s = "%s %s" % (self.__class__.__name__, repr(self.value)) 133 elif hasattr(self, "ref"): 134 s = "%s '%s'" % (self.__class__.__name__, name(self.ref, self.ref.name)) 135 else: 136 s = "%s" % (self.__class__.__name__,) 137 138 # Annotations. 139 140 if hasattr(self, "types"): 141 return "%s -> %s" % (s, self.types) 142 else: 143 return s 144 145 def _pprint(self, indent, continuation, s, stream=None): 146 stream = stream or sys.stdout 147 if continuation: 148 print >>stream, (" " * max(0, indent - len(continuation))) + continuation + s 149 else: 150 print >>stream, (" " * indent) + s 151 152 def pprint(self, indent=0, continuation=None, stream=None): 153 stream = stream or sys.stdout 154 self._pprint(indent, continuation, repr(self), stream) 155 156 # Subprogram-related details. 157 158 if hasattr(self, "params"): 159 for name, default in self.params: 160 self._pprint(indent + 2, "( ", "%s -> %s" % (name, default), stream=stream) 161 if hasattr(self, "star") and self.star: 162 name, default = self.star 163 self._pprint(indent + 2, "( ", "%s -> %s" % (name, default), stream=stream) 164 if hasattr(self, "dstar") and self.dstar: 165 name, default = self.dstar 166 self._pprint(indent + 2, "( ", "%s -> %s" % (name, default), stream=stream) 167 if getattr(self, "acquire_locals", 0): 168 self._pprint(indent + 2, "( ", "acquiring locals", stream=stream) 169 if getattr(self, "structure", 0): 170 self._pprint(indent + 2, "( ", "structure '%s'" % self.structure.name, stream=stream) 171 172 # Statement-related details. 173 174 if hasattr(self, "test"): 175 self.test.pprint(indent + 2, "? ", stream=stream) 176 for attr in "code", "body", "else_", "handler", "finally_", "choices": 177 if hasattr(self, attr) and getattr(self, attr): 178 self._pprint(indent, "", "%s {" % attr, stream=stream) 179 for node in getattr(self, attr): 180 node.pprint(indent + 2, stream=stream) 181 self._pprint(indent, "", "}", stream=stream) 182 183 # Expression-related details. 184 185 if hasattr(self, "expr"): 186 self.expr.pprint(indent + 2, "- ", stream=stream) 187 if hasattr(self, "nodes"): 188 for node in self.nodes: 189 node.pprint(indent + 2, "- ", stream=stream) 190 if hasattr(self, "lvalue"): 191 self.lvalue.pprint(indent + 2, "->", stream=stream) 192 if hasattr(self, "nstype"): 193 self._pprint(indent + 2, "", self.nstype, stream=stream) 194 if hasattr(self, "args"): 195 for arg in self.args: 196 arg.pprint(indent + 2, "( ", stream=stream) 197 if hasattr(self, "star") and self.star: 198 self.star.pprint(indent + 2, "( ", stream=stream) 199 if hasattr(self, "dstar") and self.dstar: 200 self.dstar.pprint(indent + 2, "( ", stream=stream) 201 202 # Annotations. 203 204 if hasattr(self, "accesses"): 205 self._pprint(indent, "", "--------", stream=stream) 206 for ref, attributes in self.accesses.items(): 207 self._pprint(indent + 2, "| ", "when %s: %s" % (ref, ", ".join([("%s via %s" % attr_acc) for attr_acc in attributes])), stream=stream) 208 self._pprint(indent, "", "--------", stream=stream) 209 if hasattr(self, "writes"): 210 self._pprint(indent, "", "--------", stream=stream) 211 for ref, attribute in self.writes.items(): 212 self._pprint(indent + 2, "| ", "when %s: %s" % (ref, attribute), stream=stream) 213 self._pprint(indent, "", "--------", stream=stream) 214 215 class Pass(Node): "A placeholder node corresponding to pass." 216 class Return(Node): "Return an evaluated expression." 217 class Assign(Node): "A grouping node for assignment-related operations." 218 class Keyword(Node): "A grouping node for keyword arguments." 219 class Global(Node): "A global name designator." 220 class Import(Node): "A module import operation." 221 class LoadTemp(Node): "Load a previously-stored temporary value." 222 class LoadName(Node): "Load a named object." 223 class LoadAttr(Node): "Load an object attribute." 224 class LoadRef(Node): "Load a reference, typically a subprogram or a constant." 225 class LoadExc(Node): "Load a handled exception." 226 class StoreTemp(Node): "Store a temporary value." 227 class StoreName(Node): "Associate a name with an object." 228 class StoreAttr(Node): "Associate an object's attribute with a value." 229 class ReleaseTemp(Node): "Release a temporary value." 230 class Conditional(Node): "A conditional node consisting of a test and outcomes." 231 class Try(Node): "A try...except...else...finally grouping node." 232 class Raise(Node): "An exception raising node." 233 class Not(Node): "A negation of an expression." 234 class Choice(Node): "A special node which indicates a choice of expressions." 235 236 # Invocations involve some more work to process calculated attributes. 237 238 class Invoke(Node): "An invocation." 239 240 class InvokeFunction(Invoke): 241 242 "A function or method invocation." 243 244 def __init__(self, *args, **kw): 245 Node.__init__(self, *args, **kw) 246 if hasattr(self, "args"): 247 self.set_args(self.args) 248 249 def set_args(self, args): 250 251 "Sort the 'args' into positional and keyword arguments." 252 253 self.pos_args = [] 254 self.kw_args = [] 255 add_kw = 0 256 for arg in args: 257 if not add_kw: 258 if not isinstance(arg, Keyword): 259 self.pos_args.append(arg) 260 else: 261 add_kw = 1 262 if add_kw: 263 if isinstance(arg, Keyword): 264 self.kw_args.append(arg) 265 else: 266 raise TypeError, "Positional argument appears after keyword arguments in '%s'." % self 267 268 class InvokeBlock(Invoke): "A block or loop invocation." 269 270 # Named nodes are those which can be referenced in some way. 271 272 class WithName: 273 274 "Node naming." 275 276 def __init__(self): 277 self.full_name = name(self, self.name or "$untitled") 278 279 class Module(Node, WithName): 280 281 "A Python module." 282 283 def __init__(self, *args, **kw): 284 Node.__init__(self, *args, **kw) 285 WithName.__init__(self) 286 287 class Subprogram(Node, WithName): 288 289 "A subprogram: functions, methods and loops." 290 291 def __init__(self, *args, **kw): 292 Node.__init__(self, *args, **kw) 293 WithName.__init__(self) 294 295 # Special non-program nodes. 296 297 class Structure(Node): "A non-program node containing some kind of namespace." 298 299 class Class(Structure, WithName): 300 301 "A Python class." 302 303 def __init__(self, *args, **kw): 304 Structure.__init__(self, *args, **kw) 305 WithName.__init__(self) 306 307 class Instance(Structure): 308 309 "An instance." 310 311 def __repr__(self): 312 return "Instance of type '%s'" % self.namespace.load("__class__")[0].type.full_name 313 314 class Constant(Instance): 315 316 "A constant initialised with a type name for future processing." 317 318 def __init__(self, *args, **kw): 319 Instance.__init__(self, *args, **kw) 320 self.typename = self.value.__class__.__name__ 321 322 # vim: tabstop=4 expandtab shiftwidth=4