// 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."); } }
// 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 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."); } }
// 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"); } }
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 void myCheck(SamTokenizer f, String s) throws TokenizerException { if (!f.check(s)) throw new TokenizerException("Error: Expected " + s + " is lost."); }
// The following two functions can check if a char or a string is as expected in the grammar. // If not, we will throw a TokenizerException along with message about the expected symbol. static void myCheck(SamTokenizer f, char sym) throws TokenizerException { if (!f.check(sym)) throw new TokenizerException("Error: Expected " + sym + " is lost."); }
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."); } }