/* file: SimpleCalc.java author: Robert Keller */ import polya.*; import java.io.*; /** * Calculator on positive integer numerals, +, *, () * * The input grammar is: * * A -> P { '+' P } sum * * P -> U { '*' U } product * * U -> '(' A ')' | N parenthesized expression or numeral * * N -> D {D} numeral * * D -> 0|1|2|3|4|5|6|7|8|9 digit */ class SimpleCalc extends ParseFromString { /** * test program */ static public void main(String arg[]) { // BufferedReader is used to read a line at a time BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); String input; try { while( prompt() && (input = in.readLine()) != null ) { SimpleCalc calc = new SimpleCalc(input); Object result = calc.parse(); if( result instanceof ParseFailure ) // show result System.out.println("*** syntax error ***"); else System.out.println("result: " + result); System.out.println(); } } catch( IOException e ) { System.err.println("*** IOException caught; terminating"); } System.out.println(); } /** * Top-level parse method */ Object parse() { Object result = A(); if( position < lastPosition ) { return failure; } return result; } /** * Parse method for A -> P { '+' P } */ Object A() { Object result = P(); // get first addend if( isFailure(result) ) return failure; while( skipWhitespace() && nextCharIs('+') ) { Object P2 = P(); // get next addend if( isFailure(P2) ) return failure; try { result = Arith.add(result, P2); // accumulate result } catch( IllegalArgumentException e ) { System.err.println("internal error: IllegalArgumentException caught"); } } return result; } /** * Parse method for P -> U { '*' U } */ Object P() { Object result = U(); // get first factor if( isFailure(result) ) return failure; while( skipWhitespace() && nextCharIs('*') ) // while more factors { Object U2 = U(); // get next factor if( isFailure(U2) ) return failure; try { result = Arith.multiply(result, U2); // accumulate result } catch( IllegalArgumentException e ) { System.err.println("internal error: IllegalArgumentException caught"); } } return result; } /** * Parse method for U -> '(' A ')' | N */ Object U() { skipWhitespace(); if( nextCharIs('(') ) // Do we have a parenthesized expression? { Object A1 = A(); // Get what's inside parens skipWhitespace(); if( nextCharIs(')') ) { return A1; } return failure; } return N(); // Try for numeral } /** * Parse method for N -> D {D} */ Object N() { if( !Character.isDigit(peek()) ) // Do we have a numeral? return failure; StringBuffer buff = new StringBuffer(); while( Character.isDigit(peek()) ) // Accumulate digits of numeral { buff.append(nextChar()); } return new Long(buff.toString()); // Convert buffered string to Long } static String promptString = "input: "; // prompt string public static final ParseFailure failure = new ParseFailure(); // failure /** * constructor for parser */ SimpleCalc(String input) { super(input); } /** * prompter */ static boolean prompt() { System.out.print(promptString); System.out.flush(); return true; } } // class SimpleCalc /** * ParseFromString is the base class for parsing from a String, such as a * single input line */ class ParseFromString { public final static ParseFailure failure = new ParseFailure(); // failure protected String input; // input to parser protected int position; // position of current character protected int lastPosition; // last position in input string protected boolean eof; // end-of-file condition exists /** * Create ParseFromString object for string to be parsed. */ ParseFromString(String input) { this.input = input; position = -1; lastPosition = input.length()-1; eof = lastPosition == -1; } /** * Get the next input character. */ char nextChar() { if( position >= lastPosition ) { eof = true; return ' '; } return input.charAt(++position); } /** * If next char is the specified one, remove it and return true. * Otherwise return false. */ boolean nextCharIs(char c) { if( position >= lastPosition ) { eof = true; return false; } if( c == peek() ) { nextChar(); return true; } return false; } /** * Return the next character in the input, but don't remove it. */ char peek() { if( position >= lastPosition ) { return ' '; } return input.charAt(position+1); } /** * Put previously read character back. */ void putBack() { if( position >= 0 ) { position--; } } /** * Skip any whitespace. */ boolean skipWhitespace() { while( !eof && peek() == ' ' ) { nextChar(); } return true; } /** * Test whether the argument is a ParseFailure object. */ boolean isFailure(Object ob) { return ob instanceof ParseFailure; } } // class ParseFromString /** * ParseFailure object is used to indicate a parse failure. */ class ParseFailure { }