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 # Exceptions. 28 29 class SimplifiedError(Exception): 30 31 "An error in the annotation process." 32 33 def __init__(self, exc, node, *args): 34 35 """ 36 Initialise the error with an existing exception 'exc', the 'node' at 37 which this error occurs, along with additional optional arguments. 38 """ 39 40 Exception.__init__(self, *args) 41 self.nodes = [node] 42 self.exc = exc 43 44 def add(self, node): 45 46 "Add the given 'node' to the path of nodes leading from the exception." 47 48 self.nodes.append(node) 49 50 def __str__(self): 51 52 "Return a string showing the principal exception details." 53 54 return "%s, %s" % (self.exc, self.nodes) 55 56 # Unique name registration. 57 58 class Naming: 59 60 "Maintain records of unique names for each simple name." 61 62 index_separator = "-" 63 64 def __init__(self): 65 self.obj_to_name = {} 66 self.names = {} 67 68 def get(self, obj): 69 return self.obj_to_name[obj] 70 71 def set(self, obj, name): 72 if self.obj_to_name.has_key(obj): 73 return 74 if not self.names.has_key(name): 75 self.names[name] = 0 76 n = self.names[name] + 1 77 self.names[name] = n 78 self.obj_to_name[obj] = "%s%s%d" % (name, self.index_separator, n) 79 80 naming = Naming() 81 82 def name(obj, name): 83 84 "Return a unique name for the given 'obj', indicating the base 'name'." 85 86 naming.set(obj, name) 87 return naming.get(obj) 88 89 # Elementary visitor support. 90 91 class Visitor(ASTVisitor): 92 93 "A visitor base class." 94 95 def __init__(self): 96 ASTVisitor.__init__(self) 97 98 def default(self, node, *args): 99 raise ValueError, node.__class__ 100 101 def dispatch(self, node, *args): 102 return ASTVisitor.dispatch(self, node, *args) 103 104 def dispatches(self, nodes, *args): 105 results = [] 106 for node in nodes: 107 results.append(self.dispatch(node, *args)) 108 return results 109 110 # Simplified program nodes. 111 112 class Node: 113 114 """ 115 A result node with common attributes: 116 117 original The original node from which this node was created. 118 defining Whether the node defines something in the original program. 119 name Any name involved (variable or attribute). 120 index Any index involved (temporary variable name). 121 value Any constant value. 122 ref Any reference to (for example) subprograms. 123 nstype Any indication of the namespace type involved in a name access. 124 125 Expression-related attributes: 126 127 expr Any contributing expression. 128 lvalue Any target expression. 129 test Any test expression in a conditional instruction. 130 131 Invocation and subprogram attributes: 132 133 args Any collection of argument nodes. 134 params Any collection of parameter nodes and defaults. 135 136 Statement-grouping attributes: 137 138 body Any conditional code depending on the success of a test. 139 else_ Any conditional code depending on the failure of a test. 140 handler Any exception handler code. 141 finally_ Any code which will be executed regardless. 142 code Any unconditional code. 143 choices Any choices which may be included in the final program. 144 """ 145 146 def __init__(self, original=None, defining=0, **kw): 147 148 """ 149 Initialise a program node with a link to an optional 'original' AST 150 node. An optional 'defining' parameter (if set to a true value), sets 151 this node as the defining node in the original. 152 """ 153 154 self.original = original 155 self.defining = defining 156 157 if self.original is not None and defining: 158 self.original._node = self 159 for name, value in kw.items(): 160 setattr(self, name, value) 161 162 def __repr__(self): 163 164 "Return a readable representation." 165 166 if hasattr(self, "full_name"): 167 s = "%s '%s'" % (self.__class__.__name__, self.full_name()) 168 elif hasattr(self, "name"): 169 s = "%s '%s'" % (self.__class__.__name__, self.name) 170 elif hasattr(self, "index"): 171 s = "%s (%s)" % (self.__class__.__name__, self.index) 172 elif hasattr(self, "value"): 173 s = "%s %s" % (self.__class__.__name__, repr(self.value)) 174 elif hasattr(self, "ref"): 175 s = "%s '%s'" % (self.__class__.__name__, name(self.ref, self.ref.name)) 176 else: 177 s = "%s" % (self.__class__.__name__,) 178 179 # Annotations. 180 181 if hasattr(self, "types"): 182 return "%s -> %s" % (s, self.types) 183 else: 184 return s 185 186 def _pprint(self, indent, continuation, s, stream=None): 187 188 """ 189 Print, at the given 'indent' level, with the given 'continuation' text, 190 the string 's', either to the given, optional 'stream' or to standard 191 output, this node's "pretty" representation. 192 """ 193 194 stream = stream or sys.stdout 195 if continuation: 196 print >>stream, (" " * max(0, indent - len(continuation))) + continuation + s 197 else: 198 print >>stream, (" " * indent) + s 199 200 def pprint(self, indent=0, continuation=None, stream=None): 201 202 """ 203 Print, at the given, optional 'indent', with the given optional 204 'continuation' text, either to the given, optional 'stream' or to 205 standard output, this node's "pretty" representation along with its 206 children and their "pretty" representation (and so on). 207 """ 208 209 stream = stream or sys.stdout 210 self._pprint(indent, continuation, repr(self), stream) 211 212 # Subprogram-related details. 213 214 if hasattr(self, "params"): 215 for name, default in self.params: 216 self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream) 217 if hasattr(self, "star") and self.star: 218 name, default = self.star 219 self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream) 220 if hasattr(self, "dstar") and self.dstar: 221 name, default = self.dstar 222 self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream) 223 if getattr(self, "acquire_locals", 0): 224 self._pprint(indent + 2, "( ", "acquiring locals", stream=stream) 225 if getattr(self, "structure", 0): 226 self._pprint(indent + 2, "( ", "structure '%s'" % self.structure.name, stream=stream) 227 228 # Statement-related details. 229 230 if hasattr(self, "test"): 231 self.test.pprint(indent + 2, "? ", stream=stream) 232 for attr in "code", "body", "else_", "handler", "finally_", "choices": 233 if hasattr(self, attr) and getattr(self, attr): 234 self._pprint(indent, "", "%s {" % attr, stream=stream) 235 for node in getattr(self, attr): 236 node.pprint(indent + 2, stream=stream) 237 self._pprint(indent, "", "}", stream=stream) 238 239 # Expression-related details. 240 241 if hasattr(self, "expr"): 242 self.expr.pprint(indent + 2, "- ", stream=stream) 243 if hasattr(self, "nodes"): 244 for node in self.nodes: 245 node.pprint(indent + 2, "- ", stream=stream) 246 if hasattr(self, "lvalue"): 247 self.lvalue.pprint(indent + 2, "->", stream=stream) 248 if hasattr(self, "nstype"): 249 self._pprint(indent + 2, "", self.nstype, stream=stream) 250 if hasattr(self, "args"): 251 for arg in self.args: 252 arg.pprint(indent + 2, "( ", stream=stream) 253 if hasattr(self, "star") and self.star: 254 self.star.pprint(indent + 2, "( ", stream=stream) 255 if hasattr(self, "dstar") and self.dstar: 256 self.dstar.pprint(indent + 2, "( ", stream=stream) 257 258 # Annotations. 259 260 if hasattr(self, "accesses"): 261 self._pprint(indent, "", "--------", stream=stream) 262 for ref, attributes in self.accesses.items(): 263 self._pprint(indent + 2, "| ", "when %s: %s" % (ref, ", ".join([("%s via %s" % attr_acc) for attr_acc in attributes])), stream=stream) 264 self._pprint(indent, "", "--------", stream=stream) 265 if hasattr(self, "writes"): 266 self._pprint(indent, "", "--------", stream=stream) 267 for ref, attribute in self.writes.items(): 268 self._pprint(indent + 2, "| ", "when %s: %s" % (ref, attribute), stream=stream) 269 self._pprint(indent, "", "--------", stream=stream) 270 271 class Pass(Node): "A placeholder node corresponding to pass." 272 class Return(Node): "Return an evaluated expression." 273 class Assign(Node): "A grouping node for assignment-related operations." 274 class Keyword(Node): "A grouping node for keyword arguments." 275 class Global(Node): "A global name designator." 276 class Import(Node): "A module import operation." 277 class LoadTemp(Node): "Load a previously-stored temporary value." 278 class LoadName(Node): "Load a named object." 279 class LoadAttr(Node): "Load an object attribute." 280 class LoadRef(Node): "Load a reference, typically a subprogram or a constant." 281 class LoadExc(Node): "Load a handled exception." 282 class StoreTemp(Node): "Store a temporary value." 283 class StoreName(Node): "Associate a name with an object." 284 class StoreAttr(Node): "Associate an object's attribute with a value." 285 class ReleaseTemp(Node): "Release a temporary value." 286 class Conditional(Node): "A conditional node consisting of a test and outcomes." 287 class Try(Node): "A try...except...else...finally grouping node." 288 class Raise(Node): "An exception raising node." 289 class Not(Node): "A negation of an expression." 290 class Choice(Node): "A special node which indicates a choice of expressions." 291 292 # Invocations involve some more work to process calculated attributes. 293 294 class Invoke(Node): "An invocation." 295 296 class InvokeFunction(Invoke): 297 298 "A function or method invocation." 299 300 def __init__(self, *args, **kw): 301 Node.__init__(self, *args, **kw) 302 if hasattr(self, "args"): 303 self.set_args(self.args) 304 305 def set_args(self, args): 306 307 "Sort the 'args' into positional and keyword arguments." 308 309 self.pos_args = [] 310 self.kw_args = [] 311 add_kw = 0 312 for arg in args: 313 if not add_kw: 314 if not isinstance(arg, Keyword): 315 self.pos_args.append(arg) 316 else: 317 add_kw = 1 318 if add_kw: 319 if isinstance(arg, Keyword): 320 self.kw_args.append(arg) 321 else: 322 raise TypeError, "Positional argument appears after keyword arguments in '%s'." % self 323 324 class InvokeBlock(Invoke): "A block or loop invocation." 325 326 # Named nodes are those which can be referenced in some way. 327 328 class WithName: 329 330 "Node naming." 331 332 def __init__(self): 333 self._full_name = name(self, self.name or "$untitled") 334 335 def full_name(self): 336 return self._full_name 337 338 class Module(Node): 339 340 "A Python module." 341 342 def full_name(self): 343 return self.name 344 345 class Subprogram(Node, WithName): 346 347 "A subprogram: functions, methods and loops." 348 349 def __init__(self, *args, **kw): 350 Node.__init__(self, *args, **kw) 351 WithName.__init__(self) 352 353 # Special non-program nodes. 354 355 class Structure(Node): "A non-program node containing some kind of namespace." 356 357 class Class(Structure, WithName): 358 359 "A Python class." 360 361 def __init__(self, *args, **kw): 362 Structure.__init__(self, *args, **kw) 363 WithName.__init__(self) 364 self.instances = [] 365 366 class Instance(Structure): 367 368 "An instance." 369 370 def full_name(self): 371 # NOTE: Wrap the result in a call to name(self, ...) where multiple 372 # NOTE: instances per class can occur. 373 return self.namespace.load("__class__")[0].type.full_name() 374 375 def __repr__(self): 376 return "Instance of type '%s'" % self.full_name() 377 378 def __eq__(self, other): 379 # NOTE: Assuming that multiple instances of the same class are equal. 380 return self.full_name() == other.full_name() 381 382 def __hash__(self): 383 return id(self) 384 385 class Constant(Instance): 386 387 "A constant initialised with a type name for future processing." 388 389 def __init__(self, *args, **kw): 390 Instance.__init__(self, *args, **kw) 391 self.typename = self.value.__class__.__name__ 392 393 # vim: tabstop=4 expandtab shiftwidth=4