# HG changeset patch # User paulb@jeremy # Date 1160867024 -7200 # Node ID b276109372b868e46f41ca31fc2ac3f3bf14879c # Parent 64fd5dcc0233b84b5d6444b5d704d6fe314726eb Added a non-accesses annotation to LoadAttr nodes. Introduced a new_instance method in the Annotator class in order to keep object construction conservative. Introduced full dependence on the syscount annotation when deciding whether to pursue an invocation. Introduced a defining attribute on simplified nodes in order to help find such nodes when navigating the original AST. Changed the full_name attributes to be methods instead, introducing such a method to the Instance class. Expanded the HTML output and added scope pop-up elements. diff -r 64fd5dcc0233 -r b276109372b8 annotate.py --- a/annotate.py Sat Oct 14 02:17:40 2006 +0200 +++ b/annotate.py Sun Oct 15 01:03:44 2006 +0200 @@ -149,11 +149,6 @@ mutate nodes in the original program. """ - # Prevent infinite recursion. - - if node in self.current_subprograms: - return node - # Determine the namespace. if locals is not None: @@ -282,17 +277,22 @@ loadattr.expr = self.dispatch(loadattr.expr) types = [] accesses = {} + non_accesses = {} for attr in self.namespace.types: - if not accesses.has_key(attr.type): - accesses[attr.type] = [] for attribute, accessor in get_attributes(attr.type, loadattr.name): if attribute is not None: types.append(attribute) + if not accesses.has_key(attr.type): + accesses[attr.type] = [] + accesses[attr.type].append((attribute, accessor)) else: - print "Empty attribute via accessor", accessor - accesses[attr.type].append((attribute, accessor)) + print "Empty attribute", loadattr.name, "via accessor", accessor + if not non_accesses.has_key(attr.type): + non_accesses[attr.type] = [] + non_accesses[attr.type].append((attribute, accessor)) self.namespace.set_types(types) loadattr.accesses = accesses + loadattr.non_accesses = non_accesses self.annotate(loadattr) return loadattr @@ -391,9 +391,7 @@ # Instantiate the class. # NOTE: Should probably only allocate a single instance. - instance = Instance() - instance.namespace = Namespace() - instance.namespace.store("__class__", [Attribute(None, attr.type)]) + instance = self.new_instance(invoke, "new", attr.type.full_name(), attr.type) # For instantiations, switch the context. @@ -410,9 +408,11 @@ if attribute.type not in invocations: invocations.append(attribute.type) - else: + elif not isinstance(attr.type, Class): print "Invocation type is None for", accessor + # Special case: initialisation. + if isinstance(attr.type, Class): # Associate the instance with the result of this invocation. @@ -429,17 +429,35 @@ # Utility methods. + def new_instance(self, node, reason, target, type): + + "Create, on the given 'node', a new instance with the given 'type'." + + if not hasattr(node, "instances"): + node.instances = {} + + if not node.instances.has_key((reason, target, type)): + instance = Instance() + instance.namespace = Namespace() + instance.namespace.store("__class__", [Attribute(None, type)]) + node.instances[(reason, target, type)] = instance + + return node.instances[(reason, target, type)] + def invoke_subprogram(self, invoke, subprogram): - """ - Invoke using the given 'invoke' node the given 'subprogram'. - """ + "Invoke using the given 'invoke' node the given 'subprogram'." # Test to see if anything has changed. if hasattr(invoke, "syscount") and invoke.syscount == self.system.count: return + # Remember the state of the system. + + else: + invoke.syscount = self.system.count + # Test for context information, making it into a real attribute. if subprogram.context is not None: @@ -472,10 +490,6 @@ for locals in self.returned_locals: self.namespace.merge_namespace(locals) - # Remember the state of the system. - - invoke.syscount = self.system.count - def process_args(self, invoke): # NOTE: Consider initialiser invocation for classes. @@ -568,18 +582,14 @@ if star_args: list_type = self.builtins_namespace.load("list")[0] # NOTE: Hack to get list type. - star = Instance() - star.namespace = Namespace() - star.namespace.store("__class__", [Attribute(None, list_type.type)]) + star = self.new_instance(invocation, "star", subprogram.full_name(), list_type.type) star_types = [Attribute(None, star)] else: star_types = None if dstar_args: dict_type = self.builtins_namespace.load("dict")[0] # NOTE: Hack to get dict type. - dstar = Instance() - dstar.namespace = Namespace() - dstar.namespace.store("__class__", [Attribute(None, dict_type.type)]) + dstar = self.new_instance(invocation, "dstar", subprogram.full_name(), dict_type.type) dstar_types = [Attribute(None, dstar)] else: dstar_types = None diff -r 64fd5dcc0233 -r b276109372b8 fixnames.py --- a/fixnames.py Sat Oct 14 02:17:40 2006 +0200 +++ b/fixnames.py Sun Oct 15 01:03:44 2006 +0200 @@ -248,7 +248,7 @@ if scope == "structure": result = self.dispatch( - LoadAttr(loadname.original, + LoadAttr(loadname.original, loadname.defining, expr=LoadRef(loadname.original, ref=self.namespace.structure), name=loadname.name, @@ -269,7 +269,7 @@ if scope == "global": result = self.dispatch( - LoadAttr(loadname.original, + LoadAttr(loadname.original, loadname.defining, expr=LoadRef(loadname.original, ref=self.builtins), name=loadname.name, @@ -281,7 +281,7 @@ else: result = self.dispatch( - LoadAttr(loadname.original, + LoadAttr(loadname.original, loadname.defining, expr=LoadRef(loadname.original, ref=self.module), name=loadname.name, @@ -293,7 +293,7 @@ else: result = self.dispatch( - LoadAttr(loadname.original, + LoadAttr(loadname.original, loadname.defining, expr=LoadRef(loadname.original, ref=self.builtins), name=loadname.name, @@ -314,7 +314,7 @@ else: result = self.dispatch( - LoadAttr(loadname.original, + LoadAttr(loadname.original, loadname.defining, expr=LoadRef(loadname.original, ref=self.module), name=loadname.name, @@ -333,7 +333,7 @@ if scope == "structure": return self.dispatch( - StoreAttr(storename.original, + StoreAttr(storename.original, storename.defining, lvalue=LoadRef(storename.original, ref=self.namespace.structure), name=storename.name, @@ -346,7 +346,7 @@ elif scope == "global": return self.dispatch( - StoreAttr(storename.original, + StoreAttr(storename.original, storename.defining, lvalue=LoadRef(storename.original, ref=self.module), name=storename.name, @@ -369,7 +369,7 @@ else: return self.dispatch( - StoreAttr(storename.original, + StoreAttr(storename.original, storename.defining, lvalue=LoadRef(storename.original, ref=self.module), name=storename.name, diff -r 64fd5dcc0233 -r b276109372b8 simplified.py --- a/simplified.py Sat Oct 14 02:17:40 2006 +0200 +++ b/simplified.py Sun Oct 15 01:03:44 2006 +0200 @@ -110,22 +110,25 @@ choices Any choices which may be included in the final program. """ - def __init__(self, original=None, **kw): + def __init__(self, original=None, defining=0, **kw): """ - Initialise a program node with an optional link to an 'original' AST - node. + Initialise a program node with a link to an optional 'original' AST + node. An optional 'defining' parameter (if set to a true value), sets + this node as the defining node in the original. """ self.original = original - if self.original is not None: + self.defining = defining + + if self.original is not None and defining: self.original._node = self for name, value in kw.items(): setattr(self, name, value) def __repr__(self): if hasattr(self, "full_name"): - s = "%s '%s'" % (self.__class__.__name__, self.full_name) + s = "%s '%s'" % (self.__class__.__name__, self.full_name()) elif hasattr(self, "name"): s = "%s '%s'" % (self.__class__.__name__, self.name) elif hasattr(self, "index"): @@ -276,7 +279,10 @@ "Node naming." def __init__(self): - self.full_name = name(self, self.name or "$untitled") + self._full_name = name(self, self.name or "$untitled") + + def full_name(self): + return self._full_name class Module(Node, WithName): @@ -310,8 +316,11 @@ "An instance." + def full_name(self): + return self.namespace.load("__class__")[0].type.full_name() + def __repr__(self): - return "Instance of type '%s'" % self.namespace.load("__class__")[0].type.full_name + return "Instance of type '%s'" % self.full_name() class Constant(Instance): diff -r 64fd5dcc0233 -r b276109372b8 simplify.py --- a/simplify.py Sat Oct 14 02:17:40 2006 +0200 +++ b/simplify.py Sun Oct 15 01:03:44 2006 +0200 @@ -91,7 +91,7 @@ if node is not None: return self.dispatch(node, *args) else: - return LoadName(name="None") + return LoadName(node, name="None") # Placeholder or deletion transformations. @@ -99,7 +99,7 @@ return self.dispatches(stmt.nodes) def visitPass(self, pass_): - return Pass(pass_) + return Pass(pass_, 1) def visitDiscard(self, discard): return self.dispatch(discard.expr) @@ -113,7 +113,7 @@ resulting program nodes. """ - result = Module(module, name="module") + result = Module(module, 1, name="module") module_code = self.dispatch(module.node) # NOTE: Constant initialisation necessary for annotation but perhaps @@ -138,27 +138,27 @@ return result def visitGetattr(self, getattr): - result = LoadAttr(getattr, + result = LoadAttr(getattr, 1, name=getattr.attrname, expr=self.dispatch(getattr.expr) ) return result def visitKeyword(self, keyword): - result = Keyword(keyword, + result = Keyword(keyword, 1, name=keyword.name, expr=self.dispatch(keyword.expr) ) return result def visitGlobal(self, global_): - result = Global(global_, + result = Global(global_, 1, names=global_.names ) return result def visitImport(self, import_): - result = Assign(import_) + result = Assign(import_, 1) code = [] for path, alias in import_.names: importer = Import(import_, name=path) @@ -168,7 +168,7 @@ return result def visitFrom(self, from_): - result = Assign(from_) + result = Assign(from_, 1) code = [] code.append(StoreTemp(from_, expr=Import(from_, name=from_.modname))) for name, alias in from_.names: @@ -184,33 +184,33 @@ return result def visitName(self, name): - result = LoadName(name, name=name.name) + result = LoadName(name, 1, name=name.name) return result def visitConst(self, const): if not self.constants.has_key(const.value): self.constants[const.value] = Constant(const, name=repr(const.value), value=const.value) - result = LoadRef(const, ref=self.constants[const.value]) + result = LoadRef(const, 1, ref=self.constants[const.value]) return result def visitReturn(self, return_): - result = Return(return_, + result = Return(return_, 1, expr=self.dispatch(return_.value) ) return result def visitBreak(self, break_): - result = Return(break_) + result = Return(break_, 1) return result def visitContinue(self, continue_): - result = InvokeBlock(continue_, + result = InvokeBlock(continue_, 1, expr=LoadRef(continue_, ref=self.current_subprograms[-1]) ) return result def visitRaise(self, raise_): - result = Raise(raise_, expr=self.dispatch(raise_.expr1), traceback=None) + result = Raise(raise_, 1, expr=self.dispatch(raise_.expr1), traceback=None) if raise_.expr2 is not None: result.args = [self.dispatch(raise_.expr2)] if raise_.expr3 is not None: @@ -218,7 +218,7 @@ return result def _visitBuiltin(self, builtin, name): - result = InvokeFunction(builtin, expr=LoadName(builtin, name=name), args=self.dispatches(builtin.nodes), star=None, dstar=None) + result = InvokeFunction(builtin, 1, expr=LoadName(builtin, name=name), args=self.dispatches(builtin.nodes), star=None, dstar=None) return result def visitTuple(self, tuple): @@ -228,7 +228,7 @@ return self._visitBuiltin(list, "list") def visitDict(self, dict): - result = InvokeFunction(dict, expr=LoadName(dict, name="dict"), star=None, dstar=None) + result = InvokeFunction(dict, 1, expr=LoadName(dict, name="dict"), star=None, dstar=None) args = [] for key, value in dict.items: tuple = InvokeFunction(dict, expr=LoadName(dict, name="tuple"), star=None, dstar=None) @@ -266,8 +266,12 @@ # Produce something like... # expr.__true__() ? body + first = 1 for compare, stmt in if_.tests: - test = Conditional(if_, + + # Set the first as the defining node. + + test = Conditional(if_, first, test=InvokeFunction(if_, expr=LoadAttr(if_, expr=self.dispatch(compare), @@ -280,6 +284,7 @@ test.body = self.dispatch(stmt) nodes.append(test) nodes = test.else_ = [] + first = 0 # Add the compound statement from any else clause to the end. @@ -312,7 +317,7 @@ (else) -> ... """ - result = Try(tryexcept, body=[], else_=[], finally_=[]) + result = Try(tryexcept, 1, body=[], else_=[], finally_=[]) if tryexcept.body is not None: result.body = self.dispatch(tryexcept.body) @@ -459,11 +464,11 @@ subprogram.code = results self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name] = subprogram + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram # Make an invocation of the subprogram. - result = InvokeBlock(compare, produces_result=1) + result = InvokeBlock(compare, 1, produces_result=1) result.expr = LoadRef(compare, ref=subprogram) return result @@ -519,11 +524,11 @@ subprogram.code = results self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name] = subprogram + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram # Make an invocation of the subprogram. - result = InvokeBlock(and_, produces_result=1) + result = InvokeBlock(and_, 1, produces_result=1) result.expr = LoadRef(and_, ref=subprogram) return result @@ -581,28 +586,28 @@ subprogram.code = results self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name] = subprogram + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram # Make an invocation of the subprogram. - result = InvokeBlock(or_, produces_result=1) + result = InvokeBlock(or_, 1, produces_result=1) result.expr = LoadRef(or_, ref=subprogram) return result def visitNot(self, not_): - result = Not(not_, expr=InvokeFunction(not_, expr=LoadAttr(not_, expr=self.dispatch(not_.expr), name="__true__"), args=[], star=None, dstar=None)) + result = Not(not_, 1, expr=InvokeFunction(not_, expr=LoadAttr(not_, expr=self.dispatch(not_.expr), name="__true__"), args=[], star=None, dstar=None)) return result # Operators. def visitUnaryAdd(self, unaryadd): - return InvokeFunction(unaryadd, expr=LoadAttr(unaryadd, expr=self.dispatch(unaryadd.expr), name="__pos__"), args=[], star=None, dstar=None) + return InvokeFunction(unaryadd, 1, expr=LoadAttr(unaryadd, expr=self.dispatch(unaryadd.expr), name="__pos__"), args=[], star=None, dstar=None) def visitUnarySub(self, unarysub): - return InvokeFunction(unarysub, expr=LoadAttr(unarysub, expr=self.dispatch(unarysub.expr), name="__neg__"), args=[], star=None, dstar=None) + return InvokeFunction(unarysub, 1, expr=LoadAttr(unarysub, expr=self.dispatch(unarysub.expr), name="__neg__"), args=[], star=None, dstar=None) def visitInvert(self, invert): - return InvokeFunction(invert, expr=LoadAttr(invert, expr=self.dispatch(invert.expr), name="__invert__"), args=[], star=None, dstar=None) + return InvokeFunction(invert, 1, expr=LoadAttr(invert, expr=self.dispatch(invert.expr), name="__invert__"), args=[], star=None, dstar=None) def visitAdd(self, add): @@ -616,7 +621,7 @@ (else) """ - result = Try(add, + result = Try(add, 1, body=[ InvokeFunction(add, expr=LoadAttr(add, expr=self.dispatch(add.left), name="__add__"), @@ -656,7 +661,7 @@ } def visitAugAssign(self, augassign): - result = Assign(augassign) + result = Assign(augassign, 1) expr = self.dispatch(augassign.expr) # Simple augmented assignment: name += expr @@ -800,7 +805,7 @@ return result def visitAssign(self, assign): - result = Assign(assign) + result = Assign(assign, 1) store = StoreTemp(assign, expr=self.dispatch(assign.expr)) release = ReleaseTemp(assign) result.code = [store] + self.dispatches(assign.nodes, 0) + [release] @@ -811,7 +816,7 @@ expr = LoadTemp(asslist) else: expr = InvokeFunction(asslist, expr=LoadAttr(asslist, expr=LoadTemp(asslist), name="next"), star=None, dstar=None, args=[]) - result = Assign(asslist) + result = Assign(asslist, 1) store = StoreTemp(asslist, expr=InvokeFunction(asslist, expr=LoadAttr(asslist, name="__iter__", expr=expr), star=None, dstar=None, args=[])) release = ReleaseTemp(asslist) result.code = [store] + self.dispatches(asslist.nodes, 1) + [release] @@ -827,25 +832,25 @@ def visitAssName(self, assname, in_sequence=0): expr = self._visitAssNameOrAttr(assname, in_sequence) - result = StoreName(assname, name=assname.name, expr=expr) + result = StoreName(assname, 1, name=assname.name, expr=expr) return result def visitAssAttr(self, assattr, in_sequence=0): expr = self._visitAssNameOrAttr(assattr, in_sequence) lvalue = self.dispatch(assattr.expr) - result = StoreAttr(assattr, name=assattr.attrname, lvalue=lvalue, expr=expr) + result = StoreAttr(assattr, 1, name=assattr.attrname, lvalue=lvalue, expr=expr) return result def _visitSlice(self, slice, expr, lower, upper, flags, value=None): if flags == "OP_ASSIGN": args = [value] - result = InvokeFunction(slice, expr=LoadAttr(slice, expr=expr, name="__setslice__"), star=None, dstar=None, args=[]) + result = InvokeFunction(slice, 1, expr=LoadAttr(slice, expr=expr, name="__setslice__"), star=None, dstar=None, args=[]) elif flags == "OP_APPLY": args = [] - result = InvokeFunction(slice, expr=LoadAttr(slice, expr=expr, name="__getslice__"), star=None, dstar=None, args=[]) + result = InvokeFunction(slice, 1, expr=LoadAttr(slice, expr=expr, name="__getslice__"), star=None, dstar=None, args=[]) elif flags == "OP_DELETE": args = [] - result = InvokeFunction(slice, expr=LoadAttr(slice, expr=expr, name="__delslice__"), star=None, dstar=None, args=[]) + result = InvokeFunction(slice, 1, expr=LoadAttr(slice, expr=expr, name="__delslice__"), star=None, dstar=None, args=[]) else: raise NotImplementedError, flags @@ -865,13 +870,13 @@ def _visitSubscript(self, subscript, expr, subs, flags, value=None): if flags == "OP_ASSIGN": args = [value] - result = InvokeFunction(subscript, expr=LoadAttr(subscript, expr=expr, name="__setitem__"), star=None, dstar=None, args=[]) + result = InvokeFunction(subscript, 1, expr=LoadAttr(subscript, expr=expr, name="__setitem__"), star=None, dstar=None, args=[]) elif flags == "OP_APPLY": args = [] - result = InvokeFunction(subscript, expr=LoadAttr(subscript, expr=expr, name="__getitem__"), star=None, dstar=None, args=[]) + result = InvokeFunction(subscript, 1, expr=LoadAttr(subscript, expr=expr, name="__getitem__"), star=None, dstar=None, args=[]) elif flags == "OP_DELETE": args = [] - result = InvokeFunction(subscript, expr=LoadAttr(subscript, expr=expr, name="__delitem__"), star=None, dstar=None, args=[]) + result = InvokeFunction(subscript, 1, expr=LoadAttr(subscript, expr=expr, name="__delitem__"), star=None, dstar=None, args=[]) else: raise NotImplementedError, flags @@ -887,7 +892,7 @@ if len(subs) == 1: return self.dispatch(subs[0]) else: - return InvokeFunction(node, + return InvokeFunction(node, 1, expr=LoadName(node, name="tuple"), args=self.dispatches(subs), star=None, @@ -915,13 +920,13 @@ subprogram.code = self.dispatch(class_.code) + [Return(class_)] self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name] = subprogram + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram # Make a definition of the class associating it with a name. result = Assign(class_, code=[ - StoreName(class_, + StoreName(class_, 1, # defines the class name=class_.name, expr=LoadRef(class_, ref=structure) ), @@ -958,11 +963,17 @@ # Produce star and dstar parameters with appropriate defaults. if has_star: - star = (function.argnames[npositional], InvokeFunction(function, expr=LoadName(function, name="list"), args=[], star=None, dstar=None)) + star = ( + function.argnames[npositional], + InvokeFunction(function, expr=LoadName(function, name="list"), args=[], star=None, dstar=None) + ) else: star = None if has_dstar: - dstar = (function.argnames[npositional + has_star], InvokeFunction(function, expr=LoadName(function, name="dict"), args=[], star=None, dstar=None)) + dstar = ( + function.argnames[npositional + has_star], + InvokeFunction(function, expr=LoadName(function, name="dict"), args=[], star=None, dstar=None) + ) else: dstar = None @@ -982,7 +993,7 @@ subprogram.params = params subprogram.star = star subprogram.dstar = dstar - self.subprograms.append(subprogram); self.subnames[subprogram.full_name] = subprogram + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram def visitFunction(self, function): @@ -997,7 +1008,7 @@ # Make a definition of the function associating it with a name. - result = StoreName(function, name=function.name, expr=LoadRef(function, ref=subprogram)) + result = StoreName(function, 1, name=function.name, expr=LoadRef(function, ref=subprogram)) return result def visitLambda(self, lambda_): @@ -1013,10 +1024,10 @@ # Get the subprogram reference to the lambda. - return LoadRef(lambda_, ref=subprogram) + return LoadRef(lambda_, 1, ref=subprogram) def visitCallFunc(self, callfunc): - result = InvokeFunction(callfunc, star=None, dstar=None, args=self.dispatches(callfunc.args)) + result = InvokeFunction(callfunc, 1, star=None, dstar=None, args=self.dispatches(callfunc.args)) if callfunc.star_args is not None: result.star = self.dispatch(callfunc.star_args) if callfunc.dstar_args is not None: @@ -1068,11 +1079,11 @@ subprogram.code = [test] self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name] = subprogram + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram # Make an invocation of the subprogram. - result = InvokeBlock(while_) + result = InvokeBlock(while_, 1) result.expr = LoadRef(while_, ref=subprogram) return result @@ -1137,14 +1148,24 @@ # Finish the subprogram definition. self.current_subprograms.pop() - self.subprograms.append(subprogram); self.subnames[subprogram.full_name] = subprogram + self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram # Obtain an iterator for the sequence involved. # Then, make an invocation of the subprogram. - result = Assign(for_) + result = Assign(for_, 1) result.code = [ - StoreTemp(for_, expr=InvokeFunction(for_, expr=LoadAttr(for_, name="__iter__", expr=self.dispatch(for_.list)), args=[], star=None, dstar=None)), + StoreTemp(for_, + expr=InvokeFunction(for_, + expr=LoadAttr(for_, + name="__iter__", + expr=self.dispatch(for_.list) + ), + args=[], + star=None, + dstar=None + ) + ), InvokeBlock(for_, expr=LoadRef(for_, ref=subprogram)), ReleaseTemp(for_) ] @@ -1153,7 +1174,7 @@ # Exception node transformations. def visitTryFinally(self, tryfinally): - result = Try(tryfinally, body=[], else_=[], finally_=[]) + result = Try(tryfinally, 1, body=[], else_=[], finally_=[]) if tryfinally.body is not None: result.body = self.dispatch(tryfinally.body) if tryfinally.final is not None: diff -r 64fd5dcc0233 -r b276109372b8 viewer.py --- a/viewer.py Sat Oct 14 02:17:40 2006 +0200 +++ b/viewer.py Sun Oct 15 01:03:44 2006 +0200 @@ -109,11 +109,18 @@ .types { display: none; z-index: 2; - position: absolute; top: 3em; left: 0.5em; + position: absolute; top: 1em; left: 6.5em; padding: 0.5em; background-color: #0000FF; } - .name:hover > .types + .scopes { + display: none; z-index: 2; + position: absolute; top: 1em; left: 0.5em; + padding: 0.5em; background-color: #007700; + } + + .name:hover > .types, + .name:hover > .scopes { display: block; } @@ -146,34 +153,86 @@ def visitModule(self, node): self.default(node) + # Statements. + def visitPass(self, node): self._keyword("pass") def visitClass(self, node): - structure = node._node.expr.types[0].type - self.stream.write("
\n" % self._url(structure.full_name)) + definition = node._node + structure = definition.expr.types[0].type + self.stream.write("
\n" % self._url(structure.full_name())) self.stream.write("

\n") self._keyword("class") - self._name(structure) + self._name_start(structure) + self._scopes(definition) + self._name_end() bases = structure.bases if bases: - self.stream.write("(\n") + self.stream.write("(") first = 1 for base in bases: if not first: self.stream.write(",\n") - self._name_and_types(base) + self._name_start(base) + self._types(base) + self._scopes(base) + self._name_end() first = 0 self.stream.write(")") self.stream.write(":\n") - self._comment(self._text(structure.full_name)) + self._comment(self._text(structure.full_name())) self.stream.write("

\n") self.stream.write("
\n") - self.default(node) + self._doc(node) + self.dispatch(node.code) self.stream.write("
\n") self.stream.write("
\n") + def visitAssign(self, node): + self.stream.write("
\n") + for lvalue in node.nodes: + self.dispatch(lvalue) + self.stream.write("=\n") + self.dispatch(node.expr) + self.stream.write("
\n") + + # Expressions. + + def visitTuple(self, node): + self.stream.write("\n") + self.stream.write("(") + self._sequence(node) + self.stream.write(")\n") + self.stream.write("\n") + + visitAssTuple = visitTuple + + def visitList(self, node): + self.stream.write("\n") + self.stream.write("[") + self._sequence(node) + self.stream.write("]\n") + self.stream.write("\n") + + visitAssList = visitList + + def visitName(self, node): + self._name_start(node._node) + self._types(node._node) + self._scopes(node._node) + self._name_end() + + def visitAssName(self, node): + self._name_start(node._node) + self._types(node._node.expr) + self._scopes(node._node) + self._name_end() + + def visitConst(self, node): + self.stream.write(repr(node.value)) + # Output preparation methods. def _text(self, text): @@ -191,17 +250,46 @@ def _keyword(self, kw): self.stream.write("%s " % kw) - def _name(self, node): - self.stream.write("%s " % node.name) + def _doc(self, node): + if node.doc is not None: + self.stream.write("
%s
\n" % self._text(node.doc)) + + def _sequence(self, node): + first = 1 + for n in node.nodes: + if not first: + self.stream.write(",\n") + self.dispatch(n) + first = 0 - def _name_and_types(self, node): - self.stream.write("%s\n
\n" % node.name) + def _name(self, node): + self.stream.write("%s\n" % node.name) + + def _name_start(self, node): + self.stream.write("%s\n" % node.name) + + def _name_end(self): + self.stream.write("\n") + + def _types(self, node): + self.stream.write("
\n") for type in node.types: - fn = type.type.full_name - self.stream.write("
\n") + fn = type.type.full_name() + self.stream.write("
") self.stream.write(self._text(fn)) self.stream.write("
\n") - self.stream.write("
\n\n") + self.stream.write("
\n") + + def _scopes(self, node): + self.stream.write("
\n") + if not hasattr(node, "writes") and not hasattr(node, "accesses"): + raise AttributeError, node + for ref in getattr(node, "writes", getattr(node, "accesses", {})).keys(): + fn = ref.full_name() + self.stream.write("
") + self.stream.write(self._text(fn)) + self.stream.write("
\n") + self.stream.write("
\n") # Convenience functions.