1.1 --- a/annotate.py Sun Apr 01 17:43:20 2007 +0200
1.2 +++ b/annotate.py Fri Apr 06 00:53:18 2007 +0200
1.3 @@ -75,7 +75,7 @@
1.4 "Initialise a 'node' for annotation."
1.5
1.6 if not hasattr(node, attr):
1.7 - setattr(node, attr, [])
1.8 + setattr(node, attr, set())
1.9
1.10 def annotate(self, node, types, attr="types"):
1.11
1.12 @@ -92,7 +92,7 @@
1.13
1.14 for type in types:
1.15 if type not in target:
1.16 - target.append(type)
1.17 + target.add(type)
1.18 self.count += 1
1.19
1.20 system = System()
1.21 @@ -176,14 +176,13 @@
1.22
1.23 # NOTE: Not declaring module namespace usage, even though it is used.
1.24
1.25 - return self.process_node(module, self.global_namespace, 0)
1.26 + self.process_node(module, self.global_namespace, 0)
1.27
1.28 def process_node(self, node, locals, using_module_namespace):
1.29
1.30 """
1.31 Process a subprogram or module 'node', indicating the initial 'locals'.
1.32 - Return an annotated subprogram or module. Note that this method may
1.33 - mutate nodes in the original program.
1.34 + Note that this method may mutate nodes in the original program.
1.35 """
1.36
1.37 # Recursion test.
1.38 @@ -192,7 +191,7 @@
1.39 if not self.rerun_subprograms.has_key(node):
1.40 self.rerun_subprograms[node] = []
1.41 self.rerun_subprograms[node].append(locals)
1.42 - return node
1.43 + return
1.44
1.45 # Record the current subprogram and namespace.
1.46
1.47 @@ -222,8 +221,8 @@
1.48 node.namespace = self.namespace
1.49 self.set_module_namespace(using_module_namespace)
1.50
1.51 - result = self.dispatch(node)
1.52 - self.extract_results(result)
1.53 + self.dispatch(node)
1.54 + self.extract_results(node)
1.55
1.56 while self.rerun_subprograms.has_key(node):
1.57 all_rerun_locals = self.rerun_subprograms[node]
1.58 @@ -235,8 +234,8 @@
1.59 node.namespace = rerun_locals
1.60 self.set_module_namespace(using_module_namespace)
1.61
1.62 - result = self.dispatch(node)
1.63 - self.extract_results(result)
1.64 + self.dispatch(node)
1.65 + self.extract_results(node)
1.66
1.67 # Restore the previous subprogram and namespace.
1.68
1.69 @@ -244,8 +243,6 @@
1.70 self.current_subprograms.pop()
1.71 self.reset_module_namespace(using_module_namespace)
1.72
1.73 - return result
1.74 -
1.75 def set_module_namespace(self, using_module_namespace):
1.76
1.77 """
1.78 @@ -266,15 +263,15 @@
1.79 if using_module_namespace:
1.80 self.module.namespace = self.namespace
1.81
1.82 - def extract_results(self, result):
1.83 + def extract_results(self, node):
1.84
1.85 "Extract results from the namespace."
1.86
1.87 - result.namespace = self.namespace
1.88 - self.system.annotate(result, self.namespace.raises, "raises")
1.89 - self.system.annotate(result, self.namespace.returns, "returns")
1.90 - if hasattr(result, "return_locals"):
1.91 - combine(result.return_locals, self.namespace.return_locals)
1.92 + node.namespace = self.namespace
1.93 + self.system.annotate(node, self.namespace.raises, "raises")
1.94 + self.system.annotate(node, self.namespace.returns, "returns")
1.95 + if hasattr(node, "return_locals"):
1.96 + node.return_locals.update(self.namespace.return_locals)
1.97
1.98 def annotate(self, node, types=None):
1.99
1.100 @@ -300,7 +297,7 @@
1.101
1.102 for param, types in items:
1.103 if not node.paramtypes.has_key(param):
1.104 - node.paramtypes[param] = []
1.105 + node.paramtypes[param] = set()
1.106 self.system.combine(node.paramtypes[param], types)
1.107
1.108 # Visitor methods.
1.109 @@ -316,7 +313,7 @@
1.110
1.111 def dispatch(self, node, *args):
1.112 try:
1.113 - return Visitor.dispatch(self, node, *args)
1.114 + Visitor.dispatch(self, node, *args)
1.115 except AnnotationError, exc:
1.116 exc.add(node)
1.117 raise
1.118 @@ -328,31 +325,29 @@
1.119 def visitAssign(self, assign):
1.120
1.121 """
1.122 - Return the 'assign' node whose contents (merely a group of nodes) have
1.123 - been processed.
1.124 + Process the 'assign' node and its contents.
1.125 """
1.126
1.127 - assign.code = self.dispatches(assign.code)
1.128 - return assign
1.129 + self.dispatches(assign.code)
1.130
1.131 def visitCheckType(self, checktype):
1.132
1.133 """
1.134 - Return the 'checktype' node, processing the expression to find the
1.135 - possible types of the exception, and processing each choice to build a
1.136 - list of checked types for the exception.
1.137 + Process the 'checktype' node, finding the possible types of the
1.138 + exception, and processing each choice to build a list of checked types
1.139 + for the exception.
1.140 """
1.141
1.142 inverted = getattr(checktype, "inverted", 0)
1.143 - checktype.expr = self.dispatch(checktype.expr)
1.144 + self.dispatch(checktype.expr)
1.145
1.146 expr_types = self.namespace.types
1.147 - choice_types = []
1.148 + choice_types = set()
1.149 choices = []
1.150
1.151 for choice in checktype.choices:
1.152 choices.append(self.dispatch(choice))
1.153 - choice_types += self.namespace.types
1.154 + choice_types.update(self.namespace.types)
1.155
1.156 for expr_type in expr_types:
1.157 in_choices = expr_type.type.get_class() in choice_types
1.158 @@ -363,12 +358,10 @@
1.159 if not inverted and not in_choices or inverted and in_choices:
1.160 self._prune_non_accesses(checktype.expr, expr_type)
1.161
1.162 - return checktype
1.163 -
1.164 def visitConditional(self, conditional):
1.165
1.166 """
1.167 - Return the 'conditional' node, processing the test, body and else
1.168 + Process the 'conditional' node, processing the test, body and else
1.169 clauses and recording their processed forms. The body and else clauses
1.170 are processed within their own namespaces, and the test is also
1.171 processed in its own namespace if 'isolate_test' is set on the
1.172 @@ -391,7 +384,7 @@
1.173 self.module.namespace = self.namespace
1.174 self.namespace.merge_namespace(saved_namespace)
1.175
1.176 - conditional.test = self.dispatch(conditional.test)
1.177 + self.dispatch(conditional.test)
1.178
1.179 # Where the test may affect the body and the else clause, save the
1.180 # namespace after processing the test.
1.181 @@ -406,12 +399,12 @@
1.182 # NOTE: Exception recording.
1.183
1.184 else:
1.185 - test_raises = []
1.186 - combine(test_raises, self.namespace.raises)
1.187 + test_raises = set()
1.188 + test_raises.update(self.namespace.raises)
1.189
1.190 # Process the body clause.
1.191
1.192 - conditional.body = self.dispatches(conditional.body)
1.193 + self.dispatches(conditional.body)
1.194 body_namespace = self.namespace
1.195
1.196 # Use the saved namespace as a template for the else clause.
1.197 @@ -423,7 +416,7 @@
1.198
1.199 # Process the else clause.
1.200
1.201 - conditional.else_ = self.dispatches(conditional.else_)
1.202 + self.dispatches(conditional.else_)
1.203 else_namespace = self.namespace
1.204
1.205 # Merge the body and else namespaces.
1.206 @@ -442,37 +435,34 @@
1.207 if exc_type not in body_namespace.raises:
1.208 self.namespace.revoke_exception_type(exc_type)
1.209
1.210 - return conditional
1.211 -
1.212 def visitGlobal(self, global_):
1.213
1.214 """
1.215 - Return the 'global_' node unprocessed since namespaces should have
1.216 + Leave the 'global_' node unprocessed since namespaces should have
1.217 already been altered to take global names into consideration.
1.218 """
1.219
1.220 - return global_
1.221 + pass
1.222
1.223 def visitImport(self, import_):
1.224
1.225 """
1.226 - Return the 'import_' node, importing the module with the stated name
1.227 + Process the 'import_' node, importing the module with the stated name
1.228 and storing details on the node.
1.229 """
1.230
1.231 module = self.importer.load(import_.name, self.builtins, getattr(import_, "alias", None))
1.232 if module is not None:
1.233 - self.namespace.set_types([module])
1.234 + self.namespace.set_types(set([module]))
1.235 else:
1.236 - self.namespace.set_types([])
1.237 + self.namespace.set_types(set())
1.238 self.annotate(import_) # mainly for viewing purposes
1.239 - return import_
1.240
1.241 def _visitInvoke(self, invoke, invocation_types, have_args):
1.242
1.243 """
1.244 - Return the processed 'invoke' node, using the given 'invocation_types'
1.245 - as the list of callables to be investigated for instantiation or for the
1.246 + Process the 'invoke' node, using the given 'invocation_types' as the
1.247 + list of callables to be investigated for instantiation or for the
1.248 invocation of functions or blocks. If 'have_args' is a true value, any
1.249 invocation or instantiation will involve arguments.
1.250 """
1.251 @@ -556,21 +546,20 @@
1.252
1.253 # Associate the instance with the result of this invocation.
1.254
1.255 - self.namespace.set_types([Attribute(None, instance)])
1.256 + self.namespace.set_types(set([Attribute(None, instance)]))
1.257 self.annotate(invoke)
1.258
1.259 # Remember the invocations that were found, along with the return type
1.260 # information.
1.261
1.262 invoke.invocations = invocations
1.263 - self.namespace.set_types(getattr(invoke, "types", []))
1.264 - return invoke
1.265 + self.namespace.set_types(getattr(invoke, "types", set()))
1.266
1.267 def visitInvokeRef(self, invoke):
1.268
1.269 """
1.270 - Return the processed 'invoke' node, first finding the callables
1.271 - indicated by the reference.
1.272 + Process the 'invoke' node, first finding the callables indicated by the
1.273 + reference.
1.274 """
1.275
1.276 # Where the invocation belongs to an instance but the invoked subprogram
1.277 @@ -585,57 +574,79 @@
1.278 #print "Created", invoke.ref, "for", getattr(invoke.ref, "instance", None)
1.279 invoke.ref.module.simplifier.subnames[invoke.ref.full_name()] = invoke.ref
1.280 invocation_types = [Attribute(None, invoke.ref)]
1.281 - return self._visitInvoke(invoke, invocation_types, have_args=0)
1.282 + self._visitInvoke(invoke, invocation_types, have_args=0)
1.283
1.284 def visitInvokeFunction(self, invoke):
1.285
1.286 """
1.287 - Return the processed 'invoke' node, first finding the callables
1.288 - indicated by the expression.
1.289 + Process the 'invoke' node, first finding the callables indicated by the
1.290 + expression.
1.291 """
1.292
1.293 - invoke.expr = self.dispatch(invoke.expr)
1.294 + self.dispatch(invoke.expr)
1.295 invocation_types = self.namespace.types
1.296
1.297 # Invocation processing starts with making sure that the arguments have
1.298 # been processed.
1.299
1.300 - return self._visitInvoke(invoke, invocation_types, have_args=self.process_args(invoke))
1.301 + self._visitInvoke(invoke, invocation_types, have_args=self.process_args(invoke))
1.302
1.303 def visitLoadAttr(self, loadattr):
1.304
1.305 """
1.306 - Return the 'loadattr' node, processing and storing the expression, and
1.307 + Process the 'loadattr' node, processing and storing the expression, and
1.308 using the expression's types to construct records of accesses and
1.309 non-accesses using the stated attribute name.
1.310 """
1.311
1.312 - loadattr.expr = self.dispatch(loadattr.expr)
1.313 - types = []
1.314 + self.dispatch(loadattr.expr)
1.315 + types = set()
1.316 non_accesses = []
1.317 accesses = {}
1.318 +
1.319 + # For each expression type...
1.320 +
1.321 for attr in self.namespace.types:
1.322 +
1.323 + # Find types for the named attribute.
1.324 +
1.325 attributes = get_attributes(attr.type, loadattr.name)
1.326 +
1.327 + # Where no attributes exist...
1.328 +
1.329 if not attributes:
1.330 +
1.331 + # Register new invalid accesses and mark a possible exception.
1.332 +
1.333 if not attr in non_accesses:
1.334 non_accesses.append(attr)
1.335 - combine(self.namespace.raises, self.get_builtin_instances(loadattr, "AttributeError"))
1.336 + self.namespace.raises.update(self.get_builtin_instances(loadattr, "AttributeError"))
1.337
1.338 # Revoke this type from any name involved.
1.339
1.340 self._prune_non_accesses(loadattr.expr, attr)
1.341
1.342 + # For each type found...
1.343 +
1.344 for attribute, accessor in attributes:
1.345 +
1.346 + # For actual attributes, register the type and remember the
1.347 + # access.
1.348 +
1.349 if attribute is not None:
1.350 - types.append(attribute)
1.351 + types.add(attribute)
1.352 if not accesses.has_key(attr.type):
1.353 accesses[attr.type] = []
1.354 if not (attribute, accessor) in accesses[attr.type]:
1.355 accesses[attr.type].append((attribute, accessor))
1.356 +
1.357 + # Otherwise, register new invalid accesses and note a possible
1.358 + # exception.
1.359 +
1.360 else:
1.361 if not attr in non_accesses:
1.362 non_accesses.append(attr)
1.363 - combine(self.namespace.raises, self.get_builtin_instances(loadattr, "AttributeError"))
1.364 + self.namespace.raises.update(self.get_builtin_instances(loadattr, "AttributeError"))
1.365
1.366 # Revoke this type from any name involved.
1.367
1.368 @@ -643,11 +654,13 @@
1.369
1.370 if not types:
1.371 print "No attribute found for", loadattr.name, "given", self.namespace.types
1.372 +
1.373 + # Remember the result types.
1.374 +
1.375 self.namespace.set_types(types)
1.376 loadattr.non_accesses = non_accesses
1.377 loadattr.accesses = accesses
1.378 self.annotate(loadattr)
1.379 - return loadattr
1.380
1.381 def _prune_non_accesses(self, expr, attr):
1.382
1.383 @@ -679,41 +692,37 @@
1.384 def visitLoadExc(self, loadexc):
1.385
1.386 """
1.387 - Return the 'loadexc' node, discovering the possible exception types
1.388 + Process the 'loadexc' node, discovering the possible exception types
1.389 raised.
1.390 """
1.391
1.392 self.namespace.set_types(self.namespace.raises)
1.393 self.annotate(loadexc)
1.394 - return loadexc
1.395
1.396 def visitLoadName(self, loadname):
1.397
1.398 """
1.399 - Return the 'loadname' node, processing the name information on the node
1.400 + Process the 'loadname' node, processing the name information on the node
1.401 to determine which types are involved with the name.
1.402 """
1.403
1.404 self.namespace.set_types(self.namespace.load(loadname.name))
1.405 - result = loadname
1.406 - self.annotate(result)
1.407 - return result
1.408 + self.annotate(loadname)
1.409
1.410 def visitLoadRef(self, loadref):
1.411
1.412 """
1.413 - Return the 'loadref' node, obtaining type information about the
1.414 + Process the 'loadref' node, obtaining type information about the
1.415 reference stated on the node.
1.416 """
1.417
1.418 - self.namespace.set_types([Attribute(None, loadref.ref)])
1.419 + self.namespace.set_types(set([Attribute(None, loadref.ref)]))
1.420 self.annotate(loadref)
1.421 - return loadref
1.422
1.423 def visitLoadTemp(self, loadtemp):
1.424
1.425 """
1.426 - Return the 'loadtemp' node, obtaining type information about the
1.427 + Process the 'loadtemp' node, obtaining type information about the
1.428 temporary variable accessed, and removing variable information where the
1.429 'release' attribute has been set on the node.
1.430 """
1.431 @@ -727,35 +736,31 @@
1.432 except KeyError:
1.433 raise AnnotationMessage, "Temporary store index '%s' not defined." % index
1.434 self.annotate(loadtemp)
1.435 - return loadtemp
1.436
1.437 def visitModule(self, module):
1.438
1.439 """
1.440 - Return the processed 'module' whose contents (merely a group of nodes)
1.441 - are processed.
1.442 + Process the 'module' and its contents.
1.443 """
1.444
1.445 - module.code = self.dispatches(module.code)
1.446 - return module
1.447 + self.dispatches(module.code)
1.448
1.449 def visitNot(self, not_):
1.450
1.451 - "Return the 'not_' node whose expression is processed."
1.452 + "Process the 'not_' node and its expression."
1.453
1.454 - not_.expr = self.dispatch(not_.expr)
1.455 - return not_
1.456 + self.dispatch(not_.expr)
1.457
1.458 def visitPass(self, pass_):
1.459
1.460 - "Return the unprocessed 'pass_' node."
1.461 + "Leave the 'pass_' node unprocessed."
1.462
1.463 - return pass_
1.464 + pass
1.465
1.466 def visitRaise(self, raise_):
1.467
1.468 """
1.469 - Return the 'raise_' node, processing any traceback information along
1.470 + Process the 'raise_' node, processing any traceback information along
1.471 with the raised exception expression, converting the node into a kind of
1.472 invocation where the expression is found not to be an invocation itself.
1.473 This node affects the namespace, adding exception types to the list of
1.474 @@ -763,8 +768,8 @@
1.475 """
1.476
1.477 if getattr(raise_, "traceback", None) is not None:
1.478 - raise_.traceback = self.dispatch(raise_.traceback)
1.479 - raise_.expr = self.dispatch(raise_.expr)
1.480 + self.dispatch(raise_.traceback)
1.481 + self.dispatch(raise_.expr)
1.482
1.483 # Handle bare name exceptions by converting any classes to instances.
1.484
1.485 @@ -773,21 +778,20 @@
1.486 raise_.kw_args = {}
1.487 raise_.star = None
1.488 raise_.dstar = None
1.489 - types = []
1.490 + types = set()
1.491 for attr in self.namespace.types:
1.492 if isinstance(attr.type, Class):
1.493 self._visitInvoke(raise_, [attr], have_args=0)
1.494 - types += self.namespace.types
1.495 + types.update(self.namespace.types)
1.496 else:
1.497 types = self.namespace.types
1.498
1.499 - combine(self.namespace.raises, types)
1.500 - return raise_
1.501 + self.namespace.raises.update(types)
1.502
1.503 def visitReleaseTemp(self, releasetemp):
1.504
1.505 """
1.506 - Return the 'releasetemp' node, removing temporary variable information
1.507 + Process the 'releasetemp' node, removing temporary variable information
1.508 from the current namespace.
1.509 """
1.510
1.511 @@ -798,16 +802,14 @@
1.512 raise AnnotationMessage, "Temporary store index '%s' not defined." % index
1.513 except IndexError:
1.514 pass #raise AnnotationMessage, "Temporary store index '%s' is empty." % index
1.515 - return releasetemp
1.516
1.517 def visitResetExc(self, resetexc):
1.518 - self.namespace.raises = []
1.519 - return resetexc
1.520 + self.namespace.raises = set()
1.521
1.522 def visitReturn(self, return_):
1.523
1.524 """
1.525 - Return the 'return_' node, processing any expression and obtaining type
1.526 + Process the 'return_' node, processing any expression and obtaining type
1.527 information to be accumulated in the current namespace's list of return
1.528 types. A snapshot of the namespace is taken for the purposes of
1.529 reconciling or merging namespaces where subprograms actually share
1.530 @@ -815,11 +817,10 @@
1.531 """
1.532
1.533 if hasattr(return_, "expr"):
1.534 - return_.expr = self.dispatch(return_.expr)
1.535 - combine(self.namespace.returns, self.namespace.types)
1.536 + self.dispatch(return_.expr)
1.537 + self.namespace.returns.update(self.namespace.types)
1.538 self.annotate(return_)
1.539 self.namespace.snapshot()
1.540 - return return_
1.541
1.542 visitReturnFromBlock = visitReturn
1.543 visitReturnFromFunction = visitReturn
1.544 @@ -827,15 +828,15 @@
1.545 def visitStoreAttr(self, storeattr):
1.546
1.547 """
1.548 - Return the 'storeattr' node, processing the expression and target, and
1.549 + Process the 'storeattr' node, processing the expression and target, and
1.550 using the type information obtained to build records of legitimate
1.551 writes to the stated attribute, along with "impossible" non-writes to
1.552 the attribute.
1.553 """
1.554
1.555 - storeattr.expr = self.dispatch(storeattr.expr)
1.556 + self.dispatch(storeattr.expr)
1.557 expr = self.namespace.types
1.558 - storeattr.lvalue = self.dispatch(storeattr.lvalue)
1.559 + self.dispatch(storeattr.lvalue)
1.560 writes = {}
1.561 non_writes = []
1.562 for attr in self.namespace.types:
1.563 @@ -850,50 +851,46 @@
1.564 print "Unable to store attribute", storeattr.name, "given", self.namespace.types
1.565 storeattr.writes = writes
1.566 storeattr.non_writes = non_writes
1.567 - return storeattr
1.568
1.569 def visitStoreName(self, storename):
1.570
1.571 """
1.572 - Return the 'storename' node, processing the expression on the node and
1.573 + Process the 'storename' node, processing the expression on the node and
1.574 associating the type information obtained with the stated name in the
1.575 current namespace.
1.576 """
1.577
1.578 - storename.expr = self.dispatch(storename.expr)
1.579 + self.dispatch(storename.expr)
1.580 self.namespace.store(storename.name, self.namespace.types)
1.581 self.annotate(storename)
1.582 - return storename
1.583
1.584 def visitStoreTemp(self, storetemp):
1.585
1.586 """
1.587 - Return the 'storetemp' node, processing the expression on the node and
1.588 + Process the 'storetemp' node, processing the expression on the node and
1.589 associating the type information obtained with a temporary variable in
1.590 the current namespace.
1.591 """
1.592
1.593 - storetemp.expr = self.dispatch(storetemp.expr)
1.594 + self.dispatch(storetemp.expr)
1.595 index = getattr(storetemp, "index", None)
1.596 if not self.namespace.temp.has_key(index):
1.597 self.namespace.temp[index] = []
1.598 self.namespace.temp[index].append(self.namespace.types)
1.599 - return storetemp
1.600
1.601 def visitSubprogram(self, subprogram):
1.602
1.603 """
1.604 - Return the 'subprogram' node, processing its contents (a group of nodes
1.605 + Process the 'subprogram' node, processing its contents (a group of nodes
1.606 comprising the subprogram).
1.607 """
1.608
1.609 - subprogram.code = self.dispatches(subprogram.code)
1.610 - return subprogram
1.611 + self.dispatches(subprogram.code)
1.612
1.613 def visitTry(self, try_):
1.614
1.615 """
1.616 - Return the 'try_' node, processing the body clause in its own namespace
1.617 + Process the 'try_' node, processing the body clause in its own namespace
1.618 derived from the current namespace, processing any handler clause using
1.619 the namespace information accumulated in the body, and processing any
1.620 else and finally clauses, attempting to supply each with appropriate
1.621 @@ -902,7 +899,7 @@
1.622
1.623 is_module = self.namespace is self.module.namespace
1.624
1.625 - try_.body = self.dispatches(try_.body)
1.626 + self.dispatches(try_.body)
1.627
1.628 # Save the namespace from the body.
1.629
1.630 @@ -912,7 +909,7 @@
1.631 # Process the handler.
1.632
1.633 if hasattr(try_, "handler"):
1.634 - try_.handler = self.dispatches(try_.handler)
1.635 + self.dispatches(try_.handler)
1.636
1.637 # Save the namespace from the handler.
1.638
1.639 @@ -935,8 +932,8 @@
1.640
1.641 # Empty the raised exceptions for the else clause.
1.642
1.643 - self.namespace.raises = []
1.644 - try_.else_ = self.dispatches(try_.else_)
1.645 + self.namespace.raises = set()
1.646 + self.dispatches(try_.else_)
1.647 self.namespace.raises = raises
1.648
1.649 # Merge the namespaces.
1.650 @@ -949,8 +946,7 @@
1.651
1.652 # Process the finally clause, if any.
1.653
1.654 - try_.finally_ = self.dispatches(try_.finally_)
1.655 - return try_
1.656 + self.dispatches(try_.finally_)
1.657
1.658 def visitYield(self, yield_):
1.659 raise NotImplementedError, "The yield statement is not currently supported."
1.660 @@ -967,7 +963,7 @@
1.661 if not type.has_instance(node):
1.662 instance = Instance()
1.663 instance.namespace = Namespace()
1.664 - instance.namespace.store("__class__", [Attribute(None, type)])
1.665 + instance.namespace.store("__class__", set([Attribute(None, type)]))
1.666 type.add_instance(node, instance)
1.667 else:
1.668 instance = type.get_instance(node)
1.669 @@ -1029,7 +1025,7 @@
1.670 # NOTE: within a method definition.
1.671
1.672 if getattr(target, "name", None) is not None and not getattr(target, "is_method", 0):
1.673 - namespace.store(target.name, [Attribute(None, target)])
1.674 + namespace.store(target.name, set([Attribute(None, target)]))
1.675
1.676 # Process the subprogram.
1.677
1.678 @@ -1070,9 +1066,9 @@
1.679 # Incorporate any raised exceptions.
1.680
1.681 if not hasattr(invoke, "raises"):
1.682 - invoke.raises = []
1.683 - combine(invoke.raises, target.raises)
1.684 - combine(self.namespace.raises, target.raises)
1.685 + invoke.raises = set()
1.686 + invoke.raises.update(target.raises)
1.687 + self.namespace.raises.update(target.raises)
1.688
1.689 def process_args(self, invocation):
1.690
1.691 @@ -1081,19 +1077,19 @@
1.692 any arguments were processed.
1.693 """
1.694
1.695 - invocation.pos_args = self.dispatches(invocation.pos_args)
1.696 - invocation.kw_args = self.dispatch_dict(invocation.kw_args)
1.697 + self.dispatches(invocation.pos_args)
1.698 + self.dispatch_dict(invocation.kw_args)
1.699
1.700 # Get type information for star and dstar arguments.
1.701
1.702 if invocation.star is not None:
1.703 param, default = invocation.star
1.704 - default = self.dispatch(default)
1.705 + self.dispatch(default)
1.706 invocation.star = param, default
1.707
1.708 if invocation.dstar is not None:
1.709 param, default = invocation.dstar
1.710 - default = self.dispatch(default)
1.711 + self.dispatch(default)
1.712 invocation.dstar = param, default
1.713
1.714 if invocation.pos_args or invocation.kw_args or invocation.star or invocation.dstar:
1.715 @@ -1108,7 +1104,9 @@
1.716 given 'context' (which may be None).
1.717 """
1.718
1.719 - if context is not None:
1.720 + # NOTE: Support class methods!
1.721 +
1.722 + if context is not None and isinstance(context.type, Instance):
1.723 pos_args = [Self(context)] + invocation.pos_args
1.724 else:
1.725 pos_args = invocation.pos_args
1.726 @@ -1134,7 +1132,7 @@
1.727 if hasattr(arg, "types"):
1.728 items.append((param, arg.types))
1.729 else:
1.730 - items.append((param, [])) # Annotation has not succeeded.
1.731 + items.append((param, set())) # Annotation has not succeeded.
1.732 params = params[1:]
1.733 else:
1.734 star_args.append(arg)
1.735 @@ -1147,13 +1145,14 @@
1.736 arg = kw_args[param]
1.737 del kw_args[param]
1.738 elif default is not None:
1.739 - arg = self.dispatch(default)
1.740 + self.dispatch(default)
1.741 + arg = default
1.742 else:
1.743 raise AnnotationMessage, "No argument supplied in '%s' for parameter '%s'." % (subprogram, param)
1.744 if hasattr(arg, "types"):
1.745 items.append((param, arg.types))
1.746 else:
1.747 - items.append((param, [])) # Annotation has not succeeded.
1.748 + items.append((param, set())) # Annotation has not succeeded.
1.749 params = params[1:]
1.750
1.751 dstar_args = kw_args.items()
1.752 @@ -1409,24 +1408,24 @@
1.753 """
1.754
1.755 self.names = {}
1.756 - self.returns = []
1.757 - self.return_locals = []
1.758 - self.raises = []
1.759 + self.returns = set()
1.760 + self.return_locals = set()
1.761 + self.raises = set()
1.762 self.temp = {}
1.763 - self.types = []
1.764 + self.types = set()
1.765
1.766 def set_types(self, types):
1.767
1.768 "Set the current collection of 'types'."
1.769
1.770 - self.types = types[:]
1.771 + self.types = types.copy()
1.772
1.773 def add(self, name, types):
1.774
1.775 "Add to the entry with the given 'name' the specified 'types'."
1.776
1.777 if self.names.has_key(name):
1.778 - combine(self.names[name], types)
1.779 + self.names[name].update(types)
1.780 else:
1.781 self.store(name, types)
1.782
1.783 @@ -1434,7 +1433,7 @@
1.784
1.785 "Store in (or associate with) the given 'name' the specified 'types'."
1.786
1.787 - self.names[name] = types[:]
1.788 + self.names[name] = types.copy()
1.789
1.790 __setitem__ = store
1.791
1.792 @@ -1453,7 +1452,7 @@
1.793
1.794 "Revoke from the entry for the given 'name' the specified 'type'."
1.795
1.796 - new_types = self.names[name][:]
1.797 + new_types = self.names[name].copy()
1.798 new_types.remove(type)
1.799 self.names[name] = new_types
1.800
1.801 @@ -1467,7 +1466,7 @@
1.802
1.803 "Revoke from the temporary variable 'index' the given 'type'."
1.804
1.805 - new_types = self.temp[index][-1][:]
1.806 + new_types = self.temp[index][-1].copy()
1.807 new_types.remove(type)
1.808 self.temp[index][-1] = new_types
1.809
1.810 @@ -1481,15 +1480,15 @@
1.811 """
1.812
1.813 self.merge_items(namespace.names.items())
1.814 - combine(self.raises, namespace.raises)
1.815 + self.raises.update(namespace.raises)
1.816 if everything:
1.817 - combine(self.returns, namespace.returns)
1.818 - combine(self.return_locals, namespace.return_locals)
1.819 + self.returns.update(namespace.returns)
1.820 + self.return_locals.update(namespace.return_locals)
1.821 for name, values in namespace.temp.items():
1.822 if values:
1.823 if not self.temp.has_key(name) or not self.temp[name]:
1.824 - self.temp[name] = [[]]
1.825 - combine(self.temp[name][-1], values[-1])
1.826 + self.temp[name] = [set()]
1.827 + self.temp[name][-1].update(values[-1])
1.828
1.829 def merge_items(self, items):
1.830
1.831 @@ -1503,10 +1502,10 @@
1.832 "Merge the entry for the given 'name' and 'types' with this namespace."
1.833
1.834 if not self.names.has_key(name):
1.835 - self.names[name] = types[:]
1.836 + self.names[name] = types.copy()
1.837 else:
1.838 existing = self.names[name]
1.839 - combine(existing, types)
1.840 + existing.update(types)
1.841
1.842 def snapshot(self):
1.843
1.844 @@ -1514,7 +1513,7 @@
1.845
1.846 namespace = Namespace()
1.847 namespace.merge_namespace(self)
1.848 - self.return_locals.append(namespace)
1.849 + self.return_locals.add(namespace)
1.850
1.851 def reset(self):
1.852
1.853 @@ -1766,7 +1765,7 @@
1.854 try:
1.855 while 1:
1.856 s = raw_input("> ")
1.857 - print eval(s)
1.858 + print eval(s, vars)
1.859 except EOFError:
1.860 pass
1.861
2.1 --- a/simplified.py Sun Apr 01 17:43:20 2007 +0200
2.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2.3 @@ -1,776 +0,0 @@
2.4 -#!/usr/bin/env python
2.5 -
2.6 -"""
2.7 -Simplified program nodes for easier type propagation and analysis. This module
2.8 -contains nodes representing program instructions or operations, program
2.9 -structure or organisation, and abstract program data.
2.10 -
2.11 -Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
2.12 -
2.13 -This software is free software; you can redistribute it and/or
2.14 -modify it under the terms of the GNU General Public License as
2.15 -published by the Free Software Foundation; either version 2 of
2.16 -the License, or (at your option) any later version.
2.17 -
2.18 -This software is distributed in the hope that it will be useful,
2.19 -but WITHOUT ANY WARRANTY; without even the implied warranty of
2.20 -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2.21 -GNU General Public License for more details.
2.22 -
2.23 -You should have received a copy of the GNU General Public
2.24 -License along with this library; see the file LICENCE.txt
2.25 -If not, write to the Free Software Foundation, Inc.,
2.26 -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
2.27 -"""
2.28 -
2.29 -from compiler.visitor import ASTVisitor
2.30 -import sys
2.31 -
2.32 -# Location of the built-in libraries.
2.33 -# NOTE: Change this if the package structure changes.
2.34 -
2.35 -import os
2.36 -
2.37 -libdir = os.path.join(os.path.split(__file__)[0], "lib")
2.38 -
2.39 -# Exceptions.
2.40 -
2.41 -class SimplifiedError(Exception):
2.42 -
2.43 - "An error in the annotation process."
2.44 -
2.45 - def __init__(self, exc, node, *args):
2.46 -
2.47 - """
2.48 - Initialise the error with an existing exception 'exc', the 'node' at
2.49 - which this error occurs, along with additional optional arguments.
2.50 - """
2.51 -
2.52 - Exception.__init__(self, *args)
2.53 - self.nodes = [node]
2.54 - self.exc = exc
2.55 -
2.56 - def add(self, node):
2.57 -
2.58 - "Add the given 'node' to the path of nodes leading from the exception."
2.59 -
2.60 - self.nodes.append(node)
2.61 -
2.62 - def __str__(self):
2.63 -
2.64 - "Return a string showing the principal exception details."
2.65 -
2.66 - return "%s, %s" % (self.exc, self.nodes)
2.67 -
2.68 -# Unique name registration.
2.69 -
2.70 -class Naming:
2.71 -
2.72 - "Maintain records of unique names for each simple name."
2.73 -
2.74 - index_separator = "-"
2.75 -
2.76 - def __init__(self):
2.77 - self.names = {}
2.78 -
2.79 - def get(self, obj):
2.80 - return obj._unique_name
2.81 -
2.82 - def set(self, obj, name):
2.83 - if hasattr(obj, "_unique_name"):
2.84 - return
2.85 - if not self.names.has_key(name):
2.86 - self.names[name] = 0
2.87 - n = self.names[name] + 1
2.88 - self.names[name] = n
2.89 - obj._unique_name = "%s%s%d" % (name, self.index_separator, n)
2.90 -
2.91 -naming = Naming()
2.92 -
2.93 -def name(obj, name):
2.94 -
2.95 - "Return a unique name for the given 'obj', indicating the base 'name'."
2.96 -
2.97 - naming.set(obj, name)
2.98 - return naming.get(obj)
2.99 -
2.100 -# Named nodes are those which can be referenced in some way.
2.101 -
2.102 -class WithName:
2.103 -
2.104 - "Node naming."
2.105 -
2.106 - def __init__(self):
2.107 - self._full_name = name(self, self.name or "$untitled")
2.108 -
2.109 - def full_name(self):
2.110 - return self._full_name
2.111 -
2.112 -# Elementary visitor support.
2.113 -
2.114 -class Visitor(ASTVisitor):
2.115 -
2.116 - "A visitor base class."
2.117 -
2.118 - def __init__(self):
2.119 - ASTVisitor.__init__(self)
2.120 -
2.121 - def default(self, node, *args):
2.122 - raise SimplifiedError, (None, node)
2.123 -
2.124 - def dispatch(self, node, *args):
2.125 - return ASTVisitor.dispatch(self, node, *args)
2.126 -
2.127 - def dispatches(self, nodes, *args):
2.128 - results = []
2.129 - for node in nodes:
2.130 - results.append(self.dispatch(node, *args))
2.131 - return results
2.132 -
2.133 - def dispatch_dict(self, d, *args):
2.134 - results = {}
2.135 - for name, node in d.items():
2.136 - results[name] = self.dispatch(node, *args)
2.137 - return results
2.138 -
2.139 -# Simplified program nodes.
2.140 -
2.141 -class Node:
2.142 -
2.143 - """
2.144 - A result node with common attributes:
2.145 -
2.146 - original The original node from which this node was created.
2.147 - defining Whether the node defines something in the original program.
2.148 - name Any name involved (variable or attribute).
2.149 - index Any index involved (temporary variable name).
2.150 - value Any constant value.
2.151 - ref Any reference to (for example) subprograms.
2.152 - nstype Any indication of the namespace type involved in a name access.
2.153 -
2.154 - Expression-related attributes:
2.155 -
2.156 - expr Any contributing expression.
2.157 - lvalue Any target expression.
2.158 - test Any test expression in a conditional instruction.
2.159 -
2.160 - Invocation and subprogram attributes:
2.161 -
2.162 - args Any collection of argument nodes.
2.163 - params Any collection of parameter nodes and defaults.
2.164 -
2.165 - Statement-grouping attributes:
2.166 -
2.167 - body Any conditional code depending on the success of a test.
2.168 - else_ Any conditional code depending on the failure of a test.
2.169 - handler Any exception handler code.
2.170 - finally_ Any code which will be executed regardless.
2.171 - code Any unconditional code.
2.172 - choices Any choices which may be included in the final program.
2.173 - """
2.174 -
2.175 - common_attributes = "name", "index", "value", "nstype", "internal", "returns_value", "is_method", "ref", "module", "structures", "original"
2.176 - expression_attributes = "expr", "lvalue", "test"
2.177 - argument_attributes = "star", "dstar"
2.178 - invocation_attributes = "params", # not "args" - see "pos_args", "kw_args"
2.179 - grouping_attributes = "code", "body", "else_", "handler", "finally_", "choices"
2.180 -
2.181 - def __init__(self, original=None, defining=0, **kw):
2.182 -
2.183 - """
2.184 - Initialise a program node with a link to an optional 'original' AST
2.185 - node. An optional 'defining' parameter (if set to a true value), sets
2.186 - this node as the defining node in the original.
2.187 - """
2.188 -
2.189 - self.original = original
2.190 - self.defining = defining
2.191 - self.copies = {}
2.192 -
2.193 - if self.original is not None and defining:
2.194 - self.original._node = self
2.195 - for name, value in kw.items():
2.196 - setattr(self, name, value)
2.197 -
2.198 - def __repr__(self):
2.199 -
2.200 - "Return a readable representation."
2.201 -
2.202 - if hasattr(self, "full_name"):
2.203 - s = "%s '%s'" % (self.__class__.__name__, self.full_name())
2.204 - elif hasattr(self, "name"):
2.205 - s = "%s '%s'" % (self.__class__.__name__, self.name)
2.206 - elif hasattr(self, "index"):
2.207 - s = "%s (%s)" % (self.__class__.__name__, self.index)
2.208 - elif hasattr(self, "value"):
2.209 - s = "%s %s" % (self.__class__.__name__, repr(self.value))
2.210 - elif hasattr(self, "ref"):
2.211 - s = "%s '%s'" % (self.__class__.__name__, name(self.ref, self.ref.name))
2.212 - else:
2.213 - s = "%s" % (self.__class__.__name__,)
2.214 -
2.215 - # Annotations.
2.216 -
2.217 - if hasattr(self, "types"):
2.218 - return "%s -> %s" % (s, self.types)
2.219 - else:
2.220 - return s
2.221 -
2.222 - def _pprint(self, indent, continuation, s, stream=None):
2.223 -
2.224 - """
2.225 - Print, at the given 'indent' level, with the given 'continuation' text,
2.226 - the string 's', either to the given, optional 'stream' or to standard
2.227 - output, this node's "pretty" representation.
2.228 - """
2.229 -
2.230 - stream = stream or sys.stdout
2.231 - if continuation:
2.232 - print >>stream, (" " * max(0, indent - len(continuation))) + continuation + s
2.233 - else:
2.234 - print >>stream, (" " * indent) + s
2.235 -
2.236 - def pprint(self, indent=0, continuation=None, stream=None):
2.237 -
2.238 - """
2.239 - Print, at the given, optional 'indent', with the given optional
2.240 - 'continuation' text, either to the given, optional 'stream' or to
2.241 - standard output, this node's "pretty" representation along with its
2.242 - children and their "pretty" representation (and so on).
2.243 - """
2.244 -
2.245 - stream = stream or sys.stdout
2.246 - self._pprint(indent, continuation, repr(self), stream)
2.247 -
2.248 - # Subprogram-related details.
2.249 -
2.250 - if hasattr(self, "params"):
2.251 - for name, default in self.params:
2.252 - self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
2.253 - if hasattr(self, "star") and self.star:
2.254 - name, default = self.star
2.255 - self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
2.256 - if hasattr(self, "dstar") and self.dstar:
2.257 - name, default = self.dstar
2.258 - self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
2.259 - if getattr(self, "internal", 0):
2.260 - self._pprint(indent + 2, "( ", "internal", stream=stream)
2.261 - if getattr(self, "structure", 0):
2.262 - self._pprint(indent + 2, "( ", "structure '%s'" % self.structure.name, stream=stream)
2.263 -
2.264 - # Expression-related details.
2.265 -
2.266 - if hasattr(self, "expr"):
2.267 - self.expr.pprint(indent + 2, "- ", stream=stream)
2.268 - if hasattr(self, "nodes"):
2.269 - for node in self.nodes:
2.270 - node.pprint(indent + 2, "- ", stream=stream)
2.271 - if hasattr(self, "lvalue"):
2.272 - self.lvalue.pprint(indent + 2, "->", stream=stream)
2.273 - if hasattr(self, "nstype"):
2.274 - self._pprint(indent + 2, "", self.nstype, stream=stream)
2.275 - if hasattr(self, "args"):
2.276 - for arg in self.pos_args:
2.277 - arg.pprint(indent + 2, "( ", stream=stream)
2.278 - for name, arg in self.kw_args.items():
2.279 - arg.pprint(indent + 2, "( ", stream=stream)
2.280 - if hasattr(self, "star") and self.star:
2.281 - self.star.pprint(indent + 2, "( ", stream=stream)
2.282 - if hasattr(self, "dstar") and self.dstar:
2.283 - self.dstar.pprint(indent + 2, "( ", stream=stream)
2.284 -
2.285 - # Statement-related details.
2.286 -
2.287 - if hasattr(self, "test"):
2.288 - self.test.pprint(indent + 2, "? ", stream=stream)
2.289 - for attr in self.grouping_attributes:
2.290 - if hasattr(self, attr) and getattr(self, attr):
2.291 - self._pprint(indent, "", "%s {" % attr, stream=stream)
2.292 - for node in getattr(self, attr):
2.293 - node.pprint(indent + 2, stream=stream)
2.294 - self._pprint(indent, "", "}", stream=stream)
2.295 -
2.296 - # Annotations.
2.297 -
2.298 - if hasattr(self, "accesses"):
2.299 - self._pprint(indent, "", "--------", stream=stream)
2.300 - for ref, attributes in self.accesses.items():
2.301 - self._pprint(indent + 2, "| ", "when %s: %s" % (ref, ", ".join([("%s via %s" % attr_acc) for attr_acc in attributes])), stream=stream)
2.302 - self._pprint(indent, "", "--------", stream=stream)
2.303 - if hasattr(self, "writes"):
2.304 - self._pprint(indent, "", "--------", stream=stream)
2.305 - for ref, attribute in self.writes.items():
2.306 - self._pprint(indent + 2, "| ", "when %s: %s" % (ref, attribute), stream=stream)
2.307 - self._pprint(indent, "", "--------", stream=stream)
2.308 -
2.309 - # Node discovery functions.
2.310 -
2.311 - def active(self):
2.312 -
2.313 - "Return the active copies of this node or a list containing this node."
2.314 -
2.315 - return self.copies.values() or [self]
2.316 -
2.317 - # Node manipulation functions.
2.318 -
2.319 - def copy(self, instance=None, new_name=None):
2.320 -
2.321 - """
2.322 - Perform a deep copy of the node, optionally specifying the 'instance'
2.323 - for whom the copy has been requested and a 'new_name' for the copied
2.324 - node. Return new unannotated copies of the node and its descendants.
2.325 - """
2.326 -
2.327 - # Copy the common attributes of this node.
2.328 -
2.329 - common = {}
2.330 - for attr in self.common_attributes:
2.331 - if hasattr(self, attr):
2.332 - common[attr] = getattr(self, attr)
2.333 -
2.334 - # Add new attributes specially for copies.
2.335 -
2.336 - common["instance"] = instance
2.337 -
2.338 - if new_name is not None:
2.339 - common["copy_of"] = self
2.340 - common["name"] = new_name
2.341 -
2.342 - # Instantiate the copy, avoiding side-effects with original and defining.
2.343 -
2.344 - node = self.__class__(**common)
2.345 - node.defining = self.defining
2.346 -
2.347 - # Add links to copies from originals.
2.348 -
2.349 - self.copies[instance] = node
2.350 -
2.351 - # Copy attributes of different types.
2.352 -
2.353 - for attr in self.expression_attributes:
2.354 - if hasattr(self, attr):
2.355 - n = getattr(self, attr)
2.356 - if n is None:
2.357 - n2 = n
2.358 - else:
2.359 - n2 = n.copy(instance)
2.360 - setattr(node, attr, n2)
2.361 -
2.362 - for attr in self.argument_attributes:
2.363 - if hasattr(self, attr):
2.364 - t = getattr(self, attr)
2.365 - if t is None:
2.366 - t2 = t
2.367 - else:
2.368 - name, n = t
2.369 - n2 = n.copy(instance)
2.370 - t2 = name, n2
2.371 - setattr(node, attr, t2)
2.372 -
2.373 - for attr in self.invocation_attributes:
2.374 - if hasattr(self, attr):
2.375 - l = getattr(self, attr)
2.376 - l2 = []
2.377 - for name, n in l:
2.378 - if n is None:
2.379 - l2.append((name, n))
2.380 - else:
2.381 - l2.append((name, n.copy(instance)))
2.382 - setattr(node, attr, l2)
2.383 -
2.384 - for attr in self.grouping_attributes:
2.385 - if hasattr(self, attr):
2.386 - l = getattr(self, attr)
2.387 - setattr(node, attr, [n.copy(instance) for n in l])
2.388 -
2.389 - # Arguments are usually processed further - "args" is useless.
2.390 -
2.391 - if hasattr(self, "pos_args"):
2.392 - node.pos_args = [n.copy(instance) for n in self.pos_args]
2.393 -
2.394 - if hasattr(self, "kw_args"):
2.395 - node.kw_args = {}
2.396 - for name, n in self.kw_args.items():
2.397 - node.kw_args[name] = n.copy(instance)
2.398 -
2.399 - return node
2.400 -
2.401 -# Comparable nodes based on naming.
2.402 -
2.403 -class Comparable(Node):
2.404 -
2.405 - "Comparable nodes implementing the 'full_name' method."
2.406 -
2.407 - def __eq__(self, other):
2.408 - # NOTE: Single instance: all instances are the same
2.409 - # NOTE: Multiple instances: all instances are different
2.410 - if hasattr(other, "full_name"):
2.411 - return self.full_name() == other.full_name()
2.412 - else:
2.413 - return NotImplemented
2.414 -
2.415 - def __hash__(self):
2.416 - return id(self)
2.417 -
2.418 -# These are the supported "operations" described by simplified program nodes.
2.419 -
2.420 -class Pass(Node): "A placeholder node corresponding to pass."
2.421 -class Assign(Node): "A grouping node for assignment-related operations."
2.422 -class Keyword(Node): "A grouping node for keyword arguments."
2.423 -class Global(Node): "A global name designator."
2.424 -class Import(Node): "A module import operation."
2.425 -class LoadTemp(Node): "Load a previously-stored temporary value."
2.426 -class LoadName(Node): "Load a named object."
2.427 -class LoadAttr(Node): "Load an object attribute."
2.428 -class LoadRef(Node): "Load a reference, typically a subprogram or a constant."
2.429 -class LoadExc(Node): "Load a handled exception."
2.430 -class ResetExc(Node): "Reset the exception state."
2.431 -class StoreTemp(Node): "Store a temporary value."
2.432 -class StoreName(Node): "Associate a name with an object."
2.433 -class StoreAttr(Node): "Associate an object's attribute with a value."
2.434 -class ReleaseTemp(Node): "Release a temporary value."
2.435 -class Try(Node): "A try...except...else...finally grouping node."
2.436 -class Raise(Node): "An exception raising node."
2.437 -class Not(Node): "A negation of an expression."
2.438 -class CheckType(Node): "Check a value's type from a list of choices."
2.439 -
2.440 -# There are two types of return node: return from function and return from
2.441 -# block.
2.442 -
2.443 -class Return(Node):
2.444 -
2.445 - "Return an evaluated expression."
2.446 -
2.447 - pass
2.448 -
2.449 -class ReturnFromFunction(Return):
2.450 - pass
2.451 -
2.452 -class ReturnFromBlock(Return):
2.453 - pass
2.454 -
2.455 -# NOTE: Not actually supported.
2.456 -# Additionally, yield statements act like return statements for the purposes
2.457 -# of this system.
2.458 -
2.459 -class Yield(ReturnFromFunction):
2.460 - pass
2.461 -
2.462 -# Some behaviour is set as the default in conditional nodes but may be
2.463 -# overridden.
2.464 -
2.465 -class Conditional(Node):
2.466 -
2.467 - "A conditional node consisting of a test and outcomes."
2.468 -
2.469 - def __init__(self, *args, **kw):
2.470 - self.isolate_test = 0
2.471 - Node.__init__(self, *args, **kw)
2.472 -
2.473 -# Invocations involve some more work to process calculated attributes.
2.474 -
2.475 -class Invoke(Node):
2.476 -
2.477 - "An invocation."
2.478 -
2.479 - pass
2.480 -
2.481 -class InvokeFunction(Invoke):
2.482 -
2.483 - "A function or method invocation."
2.484 -
2.485 - def __init__(self, *args, **kw):
2.486 - self.args = []
2.487 - self.star = None
2.488 - self.dstar = None
2.489 - Invoke.__init__(self, *args, **kw)
2.490 - self.set_args(self.args)
2.491 - self.share_locals = 0
2.492 -
2.493 - def set_args(self, args):
2.494 -
2.495 - "Sort the 'args' into positional and keyword arguments."
2.496 -
2.497 - self.pos_args = []
2.498 - self.kw_args = {}
2.499 - add_kw = 0
2.500 - for arg in args:
2.501 - if not add_kw:
2.502 - if not isinstance(arg, Keyword):
2.503 - self.pos_args.append(arg)
2.504 - else:
2.505 - add_kw = 1
2.506 - if add_kw:
2.507 - if isinstance(arg, Keyword):
2.508 - self.kw_args[arg.name] = arg.expr
2.509 - else:
2.510 - raise TypeError, "Positional argument appears after keyword arguments in '%s'." % self
2.511 -
2.512 -class InvokeRef(Invoke):
2.513 -
2.514 - "A block or loop invocation."
2.515 -
2.516 - def __init__(self, *args, **kw):
2.517 - self.share_locals = 1
2.518 - Invoke.__init__(self, *args, **kw)
2.519 -
2.520 -# Program structure nodes.
2.521 -
2.522 -class Subprogram(Node, WithName):
2.523 -
2.524 - "A subprogram: functions, methods and loops."
2.525 -
2.526 - def __init__(self, *args, **kw):
2.527 - Node.__init__(self, *args, **kw)
2.528 - WithName.__init__(self)
2.529 - self.returns = []
2.530 - self.return_locals = []
2.531 - self.raises = []
2.532 -
2.533 -class Module(Comparable):
2.534 -
2.535 - "A Python module."
2.536 -
2.537 - def full_name(self):
2.538 - return "module %s" % self.name
2.539 -
2.540 -# Special non-program nodes.
2.541 -
2.542 -class Structure(Comparable): "A non-program node containing some kind of namespace."
2.543 -
2.544 -class _Class(Structure, WithName):
2.545 -
2.546 - "A Python class."
2.547 -
2.548 - def __init__(self, *args, **kw):
2.549 - Structure.__init__(self, *args, **kw)
2.550 - WithName.__init__(self)
2.551 -
2.552 - def full_name(self):
2.553 - return "class %s" % self._full_name
2.554 -
2.555 -class SingleInstanceClass(_Class):
2.556 -
2.557 - "A Python class."
2.558 -
2.559 - def __init__(self, *args, **kw):
2.560 - _Class.__init__(self, *args, **kw)
2.561 - self.instance = None
2.562 -
2.563 - def has_instance(self, node):
2.564 - return self.instance is not None
2.565 -
2.566 - def add_instance(self, node, instance):
2.567 - self.instance = instance
2.568 -
2.569 - def get_instance(self, node):
2.570 - return self.instance
2.571 -
2.572 - def get_instance_name(self, instance):
2.573 - return self._full_name
2.574 -
2.575 - # Attribute propagation.
2.576 -
2.577 - def get_attribute_for_instance(self, attribute, instance):
2.578 - return attribute
2.579 -
2.580 -class MultipleInstanceClass(_Class):
2.581 -
2.582 - "A Python class."
2.583 -
2.584 - def __init__(self, *args, **kw):
2.585 - _Class.__init__(self, *args, **kw)
2.586 - self.instances = {}
2.587 - self.attributes_for_instances = {}
2.588 -
2.589 - def _get_key(self, node):
2.590 - return id(getattr(node, "original", None)) # self.module.original
2.591 -
2.592 - def has_instance(self, node):
2.593 - return self.instances.has_key(self._get_key(node))
2.594 -
2.595 - def add_instance(self, node, instance):
2.596 - self.instances[self._get_key(node)] = instance
2.597 -
2.598 - def get_instance(self, node):
2.599 - return self.instances[self._get_key(node)]
2.600 -
2.601 - def get_instance_name(self, instance):
2.602 - return name(instance, self._full_name)
2.603 -
2.604 - # Attribute propagation.
2.605 -
2.606 - def get_attribute_for_instance(self, attribute, instance):
2.607 -
2.608 - # Create specialised methods.
2.609 -
2.610 - if isinstance(attribute.type, Subprogram):
2.611 - subprogram = attribute.type
2.612 -
2.613 - # Each instance may have its own version of the subprogram.
2.614 -
2.615 - key = (subprogram, instance)
2.616 - if not self.attributes_for_instances.has_key(key):
2.617 - new_subprogram = subprogram.copy(instance, subprogram.full_name())
2.618 - subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram
2.619 - self.attributes_for_instances[key] = Attribute(attribute.context, new_subprogram)
2.620 - print "New subprogram", new_subprogram, "for", key
2.621 -
2.622 - return self.attributes_for_instances[key]
2.623 -
2.624 - # The original nodes are returned for other attributes.
2.625 -
2.626 - else:
2.627 - return attribute
2.628 -
2.629 -class SelectiveMultipleInstanceClass(MultipleInstanceClass):
2.630 -
2.631 - "A Python class which provides multiple instances depending on the class."
2.632 -
2.633 - def _get_key(self, node):
2.634 - if self.namespace.has_key("__atomic__"):
2.635 - return id(self)
2.636 - else:
2.637 - return MultipleInstanceClass._get_key(self, node)
2.638 -
2.639 -class ProlificMultipleInstanceClass(MultipleInstanceClass):
2.640 -
2.641 - """
2.642 - A Python class which provides multiple instances for different versions of
2.643 - methods. In order to avoid unbounded instance production (since new
2.644 - instances cause new copies of methods which in turn would cause new
2.645 - instances), a relations dictionary is maintained which attempts to map
2.646 - "requesting instances" to existing instances, suggesting such instances in
2.647 - preference to new ones.
2.648 - """
2.649 -
2.650 - def __init__(self, *args, **kw):
2.651 - MultipleInstanceClass.__init__(self, *args, **kw)
2.652 - self.instance_relations = {}
2.653 -
2.654 - def _get_key(self, node):
2.655 - if self.namespace.has_key("__atomic__"):
2.656 - return id(self)
2.657 - else:
2.658 - return id(node)
2.659 -
2.660 - def has_instance(self, node):
2.661 - requesting_instance = getattr(node, "instance", None)
2.662 - #return requesting_instance is not None and requesting_instance.get_class() is self or \
2.663 - return self.instance_relations.has_key(requesting_instance) or self.instances.has_key(self._get_key(node))
2.664 -
2.665 - def add_instance(self, node, instance):
2.666 - requesting_instance = getattr(node, "instance", None)
2.667 - print "New instance", instance, "for", id(node), requesting_instance
2.668 - self.instances[self._get_key(node)] = instance
2.669 - if requesting_instance is not None:
2.670 - self.instance_relations[requesting_instance] = instance
2.671 - requesting_instance.get_class().instance_relations[instance] = requesting_instance
2.672 -
2.673 - def get_instance(self, node):
2.674 - requesting_instance = getattr(node, "instance", None)
2.675 - #if requesting_instance is not None and requesting_instance.get_class() is self:
2.676 - # return requesting_instance
2.677 - return self.instance_relations.get(requesting_instance) or self.instances[self._get_key(node)]
2.678 -
2.679 -class Instance(Structure):
2.680 -
2.681 - "An instance."
2.682 -
2.683 - def full_name(self):
2.684 - return self.get_class().get_instance_name(self)
2.685 -
2.686 - def get_class(self):
2.687 - return self.namespace.load("__class__")[0].type
2.688 -
2.689 - def __repr__(self):
2.690 - return "Instance of type '%s'" % self.full_name()
2.691 -
2.692 - def __eq__(self, other):
2.693 - # NOTE: Single instance: all instances are the same
2.694 - # NOTE: Multiple instances: all instances are different
2.695 - return self.full_name() == other.full_name()
2.696 -
2.697 - def __hash__(self):
2.698 - return id(self)
2.699 -
2.700 -class Constant(Instance):
2.701 -
2.702 - "A constant initialised with a type name for future processing."
2.703 -
2.704 - def __init__(self, *args, **kw):
2.705 - Instance.__init__(self, *args, **kw)
2.706 - self.typename = self.value.__class__.__name__
2.707 -
2.708 - # NOTE: Hacked full_name avoiding instantiation ordering issues:
2.709 - # NOTE: initialise built-in types, initialise built-in constants.
2.710 -
2.711 - #def full_name(self):
2.712 - # try:
2.713 - # return Instance.full_name(self)
2.714 - # except KeyError:
2.715 - # return self.typename + "-c"
2.716 -
2.717 -class Attribute:
2.718 -
2.719 - """
2.720 - An attribute abstraction, indicating the type of the attribute along with
2.721 - its context or origin.
2.722 - """
2.723 -
2.724 - def __init__(self, context, type):
2.725 - self.context = context
2.726 - self.type = type
2.727 -
2.728 - def __eq__(self, other):
2.729 - return hasattr(other, "type") and other.type == self.type or other == self.type
2.730 -
2.731 - def __repr__(self):
2.732 - return "Attribute(%s, %s)" % (repr(self.context), repr(self.type))
2.733 -
2.734 - def __hash__(self):
2.735 - return id(self)
2.736 -
2.737 -# Additional program and AST nodes.
2.738 -
2.739 -class Self:
2.740 -
2.741 - """
2.742 - A program node encapsulating object/context information in an argument list.
2.743 - This is not particularly like Attribute, Class, Instance or other such
2.744 - things, since it actually appears in the program representation.
2.745 - """
2.746 -
2.747 - def __init__(self, attribute):
2.748 - self.types = [attribute]
2.749 -
2.750 -class Op:
2.751 -
2.752 - "A replacement AST node representing an operation in a Compare construct."
2.753 -
2.754 - def __init__(self, name, expr):
2.755 - self.name = name
2.756 - self.expr = expr
2.757 -
2.758 -# Configuration setting.
2.759 -
2.760 -Class = SingleInstanceClass
2.761 -#Class = MultipleInstanceClass
2.762 -
2.763 -def set_single_instance_mode():
2.764 - global Class
2.765 - Class = SingleInstanceClass
2.766 -
2.767 -def set_multiple_instance_mode():
2.768 - global Class
2.769 - Class = MultipleInstanceClass
2.770 -
2.771 -def set_selective_multiple_instance_mode():
2.772 - global Class
2.773 - Class = SelectiveMultipleInstanceClass
2.774 -
2.775 -def set_prolific_multiple_instance_mode():
2.776 - global Class
2.777 - Class = ProlificMultipleInstanceClass
2.778 -
2.779 -# vim: tabstop=4 expandtab shiftwidth=4
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/simplified/__init__.py Fri Apr 06 00:53:18 2007 +0200
3.3 @@ -0,0 +1,37 @@
3.4 +#!/usr/bin/env python
3.5 +
3.6 +"""
3.7 +Simplified program representation.
3.8 +
3.9 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
3.10 +
3.11 +This software is free software; you can redistribute it and/or
3.12 +modify it under the terms of the GNU General Public License as
3.13 +published by the Free Software Foundation; either version 2 of
3.14 +the License, or (at your option) any later version.
3.15 +
3.16 +This software is distributed in the hope that it will be useful,
3.17 +but WITHOUT ANY WARRANTY; without even the implied warranty of
3.18 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3.19 +GNU General Public License for more details.
3.20 +
3.21 +You should have received a copy of the GNU General Public
3.22 +License along with this library; see the file LICENCE.txt
3.23 +If not, write to the Free Software Foundation, Inc.,
3.24 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
3.25 +"""
3.26 +
3.27 +__version__ = "0.1"
3.28 +
3.29 +from simplified.ast import *
3.30 +from simplified.data import *
3.31 +from simplified.program import *
3.32 +from simplified.utils import *
3.33 +import os
3.34 +
3.35 +# Location of the built-in libraries.
3.36 +# NOTE: Change this if the package structure changes.
3.37 +
3.38 +libdir = os.path.join(os.path.split(os.path.split(__file__)[0])[0], "lib")
3.39 +
3.40 +# vim: tabstop=4 expandtab shiftwidth=4
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/simplified/ast.py Fri Apr 06 00:53:18 2007 +0200
4.3 @@ -0,0 +1,44 @@
4.4 +#!/usr/bin/env python
4.5 +
4.6 +"""
4.7 +Additional program AST nodes.
4.8 +
4.9 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
4.10 +
4.11 +This software is free software; you can redistribute it and/or
4.12 +modify it under the terms of the GNU General Public License as
4.13 +published by the Free Software Foundation; either version 2 of
4.14 +the License, or (at your option) any later version.
4.15 +
4.16 +This software is distributed in the hope that it will be useful,
4.17 +but WITHOUT ANY WARRANTY; without even the implied warranty of
4.18 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4.19 +GNU General Public License for more details.
4.20 +
4.21 +You should have received a copy of the GNU General Public
4.22 +License along with this library; see the file LICENCE.txt
4.23 +If not, write to the Free Software Foundation, Inc.,
4.24 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
4.25 +"""
4.26 +
4.27 +class Self:
4.28 +
4.29 + """
4.30 + A program node encapsulating object/context information in an argument list.
4.31 + This is not particularly like Attribute, Class, Instance or other such
4.32 + things, since it actually appears in the program representation.
4.33 + """
4.34 +
4.35 + def __init__(self, attribute):
4.36 + self.types = set()
4.37 + self.types.add(attribute)
4.38 +
4.39 +class Op:
4.40 +
4.41 + "A replacement AST node representing an operation in a Compare construct."
4.42 +
4.43 + def __init__(self, name, expr):
4.44 + self.name = name
4.45 + self.expr = expr
4.46 +
4.47 +# vim: tabstop=4 expandtab shiftwidth=4
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/simplified/data.py Fri Apr 06 00:53:18 2007 +0200
5.3 @@ -0,0 +1,237 @@
5.4 +#!/usr/bin/env python
5.5 +
5.6 +"""
5.7 +Simplified program data.
5.8 +
5.9 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
5.10 +
5.11 +This software is free software; you can redistribute it and/or
5.12 +modify it under the terms of the GNU General Public License as
5.13 +published by the Free Software Foundation; either version 2 of
5.14 +the License, or (at your option) any later version.
5.15 +
5.16 +This software is distributed in the hope that it will be useful,
5.17 +but WITHOUT ANY WARRANTY; without even the implied warranty of
5.18 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5.19 +GNU General Public License for more details.
5.20 +
5.21 +You should have received a copy of the GNU General Public
5.22 +License along with this library; see the file LICENCE.txt
5.23 +If not, write to the Free Software Foundation, Inc.,
5.24 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
5.25 +"""
5.26 +
5.27 +from simplified.utils import Structure, WithName, name
5.28 +
5.29 +# Special non-program nodes.
5.30 +
5.31 +class _Class(Structure, WithName):
5.32 +
5.33 + "A Python class."
5.34 +
5.35 + def __init__(self, *args, **kw):
5.36 + Structure.__init__(self, *args, **kw)
5.37 + WithName.__init__(self)
5.38 +
5.39 + def full_name(self):
5.40 + return "class %s" % self._full_name
5.41 +
5.42 +class SingleInstanceClass(_Class):
5.43 +
5.44 + "A Python class."
5.45 +
5.46 + def __init__(self, *args, **kw):
5.47 + _Class.__init__(self, *args, **kw)
5.48 + self.instance = None
5.49 +
5.50 + def has_instance(self, node):
5.51 + return self.instance is not None
5.52 +
5.53 + def add_instance(self, node, instance):
5.54 + self.instance = instance
5.55 +
5.56 + def get_instance(self, node):
5.57 + return self.instance
5.58 +
5.59 + def get_instance_name(self, instance):
5.60 + return self._full_name
5.61 +
5.62 + # Attribute propagation.
5.63 +
5.64 + def get_attribute_for_instance(self, attribute, instance):
5.65 + return attribute
5.66 +
5.67 +class MultipleInstanceClass(_Class):
5.68 +
5.69 + "A Python class."
5.70 +
5.71 + def __init__(self, *args, **kw):
5.72 + _Class.__init__(self, *args, **kw)
5.73 + self.instances = {}
5.74 + self.attributes_for_instances = {}
5.75 +
5.76 + def _get_key(self, node):
5.77 + return id(getattr(node, "original", None)) # self.module.original
5.78 +
5.79 + def has_instance(self, node):
5.80 + return self.instances.has_key(self._get_key(node))
5.81 +
5.82 + def add_instance(self, node, instance):
5.83 + self.instances[self._get_key(node)] = instance
5.84 +
5.85 + def get_instance(self, node):
5.86 + return self.instances[self._get_key(node)]
5.87 +
5.88 + def get_instance_name(self, instance):
5.89 + return name(instance, self._full_name)
5.90 +
5.91 + # Attribute propagation.
5.92 +
5.93 + def get_attribute_for_instance(self, attribute, instance):
5.94 +
5.95 + # Create specialised methods.
5.96 +
5.97 + if isinstance(attribute.type, Subprogram):
5.98 + subprogram = attribute.type
5.99 +
5.100 + # Each instance may have its own version of the subprogram.
5.101 +
5.102 + key = (subprogram, instance)
5.103 + if not self.attributes_for_instances.has_key(key):
5.104 + new_subprogram = subprogram.copy(instance, subprogram.full_name())
5.105 + subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram
5.106 + self.attributes_for_instances[key] = Attribute(attribute.context, new_subprogram)
5.107 + print "New subprogram", new_subprogram, "for", key
5.108 +
5.109 + return self.attributes_for_instances[key]
5.110 +
5.111 + # The original nodes are returned for other attributes.
5.112 +
5.113 + else:
5.114 + return attribute
5.115 +
5.116 +class SelectiveMultipleInstanceClass(MultipleInstanceClass):
5.117 +
5.118 + "A Python class which provides multiple instances depending on the class."
5.119 +
5.120 + def _get_key(self, node):
5.121 + if self.namespace.has_key("__atomic__"):
5.122 + return id(self)
5.123 + else:
5.124 + return MultipleInstanceClass._get_key(self, node)
5.125 +
5.126 +class ProlificMultipleInstanceClass(MultipleInstanceClass):
5.127 +
5.128 + """
5.129 + A Python class which provides multiple instances for different versions of
5.130 + methods. In order to avoid unbounded instance production (since new
5.131 + instances cause new copies of methods which in turn would cause new
5.132 + instances), a relations dictionary is maintained which attempts to map
5.133 + "requesting instances" to existing instances, suggesting such instances in
5.134 + preference to new ones.
5.135 + """
5.136 +
5.137 + def __init__(self, *args, **kw):
5.138 + MultipleInstanceClass.__init__(self, *args, **kw)
5.139 + self.instance_relations = {}
5.140 +
5.141 + def _get_key(self, node):
5.142 + if self.namespace.has_key("__atomic__"):
5.143 + return id(self)
5.144 + else:
5.145 + return id(node)
5.146 +
5.147 + def has_instance(self, node):
5.148 + requesting_instance = getattr(node, "instance", None)
5.149 + #return requesting_instance is not None and requesting_instance.get_class() is self or \
5.150 + return self.instance_relations.has_key(requesting_instance) or self.instances.has_key(self._get_key(node))
5.151 +
5.152 + def add_instance(self, node, instance):
5.153 + requesting_instance = getattr(node, "instance", None)
5.154 + print "New instance", instance, "for", id(node), requesting_instance
5.155 + self.instances[self._get_key(node)] = instance
5.156 + if requesting_instance is not None:
5.157 + self.instance_relations[requesting_instance] = instance
5.158 + requesting_instance.get_class().instance_relations[instance] = requesting_instance
5.159 +
5.160 + def get_instance(self, node):
5.161 + requesting_instance = getattr(node, "instance", None)
5.162 + #if requesting_instance is not None and requesting_instance.get_class() is self:
5.163 + # return requesting_instance
5.164 + return self.instance_relations.get(requesting_instance) or self.instances[self._get_key(node)]
5.165 +
5.166 +class Instance(Structure):
5.167 +
5.168 + "An instance."
5.169 +
5.170 + def full_name(self):
5.171 + return self.get_class().get_instance_name(self)
5.172 +
5.173 + def get_class(self):
5.174 + for n in self.namespace.load("__class__"):
5.175 + return n.type
5.176 + else:
5.177 + raise ValueError, "__class__"
5.178 +
5.179 + def __repr__(self):
5.180 + return "Instance of type '%s'" % self.full_name()
5.181 +
5.182 + def __eq__(self, other):
5.183 + # NOTE: Single instance: all instances are the same
5.184 + # NOTE: Multiple instances: all instances are different
5.185 + return self.full_name() == other.full_name()
5.186 +
5.187 + def __hash__(self):
5.188 + return id(self)
5.189 +
5.190 +class Constant:
5.191 +
5.192 + "A constant initialised with a type name for future processing."
5.193 +
5.194 + def __init__(self, name, value):
5.195 + self.name = name
5.196 + self.value = value
5.197 + self.typename = self.value.__class__.__name__
5.198 +
5.199 +class Attribute:
5.200 +
5.201 + """
5.202 + An attribute abstraction, indicating the type of the attribute along with
5.203 + its context or origin.
5.204 + """
5.205 +
5.206 + def __init__(self, context, type):
5.207 + self.context = context
5.208 + self.type = type
5.209 +
5.210 + def __eq__(self, other):
5.211 + return hasattr(other, "type") and other.type == self.type or other == self.type
5.212 +
5.213 + def __repr__(self):
5.214 + return "Attribute(%s, %s)" % (repr(self.context), repr(self.type))
5.215 +
5.216 + def __hash__(self):
5.217 + return id(self.type)
5.218 +
5.219 +# Configuration setting.
5.220 +
5.221 +Class = SingleInstanceClass
5.222 +#Class = MultipleInstanceClass
5.223 +
5.224 +def set_single_instance_mode():
5.225 + global Class
5.226 + Class = SingleInstanceClass
5.227 +
5.228 +def set_multiple_instance_mode():
5.229 + global Class
5.230 + Class = MultipleInstanceClass
5.231 +
5.232 +def set_selective_multiple_instance_mode():
5.233 + global Class
5.234 + Class = SelectiveMultipleInstanceClass
5.235 +
5.236 +def set_prolific_multiple_instance_mode():
5.237 + global Class
5.238 + Class = ProlificMultipleInstanceClass
5.239 +
5.240 +# vim: tabstop=4 expandtab shiftwidth=4
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/simplified/program.py Fri Apr 06 00:53:18 2007 +0200
6.3 @@ -0,0 +1,407 @@
6.4 +#!/usr/bin/env python
6.5 +
6.6 +"""
6.7 +Simplified program nodes for easier type propagation and analysis. This module
6.8 +contains nodes representing program instructions or operations, program
6.9 +structure or organisation, and abstract program data.
6.10 +
6.11 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
6.12 +
6.13 +This software is free software; you can redistribute it and/or
6.14 +modify it under the terms of the GNU General Public License as
6.15 +published by the Free Software Foundation; either version 2 of
6.16 +the License, or (at your option) any later version.
6.17 +
6.18 +This software is distributed in the hope that it will be useful,
6.19 +but WITHOUT ANY WARRANTY; without even the implied warranty of
6.20 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6.21 +GNU General Public License for more details.
6.22 +
6.23 +You should have received a copy of the GNU General Public
6.24 +License along with this library; see the file LICENCE.txt
6.25 +If not, write to the Free Software Foundation, Inc.,
6.26 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
6.27 +"""
6.28 +
6.29 +from simplified.utils import Structure, WithName, name
6.30 +import sys
6.31 +
6.32 +# Simplified program nodes.
6.33 +
6.34 +class Node:
6.35 +
6.36 + """
6.37 + A result node with common attributes:
6.38 +
6.39 + original The original node from which this node was created.
6.40 + defining Whether the node defines something in the original program.
6.41 + name Any name involved (variable or attribute).
6.42 + index Any index involved (temporary variable name).
6.43 + value Any constant value.
6.44 + ref Any reference to (for example) subprograms.
6.45 + nstype Any indication of the namespace type involved in a name access.
6.46 +
6.47 + Expression-related attributes:
6.48 +
6.49 + expr Any contributing expression.
6.50 + lvalue Any target expression.
6.51 + test Any test expression in a conditional instruction.
6.52 +
6.53 + Invocation and subprogram attributes:
6.54 +
6.55 + args Any collection of argument nodes.
6.56 + params Any collection of parameter nodes and defaults.
6.57 +
6.58 + Statement-grouping attributes:
6.59 +
6.60 + body Any conditional code depending on the success of a test.
6.61 + else_ Any conditional code depending on the failure of a test.
6.62 + handler Any exception handler code.
6.63 + finally_ Any code which will be executed regardless.
6.64 + code Any unconditional code.
6.65 + choices Any choices which may be included in the final program.
6.66 + """
6.67 +
6.68 + common_attributes = "name", "index", "value", "nstype", "internal", "returns_value", "is_method", "ref", "module", "structures", "original"
6.69 + expression_attributes = "expr", "lvalue", "test"
6.70 + argument_attributes = "star", "dstar"
6.71 + invocation_attributes = "params", # not "args" - see "pos_args", "kw_args"
6.72 + grouping_attributes = "code", "body", "else_", "handler", "finally_", "choices"
6.73 +
6.74 + def __init__(self, original=None, defining=0, **kw):
6.75 +
6.76 + """
6.77 + Initialise a program node with a link to an optional 'original' AST
6.78 + node. An optional 'defining' parameter (if set to a true value), sets
6.79 + this node as the defining node in the original.
6.80 + """
6.81 +
6.82 + self.original = original
6.83 + self.defining = defining
6.84 + self.copies = {}
6.85 +
6.86 + if self.original is not None and defining:
6.87 + self.original._node = self
6.88 + for name, value in kw.items():
6.89 + setattr(self, name, value)
6.90 +
6.91 + # Annotations.
6.92 +
6.93 + self.types = set()
6.94 +
6.95 + def __repr__(self):
6.96 +
6.97 + "Return a readable representation."
6.98 +
6.99 + if hasattr(self, "full_name"):
6.100 + s = "%s '%s'" % (self.__class__.__name__, self.full_name())
6.101 + elif hasattr(self, "name"):
6.102 + s = "%s '%s'" % (self.__class__.__name__, self.name)
6.103 + elif hasattr(self, "index"):
6.104 + s = "%s (%s)" % (self.__class__.__name__, self.index)
6.105 + elif hasattr(self, "value"):
6.106 + s = "%s %s" % (self.__class__.__name__, repr(self.value))
6.107 + elif hasattr(self, "ref"):
6.108 + s = "%s '%s'" % (self.__class__.__name__, name(self.ref, self.ref.name))
6.109 + else:
6.110 + s = "%s" % (self.__class__.__name__,)
6.111 +
6.112 + # Annotations.
6.113 +
6.114 + if self.types:
6.115 + return "%s -> %s" % (s, self.types)
6.116 + else:
6.117 + return s
6.118 +
6.119 + def _pprint(self, indent, continuation, s, stream=None):
6.120 +
6.121 + """
6.122 + Print, at the given 'indent' level, with the given 'continuation' text,
6.123 + the string 's', either to the given, optional 'stream' or to standard
6.124 + output, this node's "pretty" representation.
6.125 + """
6.126 +
6.127 + stream = stream or sys.stdout
6.128 + if continuation:
6.129 + print >>stream, (" " * max(0, indent - len(continuation))) + continuation + s
6.130 + else:
6.131 + print >>stream, (" " * indent) + s
6.132 +
6.133 + def pprint(self, indent=0, continuation=None, stream=None):
6.134 +
6.135 + """
6.136 + Print, at the given, optional 'indent', with the given optional
6.137 + 'continuation' text, either to the given, optional 'stream' or to
6.138 + standard output, this node's "pretty" representation along with its
6.139 + children and their "pretty" representation (and so on).
6.140 + """
6.141 +
6.142 + stream = stream or sys.stdout
6.143 + self._pprint(indent, continuation, repr(self), stream)
6.144 +
6.145 + # Subprogram-related details.
6.146 +
6.147 + if hasattr(self, "params"):
6.148 + for name, default in self.params:
6.149 + self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
6.150 + if hasattr(self, "star") and self.star:
6.151 + name, default = self.star
6.152 + self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
6.153 + if hasattr(self, "dstar") and self.dstar:
6.154 + name, default = self.dstar
6.155 + self._pprint(indent + 2, "( ", "%s default %s" % (name, default), stream=stream)
6.156 + if getattr(self, "internal", 0):
6.157 + self._pprint(indent + 2, "( ", "internal", stream=stream)
6.158 + if getattr(self, "structure", 0):
6.159 + self._pprint(indent + 2, "( ", "structure '%s'" % self.structure.name, stream=stream)
6.160 +
6.161 + # Expression-related details.
6.162 +
6.163 + if hasattr(self, "expr"):
6.164 + self.expr.pprint(indent + 2, "- ", stream=stream)
6.165 + if hasattr(self, "nodes"):
6.166 + for node in self.nodes:
6.167 + node.pprint(indent + 2, "- ", stream=stream)
6.168 + if hasattr(self, "lvalue"):
6.169 + self.lvalue.pprint(indent + 2, "->", stream=stream)
6.170 + if hasattr(self, "nstype"):
6.171 + self._pprint(indent + 2, "", self.nstype, stream=stream)
6.172 + if hasattr(self, "args"):
6.173 + for arg in self.pos_args:
6.174 + arg.pprint(indent + 2, "( ", stream=stream)
6.175 + for name, arg in self.kw_args.items():
6.176 + arg.pprint(indent + 2, "( ", stream=stream)
6.177 + if hasattr(self, "star") and self.star:
6.178 + self.star.pprint(indent + 2, "( ", stream=stream)
6.179 + if hasattr(self, "dstar") and self.dstar:
6.180 + self.dstar.pprint(indent + 2, "( ", stream=stream)
6.181 +
6.182 + # Statement-related details.
6.183 +
6.184 + if hasattr(self, "test"):
6.185 + self.test.pprint(indent + 2, "? ", stream=stream)
6.186 + for attr in self.grouping_attributes:
6.187 + if hasattr(self, attr) and getattr(self, attr):
6.188 + self._pprint(indent, "", "%s {" % attr, stream=stream)
6.189 + for node in getattr(self, attr):
6.190 + node.pprint(indent + 2, stream=stream)
6.191 + self._pprint(indent, "", "}", stream=stream)
6.192 +
6.193 + # Annotations.
6.194 +
6.195 + if hasattr(self, "accesses"):
6.196 + self._pprint(indent, "", "--------", stream=stream)
6.197 + for ref, attributes in self.accesses.items():
6.198 + self._pprint(indent + 2, "| ", "when %s: %s" % (ref, ", ".join([("%s via %s" % attr_acc) for attr_acc in attributes])), stream=stream)
6.199 + self._pprint(indent, "", "--------", stream=stream)
6.200 + if hasattr(self, "writes"):
6.201 + self._pprint(indent, "", "--------", stream=stream)
6.202 + for ref, attribute in self.writes.items():
6.203 + self._pprint(indent + 2, "| ", "when %s: %s" % (ref, attribute), stream=stream)
6.204 + self._pprint(indent, "", "--------", stream=stream)
6.205 +
6.206 + # Node discovery functions.
6.207 +
6.208 + def active(self):
6.209 +
6.210 + "Return the active copies of this node or a list containing this node."
6.211 +
6.212 + return self.copies.values() or [self]
6.213 +
6.214 + # Node manipulation functions.
6.215 +
6.216 + def copy(self, instance=None, new_name=None):
6.217 +
6.218 + """
6.219 + Perform a deep copy of the node, optionally specifying the 'instance'
6.220 + for whom the copy has been requested and a 'new_name' for the copied
6.221 + node. Return new unannotated copies of the node and its descendants.
6.222 + """
6.223 +
6.224 + # Copy the common attributes of this node.
6.225 +
6.226 + common = {}
6.227 + for attr in self.common_attributes:
6.228 + if hasattr(self, attr):
6.229 + common[attr] = getattr(self, attr)
6.230 +
6.231 + # Add new attributes specially for copies.
6.232 +
6.233 + common["instance"] = instance
6.234 +
6.235 + if new_name is not None:
6.236 + common["copy_of"] = self
6.237 + common["name"] = new_name
6.238 +
6.239 + # Instantiate the copy, avoiding side-effects with original and defining.
6.240 +
6.241 + node = self.__class__(**common)
6.242 + node.defining = self.defining
6.243 +
6.244 + # Add links to copies from originals.
6.245 +
6.246 + self.copies[instance] = node
6.247 +
6.248 + # Copy attributes of different types.
6.249 +
6.250 + for attr in self.expression_attributes:
6.251 + if hasattr(self, attr):
6.252 + n = getattr(self, attr)
6.253 + if n is None:
6.254 + n2 = n
6.255 + else:
6.256 + n2 = n.copy(instance)
6.257 + setattr(node, attr, n2)
6.258 +
6.259 + for attr in self.argument_attributes:
6.260 + if hasattr(self, attr):
6.261 + t = getattr(self, attr)
6.262 + if t is None:
6.263 + t2 = t
6.264 + else:
6.265 + name, n = t
6.266 + n2 = n.copy(instance)
6.267 + t2 = name, n2
6.268 + setattr(node, attr, t2)
6.269 +
6.270 + for attr in self.invocation_attributes:
6.271 + if hasattr(self, attr):
6.272 + l = getattr(self, attr)
6.273 + l2 = []
6.274 + for name, n in l:
6.275 + if n is None:
6.276 + l2.append((name, n))
6.277 + else:
6.278 + l2.append((name, n.copy(instance)))
6.279 + setattr(node, attr, l2)
6.280 +
6.281 + for attr in self.grouping_attributes:
6.282 + if hasattr(self, attr):
6.283 + l = getattr(self, attr)
6.284 + setattr(node, attr, [n.copy(instance) for n in l])
6.285 +
6.286 + # Arguments are usually processed further - "args" is useless.
6.287 +
6.288 + if hasattr(self, "pos_args"):
6.289 + node.pos_args = [n.copy(instance) for n in self.pos_args]
6.290 +
6.291 + if hasattr(self, "kw_args"):
6.292 + node.kw_args = {}
6.293 + for name, n in self.kw_args.items():
6.294 + node.kw_args[name] = n.copy(instance)
6.295 +
6.296 + return node
6.297 +
6.298 +# These are the supported "operations" described by simplified program nodes.
6.299 +
6.300 +class Pass(Node): "A placeholder node corresponding to pass."
6.301 +class Assign(Node): "A grouping node for assignment-related operations."
6.302 +class Keyword(Node): "A grouping node for keyword arguments."
6.303 +class Global(Node): "A global name designator."
6.304 +class Import(Node): "A module import operation."
6.305 +class LoadTemp(Node): "Load a previously-stored temporary value."
6.306 +class LoadName(Node): "Load a named object."
6.307 +class LoadAttr(Node): "Load an object attribute."
6.308 +class LoadRef(Node): "Load a reference, typically a subprogram or a constant."
6.309 +class LoadExc(Node): "Load a handled exception."
6.310 +class ResetExc(Node): "Reset the exception state."
6.311 +class StoreTemp(Node): "Store a temporary value."
6.312 +class StoreName(Node): "Associate a name with an object."
6.313 +class StoreAttr(Node): "Associate an object's attribute with a value."
6.314 +class ReleaseTemp(Node): "Release a temporary value."
6.315 +class Try(Node): "A try...except...else...finally grouping node."
6.316 +class Raise(Node): "An exception raising node."
6.317 +class Not(Node): "A negation of an expression."
6.318 +class CheckType(Node): "Check a value's type from a list of choices."
6.319 +class Return(Node): "Return an evaluated expression."
6.320 +class Invoke(Node): "An invocation."
6.321 +
6.322 +# There are two types of return node: return from function and return from
6.323 +# block.
6.324 +
6.325 +class ReturnFromFunction(Return):
6.326 + pass
6.327 +
6.328 +class ReturnFromBlock(Return):
6.329 + pass
6.330 +
6.331 +# NOTE: Not actually supported.
6.332 +# Additionally, yield statements act like return statements for the purposes
6.333 +# of this system.
6.334 +
6.335 +class Yield(ReturnFromFunction):
6.336 + pass
6.337 +
6.338 +# Some behaviour is set as the default in conditional nodes but may be
6.339 +# overridden.
6.340 +
6.341 +class Conditional(Node):
6.342 +
6.343 + "A conditional node consisting of a test and outcomes."
6.344 +
6.345 + def __init__(self, *args, **kw):
6.346 + self.isolate_test = 0
6.347 + Node.__init__(self, *args, **kw)
6.348 +
6.349 +# Invocations involve some more work to process calculated attributes.
6.350 +
6.351 +class InvokeFunction(Invoke):
6.352 +
6.353 + "A function or method invocation."
6.354 +
6.355 + def __init__(self, *args, **kw):
6.356 + self.args = []
6.357 + self.star = None
6.358 + self.dstar = None
6.359 + Invoke.__init__(self, *args, **kw)
6.360 + self.set_args(self.args)
6.361 + self.share_locals = 0
6.362 +
6.363 + def set_args(self, args):
6.364 +
6.365 + "Sort the 'args' into positional and keyword arguments."
6.366 +
6.367 + self.pos_args = []
6.368 + self.kw_args = {}
6.369 + add_kw = 0
6.370 + for arg in args:
6.371 + if not add_kw:
6.372 + if not isinstance(arg, Keyword):
6.373 + self.pos_args.append(arg)
6.374 + else:
6.375 + add_kw = 1
6.376 + if add_kw:
6.377 + if isinstance(arg, Keyword):
6.378 + self.kw_args[arg.name] = arg.expr
6.379 + else:
6.380 + raise TypeError, "Positional argument appears after keyword arguments in '%s'." % self
6.381 +
6.382 +class InvokeRef(Invoke):
6.383 +
6.384 + "A block or loop invocation."
6.385 +
6.386 + def __init__(self, *args, **kw):
6.387 + self.share_locals = 1
6.388 + Invoke.__init__(self, *args, **kw)
6.389 +
6.390 +# Program structure nodes.
6.391 +
6.392 +class Module(Node, Structure):
6.393 +
6.394 + "A Python module."
6.395 +
6.396 + def full_name(self):
6.397 + return "module %s" % self.name
6.398 +
6.399 +class Subprogram(Node, WithName):
6.400 +
6.401 + "A subprogram: functions, methods and loops."
6.402 +
6.403 + def __init__(self, *args, **kw):
6.404 + Node.__init__(self, *args, **kw)
6.405 + WithName.__init__(self)
6.406 + self.raises = set()
6.407 + self.returns = set()
6.408 + self.return_locals = set()
6.409 +
6.410 +# vim: tabstop=4 expandtab shiftwidth=4
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/simplified/utils.py Fri Apr 06 00:53:18 2007 +0200
7.3 @@ -0,0 +1,167 @@
7.4 +#!/usr/bin/env python
7.5 +
7.6 +"""
7.7 +Simplified program utilities.
7.8 +
7.9 +Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk>
7.10 +
7.11 +This software is free software; you can redistribute it and/or
7.12 +modify it under the terms of the GNU General Public License as
7.13 +published by the Free Software Foundation; either version 2 of
7.14 +the License, or (at your option) any later version.
7.15 +
7.16 +This software is distributed in the hope that it will be useful,
7.17 +but WITHOUT ANY WARRANTY; without even the implied warranty of
7.18 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7.19 +GNU General Public License for more details.
7.20 +
7.21 +You should have received a copy of the GNU General Public
7.22 +License along with this library; see the file LICENCE.txt
7.23 +If not, write to the Free Software Foundation, Inc.,
7.24 +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
7.25 +"""
7.26 +
7.27 +from compiler.visitor import ASTVisitor
7.28 +
7.29 +# Exceptions.
7.30 +
7.31 +class SimplifiedError(Exception):
7.32 +
7.33 + "An error in the annotation process."
7.34 +
7.35 + def __init__(self, exc, node, *args):
7.36 +
7.37 + """
7.38 + Initialise the error with an existing exception 'exc', the 'node' at
7.39 + which this error occurs, along with additional optional arguments.
7.40 + """
7.41 +
7.42 + Exception.__init__(self, *args)
7.43 + self.nodes = [node]
7.44 + self.exc = exc
7.45 +
7.46 + def add(self, node):
7.47 +
7.48 + "Add the given 'node' to the path of nodes leading from the exception."
7.49 +
7.50 + self.nodes.append(node)
7.51 +
7.52 + def __str__(self):
7.53 +
7.54 + "Return a string showing the principal exception details."
7.55 +
7.56 + return "%s, %s" % (self.exc, self.nodes)
7.57 +
7.58 +# Elementary visitor support.
7.59 +
7.60 +class Visitor(ASTVisitor):
7.61 +
7.62 + "A visitor base class."
7.63 +
7.64 + def __init__(self):
7.65 + ASTVisitor.__init__(self)
7.66 +
7.67 + def default(self, node, *args):
7.68 + raise SimplifiedError, (None, node)
7.69 +
7.70 + def dispatch(self, node, *args):
7.71 + return ASTVisitor.dispatch(self, node, *args)
7.72 +
7.73 + def dispatches(self, nodes, *args):
7.74 + results = []
7.75 + for node in nodes:
7.76 + results.append(self.dispatch(node, *args))
7.77 + return results
7.78 +
7.79 + def dispatch_dict(self, d, *args):
7.80 + results = {}
7.81 + for name, node in d.items():
7.82 + results[name] = self.dispatch(node, *args)
7.83 + return results
7.84 +
7.85 +# Unique name registration.
7.86 +
7.87 +class Naming:
7.88 +
7.89 + "Maintain records of unique names for each simple name."
7.90 +
7.91 + index_separator = "-"
7.92 +
7.93 + def __init__(self):
7.94 + self.names = {}
7.95 +
7.96 + def get(self, obj):
7.97 + return obj._unique_name
7.98 +
7.99 + def set(self, obj, name):
7.100 + if hasattr(obj, "_unique_name"):
7.101 + return
7.102 + if not self.names.has_key(name):
7.103 + self.names[name] = 0
7.104 + n = self.names[name] + 1
7.105 + self.names[name] = n
7.106 + obj._unique_name = "%s%s%d" % (name, self.index_separator, n)
7.107 +
7.108 +def name(obj, name):
7.109 +
7.110 + "Return a unique name for the given 'obj', indicating the base 'name'."
7.111 +
7.112 + naming.set(obj, name)
7.113 + return naming.get(obj)
7.114 +
7.115 +# Naming singleton.
7.116 +
7.117 +naming = Naming()
7.118 +
7.119 +# Named nodes are those which can be referenced in some way.
7.120 +
7.121 +class WithName:
7.122 +
7.123 + "Node naming."
7.124 +
7.125 + def __init__(self):
7.126 +
7.127 + "Initialise the object's full name."
7.128 +
7.129 + self._full_name = name(self, self.name or "$untitled")
7.130 +
7.131 + def full_name(self):
7.132 +
7.133 + "Return the object's full name."
7.134 +
7.135 + return self._full_name
7.136 +
7.137 +# Comparable nodes based on naming.
7.138 +
7.139 +class Comparable:
7.140 +
7.141 + "Comparable nodes implementing the 'full_name' method."
7.142 +
7.143 + def __eq__(self, other):
7.144 +
7.145 + "This object is equal to 'other' if the full names are the same."
7.146 +
7.147 + # NOTE: Single instance: all instances are the same
7.148 + # NOTE: Multiple instances: all instances are different
7.149 + if hasattr(other, "full_name"):
7.150 + return self.full_name() == other.full_name()
7.151 + else:
7.152 + return NotImplemented
7.153 +
7.154 + def __hash__(self):
7.155 +
7.156 + "The hash of this object is based on its full name."
7.157 +
7.158 + return hash(self.full_name())
7.159 +
7.160 +# Structure nodes indicating namespace-bearing objects.
7.161 +
7.162 +class Structure(Comparable):
7.163 +
7.164 + "A non-program node containing some kind of namespace."
7.165 +
7.166 + def __init__(self, **kw):
7.167 + for name, value in kw.items():
7.168 + setattr(self, name, value)
7.169 +
7.170 +# vim: tabstop=4 expandtab shiftwidth=4