/* * xtc - The eXTensible Compiler * Copyright (C) 2007 Robert Grimm * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package xtc.lang; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.Reader; import java.util.HashMap; import java.util.Map; import xtc.Constants; import xtc.tree.GNode; import xtc.tree.Node; import xtc.tree.Printer; import xtc.tree.Transducer; import xtc.util.Tool; import xtc.util.Utilities; import xtc.parser.ParseException; /** * A factory of factories. This tool reads in a factory declaration, * which contains one or more snippets of literal Java or C code and * then creates the corresponding Java factory class. The class has * one method per snippet, with each method instantiating the abstract * syntax tree representing the code snippet. Code snippets may be: * <ul> * <li>Declarations,</li> * <li>Statements,</li> * <li>Expressions.</li> * </ul> * Code snippets may also contain pattern variables: * <ul> * <li><code>#</code><i>Name</i> represents a single value, to be * supplied when instantiating the pattern.</li> * <li><code>#[</code><i>Name</i><code>]</code> represents a list of * values, also to be supplied when instantiating the pattern.</li> * </ul> * For each pattern variable, the corresponding method has a parameter * of the same name; the actual argument supplies the correponding * value(s) when instantiating the pattern. Single-valued pattern * variables may appear instead of: * <ul> * <li>Declarations or statements in blocks,</li> * <li>Types or identifiers in variable declarations,</li> * <li>Expressions.</li> * </ul> * List-valued pattern variables may appear instead of: * <ul> * <li>Declarations or statements in blocks,</li> * <li>Arguments to a function or method call.</li> * </ul> * * <p />Consider this example factory description containing C * snippets:<pre> * alias number; * * factory xtc.lang.Stuff { * declare { number * #name ; } * block { { int i; double d; #[statements] } } * add { #number + 5 } * call { #function ( #[arguments] ) } * } * </pre> * The optional alias declaration may only appear in factory * declarations with C snippets; it lists all typedef names used in * the C snippets as a comma-separate list. In the example, * "<code>number</code>" is a typedef name. It is followed by the * factory declaration itself, whose name, here * "<code>xtc.lang.Stuff</code>", is the fully qualified name of the * generated Java class. The body of the factory declaration consists * of one or more method declarations, with each method declaration * consisting of a method name followed by the Java or C snippet * enclodes in curley braces. The example declares four methods:<ul> * * <li><code>declare</code> creates a variable declaration of some * <em>name</em> being a pointer to <code>number</code>.</li> * * <li><code>block</code> creates a compound statement with variable * declarations for <code>i</code> and <code>d</code> and some list of * <em>statements</em>.</li> * * <li><code>add</code> creates an additive expression that adds * some expression <em>number</em> to the integer 5.</li> * * <li><code>call</code> creates a function call of some * <em>function</em> on some list of <em>arguments</em>.</li> * </ul> * * <p />The recommended file extension for factory declarations with * Java snippets is "<code>.ffj</code>" and with C snippets * "<code>.ffc</code>". * * @author Robert Grimm * @version $Revision: 1.16 $ */ public class FactoryFactory extends Tool { /** Create a new factory factory. */ public FactoryFactory() { /* Nothing to do. */ } public String getName() { return "xtc Factory Factory"; } public String getCopy() { return Constants.COPY; } public String getExplanation() { return "This tool translates factory declarations into the corresponding " + "Java classes. Each declaration contains one or more snippets of " + "literal Java or C code, which are then translated into methods that " + "create the corresponding AST. Snippets may be declarations, " + "statements, or expressions; they may also contain pattern variables. " + "The default language for snippets is Java; use the -C option for C."; } public void init() { super.init(); runtime. bool("C", "createCFactory", false, "Create a factory for C ASTs."). bool("simplify", "simplifyAST", false, "Simplify the Java AST."); } public void prepare() { super.prepare(); if (runtime.test("createCFactory") && runtime.test("simplifyAST")) { runtime.error("simplify option only valid for Java ASTs"); } } public File locate(String name) throws IOException { File file = super.locate(name); if (Integer.MAX_VALUE < file.length()) { throw new IllegalArgumentException(file + ": file too large"); } return file; } public Node parse(Reader in, File file) throws IOException, ParseException { if (runtime.test("createCFactory")) { CFactoryParser parser = new CFactoryParser(in, file.toString(), (int)file.length()); return (Node)parser.value(parser.pFactory(0)); } else { JavaFactoryParser parser = new JavaFactoryParser(in, file.toString(), (int)file.length()); return (Node)parser.value(parser.pFactory(0)); } } public void process(Node node) { GNode factory = GNode.cast(node); // Collapse the factory class name into a string. StringBuilder buf = new StringBuilder(); boolean first = true; for (Object o : GNode.cast(factory.get(0))) { if (first) { first = false; } else { buf.append('.'); } buf.append((String)o); } String name = buf.toString(); // Determine the output file and open a printer to it. File file = new File(runtime.getOutputDirectory(), Utilities.getName(name) + ".java"); Printer out; try { out = new Printer(new PrintWriter(runtime.getWriter(file))); } catch (IOException x) { if (null == x.getMessage()) { runtime.error(file.toString() + ": I/O error"); } else { runtime.error(file.toString() + ": " + x.getMessage()); } return; } // Print the class declaration. String pkg = Utilities.getQualifier(name); printHeader(out); if (null != pkg) { out.indent().p("package ").p(pkg).pln(';'); out.pln(); } out.indent().pln("import java.util.List;").pln(); if (! "xtc.tree".equals(pkg)) { out.indent().pln("import xtc.tree.Node;"); out.indent().pln("import xtc.tree.GNode;"); out.pln(); } out.indent().pln("/**"); out.indent().p(" * Node factory <code>").p(name).pln("</code>."); out.indent().pln(" *"); out.indent().pln(" * <p />This class has been generated by"); out.indent().p(" * the xtc Factory Factory, version ").p(getVersion()). pln(','); out.indent().p(" * ").p(getCopy()).pln('.'); out.indent().pln(" */"); out.indent().p("public class ").p(Utilities.getName(name)). pln(" {").incr(); out.pln(); out.indent().pln("/** Create a new node factory. */"); out.indent().p("public ").p(Utilities.getName(name)).pln("() {").incr(); out.indent().pln("// Nothing to do."); out.decr().indent().pln('}'); out.pln(); Map<String,String> variables = new HashMap<String,String>(); variables.put("NodeVariable", "Node" ); variables.put("NodeListVariable", "List<Node>"); variables.put("StringVariable", "String"); Transducer trans = new Transducer(out, variables); JavaAstSimplifier simple = null; if (runtime.test("simplifyAST")) simple = new JavaAstSimplifier(); for (Object o : GNode.cast(factory.get(1))) { GNode clause = GNode.cast(o); Node ast = clause.getNode(1); if (runtime.test("simplifyAST")) ast = (Node)simple.dispatch(ast); trans.process(clause.getString(0), ast); out.pln(); } out.decr().indent().pln('}'); out.flush().close(); } /** * Run the factory factory with the specified command line arguments. * * @param args The command line arguments. */ public static void main(String[] args) { new FactoryFactory().run(args); } }