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