import java.util.*; import java.math.BigInteger; /** * This is the compiler. Okay, it actually compiles AND runs, but * we've called it "Compiler" anyway. */ public class Compiler { private Scanner scanner; private HashMap declaredVariables; private ArrayList statementList; /** * Constructor sets up the Scanner, so that it * can obtain the tokens constituting the program. * It also initializes the "symbol table". *

* @param filename * This is taken care of by the Scanner object, * but if filename == null then we want to * compile the program typed as keyboard input. * Otherwise, we want to compile the file named as the input. */ public Compiler( String filename ) { scanner = new Scanner( filename ); declaredVariables = new HashMap(); statementList = new ArrayList(); } /** * This method "compiles" the PL142 program - * it creates the list of Statements needed to execute the PL142 program * if its compilation doesn't find any errors in it. *

* It returns true if there were no compile-time errors, * and false otherwise. */ public boolean compile() { String firstToken; String operator; String semiColon; Statement newStatement; Variable targetVariable; Variable leftOperand; Variable rightOperand; while ( scanner.hasNextToken() ) { firstToken = scanner.getNextToken(); if ( firstToken.equals("EOF") ) { // don't do anything - we'll exit this loop } else { if ( firstToken.equals("INTEGER" ) ) { if ( !handleINTEGERDeclaration() ) { return false; } } else { // firstToken shouldn't name an existing variable targetVariable = findVariable( firstToken ); if ( targetVariable == null ) { printError("Undeclared variable: " + firstToken ); return false; } // okay, now we should see either a ';' (for a print statement) or // a '=' (for an assignment statement) operator = scanner.getNextToken(); if ( operator.equals(";") ) { newStatement = new Statement( targetVariable ); } else if ( operator.equals("=") ) { leftOperand = handleOperandReference(); if ( leftOperand == null ) { return false; } operator = scanner.getNextToken(); if ( !operator.equals("+") && !operator.equals("*") ) { printError( "Invalid syntax - unimplemented operator?: '" + operator + "'" ); return false; } rightOperand = handleOperandReference(); if ( rightOperand == null ) { return false; } semiColon = scanner.getNextToken(); if ( !semiColon.equals(";") ) { printError( "Invalid syntax: missing ';'" ); return false; } newStatement = new Statement( targetVariable, leftOperand, rightOperand, operator ); } else { printError("Bad statement type: '" + targetVariable.getName() + " " + operator + "'"); return false; } statementList.add( newStatement ); } } } return true; } /** * This method runs the PL142 program, assuming it has * been compiled and had no compile-time errors. */ public boolean run() { Statement nextStatement; int statementNumber; for ( statementNumber=0; statementNumber < statementList.size(); statementNumber++ ) { nextStatement = (Statement)statementList.get( statementNumber ); nextStatement.Evaluate(); } return true; } /** * Check variable name syntax. The only constraint this implementation enforces is that * the first character can't be a digit. */ private boolean isValidName( String name ) { if ( "0123456789".indexOf( name.charAt(0) ) != -1 ) { return false; } return true; } /** * Read the remainder of the PL142 statement after "INTEGER". * If it is a valid variable declaration, create a new Variable * object and put it in the symbol table. */ private boolean handleINTEGERDeclaration() { String varName; Variable newVariable; varName = scanner.getNextToken(); // varName should be a valid variable name if ( !isValidName( varName ) ) { printError( "Invalid variable name: " + varName ); return false; } // varName shouldn't be already declared if ( findVariable( varName ) != null ) { printError("Variable " + varName + " is already declared."); return false; } // so far so good - should fine a ';' now String semiColon = scanner.getNextToken(); if ( !semiColon.equals(";") ) { printError("Expected ';'"); return false; } // good to go - create the var and enter in symbol table newVariable = new Variable( varName ); declaredVariables.put( varName, newVariable ); return true; } /** * This routine reads the next input token, which should be * a valid operand (either a variable name or an INTEGER literal). * If the former, it returns the Variable object * corresponding to that variable name. If the latter, it creates * a new Variable object and initializes it to the * value of the literal. If neither, it returns null. */ private Variable handleOperandReference() { String operandName; Variable result; operandName = scanner.getNextToken(); // try it as a variable name result = findVariable( operandName ); if ( result != null ) { return result; } // okay, maybe it's an INTEGER literal. // It is if it's composed only of digits if ( isValidINTEGERLiteral( operandName ) ) { result = new Variable(""); result.assignValue( new BigInteger(operandName) ); return result; } // not a valid operand System.out.println( "Invalid operand: " + operandName ); return null; } /** * Checks a string to see if it looks like a valid INTEGER literal *

* @param operand
* The potential INTEGER literal, as a String. */ private boolean isValidINTEGERLiteral( String operand ) { int charNum; if ( operand==null || operand.length()==0 ) { return false; } // this loop checks that each character in the String 'operand' is a digit for ( charNum=0; charNumVariable object for it. Otherwise, * return null. */ private Variable findVariable( String name ) { Variable result = (Variable)declaredVariables.get( name); return result; } /** * Private routine to print an error message, prepending line number. */ private void printError( String errorMsg ) { System.out.println( "Error on line " + scanner.getLineNumber() + ": " + errorMsg ); } /** * Simple driver for the Compiler class. (Constructs * a Compiler object and sets it running.)
* To use this driver, see the homework instructions. */ public static void main( String args[] ) { Compiler compiler = null; if ( args.length == 1 ) { compiler = new Compiler( args[0] ); } else if ( args.length == 0 ) { compiler = new Compiler( null ); } else { System.out.println("Usage: java Compiler [filename]\n"); System.out.println("\t\twhere [filename] is an optional input file name"); System.exit( -1 ); } System.out.println("Compiling..."); if ( compiler.compile() ) { System.out.println("Running..."); compiler.run(); } } }