# HG changeset patch # User Paul Boddie # Date 1271632499 -7200 # Node ID 2524e035061e4f03595db4c1d9c499c56c05e75e # Parent c73f8387ac01b557ba38e874ce99cb2d52fa5304 Added the _def attribute to AST nodes in order to refer to inspected objects. Re-added support to the report generator for various constructs. Renamed various report generator functions. Removed various superfluous visitor methods. diff -r c73f8387ac01 -r 2524e035061e micropython/ast.py --- a/micropython/ast.py Sun Apr 18 21:28:56 2010 +0200 +++ b/micropython/ast.py Mon Apr 19 01:14:59 2010 +0200 @@ -193,9 +193,6 @@ def default(self, node, *args): raise TranslateError(self.module.full_name(), node, "Node class %r is not supported." % node.__class__) - def dispatch(self, node, *args): - return ASTVisitor.dispatch(self, node, *args) - # Concrete visitor methods. # Binary operators. diff -r c73f8387ac01 -r 2524e035061e micropython/data.py --- a/micropython/data.py Sun Apr 18 21:28:56 2010 +0200 +++ b/micropython/data.py Mon Apr 19 01:14:59 2010 +0200 @@ -726,6 +726,7 @@ self.name = name self.parent = parent self.astnode = node + node._def = self # Superclasses, descendants and attributes. @@ -1091,6 +1092,7 @@ self.has_star = has_star self.has_dstar = has_dstar self.astnode = node + node._def = self # For lambda functions with defaults, add a context argument. diff -r c73f8387ac01 -r 2524e035061e micropython/inspect.py --- a/micropython/inspect.py Sun Apr 18 21:28:56 2010 +0200 +++ b/micropython/inspect.py Mon Apr 19 01:14:59 2010 +0200 @@ -366,9 +366,6 @@ def default(self, node, *args): raise InspectError(self.full_name(), node, "Node class %r is not supported." % node.__class__) - def dispatch(self, node, *args): - return ASTVisitor.dispatch(self, node, *args) - def NOP(self, node): for n in node.getChildNodes(): self.dispatch(n) diff -r c73f8387ac01 -r 2524e035061e micropython/report.py --- a/micropython/report.py Sun Apr 18 21:28:56 2010 +0200 +++ b/micropython/report.py Mon Apr 19 01:14:59 2010 +0200 @@ -158,9 +158,6 @@ def _url(self, url): return self._attr(url).replace("#", "%23").replace("-", "%2d") - def _summary_link(self, module_name, name): - return "%s" % (module_name, os.path.extsep, self._attr(name), self._text(name)) - # Methods which write to the stream. def _comment(self, comment): @@ -181,9 +178,25 @@ def _name(self, name): self.stream.write("%s\n" % name) + def _name_link(self, module_name, full_name, name): + self.stream.write("%s" % ( + module_name, os.path.extsep, self._attr(full_name), self._text(name))) + + def _summary_link(self, module_name, full_name, name): + self.stream.write("%s" % ( + module_name, os.path.extsep, self._attr(full_name), self._text(name))) + + def _object_name_def(self, module, obj): + + """ + Link to the summary for 'module' using 'obj'. + """ + + self._summary_link(module.full_name(), obj.full_name(), obj.name) + # Summary classes. -class Summariser(Writer): +class Summary(Writer): "Summarise classes and attributes in modules." @@ -253,13 +266,277 @@ self.attribute_names = list(names) self.attribute_names.sort() +# Source code classes. + +class AnnotatedSource(ASTVisitor, Writer): + + "A module source code browser." + + def __init__(self, module): + ASTVisitor.__init__(self) + self.visitor = self + self.module = module + + def to_stream(self, stream): + + "Write the annotated code to the given 'stream'." + + self.stream = stream + self.stream.write(html_header) + self.dispatch(self.module.module) + self.stream.write(html_footer) + + def visitModule(self, node): + self.default(node) + + # Statements. + + def visitAssert(self, node): + self.stream.write("
\n") + self._keyword("assert") + self.dispatch(node.test) + if node.fail: + self.stream.write(", ") + self.dispatch(node.fail) + self.stream.write("
\n") + + def visitAssign(self, node): + self.stream.write("
\n") + for lvalue in node.nodes: + self.dispatch(lvalue) + self.stream.write("= ") + self.dispatch(node.expr) + self.stream.write("
\n") + + def visitAugAssign(self, node): + self.stream.write("
\n") + self.dispatch(node.node) + self.stream.write("%s " % node.op) + self.dispatch(node.expr) + self.stream.write("
\n") + + def visitBreak(self, node): + self.stream.write("
\n") + self._keyword("break") + self.stream.write("
\n") + + def visitClass(self, node): + + # Use inspected details where possible. + + if hasattr(node, "_def"): + cls = node._def + bases = cls.bases + self.stream.write("
\n" % cls.full_name()) + else: + print "Warning: class %s not recognised!" % node.name + return + + # Write the declaration line. + + self.stream.write("
\n") + self._keyword("class") + self._object_name_def(self.module, cls) + + # Suppress the "object" class appearing alone. + + if bases and not (len(bases) == 1 and bases[0].name == "object"): + self.stream.write("(") + first = 1 + for base in bases: + if not first: + self.stream.write(",\n") + + self._object_name_def(base.module, base) + + first = 0 + self.stream.write(")") + + self.stream.write(":\n") + self.stream.write("
\n") + + # Write the docstring and class body. + + self.stream.write("
\n") + self._doc(node) + self.dispatch(node.code) + self.stream.write("
\n") + self.stream.write("
\n") + + def visitContinue(self, node): + self.stream.write("
\n") + self._keyword("continue") + self.stream.write("
\n") + + def visitDiscard(self, node): + self.stream.write("
\n") + self.default(node) + self.stream.write("
\n") + + def visitFor(self, node): + self.stream.write("
\n") + self.stream.write("
\n") + self._keyword("for") + self.dispatch(node.assign) + self._keyword("in") + self.dispatch(node.list) + self.stream.write(":\n") + self.stream.write("
\n") + self.stream.write("
\n") + self.dispatch(node.body) + self.stream.write("
\n") + if node.else_ is not None: + self.stream.write("
\n") + self._keyword("else") + self.stream.write(":\n") + self.stream.write("
\n") + self.stream.write("
\n") + self.dispatch(node.else_) + self.stream.write("
\n") + self.stream.write("
\n") + + def visitFrom(self, node): + self.stream.write("
\n") + self._keyword("from") + self._name(node.modname) + self._keyword("import") + first = 1 + for (name, alias), _name in map(None, node.names, node._names): + if not first: + self.stream.write(",\n") + if alias: + self.stream.write(name + " ") + self._keyword("as") + self._name(alias or name) + first = 0 + self.stream.write("
\n") + + def visitFunction(self, node): + if hasattr(node, "_def"): + fn = node._def + self.stream.write("
\n" % fn.full_name()) + else: + print "Warning: function %s not recognised!" % node.name + return + + # Write the declaration line. + + self.stream.write("
\n") + self._keyword("def") + self._object_name_def(self.module, fn) + + self.stream.write("(") + self._parameters(fn) + self.stream.write(")") + self.stream.write(":\n") + self.stream.write("
\n") + + self.stream.write("
\n") + self._doc(node) + self.dispatch(node.code) + self.stream.write("
\n") + self.stream.write("
\n") + + def visitGlobal(self, node): + self.stream.write("
\n") + self._keyword("global") + first = 1 + for name in node.names: + if not first: + self.stream.write(",\n") + self.stream.write(name) + first = 0 + self.stream.write("
\n") + + def visitIf(self, node): + self.stream.write("
\n") + first = 1 + for compare, stmt in node.tests: + self.stream.write("
\n") + if first: + self._keyword("if") + else: + self._keyword("elif") + self.dispatch(compare) + self.stream.write(":\n") + self.stream.write("
\n") + self.stream.write("
\n") + self.dispatch(stmt) + self.stream.write("
\n") + first = 0 + if node.else_ is not None: + self.stream.write("
\n") + self._keyword("else") + self.stream.write(":\n") + self.stream.write("
\n") + self.stream.write("
\n") + self.dispatch(node.else_) + self.stream.write("
\n") + self.stream.write("
\n") + + # Output preparation methods. + + def _sequence(self, node): + first = 1 + for n in node.nodes: + if not first: + self.stream.write(",\n") + self.dispatch(n) + first = 0 + + def _mapping(self, node): + first = 1 + for k, v in node.items: + if not first: + self.stream.write(",\n") + self.dispatch(k) + self.stream.write(":\n") + self.dispatch(v) + first = 0 + + def _parameters(self, fn): + first = 1 + nparams = len(fn.positional_names) + ndefaults = len(fn.defaults) + first_with_default = nparams - ndefaults + + for n, param in enumerate(fn.positional_names): + if not first: + self.stream.write(",\n") + self._name(param) + n_default = n - first_with_default + if n_default >= 0: + self._default(fn.defaults[n_default]) + + if fn.has_star: + if not first: + self.stream.write(", *\n") + self._name(fn.star_name) + + if fn.has_dstar: + if not first: + self.stream.write(", **\n") + self._name(fn.dstar_name) + + def _default(self, default): + self.stream.write("=\n") + self.dispatch(default) + # Convenience functions. -def summary(module, filename): +def summarise(module, filename): stream = open(filename, "wb") try: - summariser = Summariser(module) - summariser.to_stream(stream) + summary = Summary(module) + summary.to_stream(stream) + finally: + stream.close() + +def annotate(module, filename): + stream = open(filename, "wb") + try: + source = AnnotatedSource(module) + source.to_stream(stream) finally: stream.close()