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