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 Statement
s 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; charNumnull
.
*/
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();
}
}
}