/* file: SimpleCalc.java author: Robert Keller purpose: Calculator on numerals, +, *, () compile and run: java -cs SimpleCalc A -> M { '+' M } sum M -> 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 In the grammar { }, means 0 or more of what's inside. */ import polya.*; import java.io.*; class SimpleCalc extends LineBuffer // SimpleCalc parser class { static public void main(String arg[]) // USER INTERFACE { LineBufferInputStream in = new LineBufferInputStream(System.in); while( prompt() && !in.eof() ) // while more input { String input = in.getLine(); // get line of input SimpleCalc parser = new SimpleCalc(input); // create parser Object result = parser.parse(); // use parser if( result instanceof ParseFailure ) // show result System.out.println("*** syntax error ***"); else System.out.println("result: " + result); System.out.println(); } System.out.println(); } Object A() // PARSE FUNCTION for A -> M { '+' M } { Object result = M(); // get first addend if( isFailure(result) ) return failure; while( peek() == '+' ) // while more addends { nextChar(); Object M2 = M(); // get next addend if( isFailure(M2) ) return failure; try { result = Arith.add(result, M2); // accumulate result } catch( IllegalArgumentException e ) { System.err.println("internal error: IllegalArgumentException caught"); } } return result; } Object M() // PARSE FUNCTION for M -> U { '*' U } { Object result = U(); // get first factor if( isFailure(result) ) return failure; while( peek() == '*' ) // while more factors { nextChar(); 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; } Object U() // PARSE FUNCTION for U -> '(' A ')' | N { if( peek() == '(' ) // Do we have a parenthesized expression? { nextChar(); Object A1 = A(); // Get what's inside parens if( peek() == ')' ) { nextChar(); return A1; } return failure; } return N(); // Try for numeral } Object N() // PARSE FUNCTION for N -> D {D} { 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 } Object parse() // TOP LEVEL: parse with check for residual input { Object result = A(); if( position < lastPosition ) { return failure; } return result; } static String promptString = "input: "; // prompt string static ParseFailure failure = new ParseFailure(); // failure SimpleCalc(String input) // constructor for parser { super(input); // construct LineBuffer } boolean isFailure(Object ob) // test for failure { return ob instanceof ParseFailure; } static boolean prompt() { System.out.print(promptString); System.out.flush(); return true; } } // class SimpleCalc // ParseFailure object is used to indicate a parse failure class ParseFailure { } // From here on the code from addMult.java is used. // LineBuffer provides a way of getting characters from a string // Note that eof is a variable indicating end-of-line. It should // not be confused with the eof method of LineBufferInputStream. class LineBuffer { String input; // string being parsed boolean eof; // end-of-file condition exists int position; // position of current character int lastPosition; // last position in input string LineBuffer(String input) { this.input = input; // initialize variables dealing with string position = -1; // initialize "pointer" lastPosition = input.length()-1; eof = lastPosition == -1; } char nextChar() // get next character in input { if( position >= lastPosition ) // if no more characters { eof = true; // set eof return ' '; // and return a space } return input.charAt(++position); } char peek() // return, but retain, next character { if( position >= lastPosition ) { // if no more characters return ' '; // return a space } return input.charAt(position+1); } void skipWhitespace() // skip whitespace { while( !eof && peek() == ' ' ) { nextChar(); } } } // LineBuffer // LineBufferInputStream is an input stream capable of reading one line // at a time and returning the line as a string, by calling method getLine(). // It also provides the method eof() for testing for end-of-file. // It extends PushbackInputStream from package java.io class LineBufferInputStream extends PushbackInputStream { /** * LineBufferInputStream constructs from an InputStream **/ LineBufferInputStream(InputStream in) { super(in); // call constructor of PushbackInputStream } /** * getLine() gets the next line of input **/ String getLine() { StringBuffer b = new StringBuffer(); // buffer for line try { int c; while( !eof() && ((c = read()) != '\n') ) // read to end-of-line or -file { b.append((char)c); } return b.toString(); // get string from buffer } catch( java.io.IOException e ) { handleException("getLine", e); return ""; } } /** * eof() tells whether end-of-file has been reached. **/ public boolean eof() { try { int c = read(); if( c == -1 ) { return true; } else { unread(c); return false; } } catch( IOException e ) { handleException("eof", e); } return false; } /** * handleException is called when there is an IOException in any method. **/ public void handleException(String method, IOException e) { System.out.println("IOException in LineBufferInputStream: " + e + " calling method " + method); } }