001: /*
002: * xtc - The eXTensible Compiler
003: * Copyright (C) 2007 Robert Grimm
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License
007: * version 2 as published by the Free Software Foundation.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
017: * USA.
018: */
019: package xtc.lang;
020:
021: import java.io.File;
022: import java.io.IOException;
023: import java.io.PrintWriter;
024: import java.io.Reader;
025:
026: import java.util.HashMap;
027: import java.util.Map;
028:
029: import xtc.Constants;
030:
031: import xtc.tree.GNode;
032: import xtc.tree.Node;
033: import xtc.tree.Printer;
034: import xtc.tree.Transducer;
035:
036: import xtc.util.Tool;
037: import xtc.util.Utilities;
038:
039: import xtc.parser.ParseException;
040:
041: /**
042: * A factory of factories. This tool reads in a factory declaration,
043: * which contains one or more snippets of literal Java or C code and
044: * then creates the corresponding Java factory class. The class has
045: * one method per snippet, with each method instantiating the abstract
046: * syntax tree representing the code snippet. Code snippets may be:
047: * <ul>
048: * <li>Declarations,</li>
049: * <li>Statements,</li>
050: * <li>Expressions.</li>
051: * </ul>
052: * Code snippets may also contain pattern variables:
053: * <ul>
054: * <li><code>#</code><i>Name</i> represents a single value, to be
055: * supplied when instantiating the pattern.</li>
056: * <li><code>#[</code><i>Name</i><code>]</code> represents a list of
057: * values, also to be supplied when instantiating the pattern.</li>
058: * </ul>
059: * For each pattern variable, the corresponding method has a parameter
060: * of the same name; the actual argument supplies the correponding
061: * value(s) when instantiating the pattern. Single-valued pattern
062: * variables may appear instead of:
063: * <ul>
064: * <li>Declarations or statements in blocks,</li>
065: * <li>Types or identifiers in variable declarations,</li>
066: * <li>Expressions.</li>
067: * </ul>
068: * List-valued pattern variables may appear instead of:
069: * <ul>
070: * <li>Declarations or statements in blocks,</li>
071: * <li>Arguments to a function or method call.</li>
072: * </ul>
073: *
074: * <p />Consider this example factory description containing C
075: * snippets:<pre>
076: * alias number;
077: *
078: * factory xtc.lang.Stuff {
079: * declare { number * #name ; }
080: * block { { int i; double d; #[statements] } }
081: * add { #number + 5 }
082: * call { #function ( #[arguments] ) }
083: * }
084: * </pre>
085: * The optional alias declaration may only appear in factory
086: * declarations with C snippets; it lists all typedef names used in
087: * the C snippets as a comma-separate list. In the example,
088: * "<code>number</code>" is a typedef name. It is followed by the
089: * factory declaration itself, whose name, here
090: * "<code>xtc.lang.Stuff</code>", is the fully qualified name of the
091: * generated Java class. The body of the factory declaration consists
092: * of one or more method declarations, with each method declaration
093: * consisting of a method name followed by the Java or C snippet
094: * enclodes in curley braces. The example declares four methods:<ul>
095: *
096: * <li><code>declare</code> creates a variable declaration of some
097: * <em>name</em> being a pointer to <code>number</code>.</li>
098: *
099: * <li><code>block</code> creates a compound statement with variable
100: * declarations for <code>i</code> and <code>d</code> and some list of
101: * <em>statements</em>.</li>
102: *
103: * <li><code>add</code> creates an additive expression that adds
104: * some expression <em>number</em> to the integer 5.</li>
105: *
106: * <li><code>call</code> creates a function call of some
107: * <em>function</em> on some list of <em>arguments</em>.</li>
108: * </ul>
109: *
110: * <p />The recommended file extension for factory declarations with
111: * Java snippets is "<code>.ffj</code>" and with C snippets
112: * "<code>.ffc</code>".
113: *
114: * @author Robert Grimm
115: * @version $Revision: 1.16 $
116: */
117: public class FactoryFactory extends Tool {
118:
119: /** Create a new factory factory. */
120: public FactoryFactory() {
121: /* Nothing to do. */
122: }
123:
124: public String getName() {
125: return "xtc Factory Factory";
126: }
127:
128: public String getCopy() {
129: return Constants.COPY;
130: }
131:
132: public String getExplanation() {
133: return "This tool translates factory declarations into the corresponding "
134: + "Java classes. Each declaration contains one or more snippets of "
135: + "literal Java or C code, which are then translated into methods that "
136: + "create the corresponding AST. Snippets may be declarations, "
137: + "statements, or expressions; they may also contain pattern variables. "
138: + "The default language for snippets is Java; use the -C option for C.";
139: }
140:
141: public void init() {
142: super .init();
143: runtime.bool("C", "createCFactory", false,
144: "Create a factory for C ASTs.").bool("simplify",
145: "simplifyAST", false, "Simplify the Java AST.");
146: }
147:
148: public void prepare() {
149: super .prepare();
150: if (runtime.test("createCFactory")
151: && runtime.test("simplifyAST")) {
152: runtime.error("simplify option only valid for Java ASTs");
153: }
154: }
155:
156: public File locate(String name) throws IOException {
157: File file = super .locate(name);
158: if (Integer.MAX_VALUE < file.length()) {
159: throw new IllegalArgumentException(file
160: + ": file too large");
161: }
162: return file;
163: }
164:
165: public Node parse(Reader in, File file) throws IOException,
166: ParseException {
167: if (runtime.test("createCFactory")) {
168: CFactoryParser parser = new CFactoryParser(in, file
169: .toString(), (int) file.length());
170: return (Node) parser.value(parser.pFactory(0));
171:
172: } else {
173: JavaFactoryParser parser = new JavaFactoryParser(in, file
174: .toString(), (int) file.length());
175: return (Node) parser.value(parser.pFactory(0));
176: }
177: }
178:
179: public void process(Node node) {
180: GNode factory = GNode.cast(node);
181:
182: // Collapse the factory class name into a string.
183: StringBuilder buf = new StringBuilder();
184: boolean first = true;
185: for (Object o : GNode.cast(factory.get(0))) {
186: if (first) {
187: first = false;
188: } else {
189: buf.append('.');
190: }
191: buf.append((String) o);
192: }
193: String name = buf.toString();
194:
195: // Determine the output file and open a printer to it.
196: File file = new File(runtime.getOutputDirectory(), Utilities
197: .getName(name)
198: + ".java");
199: Printer out;
200: try {
201: out = new Printer(new PrintWriter(runtime.getWriter(file)));
202: } catch (IOException x) {
203: if (null == x.getMessage()) {
204: runtime.error(file.toString() + ": I/O error");
205: } else {
206: runtime.error(file.toString() + ": " + x.getMessage());
207: }
208: return;
209: }
210:
211: // Print the class declaration.
212: String pkg = Utilities.getQualifier(name);
213:
214: printHeader(out);
215:
216: if (null != pkg) {
217: out.indent().p("package ").p(pkg).pln(';');
218: out.pln();
219: }
220:
221: out.indent().pln("import java.util.List;").pln();
222:
223: if (!"xtc.tree".equals(pkg)) {
224: out.indent().pln("import xtc.tree.Node;");
225: out.indent().pln("import xtc.tree.GNode;");
226: out.pln();
227: }
228:
229: out.indent().pln("/**");
230: out.indent().p(" * Node factory <code>").p(name)
231: .pln("</code>.");
232: out.indent().pln(" *");
233: out.indent().pln(" * <p />This class has been generated by");
234: out.indent().p(" * the xtc Factory Factory, version ").p(
235: getVersion()).pln(',');
236: out.indent().p(" * ").p(getCopy()).pln('.');
237: out.indent().pln(" */");
238: out.indent().p("public class ").p(Utilities.getName(name)).pln(
239: " {").incr();
240: out.pln();
241:
242: out.indent().pln("/** Create a new node factory. */");
243: out.indent().p("public ").p(Utilities.getName(name))
244: .pln("() {").incr();
245: out.indent().pln("// Nothing to do.");
246: out.decr().indent().pln('}');
247: out.pln();
248:
249: Map<String, String> variables = new HashMap<String, String>();
250: variables.put("NodeVariable", "Node");
251: variables.put("NodeListVariable", "List<Node>");
252: variables.put("StringVariable", "String");
253: Transducer trans = new Transducer(out, variables);
254: JavaAstSimplifier simple = null;
255: if (runtime.test("simplifyAST"))
256: simple = new JavaAstSimplifier();
257: for (Object o : GNode.cast(factory.get(1))) {
258: GNode clause = GNode.cast(o);
259: Node ast = clause.getNode(1);
260: if (runtime.test("simplifyAST"))
261: ast = (Node) simple.dispatch(ast);
262:
263: trans.process(clause.getString(0), ast);
264: out.pln();
265: }
266:
267: out.decr().indent().pln('}');
268: out.flush().close();
269: }
270:
271: /**
272: * Run the factory factory with the specified command line arguments.
273: *
274: * @param args The command line arguments.
275: */
276: public static void main(String[] args) {
277: new FactoryFactory().run(args);
278: }
279:
280: }
|