001: /*
002: * xtc - The eXTensible Compiler
003: * Copyright (C) 2004-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.lang.reflect.Method;
022:
023: import java.io.File;
024: import java.io.IOException;
025: import java.io.Reader;
026:
027: import xtc.Constants;
028: import xtc.Limits;
029:
030: import xtc.tree.Formatting;
031: import xtc.tree.GNode;
032: import xtc.tree.Location;
033: import xtc.tree.Node;
034: import xtc.tree.ParseTreePrinter;
035: import xtc.tree.ParseTreeStripper;
036: import xtc.tree.Printer;
037: import xtc.tree.Visitor;
038:
039: import xtc.type.CFactory;
040: import xtc.type.TypePrinter;
041:
042: import xtc.util.SymbolTable;
043: import xtc.util.Tool;
044:
045: import xtc.parser.Result;
046: import xtc.parser.ParseException;
047:
048: /**
049: * The C tool.
050: *
051: * @author Robert Grimm
052: * @version $Revision: 1.59 $
053: */
054: public class C extends Tool {
055:
056: /** The method for printing the memoization table's profile. */
057: protected Method profile;
058:
059: /** The method for printing the parser's memoization table. */
060: protected Method dump;
061:
062: /** Create a new C tool. */
063: public C() {
064: /* Nothing to do. */
065: }
066:
067: public String getName() {
068: return "xtc C Tool";
069: }
070:
071: public String getCopy() {
072: return Constants.COPY;
073: }
074:
075: public void init() {
076: super .init();
077: runtime
078: .bool("parsetree", "optionParseTree", false,
079: "Generate a parse tree.")
080: .bool("noincr", "optionNoIncr", false,
081: "Do not parse incrementally.")
082: .bool("memoProfile", "printMemoProfile", false,
083: "Print profile of parser's memoization table.")
084: .bool("memoTable", "printMemoTable", false,
085: "Print parser's memoization table.")
086: .bool("analyze", "optionAnalyze", false,
087: "Analyze the program's AST.")
088: .bool("typical", "optionTypical", false,
089: "Use the Typical-generated analyzer.")
090: .bool("pedantic", "optionPedantic", false,
091: "Enforce strict C99 compliance.")
092: .bool("builtins", "optionBuiltIns", false,
093: "Declare C built-ins before analysis.")
094: .bool("markAST", "optionMarkAST", false,
095: "Mark AST nodes with types.")
096: .bool("printStats", "printASTStats", false,
097: "Collect and print the program's AST statistics.")
098: .bool("printSymbolTable", "printSymbolTable", false,
099: "Print the program's symbol table.")
100: .bool("strip", "optionStrip", false,
101: "Strip any annotations before printing the AST.")
102: .bool("printAST", "printAST", false,
103: "Print the program's AST in generic form.")
104: .bool(
105: "locateAST",
106: "optionLocateAST",
107: false,
108: "Include location information when printing the AST in "
109: + "generic form.")
110: .bool("printSource", "printSource", false,
111: "Print the program's AST in C source form.")
112: .bool("preserveLines", "preserveLines", false,
113: "Preserve line spacing when printing C source.")
114: .bool("formatGNU", "formatGNU", false,
115: "Use GNU source formatting guidelines.");
116: }
117:
118: public void prepare() {
119: super .prepare();
120: if (runtime.test("printMemoProfile")) {
121: // Ensure the parser has a profile(Printer) method.
122: Class klass = runtime.test("optionParseTree") ? CReader.class
123: : CParser.class;
124: String name = runtime.test("optionParseTree") ? "C reader"
125: : "C parser";
126: try {
127: profile = klass.getMethod("profile",
128: new Class[] { Printer.class });
129: } catch (NoSuchMethodException x) {
130: runtime.error(name
131: + " generated without profile attribute");
132: } catch (SecurityException x) {
133: runtime.error("unable to access " + name
134: + "'s profile() method");
135: }
136: }
137: if (runtime.test("printMemoTable")) {
138: // Ensure the parser has a dump(Printer) method.
139: Class klass = runtime.test("optionParseTree") ? CReader.class
140: : CParser.class;
141: String name = runtime.test("optionParseTree") ? "C reader"
142: : "C parser";
143: try {
144: dump = klass.getMethod("dump",
145: new Class[] { Printer.class });
146: } catch (NoSuchMethodException x) {
147: runtime.error(name
148: + " generated without dump attribute");
149: } catch (SecurityException x) {
150: runtime.error("unable to access " + name
151: + "'s dump() method");
152: }
153: }
154: if (runtime.test("optionTypical")) {
155: if (!runtime.test("optionAnalyze")) {
156: runtime.error("typical option requires analyze option");
157: }
158: }
159: if (runtime.test("optionPedantic")) {
160: if (!runtime.test("optionAnalyze")) {
161: runtime
162: .error("pedantic option requires analyze option");
163: }
164: }
165: if (runtime.test("optionBuiltIns")) {
166: if (!runtime.test("optionAnalyze")) {
167: runtime
168: .error("builtins option requires analyze option");
169: }
170: }
171: if (runtime.test("optionMarkAST")) {
172: if (!runtime.test("optionAnalyze")) {
173: runtime.error("markAST option requires analyze option");
174: }
175: }
176: if (runtime.test("printSymbolTable")) {
177: if (!runtime.test("optionAnalyze")) {
178: runtime
179: .error("printSymbolTable option requires analyze option");
180: }
181: }
182: if (runtime.test("optionLocateAST")) {
183: if (!runtime.test("printAST")) {
184: runtime
185: .error("locateAST option requires printAST option");
186: }
187: }
188: if (runtime.test("preserveLines")) {
189: if (!runtime.test("printSource")) {
190: runtime
191: .error("preserveLines option requires printSource option");
192: }
193: }
194: if (runtime.test("formatGNU")) {
195: if (!runtime.test("printSource")) {
196: runtime
197: .error("formatGNU option requires printSource option");
198: }
199: }
200: }
201:
202: public void diagnose() {
203: runtime.console().pln();
204:
205: runtime.console().p("os : ").p(
206: System.getProperty("os.name")).p(' ').pln(
207: System.getProperty("os.version"));
208: runtime.console().p("arch : ").pln(
209: System.getProperty("os.arch"));
210: runtime.console().p("endian : ");
211: if (Limits.IS_BIG_ENDIAN) {
212: runtime.console().pln("big");
213: } else {
214: runtime.console().pln("little");
215: }
216: runtime.console().p("size_t : ").pln(
217: xtc.type.C.SIZEOF.toString());
218: runtime.console().p("ptrdiff_t : ").pln(
219: xtc.type.C.PTR_DIFF.toString());
220: runtime.console().p("wchar_t : ").pln(
221: xtc.type.C.WCHAR.toString());
222: runtime.console().p("char : ");
223: if (Limits.IS_CHAR_SIGNED) {
224: runtime.console().pln("signed");
225: } else {
226: runtime.console().pln("unsigned");
227: }
228: runtime.console().p("int : ");
229: if (Limits.IS_INT_SIGNED) {
230: runtime.console().pln("signed in bitfields");
231: } else {
232: runtime.console().pln("unsigned in bitfields");
233: }
234: runtime.console().pln();
235:
236: runtime
237: .console()
238: .pln(
239: "type | c | s | i | l | ll | f | d | ld | p");
240: runtime
241: .console()
242: .pln(
243: "----------+-----+----+----+----+----+----+----+----+----");
244: runtime.console().p("size | 1 | ").pad(
245: Limits.SHORT_SIZE, 2).p(" | ").pad(Limits.INT_SIZE, 2)
246: .p(" | ").pad(Limits.LONG_SIZE, 2).p(" | ").pad(
247: Limits.LONG_LONG_SIZE, 2).p(" | ").pad(
248: Limits.FLOAT_SIZE, 2).p(" | ").pad(
249: Limits.DOUBLE_SIZE, 2).p(" | ").pad(
250: Limits.LONG_DOUBLE_SIZE, 2).p(" | ").pad(
251: Limits.POINTER_SIZE, 2).pln();
252: runtime.console().p("align | 1 | ").pad(
253: Limits.SHORT_ALIGN, 2).p(" | ")
254: .pad(Limits.INT_ALIGN, 2).p(" | ").pad(
255: Limits.LONG_ALIGN, 2).p(" | ").pad(
256: Limits.LONG_LONG_ALIGN, 2).p(" | ").pad(
257: Limits.FLOAT_ALIGN, 2).p(" | ").pad(
258: Limits.DOUBLE_ALIGN, 2).p(" | ").pad(
259: Limits.LONG_DOUBLE_ALIGN, 2).p(" | ").pad(
260: Limits.POINTER_ALIGN, 2).pln();
261: runtime.console().pln().flush();
262: }
263:
264: public File locate(String name) throws IOException {
265: File file = super .locate(name);
266: if (runtime.test("optionNoIncr")
267: && (Integer.MAX_VALUE < file.length())) {
268: throw new IllegalArgumentException(file
269: + ": file too large");
270: }
271: return file;
272: }
273:
274: public Node parse(Reader in, File file) throws IOException,
275: ParseException {
276: if (runtime.test("optionParseTree")) { // ======================== Reader
277: if (runtime.test("optionNoIncr")) {
278: CReader parser = new CReader(in, file.toString(),
279: (int) file.length());
280: Result result = parser.pTranslationUnit(0);
281: printMemoInfo(parser, file);
282: return (Node) parser.value(result);
283:
284: } else {
285: CReader parser = new CReader(in, file.getName());
286: GNode unit = GNode.create("TranslationUnit");
287: unit.setLocation(new Location(file.toString(), 1, 0));
288: Node root = unit;
289: boolean first = true;
290:
291: while (!parser.isEOF(0)) {
292: Result result = first ? parser.pPrelude(0) : parser
293: .pExternalDeclaration(0);
294: printMemoInfo(parser, file);
295: if (!result.hasValue())
296: parser.signal(result.parseError());
297:
298: if (first) {
299: root = Formatting.before1(result
300: .semanticValue(), unit);
301: first = false;
302: } else {
303: unit.add(result.semanticValue());
304: }
305: parser.resetTo(result.index);
306: }
307:
308: // Grab any trailing annotations.
309: Result result = parser.pAnnotations(0);
310: if (!result.hasValue())
311: parser.signal(result.parseError());
312: unit.add(result.semanticValue());
313:
314: return root;
315: }
316:
317: } else if (runtime.test("optionNoIncr")) { // ==================== Parser
318: CParser parser = new CParser(in, file.toString(),
319: (int) file.length());
320: Result result = parser.pTranslationUnit(0);
321: printMemoInfo(parser, file);
322: return (Node) parser.value(result);
323:
324: } else {
325: CParser parser = new CParser(in, file.getName());
326: GNode root = GNode.create("TranslationUnit");
327: boolean first = true;
328:
329: while (!parser.isEOF(0)) {
330: Result result = first ? parser.pPrelude(0) : parser
331: .pExternalDeclaration(0);
332: printMemoInfo(parser, file);
333: if (!result.hasValue())
334: parser.signal(result.parseError());
335:
336: if (first) {
337: first = false;
338: } else {
339: root.add(result.semanticValue());
340: }
341: parser.resetTo(result.index);
342: }
343:
344: // Grab any trailing annotations.
345: Result result = parser.pAnnotations(0);
346: if (!result.hasValue())
347: parser.signal(result.parseError());
348: root.add(result.semanticValue());
349:
350: return root;
351: }
352: }
353:
354: /**
355: * Print memoization information.
356: *
357: * @param parser The parser.
358: * @param file The file.
359: */
360: private void printMemoInfo(Object parser, File file) {
361: if (runtime.test("printMemoProfile")) {
362: try {
363: profile.invoke(parser,
364: new Object[] { runtime.console() });
365: } catch (Exception x) {
366: runtime.error(file + ": " + x.getMessage());
367: }
368: runtime.console().pln().flush();
369: }
370:
371: if (runtime.test("printMemoTable")) {
372: try {
373: dump.invoke(parser, new Object[] { runtime.console() });
374: } catch (Exception x) {
375: runtime.error(file + ": " + x.getMessage());
376: }
377: runtime.console().flush();
378: }
379: }
380:
381: public void process(Node node) {
382: // Analyze the AST.
383: if (runtime.test("optionAnalyze")) {
384: SymbolTable table;
385:
386: if (runtime.test("optionTypical")) {
387: // Analyze and fill in the symbol table.
388: table = new xtc.lang.c.CAnalyzer(runtime).run(node);
389:
390: } else {
391: // Create symbol table.
392: table = new SymbolTable();
393:
394: // Add built-ins.
395: if (runtime.test("optionBuiltIns")) {
396: new CFactory("__builtin_", table.root())
397: .declareBuiltIns(false);
398: }
399:
400: // Perform type checking.
401: new CAnalyzer(runtime).analyze(node, table);
402: }
403:
404: // Print the symbol table.
405: if (runtime.test("printSymbolTable")) {
406: // Save the registered visitor.
407: Visitor visitor = runtime.console().visitor();
408: // Note that the type printer's constructor registers the just
409: // created printer with the console.
410: new TypePrinter(runtime.console());
411: try {
412: table.root().dump(runtime.console());
413: } finally {
414: // Restore the previously registered visitor.
415: runtime.console().register(visitor);
416: }
417: runtime.console().flush();
418: }
419: }
420:
421: // Print AST statistics.
422: if (runtime.test("printASTStats")) {
423: CCounter counter = new CCounter();
424: counter.dispatch(node);
425: counter.print(runtime.console());
426: runtime.console().flush();
427: }
428:
429: // Strip AST.
430: if (runtime.test("optionStrip")) {
431: node = (Node) new ParseTreeStripper().dispatch(node);
432: }
433:
434: // Print AST.
435: if (runtime.test("printAST")) {
436: runtime.console().format(node,
437: runtime.test("optionLocateAST")).pln().flush();
438: }
439:
440: // Print source.
441: if (runtime.test("printSource")) {
442: if (runtime.test("optionParseTree")
443: && (!runtime.test("optionStrip"))) {
444: new ParseTreePrinter(runtime.console()).dispatch(node);
445: } else {
446: new CPrinter(runtime.console(), runtime
447: .test("preserveLines"), runtime
448: .test("formatGNU")).dispatch(node);
449: }
450: runtime.console().flush();
451: }
452: }
453:
454: /**
455: * Run the tool with the specified command line arguments.
456: *
457: * @param args The command line arguments.
458: */
459: public static void main(String[] args) {
460: new C().run(args);
461: }
462:
463: }
|