package polyglot.visit; import java.io.File; import java.io.IOException; import java.io.Writer; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import polyglot.ast.Import; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.ast.SourceFile; import polyglot.ast.TopLevelDecl; import polyglot.ast.TypeNode; import polyglot.frontend.Job; import polyglot.frontend.TargetFactory; import polyglot.main.Options; import polyglot.types.ArrayType; import polyglot.types.ClassType; import polyglot.types.Context; import polyglot.types.Named; import polyglot.types.Package; import polyglot.types.PrimitiveType; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.TypeSystem; import polyglot.util.CodeWriter; import polyglot.util.ErrorInfo; import polyglot.util.InternalCompilerError; public class CppTranslator extends TypedTranslator { protected Context context; public CppTranslator(Job job, TypeSystem ts, NodeFactory nf, TargetFactory tf) { super(job, ts, nf, tf); } protected static boolean cppBackend() {return false;} protected static HashMap createdFiles = new HashMap(); public static HashMap getFileNames() { return createdFiles; } /** Create a new <code>Translator</code> identical to <code>this</code>, * except: a) wrapped inside a HeaderTranslator object, and b) with * a new context <code>c</code> * @param c - the new context to use * @return - a header translator identical to this one, but with new context. */ public HeaderTranslator headerContext(Context c) { HeaderTranslator ht = new HeaderTranslator(this); ht.context = c; return ht; } /** * Turns a package or class name from Java "x.y.z" format * into a C-style scope ("x::y::z") * @param s the input package or class name * @return A c-scoped version of s */ public static String cScope(String s) { String out = ""; for(int i = 0; i < s.length(); i++) { char c = s.charAt(i); if(c == '.') out = out + "::"; else out = out + c; } return out; } public String cTypeString(Type type) { if (type instanceof ArrayType) { ArrayType at = (ArrayType) type; String s = "jmatch_array< " + cTypeString(at.base()); if(at.base().isReference()) s = s + "*"; return s + " > "; } else if (type instanceof ClassType) { ClassType ct = (ClassType) type; if (ct.isTopLevel() && ct.package_() != null) { return cScope(ct.package_().translate(this.context) + "." + ct.name()); } } else if (type instanceof PrimitiveType) { PrimitiveType pt = (PrimitiveType) type; if (pt.kind() == PrimitiveType.BOOLEAN) { return "bool"; } } return type.translate(this.context); } public void print(Node parent, Node child, CodeWriter w) { if (cppBackend()) { if (child instanceof TypeNode) { TypeNode tn = (TypeNode) child; Type type = tn.type(); w.write(cTypeString(type)); } } super.print(parent, child, w); } /** 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); createdFiles.put(of.getPath(), null); if(cppBackend()) { //if we're generating a c++ class, we need to also generate //the .h file headerFile = new File(tf.headerNameForFileName(of.getPath())); headerWriter = tf.outputWriter(headerFile); wH = new CodeWriter(headerWriter, outputWidth); String className = null; if(!exports.isEmpty()) { first = (TopLevelDecl) exports.get(0); className = first.name(); } else { String name; name = sfn.source().name(); className = name.substring(0, name.lastIndexOf('.')); } writeHFileHeader(sfn, className, wH); } writeHeader(sfn, w); for (Iterator i = sfn.decls().iterator(); i.hasNext(); ) { TopLevelDecl decl = (TopLevelDecl) i.next(); if (decl.flags().isPublic() && decl != first && !cppBackend()) { // 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(cppBackend()) { Context c = sfn.enterScope(decl, this.context); decl.del().translate(wH, this.headerContext(c)); } if (i.hasNext()) { w.newline(0); } } writeFooter(sfn, w); if(cppBackend()) { writeHFileFooter(sfn, wH); wH.flush(); headerWriter.close(); } 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; } } /** * Write the opening lines of the header file for a given class * @param sfn - representation of the source file we're compiling; used for Imports. * @param className - The name of the class we're describing * @param w - The CodeWriter to write it all out to (the .h file) */ protected void writeHFileHeader(SourceFile sfn, String className, CodeWriter w) { String pkg = null; if(sfn.package_() != null) { Package p = sfn.package_().package_(); pkg = p.fullName(); } if(pkg == null || pkg.equals("")) pkg = "jmatch_primary"; String macroName = "_" + macroEscape(pkg) + "_" + macroEscape(className) + "_H"; w.write("#ifndef " + macroName); w.newline(0); w.write("#define " + macroName); w.newline(0); if(sfn.package_() != null) sfn.package_().del().translate(w, this); else w.write("namespace " + cScope(pkg) + " {"); w.newline(0); w.write("using namespace jmatch_primary;"); w.newline(0); w.write("using namespace java::lang;"); w.newline(0); //now make any more imports. for(Iterator i = sfn.imports().iterator(); i.hasNext(); ) { Import imp = (Import)i.next(); imp.del().translate(w, this); w.newline(0); } } /** * Write the footer of the .h file if we're in C++ mode * @param w */ protected void writeHFileFooter(SourceFile sfn, CodeWriter w) { int packageDepth = 0; int i; if(null != sfn.package_()) { Package p = sfn.package_().package_(); String pkgName = p.toString(); if(pkgName.length() > 0) packageDepth++; for(i = 0; i < pkgName.length(); i++) if(pkgName.charAt(i) == '.') packageDepth++; w.write("/* closing namespace */"); w.newline(0); for(i = 0; i < packageDepth; i++) w.write("}"); w.newline(0); w.newline(0); } if(packageDepth == 0) { w.newline(0); w.write("} /* namespace */"); w.newline(0); w.newline(0); } w.write("#endif"); w.newline(0); w.newline(0); } /** * C++ files also require a footer terminal '}' because they * need to close the namespace they're opening. */ protected void writeFooter(SourceFile sfn, CodeWriter w) { if(cppBackend()) { int packageDepth = 0; int i; if(null != sfn.package_()) { Package p = sfn.package_().package_(); String pkgName = p.toString(); if(pkgName.length() > 0) packageDepth++; for(i = 0; i < pkgName.length(); i++) if(pkgName.charAt(i) == '.') packageDepth++; w.write("/* closing namespace */"); w.newline(0); for(i = 0; i < packageDepth; i++) w.write("}"); w.newline(0); w.newline(0); } if(packageDepth == 0) { w.newline(0); w.write("} /* namespace */"); w.newline(0); w.newline(0); } } } /** Write the package and import declarations for a source file. */ protected void writeHeader(SourceFile sfn, CodeWriter w) { if(cppBackend()) { //package --> namespace and imports --> header includes String pkg = ""; if (sfn.package_() != null) { Package p = sfn.package_().package_(); pkg = p.toString() + "."; } int i = 0; int dots = 0; for(i = 0; i < pkg.length(); i++) if(pkg.charAt(i) == '.') dots++; //start out with global project include w.write("#include\""); for(i = 0; i < dots; i++) w.write("../"); w.write("mainproj.h\""); w.newline(0); //in C++, open the package (namespace), and then 'using' others: if(null != sfn.package_()) { sfn.package_().del().translate(w, this); w.newline(0); w.newline(0); } else { w.write("namespace jmatch_primary {"); w.newline(0); w.newline(0); } //we always are using the global jmatch namespace w.write("using namespace jmatch_primary;"); w.newline(0); //and java.lang.*; w.write("using namespace java::lang;"); w.newline(0); //now make any more imports. for(Iterator it = sfn.imports().iterator(); it.hasNext(); ) { Import imp = (Import)it.next(); imp.del().translate(w, this); w.newline(0); } } else { //standard Java header 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); } } } }