Welcome, guest | Sign In | My Account | Store | Cart

Parse the online documentation for compiler.ast and generate code for a skeletal ASTVisitor class that includes stubs for all of the various visitNODE functions you might need in a visitor, along with comments in each function that list that node type's attributes.

Python, 76 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/usr/bin/env python

# AST Visitor generator recipe
#
# Lonnie Princehouse, 2004

import re

# Set this to the current location of the compiler ast module HTML doc
# (the page that has a table of AST Node types and attributes)
compiler_ast_doc_url = 'http://docs.python.org/lib/module-compiler.ast.html'

klass = '<tt class="class">(?P<klass>\S+?)</tt></td>'
member = '<td><tt class="member">(?P<member>\S+)</tt></td>\s+<td>(?P<doc>.*?)</td>'
finder = re.compile("%s|%s" % (klass, member))

nodes = []

class ASTDocNode:
    def __init__(self, name):
        self.name = name
        self.attributes = []
    def add_attribute(self, attribute, comment):
        self.attributes.append( (attribute, comment) )
    def __str__(self):
        atlist = []
        for a,c in self.attributes:
            if a:
                if c == '&nbsp;':
                    c = ''
                atlist.append("        #     %-16s %s" % (a,c))
        attribute_list = '\n'.join(atlist)
        name = self.name
        return """
    def visit%(name)s(self, node):
        # %(name)s attributes
%(attribute_list)s
        raise NotImplementedException('visit%(name)s')""" % locals()

def generate_visitor_skeleton(output_stream, url = compiler_ast_doc_url, node_class = ASTDocNode, visitor_class_name = 'VisitorSkeleton'):
    import urllib
    document = urllib.urlopen(url)
    html_source = document.read()
    document.close()
    header = """

import compiler.visitor

class NotImplementedException(Exception): pass

class %s(compiler.visitor.ASTVisitor): """ % visitor_class_name

    for klass, attribute, comment in finder.findall(html_source):
        if klass not in ('', '&nbsp;'):
            nodes.append(node_class(klass))
        else:
            nodes[-1].add_attribute(attribute, comment)
    
    print >> output_stream, header

    for n in nodes:
        print >> output_stream,  str(n)

if __name__ == '__main__':
    import sys
    # Optional command line argument is the URL of the Node doc.
    # This argument might be useful if (a) you don't have network access, but have a local copy of the docs.
    # or (b) you want to build a visitor for an old version of Python (at the time of this writing, current
    # docs are for 2.3.4)

    if len(sys.argv) > 1:
        url = sys.argv[1]
    else:
        url = compiler_ast_doc_url

    generate_visitor_skeleton(sys.stdout, url)

If you are writing an AST Visitor, this might save you from referring back to the docs every five seconds.

I'll include a separate recipe that is simply the output of this program, but dynamically generating the visitor has the advantage of using the most up-to-date python docs (er, at least so long as they don't change the format of the doc page :P)

[side note- this doesn't have anything to do with Python generators.. I mean generator in a more general sense]