1.1 --- a/simplified/program.py Sun May 27 18:19:01 2007 +0200
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,412 +0,0 @@
1.4 -#!/usr/bin/env python
1.5 -
1.6 -"""
1.7 -Simplified program nodes for easier type propagation and analysis. This module
1.8 -contains nodes representing program instructions or operations, program
1.9 -structure or organisation, and abstract program data.
1.10 -
1.11 -Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
1.12 -
1.13 -This software is free software; you can redistribute it and/or
1.14 -modify it under the terms of the GNU General Public License as
1.15 -published by the Free Software Foundation; either version 2 of
1.16 -the License, or (at your option) any later version.
1.17 -
1.18 -This software is distributed in the hope that it will be useful,
1.19 -but WITHOUT ANY WARRANTY; without even the implied warranty of
1.20 -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.21 -GNU General Public License for more details.
1.22 -
1.23 -You should have received a copy of the GNU General Public
1.24 -License along with this library; see the file LICENCE.txt
1.25 -If not, write to the Free Software Foundation, Inc.,
1.26 -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
1.27 -"""
1.28 -
1.29 -from simplified.utils import Structure, WithName, name
1.30 -import sys
1.31 -
1.32 -# Simplified program nodes.
1.33 -
1.34 -class Node:
1.35 -
1.36 - """
1.37 - A result node with common attributes:
1.38 -
1.39 - original The original node from which this node was created.
1.40 - defining Whether the node defines something in the original program.
1.41 - name Any name involved (variable or attribute).
1.42 - index Any index involved (temporary variable name).
1.43 - value Any constant value.
1.44 - ref Any reference to (for example) subprograms.
1.45 - nstype Any indication of the namespace type involved in a name access.
1.46 -
1.47 - Expression-related attributes:
1.48 -
1.49 - expr Any contributing expression.
1.50 - lvalue Any target expression.
1.51 - test Any test expression in a conditional instruction.
1.52 -
1.53 - Invocation and subprogram attributes:
1.54 -
1.55 - args Any collection of argument nodes.
1.56 - params Any collection of parameter nodes and defaults.
1.57 -
1.58 - Tuple construction attributes:
1.59 -
1.60 - nodes Any expressions used to initialise a tuple
1.61 -
1.62 - Statement-grouping attributes:
1.63 -
1.64 - body Any conditional code depending on the success of a test.
1.65 - else_ Any conditional code depending on the failure of a test.
1.66 - handler Any exception handler code.
1.67 - finally_ Any code which will be executed regardless.
1.68 - code Any unconditional code.
1.69 - choices Any choices which may be included in the final program.
1.70 - """
1.71 -
1.72 - common_attributes = "name", "index", "value", "nstype", "internal", "returns_value", "is_method", "ref", "module", "structures", "original"
1.73 - expression_attributes = "expr", "lvalue", "test"
1.74 - argument_attributes = "star", "dstar"
1.75 - invocation_attributes = "params", # not "args" - see "pos_args", "kw_args"
1.76 - grouping_attributes = "code", "body", "else_", "handler", "finally_", "choices"
1.77 -
1.78 - def __init__(self, original=None, defining=0, **kw):
1.79 -
1.80 - """
1.81 - Initialise a program node with a link to an optional 'original' AST
1.82 - node. An optional 'defining' parameter (if set to a true value), sets
1.83 - this node as the defining node in the original.
1.84 - """
1.85 -
1.86 - self.original = original
1.87 - self.defining = defining
1.88 - self.copies = {}
1.89 -
1.90 - if self.original is not None and defining:
1.91 - self.original._node = self
1.92 - for name, value in kw.items():
1.93 - setattr(self, name, value)
1.94 -
1.95 - # Annotations.
1.96 -
1.97 - self.types = set()
1.98 -
1.99 - def __repr__(self):
1.100 -
1.101 - "Return a readable representation."
1.102 -
1.103 - if hasattr(self, "full_name"):
1.104 - s = "%s '%s'" % (self.__class__.__name__, self.full_name())
1.105 - elif hasattr(self, "name"):
1.106 - s = "%s '%s'" % (self.__class__.__name__, self.name)
1.107 - elif hasattr(self, "index"):
1.108 - s = "%s (%s)" % (self.__class__.__name__, self.index)
1.109 - elif hasattr(self, "value"):
1.110 - s = "%s %s" % (self.__class__.__name__, repr(self.value))
1.111 - elif hasattr(self, "ref"):
1.112 - s = "%s '%s'" % (self.__class__.__name__, name(self.ref, self.ref.name))
1.113 - else:
1.114 - s = "%s" % (self.__class__.__name__,)
1.115 -
1.116 - # Annotations.
1.117 -
1.118 - if self.types:
1.119 - return "%s -> %s" % (s, self.types)
1.120 - else:
1.121 - return s
1.122 -
1.123 - def _pprint(self, indent, continuation, s, stream=None):
1.124 -
1.125 - """
1.126 - Print, at the given 'indent' level, with the given 'continuation' text,
1.127 - the string 's', either to the given, optional 'stream' or to standard
1.128 - output, this node's "pretty" representation.
1.129 - """
1.130 -
1.131 - stream = stream or sys.stdout
1.132 - if continuation:
1.133 - print >>stream, (" " * max(0, indent - len(continuation))) + continuation + s
1.134 - else:
1.135 - print >>stream, (" " * indent) + s
1.136 -
1.137 - def pprint(self, indent=0, continuation=None, stream=None):
1.138 -
1.139 - """
1.140 - Print, at the given, optional 'indent', with the given optional
1.141 - 'continuation' text, either to the given, optional 'stream' or to
1.142 - standard output, this node's "pretty" representation along with its
1.143 - children and their "pretty" representation (and so on).
1.144 - """
1.145 -
1.146 - stream = stream or sys.stdout
1.147 - self._pprint(indent, continuation, repr(self), stream)
1.148 -
1.149 - # Subprogram-related details.
1.150 -
1.151 - if hasattr(self, "params"):
1.152 - for name, default in self.params:
1.153 - self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
1.154 - if hasattr(self, "star") and self.star:
1.155 - name, default = self.star
1.156 - self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
1.157 - if hasattr(self, "dstar") and self.dstar:
1.158 - name, default = self.dstar
1.159 - self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
1.160 - if getattr(self, "internal", 0):
1.161 - self._pprint(indent + 2, "( ", "internal", stream=stream)
1.162 - if getattr(self, "structure", 0):
1.163 - self._pprint(indent + 2, "( ", "structure '%s'" % self.structure.name, stream=stream)
1.164 -
1.165 - # Expression-related details.
1.166 -
1.167 - if hasattr(self, "expr"):
1.168 - self.expr.pprint(indent + 2, "- ", stream=stream)
1.169 - if hasattr(self, "nodes"):
1.170 - for node in self.nodes:
1.171 - node.pprint(indent + 2, "- ", stream=stream)
1.172 - if hasattr(self, "lvalue"):
1.173 - self.lvalue.pprint(indent + 2, "->", stream=stream)
1.174 - if hasattr(self, "nstype"):
1.175 - self._pprint(indent + 2, "", self.nstype, stream=stream)
1.176 - if hasattr(self, "args"):
1.177 - for arg in self.pos_args:
1.178 - arg.pprint(indent + 2, "( ", stream=stream)
1.179 - for name, arg in self.kw_args.items():
1.180 - arg.pprint(indent + 2, "( ", stream=stream)
1.181 - if hasattr(self, "star") and self.star:
1.182 - self.star.pprint(indent + 2, "( ", stream=stream)
1.183 - if hasattr(self, "dstar") and self.dstar:
1.184 - self.dstar.pprint(indent + 2, "( ", stream=stream)
1.185 -
1.186 - # Statement-related details.
1.187 -
1.188 - if hasattr(self, "test"):
1.189 - self.test.pprint(indent + 2, "? ", stream=stream)
1.190 - for attr in self.grouping_attributes:
1.191 - if hasattr(self, attr) and getattr(self, attr):
1.192 - self._pprint(indent, "", "%s {" % attr, stream=stream)
1.193 - for node in getattr(self, attr):
1.194 - node.pprint(indent + 2, stream=stream)
1.195 - self._pprint(indent, "", "}", stream=stream)
1.196 -
1.197 - # Annotations.
1.198 -
1.199 - if hasattr(self, "accesses"):
1.200 - self._pprint(indent, "", "--------", stream=stream)
1.201 - for ref, attributes in self.accesses.items():
1.202 - self._pprint(indent + 2, "| ", "when %s: %s" % (ref, ", ".join([("%s via %s" % attr_acc) for attr_acc in attributes])), stream=stream)
1.203 - self._pprint(indent, "", "--------", stream=stream)
1.204 - if hasattr(self, "writes"):
1.205 - self._pprint(indent, "", "--------", stream=stream)
1.206 - for ref, attribute in self.writes.items():
1.207 - self._pprint(indent + 2, "| ", "when %s: %s" % (ref, attribute), stream=stream)
1.208 - self._pprint(indent, "", "--------", stream=stream)
1.209 -
1.210 - # Node discovery functions.
1.211 -
1.212 - def active(self):
1.213 -
1.214 - "Return the active copies of this node or a list containing this node."
1.215 -
1.216 - return self.copies.values() or [self]
1.217 -
1.218 - # Node manipulation functions.
1.219 -
1.220 - def copy(self, instance=None, new_name=None):
1.221 -
1.222 - """
1.223 - Perform a deep copy of the node, optionally specifying the 'instance'
1.224 - for whom the copy has been requested and a 'new_name' for the copied
1.225 - node. Return new unannotated copies of the node and its descendants.
1.226 - """
1.227 -
1.228 - # Copy the common attributes of this node.
1.229 -
1.230 - common = {}
1.231 - for attr in self.common_attributes:
1.232 - if hasattr(self, attr):
1.233 - common[attr] = getattr(self, attr)
1.234 -
1.235 - # Add new attributes specially for copies.
1.236 -
1.237 - common["instance"] = instance
1.238 -
1.239 - if new_name is not None:
1.240 - common["copy_of"] = self
1.241 - common["name"] = new_name
1.242 -
1.243 - # Instantiate the copy, avoiding side-effects with original and defining.
1.244 -
1.245 - node = self.__class__(**common)
1.246 - node.defining = self.defining
1.247 -
1.248 - # Add links to copies from originals.
1.249 -
1.250 - self.copies[instance] = node
1.251 -
1.252 - # Copy attributes of different types.
1.253 -
1.254 - for attr in self.expression_attributes:
1.255 - if hasattr(self, attr):
1.256 - n = getattr(self, attr)
1.257 - if n is None:
1.258 - n2 = n
1.259 - else:
1.260 - n2 = n.copy(instance)
1.261 - setattr(node, attr, n2)
1.262 -
1.263 - for attr in self.argument_attributes:
1.264 - if hasattr(self, attr):
1.265 - t = getattr(self, attr)
1.266 - if t is None:
1.267 - t2 = t
1.268 - else:
1.269 - name, n = t
1.270 - n2 = n.copy(instance)
1.271 - t2 = name, n2
1.272 - setattr(node, attr, t2)
1.273 -
1.274 - for attr in self.invocation_attributes:
1.275 - if hasattr(self, attr):
1.276 - l = getattr(self, attr)
1.277 - l2 = []
1.278 - for name, n in l:
1.279 - if n is None:
1.280 - l2.append((name, n))
1.281 - else:
1.282 - l2.append((name, n.copy(instance)))
1.283 - setattr(node, attr, l2)
1.284 -
1.285 - for attr in self.grouping_attributes:
1.286 - if hasattr(self, attr):
1.287 - l = getattr(self, attr)
1.288 - setattr(node, attr, [n.copy(instance) for n in l])
1.289 -
1.290 - # Arguments are usually processed further - "args" is useless.
1.291 -
1.292 - if hasattr(self, "pos_args"):
1.293 - node.pos_args = [n.copy(instance) for n in self.pos_args]
1.294 -
1.295 - if hasattr(self, "kw_args"):
1.296 - node.kw_args = {}
1.297 - for name, n in self.kw_args.items():
1.298 - node.kw_args[name] = n.copy(instance)
1.299 -
1.300 - return node
1.301 -
1.302 -# These are the supported "operations" described by simplified program nodes.
1.303 -
1.304 -class Pass(Node): "A placeholder node corresponding to pass."
1.305 -class Assign(Node): "A grouping node for assignment-related operations."
1.306 -class Keyword(Node): "A grouping node for keyword arguments."
1.307 -class Global(Node): "A global name designator."
1.308 -class Import(Node): "A module import operation."
1.309 -class LoadTemp(Node): "Load a previously-stored temporary value."
1.310 -class LoadName(Node): "Load a named object."
1.311 -class LoadAttr(Node): "Load an object attribute."
1.312 -class LoadRef(Node): "Load a reference, typically a subprogram or a constant."
1.313 -class LoadExc(Node): "Load a handled exception."
1.314 -class ResetExc(Node): "Reset the exception state."
1.315 -class StoreTemp(Node): "Store a temporary value."
1.316 -class StoreName(Node): "Associate a name with an object."
1.317 -class StoreAttr(Node): "Associate an object's attribute with a value."
1.318 -class ReleaseTemp(Node): "Release a temporary value."
1.319 -class Try(Node): "A try...except...else...finally grouping node."
1.320 -class Raise(Node): "An exception raising node."
1.321 -class Not(Node): "A negation of an expression."
1.322 -class CheckType(Node): "Check a value's type from a list of choices."
1.323 -class Return(Node): "Return an evaluated expression."
1.324 -class Invoke(Node): "An invocation."
1.325 -class MakeTuple(Node): "Make a tuple object."
1.326 -
1.327 -# There are two types of return node: return from function and return from
1.328 -# block.
1.329 -
1.330 -class ReturnFromFunction(Return):
1.331 - pass
1.332 -
1.333 -class ReturnFromBlock(Return):
1.334 - pass
1.335 -
1.336 -# NOTE: Not actually supported.
1.337 -# Additionally, yield statements act like return statements for the purposes
1.338 -# of this system.
1.339 -
1.340 -class Yield(ReturnFromFunction):
1.341 - pass
1.342 -
1.343 -# Some behaviour is set as the default in conditional nodes but may be
1.344 -# overridden.
1.345 -
1.346 -class Conditional(Node):
1.347 -
1.348 - "A conditional node consisting of a test and outcomes."
1.349 -
1.350 - def __init__(self, *args, **kw):
1.351 - self.isolate_test = 0
1.352 - Node.__init__(self, *args, **kw)
1.353 -
1.354 -# Invocations involve some more work to process calculated attributes.
1.355 -
1.356 -class InvokeFunction(Invoke):
1.357 -
1.358 - "A function or method invocation."
1.359 -
1.360 - def __init__(self, *args, **kw):
1.361 - self.args = []
1.362 - self.star = None
1.363 - self.dstar = None
1.364 - Invoke.__init__(self, *args, **kw)
1.365 - self.set_args(self.args)
1.366 - self.share_locals = 0
1.367 -
1.368 - def set_args(self, args):
1.369 -
1.370 - "Sort the 'args' into positional and keyword arguments."
1.371 -
1.372 - self.pos_args = []
1.373 - self.kw_args = {}
1.374 - add_kw = 0
1.375 - for arg in args:
1.376 - if not add_kw:
1.377 - if not isinstance(arg, Keyword):
1.378 - self.pos_args.append(arg)
1.379 - else:
1.380 - add_kw = 1
1.381 - if add_kw:
1.382 - if isinstance(arg, Keyword):
1.383 - self.kw_args[arg.name] = arg.expr
1.384 - else:
1.385 - raise TypeError, "Positional argument appears after keyword arguments in '%s'." % self
1.386 -
1.387 -class InvokeRef(Invoke):
1.388 -
1.389 - "A block or loop invocation."
1.390 -
1.391 - def __init__(self, *args, **kw):
1.392 - self.share_locals = 1
1.393 - Invoke.__init__(self, *args, **kw)
1.394 -
1.395 -# Program structure nodes.
1.396 -
1.397 -class Module(Node, Structure):
1.398 -
1.399 - "A Python module."
1.400 -
1.401 - def full_name(self):
1.402 - return "module %s" % self.name
1.403 -
1.404 -class Subprogram(Node, WithName):
1.405 -
1.406 - "A subprogram: functions, methods and loops."
1.407 -
1.408 - def __init__(self, *args, **kw):
1.409 - Node.__init__(self, *args, **kw)
1.410 - WithName.__init__(self)
1.411 - self.raises = set()
1.412 - self.returns = set()
1.413 - self.return_locals = set()
1.414 -
1.415 -# vim: tabstop=4 expandtab shiftwidth=4