package polyglot.visit; import java.io.File; import java.io.IOException; import java.io.Writer; import java.util.*; import polyglot.ast.Import; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.SourceCollection; import polyglot.ast.SourceFile; import polyglot.ast.TopLevelDecl; import polyglot.ast.JL; import polyglot.frontend.Job; import polyglot.frontend.TargetFactory; import polyglot.types.ClassType; import polyglot.types.Context; import polyglot.types.Package; import polyglot.types.TypeSystem; import polyglot.util.CodeWriter; import polyglot.util.Copy; import polyglot.util.ErrorInfo; import polyglot.util.InternalCompilerError; import polyglot.main.*; /** * A Translator generates output code from the processed AST. * Output is sent to one or more java file in the directory * <code>Options.output_directory</code>. Each SourceFile in the AST * is output to exactly one java file. The name of that file is * determined as follows: * <ul> * <li> If the SourceFile has a declaration of a public top-level class "C", * file name is "C.java". It is an error for there to be more than one * top-level public declaration. * <li> If the SourceFile has no public declarations, the file name * is the input file name (e.g., "X.jl") with the suffix replaced with ".java" * (thus, "X.java"). * </ul> * * To use: * <pre> * new Translator(job, ts, nf, tf).translate(ast); * </pre> * The <code>ast</code> must be either a SourceFile or a SourceCollection. */ public class Translator extends PrettyPrinter implements Copy { protected Job job; protected NodeFactory nf; protected TargetFactory tf; protected TypeSystem ts; /** * Create a Translator. The output of the visitor is a collection of files * whose names are added to the collection <code>outputFiles</code>. */ public Translator(Job job, TypeSystem ts, NodeFactory nf, TargetFactory tf) { super(); this.job = job; this.nf = nf; this.tf = tf; this.ts = ts; } /** * Return the job associated with this Translator. */ public Job job() { return job; } /** Copy the translator. */ public Object copy() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new InternalCompilerError("Java clone() weirdness."); } } /** Get the extension's type system. */ public TypeSystem typeSystem() { return ts; } /** Get the extension's node factory. */ public NodeFactory nodeFactory() { return nf; } public TargetFactory targetFactory() { return tf; } /** Print an ast node using the given code writer. This method should not * be called directly to translate a source file AST; use * <code>translate(Node)</code> instead. This method should only be called * by nodes to print their children. */ public void print(Node parent, Node child, CodeWriter w) { Translator tr = this; child.del().translate(w, tr); } /** Translate the entire AST. */ public boolean translate(Node ast) { if (ast instanceof SourceFile) { SourceFile sfn = (SourceFile) ast; return translateSource(sfn); } else if (ast instanceof SourceCollection) { SourceCollection sc = (SourceCollection) ast; boolean okay = true; for (Iterator i = sc.sources().iterator(); i.hasNext(); ) { SourceFile sfn = (SourceFile) i.next(); okay &= translateSource(sfn); } return okay; } else { throw new InternalCompilerError("AST root must be a SourceFile; " + "found a " + ast.getClass().getName()); } } /** Transate a single SourceFile node */ protected boolean translateSource(SourceFile sfn) { TypeSystem ts = typeSystem(); NodeFactory nf = nodeFactory(); TargetFactory tf = this.tf; int outputWidth = job.compiler().outputWidth(); Collection outputFiles = job.compiler().outputFiles(); // Find the public declarations in the file. We'll use these to // derive the names of the target files. There will be one // target file per public declaration. If there are no public // declarations, we'll use the source file name to derive the // target file name. List exports = exports(sfn); try { File of, headerFile; Writer ofw, headerWriter = null; CodeWriter w; CodeWriter wH = null; String pkg = ""; if (sfn.package_() != null) { Package p = sfn.package_().package_(); pkg = p.toString(); } TopLevelDecl first = null; if (exports.size() == 0) { // Use the source name to derive a default output file name. of = tf.outputFile(pkg, sfn.source()); } else { first = (TopLevelDecl) exports.get(0); of = tf.outputFile(pkg, first.name(), sfn.source()); } String opfPath = of.getPath(); if (!opfPath.endsWith("$")) outputFiles.add(of.getPath()); ofw = tf.outputWriter(of); w = new CodeWriter(ofw, outputWidth); writeHeader(sfn, w); for (Iterator i = sfn.decls().iterator(); i.hasNext(); ) { TopLevelDecl decl = (TopLevelDecl) i.next(); if (decl.flags().isPublic() && decl != first) { // We hit a new exported declaration, open a new file. // But, first close the old file. w.flush(); ofw.close(); of = tf.outputFile(pkg, decl.name(), sfn.source()); outputFiles.add(of.getPath()); ofw = tf.outputWriter(of); w = new CodeWriter(ofw, outputWidth); writeHeader(sfn, w); } translateTopLevelDecl(w, sfn, decl); if (i.hasNext()) { w.newline(0); } } w.flush(); ofw.close(); return true; } catch (IOException e) { job.compiler().errorQueue().enqueue(ErrorInfo.IO_ERROR, "I/O error while translating: " + e.getMessage()); return false; } } public void translateTopLevelDecl(CodeWriter w, SourceFile parent, TopLevelDecl decl) { decl.del().translate(w, this); } /** * "Escapes" an input string "s" so that it can be used as a macro. * Removes all '.' and ':' chars and substitutes in '_' instead. * @param s - the input string to escape. * @return an escaped string. */ public static String macroEscape(String s) { String out = "_"; for(int i = 0; i < s.length(); i++) { char c = s.charAt(i); if(c == '.' || c == ':') out = out + "_"; else out = out + c; } return out; } /** Write the package and import declarations for a source file. */ protected void writeHeader(SourceFile sfn, CodeWriter w) { if (sfn.package_() != null) { w.write("package "); sfn.package_().del().translate(w, this); w.write(";"); w.newline(0); w.newline(0); } boolean newline = false; for (Iterator i = sfn.imports().iterator(); i.hasNext(); ) { Import imp = (Import) i.next(); imp.del().translate(w, this); newline = true; } if (newline) { w.newline(0); } } /** Get the list of public top-level classes declared in the source file. */ protected List exports(SourceFile sfn) { List exports = new LinkedList(); for (Iterator i = sfn.decls().iterator(); i.hasNext(); ) { TopLevelDecl decl = (TopLevelDecl) i.next(); if (decl.flags().isPublic()) { exports.add(decl); } } return exports; } public String toString() { return "Translator"; } }