[Python-checkins] python/nondist/sandbox/ast asdl_java.py,NONE,1.1

bckfnn@sourceforge.net bckfnn@sourceforge.net
Sun, 14 Apr 2002 03:18:01 -0700


Update of /cvsroot/python/python/nondist/sandbox/ast
In directory usw-pr-cvs1:/tmp/cvs-serv27998

Added Files:
	asdl_java.py 
Log Message:
Initial revision of the codegenerator for the jython AST classes.



--- NEW FILE: asdl_java.py ---
"""Generate C code from an ASDL description."""

# TO DO
# handle fields that have a type but no name

import os, sys, traceback

import asdl

TABSIZE = 4
MAX_COL = 76

def reflow_lines(s, depth):
    """Reflow the line s indented depth tabs.

    Return a sequence of lines where no line extends beyond MAX_COL
    when properly indented.  The first line is properly indented based
    exclusively on depth * TABSIZE.  All following lines -- these are
    the reflowed lines generated by this function -- start at the same
    column as the first character beyond the opening { in the first
    line.
    """
    size = MAX_COL - depth * TABSIZE
    if len(s) < size:
        return [s]

    lines = []
    cur = s
    padding = ""
    while len(cur) > size:
        i = cur.rfind(' ', 0, size)
        assert i != -1, "Impossible line to reflow: %s" % `line`
        lines.append(padding + cur[:i])
        if len(lines) == 1:
            # find new size based on brace
            j = cur.find('{', 0, i)
            if j >= 0:
                j += 2 # account for the brace and the space after it
                size -= j
                padding = " " * j
        cur = cur[i+1:]
    else:
        lines.append(padding + cur)
    return lines

class EmitVisitor(asdl.VisitorBase):
    """Visit that emits lines"""

    def __init__(self):
        super(EmitVisitor, self).__init__()

    def open(self, name):
        self.file = open("%s.java" % name, "wb")
        print >> self.file, 'package org.python.p2.ast;'
        print >> self.file, 'import org.python.p2.SimpleNode;'
    
    def close(self):
        self.file.close()

    def emit(self, s, depth):
        # XXX reflow long lines?
        lines = reflow_lines(s, depth)
        for line in lines:
            line = (" " * TABSIZE * depth) + line + "\n"
            self.file.write(line)



# This step will add a 'simple' boolean attribute to all Sum and Product 
# nodes and add a 'typedef' link to each Field node that points to the
# Sum or Product node that defines the field.

class AnalyzeVisitor(EmitVisitor):
    def visitModule(self, mod):
        self.types = {}
        for dfn in mod.dfns:
            self.types[str(dfn.name)] = dfn.value
        for dfn in mod.dfns:
            self.visit(dfn)

    def visitType(self, type, depth=0):
        self.visit(type.value, type.name, depth)

    def visitSum(self, sum, name, depth):
        sum.simple = 1
        for t in sum.types:
            if t.fields:
                sum.simple = 0
                break
        for t in sum.types:
            self.visit(t, name, depth)

    def visitProduct(self, product, name, depth):
        product.simple = 0
        for f in product.fields:
            self.visit(f, depth + 1)

    def visitConstructor(self, cons, name, depth):
        for f in cons.fields:
            self.visit(f, depth + 1)

    def visitField(self, field, depth):
        field.typedef = self.types.get(str(field.type))



# The code generator itself.
#
class JavaVisitor(EmitVisitor):
    def visitModule(self, mod):
        for dfn in mod.dfns:
            self.visit(dfn)

    def visitType(self, type, depth=0):
        self.visit(type.value, type.name, depth)

    def visitSum(self, sum, name, depth):
        if sum.simple:
            self.simple_sum(sum, name, depth)
        else:
            self.sum_with_constructor(sum, name, depth)

    def simple_sum(self, sum, name, depth):
        self.open("%sType" % name)
        self.emit("public interface %(name)sType {" % locals(), depth)
        for i in range(len(sum.types)):
            type = sum.types[i]
            self.emit("public static final int %s = %d;" % (type.name, i+1),
                                                    depth + 1)
        self.emit("}", depth)
        self.close()
   
    def sum_with_constructor(self, sum, name, depth):
        self.open("%sType" % name)
        self.emit("public abstract class %(name)sType extends SimpleNode {" %
                    locals(), depth)
        self.emit("}", depth)
        self.close()
        for t in sum.types:
            self.visit(t, name, depth)

    def visitProduct(self, product, name, depth):
        self.open("%sType" % name)
        self.emit("public class %(name)sType extends SimpleNode {" % locals(), depth)
        for f in product.fields:
            self.visit(f, depth + 1)
        self.emit("", depth);

        self.javaMethods(name, "%sType" % name, product.fields, depth+1)

        self.emit("}", depth)
        self.close()

    def visitConstructor(self, cons, name, depth):
        self.open(cons.name)
        enums = []
        for f in cons.fields:
            if f.typedef and f.typedef.simple:
                enums.append("%sType" % f.type)
        if enums:
            s = "implements %s " % ", ".join(enums)
        else:
            s = ""
        self.emit("public class %s extends %sType %s{" %
                    (cons.name, name, s), depth)
        for f in cons.fields:
            self.visit(f, depth + 1)
        self.emit("", depth)

        self.javaMethods(cons.name, cons.name, cons.fields, depth+1)

        self.emit("}", depth)
        self.close()

    def javaMethods(self, clsname, ctorname, fields, depth):
            # The java ctor
            self.emit("public %s(%s) {" % (ctorname,
                 ", ".join([self.fieldDef(f) for f in fields])), depth)
            for f in fields:
                self.emit("this.%s = %s;" % (f.name, f.name), depth+1)
            self.emit("}", depth)

            # The toString() method
            self.emit("public String toString() {", depth)
            self.emit('StringBuffer sb = new StringBuffer("%s[");' % clsname,
                        depth+1)
            for f in fields:
                self.emit('sb.append("%s=");' % f.name, depth+1);
                self.emit("sb.append(dumpThis(this.%s));" % f.name, depth+1)
                if f != fields[-1]:
                    self.emit('sb.append(", ");', depth+1)
            self.emit('sb.append("]");', depth+1)
            self.emit("return sb.toString();", depth+1)
            self.emit("}", depth)


    def visitField(self, field, depth):
        self.emit("public %s;" % self.fieldDef(field), depth)

    bltinnames = {
        'bool' : 'boolean',
        'identifier' : 'String',
        'string' : 'String',
    }

    def fieldDef(self, field):
        jtype = str(field.type)
        if field.typedef and field.typedef.simple:
            jtype = 'int'
        else:
            jtype = self.bltinnames.get(jtype, jtype + 'Type')
        name = field.name
        seq = field.seq and "[]" or ""
        return "%(jtype)s%(seq)s %(name)s" % locals()



class ChainOfVisitors:
    def __init__(self, *visitors):
        self.visitors = visitors

    def visit(self, object):
        for v in self.visitors:
            v.visit(object)

if __name__ == "__main__":
    import sys

    mod = asdl.parse(sys.argv[1])
    if not asdl.check(mod):
        sys.exit(1)
    c = ChainOfVisitors(AnalyzeVisitor(),
                        JavaVisitor())
    c.visit(mod)