1   // Copyright 2013 Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2   
3   package jminusminus;
4   
5   import java.io.FileInputStream;
6   import java.io.FileNotFoundException;
7   import static jminusminus.TokenKind.EOF;
8   
9   /**
10   * Driver class for j-- compiler using hand-written front-end. This is the main
11   * entry point for the compiler. The compiler proceeds as follows:
12   * 
13   * (1) It reads arguments that affects its behavior.
14   * 
15   * (2) It builds a scanner.
16   * 
17   * (3) It builds a parser (using the scanner) and parses the input for producing
18   * an abstact syntax tree (AST).
19   * 
20   * (4) It sends the preAnalyze() message to that AST, which recursively descends
21   * the tree so far as the memeber headers for declaring types and members in the
22   * symbol table (represented as a string of contexts).
23   * 
24   * (5) It sends the analyze() message to that AST for declaring local variables,
25   * and cheking and assigning types to expressions. Analysis also sometimes
26   * rewrites some of the abstract syntax trees for clarifying the semantics.
27   * Analysis does all of this by recursively descending the AST down to its
28   * leaves.
29   * 
30   * (6) Finally, it sends a codegen() message to the AST for generating code.
31   * Again, codegen() recursively descends the tree, down to its leaves,
32   * generating JVM code for producing a .class or .s (SPIM) file for each defined
33   * type (class).
34   */
35  
36  public class Main {
37  
38      /** Whether an error occurred during compilation. */
39      private static boolean errorHasOccurred;
40  
41      /**
42       * Entry point.
43       */
44  
45      public static void main(String args[]) {
46          String caller = "java jminusminus.Main";
47          String sourceFile = "";
48          String debugOption = "";
49          String outputDir = ".";
50          boolean spimOutput = false;
51          String registerAllocation = "";
52          errorHasOccurred = false;
53          for (int i = 0; i < args.length; i++) {
54              if (args[i].equals("j--")) {
55                  caller = "j--";
56              } else if (args[i].endsWith(".java")) {
57                  sourceFile = args[i];
58              } else if (args[i].equals("-t") || args[i].equals("-p")
59                      || args[i].equals("-pa") || args[i].equals("-a")) {
60                  debugOption = args[i];
61              } else if (args[i].endsWith("-d") && (i + 1) < args.length) {
62                  outputDir = args[++i];
63              } else if (args[i].endsWith("-s") && (i + 1) < args.length) {
64                  spimOutput = true;
65                  registerAllocation = args[++i];
66                  if (!registerAllocation.equals("naive")
67                          && !registerAllocation.equals("linear")
68                          && !registerAllocation.equals("graph")
69                          || registerAllocation.equals("")) {
70                      printUsage(caller);
71                      return;
72                  }
73              } else if (args[i].endsWith("-r") && (i + 1) < args.length) {
74                  NPhysicalRegister.MAX_COUNT = Math.min(18, Integer
75                          .parseInt(args[++i]));
76                  NPhysicalRegister.MAX_COUNT = Math.max(1,
77                          NPhysicalRegister.MAX_COUNT);
78              } else {
79                  printUsage(caller);
80                  return;
81              }
82          }
83          if (sourceFile.equals("")) {
84              printUsage(caller);
85              return;
86          }
87  
88          LookaheadScanner scanner = null;
89          try {
90              scanner = new LookaheadScanner(sourceFile);
91          } catch (FileNotFoundException e) {
92              System.err.println("Error: file " + sourceFile + " not found.");
93              return;
94          }
95  
96          if (debugOption.equals("-t")) {
97              // Just tokenize input and print the tokens to STDOUT
98              TokenInfo token;
99              do {
100                 scanner.next();
101                 token = scanner.token();
102                 System.out.printf("%d\t : %s = %s\n", token.line(), token
103                         .tokenRep(), token.image());
104             } while (token.kind() != EOF);
105             errorHasOccurred |= scanner.errorHasOccured();
106             return;
107         }
108 
109         // Parse input
110         Parser parser = new Parser(scanner);
111         JCompilationUnit ast = parser.compilationUnit();
112         errorHasOccurred |= parser.errorHasOccurred();
113         if (debugOption.equals("-p")) {
114             ast.writeToStdOut(new PrettyPrinter());
115             return;
116         }
117         if (errorHasOccurred) {
118             return;
119         }
120 
121         // Do pre-analysis
122         ast.preAnalyze();
123         errorHasOccurred |= JAST.compilationUnit.errorHasOccurred();
124         if (debugOption.equals("-pa")) {
125             ast.writeToStdOut(new PrettyPrinter());
126             return;
127         }
128         if (errorHasOccurred) {
129             return;
130         }
131 
132         // Do analysis
133         ast.analyze(null);
134         errorHasOccurred |= JAST.compilationUnit.errorHasOccurred();
135         if (debugOption.equals("-a")) {
136             ast.writeToStdOut(new PrettyPrinter());
137             return;
138         }
139         if (errorHasOccurred) {
140             return;
141         }
142 
143         // Generate JVM code
144         CLEmitter clEmitter = new CLEmitter(!spimOutput);
145         clEmitter.destinationDir(outputDir);
146         ast.codegen(clEmitter);
147         errorHasOccurred |= clEmitter.errorHasOccurred();
148         if (errorHasOccurred) {
149             return;
150         }
151 
152         // If SPIM output was asked for, convert the in-memory
153         // JVM instructions to SPIM using the specified register
154         // allocation scheme.
155         if (spimOutput) {
156             NEmitter nEmitter = new NEmitter(sourceFile, ast.clFiles(),
157                     registerAllocation);
158             nEmitter.destinationDir(outputDir);
159             nEmitter.write();
160             errorHasOccurred |= nEmitter.errorHasOccurred();
161         }
162     }
163 
164     /**
165      * Return true if an error occurred during compilation; false otherwise.
166      * 
167      * @return true or false.
168      */
169 
170     public static boolean errorHasOccurred() {
171         return errorHasOccurred;
172     }
173 
174     /**
175      * Print command usage to STDOUT.
176      * 
177      * @param caller
178      *            denotes how this class is invoked.
179      */
180 
181     private static void printUsage(String caller) {
182         String usage = "Usage: "
183                 + caller
184                 + " <options> <source file>\n"
185                 + "where possible options include:\n"
186                 + "  -t Only tokenize input and print tokens to STDOUT\n"
187                 + "  -p Only parse input and print AST to STDOUT\n"
188                 + "  -pa Only parse and pre-analyze input and print "
189                 + "AST to STDOUT\n"
190                 + "  -a Only parse, pre-analyze, and analyze input "
191                 + "and print AST to STDOUT\n"
192                 + "  -s <naive|linear|graph> Generate SPIM code\n"
193                 + "  -r <num> Max. physical registers (1-18) available for allocation; default = 8\n"
194                 + "  -d <dir> Specify where to place output files; default = .";
195         System.out.println(usage);
196     }
197 
198 }
199