// Parse ID ('=' EXP)
  static String getVariables(SamTokenizer f) throws TokenizerException {
    try {
      // Name of the variable.
      String var = f.getWord();

      // Check if the name already exists.
      if (symTables.lastElement().containsKey(var) || symMap.containsKey(var))
        throw new TokenizerException("Error: Duplicate declaration of " + var + ".");

      // Add the variable with the address to the symbol table.
      symTables.lastElement().put(var, varCounter);
      ++varCounter;

      // Reserve the space in stack for the variable declaration.
      String asmCode = "PUSHIMM 0\n";
      if (f.check('=')) {
        asmCode += getExp(f);
        asmCode += ("STOREOFF " + Integer.toString(varCounter - 1) + "\n");
      }
      if (f.check(',')) {
        asmCode += getVariables(f);
      }
      return asmCode;
    } catch (Exception e) {
      System.out.println(e.getMessage());
      throw new TokenizerException("Error: Invalid variable declaration.");
    }
  }
 static String compiler(String fileName) {
   // returns SaM code for program in file
   try {
     SamTokenizer f = new SamTokenizer(fileName);
     String pgm = getProgram(f);
     return pgm;
   } catch (Exception e) {
     System.out.println(e.getMessage());
     return "STOP\n";
   }
 }
 // For all functions that return the SaM code, the string of the code is named as asmCode.
 static String getProgram(SamTokenizer f) {
   try {
     // SaM code for running the main method.
     String asmCode = "PUSHIMM 0\n" + "LINK\n" + "JSR main\n" + "POPFBR\n" + "STOP\n";
     while (f.peekAtKind() != TokenType.EOF) {
       asmCode += getMethod(f);
     }
     // Add the final stop command.
     return asmCode + "STOP\n";
   } catch (Exception e) {
     System.out.println(e.getMessage());
     throw new TokenizerException("Fatal error: could not compile program");
   }
 }
  // No return value for this function.
  static void getFormals(SamTokenizer f) throws TokenizerException {
    try {
      // The only type for formals is int.
      myCheck(f, "int");
      String paramName = f.getWord();

      // Check if the name already exists.
      if (symTables.lastElement().containsKey(paramName))
        throw new TokenizerException("Error: Duplicate declaration of " + paramName + ".");
      params.add(paramName);
      if (f.check(',')) {
        getFormals(f);
      }
      return;
    } catch (Exception e) {
      System.out.println(e.getMessage());
      throw new TokenizerException("Error: Invalid formals");
    }
  }
  // Parse a method declaration.
  static String getMethod(SamTokenizer f) throws TokenizerException {
    try {
      // The return type for all methods must be int.
      myCheck(f, "int");

      // methodName stores the name of the method.
      String methodName = f.getWord();
      if (symMap.containsKey(methodName))
        throw new TokenizerException("Error: Duplicate declaration of method " + methodName);
      String asmCode = methodName;

      // The name of the method <-> index of the symbol table of the method in the symTables vector
      symMap.put(methodName, symTables.size());
      symTables.addElement(new LinkedHashMap<String, Integer>());

      // Since there cannot be another method declaration inside of a, the symbol table we are
      // dealing with is
      // always the last element of the symTables vector.
      symTables.lastElement().put(methodName, 0);

      asmCode += ":\n";
      myCheck(f, '(');
      if (!f.check(')')) {
        // No SaM code generated by getFormals
        getFormals(f);
        myCheck(f, ')');
      }
      actMap.put(methodName, params.size());
      for (int i = 1; i <= params.size(); ++i) {
        // Add all formals to the symbol tabel
        symTables.lastElement().put(params.elementAt(params.size() - i), -i);
      }
      asmCode += getBody(f);
      // Clear the params vector and set varCounter back to 2.
      params.clear();
      varCounter = 2;
      return asmCode;
    } catch (Exception e) {
      System.out.println(e.getMessage());
      throw new TokenizerException("Error: Invalid method declaration.");
    }
  }
 static String getActuals(SamTokenizer f, String methodName) throws TokenizerException {
   try {
     int actualCounter = 0;
     ++actualCounter;
     String asmCode = "";
     asmCode += getExp(f);
     while (f.check(',')) {
       ++actualCounter;
       asmCode += getExp(f);
     }
     if (actualCounter != actMap.get(methodName))
       throw new TokenizerException("Error: Invalid number of actuals.");
     globalCounter = actualCounter;
     actualCounter = 0;
     return asmCode;
   } catch (Exception e) {
     System.out.println(e.getMessage());
     throw new TokenizerException("Error: Invalid actuals");
   }
 }
 static String getBlock(SamTokenizer f) throws TokenizerException {
   try {
     String asmCode = "";
     Boolean returnFlag = false;
     Boolean tempFlag;
     while (!f.check('}')) {
       asmCode += getStmt(f);
       tempFlag = returnFlags.pop();
       if (tempFlag) {
         returnFlag = true;
       }
     }
     if (!returnFlag) returnFlags.push(false);
     else returnFlags.push(true);
     return asmCode;
   } catch (Exception e) {
     System.out.println(e.getMessage());
     throw new TokenizerException("Error: Invalid block.");
   }
 }
 static String getBody(SamTokenizer f) throws TokenizerException {
   try {
     String asmCode = "";
     Boolean returnFlag = false;
     Boolean tempFlag;
     myCheck(f, '{');
     switch (f.peekAtKind()) {
       case WORD:
         // Check if it is a variable declaration.
         while (f.check("int")) {
           asmCode += getVariables(f);
           myCheck(f, ';');
         }
         // Parse all the statement before }.
         while (!f.check('}')) {
           asmCode += getStmt(f);
           tempFlag = returnFlags.pop();
           if (tempFlag) {
             returnFlag = true;
           }
         }
         if (!returnFlag) throw new TokenizerException("Error: No return.");
         return asmCode;
       case OPERATOR:
         while (!f.check('}')) {
           asmCode += getStmt(f);
           tempFlag = returnFlags.pop();
           if (tempFlag) {
             returnFlag = true;
           }
         }
         if (!returnFlag) throw new TokenizerException("Error: No return.");
         return asmCode;
       default:
         throw new TokenizerException("Error: Invalid body.");
     }
   } catch (Exception e) {
     System.out.println(e.getMessage());
     throw new TokenizerException("Error: Invalid body.");
   }
 }
  static String getStmt(SamTokenizer f) throws TokenizerException {
    try {
      String asmCode = "";
      Boolean returnFlag1;
      Boolean returnFlag2;

      switch (f.peekAtKind()) {
        case WORD:
          {
            String newWord = f.getWord();
            switch (newWord) {
              case "return":
                {
                  asmCode += getExp(f);
                  myCheck(f, ';');
                  // SaM code for return.
                  asmCode +=
                      "STOREOFF -"
                          + Integer.toString(params.size() + 1)
                          + "\n"
                          + "ADDSP -"
                          + Integer.toString(varCounter - 2)
                          + "\n"
                          + "JUMPIND\n";
                  returnFlags.push(true);
                  return asmCode;
                }
              case "if":
                {
                  // Generate two valid lables for divergence.
                  String label1 = getLabel();
                  String label2 = getLabel();
                  myCheck(f, '(');
                  asmCode += getExp(f);
                  myCheck(f, ')');
                  asmCode += ("JUMPC " + label1 + "\n");
                  // Buffer the statements for if condition.
                  String tempString = getStmt(f);
                  returnFlag1 = returnFlags.pop();
                  myCheck(f, "else");
                  asmCode += getStmt(f);
                  returnFlag2 = returnFlags.pop();
                  // Manage the divergence.
                  asmCode +=
                      ("JUMP " + label2 + "\n" + label1 + ":\n" + tempString + label2 + ":\n");
                  if (returnFlag1 && returnFlag2) returnFlags.push(true);
                  else returnFlags.push(false);
                  return asmCode;
                }
              case "while":
                {
                  String label1 = getLabel();
                  String label2 = getLabel();

                  // Push the label as the return position.
                  labels.push(label2);
                  myCheck(f, '(');
                  asmCode += (label1 + ":\n");
                  asmCode += getExp(f);
                  asmCode += ("ISNIL\n" + "JUMPC " + label2 + "\n");
                  myCheck(f, ')');

                  // Flag indicating that we are in a while loop (possibly nested while loops.)
                  flags.push(true);
                  asmCode += getStmt(f);
                  asmCode += ("JUMP " + label1 + "\n" + label2 + ":\n");

                  // Once finish parsing the while loop, pop out the labels and flags.
                  labels.pop();
                  flags.pop();
                  return asmCode;
                }
              case "break":
                {
                  // flags.empty() indicates that we are not currently in a while loop.
                  if (flags.empty())
                    throw new TokenizerException("Error: Invalid break statement.");
                  myCheck(f, ';');

                  returnFlags.push(false);
                  // Jump to the current inner most return postion.
                  return "JUMP " + labels.peek() + "\n";
                }
              default:
                // Assign statement
                // Check if the variable is already defined.
                if (!symTables.lastElement().containsKey(newWord))
                  throw new TokenizerException("Error: Variable not declared.");
                myCheck(f, '=');
                asmCode += getExp(f);
                myCheck(f, ';');
                // Get the address of the variable from the symbol table.
                int addr = symTables.lastElement().get(newWord);
                asmCode += ("STOREOFF " + Integer.toString(addr) + "\n");
                returnFlags.push(false);
                return asmCode;
            }
          }
        case OPERATOR:
          {
            switch (f.getOp()) {
              case '{':
                {
                  asmCode += getBlock(f);
                  return asmCode;
                }
              case ';':
                returnFlags.push(false);
                return asmCode;
              default:
                throw new TokenizerException("Error: Invalid operator inside a statement.");
            }
          }
        default:
          throw new TokenizerException("Error: Invalid Statement.");
      }
    } catch (Exception e) {
      System.out.println(e.getMessage());
      throw new TokenizerException("Error: Invalid Statement.");
    }
  }
  // Parse an expression.
  static String getExp(SamTokenizer f) throws TokenizerException {
    try {
      String asmCode = "";
      switch (f.peekAtKind()) {
          // Single int
        case INTEGER:
          return "PUSHIMM " + f.getInt() + "\n";
        case OPERATOR:
          myCheck(f, '(');
          if (f.check('-')) {
            // Negative of x = 0 - x;
            asmCode += "PUSHIMM 0\n";
            asmCode += getExp(f);
            asmCode += "SUB\n";
          } else if (f.check('!')) {
            // Negate x
            // Return 1 for 0 and return 0 for all the others.
            asmCode += getExp(f);
            asmCode += "ISNIL\n";
          } else {
            asmCode += getExp(f);
            // System.out.println(asmCode);
            if (f.check(')')) return asmCode;
            else if (f.check('+') || f.check('|')) asmCode += (getExp(f) + "ADD\n");
            else if (f.check('-')) asmCode += (getExp(f) + "SUB\n");
            else if (f.check('*') || f.check('&')) asmCode += (getExp(f) + "TIMES\n");
            else if (f.check('/')) asmCode += (getExp(f) + "DIV\n");
            else if (f.check('<')) asmCode += (getExp(f) + "CMP\n" + "ISPOS\n");
            else if (f.check('>')) asmCode += (getExp(f) + "CMP\n" + "ISNEG\n");
            else if (f.check('=')) asmCode += (getExp(f) + "CMP\n" + "ISNIL\n");
            else throw new TokenizerException("Error: Invalid expression");
          }
          myCheck(f, ')');
          return asmCode;
        case WORD:
          String newWord = f.getWord();
          switch (newWord) {
            case "true":
              return "PUSHIMM " + "1" + "\n";
            case "false":
              return "PUSHIMM " + "0" + "\n";
            default:
              // If there is a (, it is an invocation of a method.
              // EXP->METHOD '(' ACTUALS? ')'
              if (f.check('(')) {
                // Check if the name of the method is declared before.
                if (!symMap.containsKey(newWord))
                  throw new TokenizerException("Error: Method not declared.");
                asmCode += "PUSHIMM 0\n";
                String sp = "";
                if (f.check(')')) {
                  // No actuals
                  if (actMap.get(newWord) != 0)
                    throw new TokenizerException("Error: Invalid number of actuals.");
                } else {
                  asmCode += getActuals(f, newWord);
                  myCheck(f, ')');
                  // Store the command for popping out parameters temporarily in this case.
                  sp = "ADDSP -" + Integer.toString(globalCounter) + "\n";
                }
                // Invocation of the method.
                // If there is no actuals for this method, then the string sp is an empty string.
                asmCode += ("LINK\n" + "JSR " + newWord + "\n" + "POPFBR\n" + sp);

                return asmCode;
              } else {
                // EXP->LOCATION
                // Check if the variable is already declared.
                if (!symTables.lastElement().containsKey(newWord))
                  throw new TokenizerException("Error: Variable not declared.");
                // Get the address of the variable from the symbol table.
                int addr = symTables.lastElement().get(newWord);
                asmCode += ("PUSHOFF " + Integer.toString(addr) + "\n");
                return asmCode;
              }
          }
        default:
          throw new TokenizerException("Error: Invalid expression.");
      }
    } catch (Exception e) {
      System.out.println(e.getMessage());
      throw new TokenizerException("Error: Cannot parse the expression.");
    }
  }