/* file: addMult.java author: Robert Keller purpose: Tutorial additive expression parser The program reads an additive expression and multiplicative expression (with * taking precedence over +) and shows its tree as an S expr. compile and run: java -cs additive Your CLASSPATH must include a directory containing the Poly library. A -> M { '+' M } sum M -> V { '*' V } product V -> a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z In the grammar { }, means 0 or more of what's inside. */ import Poly.*; import java.io.*; class addMult extends LineBuffer // addMult parser class { static public void main(String arg[]) // USER INTERFACE { LineBufferInputStream in = // stream of lines new LineBufferInputStream(System.in); while( prompt() && !in.eof() ) // while more lines { String input = in.getLine(); // get a line addMult parser = new addMult(input); // construct parser for line Object result = parser.parse(); // parse the line System.out.println("result: " + result); // show the result System.out.println(); } System.out.println(); } Object A() // PARSE FUNCTION for A -> M { '+' M } { Object result; Object M1 = M(); // get the first addend if( isFailure(M1) ) return failure; result = M1; while( peek() == '+' ) // while more '+' { nextChar(); // absorb the '+' Object M2 = M(); // get the next addend if( isFailure(M2) ) return failure; result = Poly.List.list("+", result, M2); // create tree } return result; } Object M() // PARSE FUNCTION for M -> V { '*' V } { Object result; Object V1 = V(); // get the first variable if( isFailure(V1) ) return failure; result = V1; while( peek() == '*' ) // while more '*' { nextChar(); // absorb the '*' Object V2 = V(); // get the next variable if( isFailure(V2) ) return failure; result = Poly.List.list("*", result, V2); // create tree } return result; } // PARSE FUNCTION for V -> a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z Object V() { if( isVar(peek()) ) // if there is a variable { return (new StringBuffer(1).append(nextChar())).toString(); } return failure; } Object parse() // TOP LEVEL: parse with check for residual input { Object result = A(); // parse an A skipWhitespace(); // ignore trailing whitespace if( position < lastPosition ) // see if any residual junk { System.out.print("*** Residual characters after input: "); while( !eof ) // print residual characters { char c = nextChar(); System.out.print(c); } System.out.println(); } return result; // return result of parse } // isVar indicates whether its argument is a variable boolean isVar(char c) { switch( c ) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': return true; default: return false; } } // SUPPORT CODE static String promptString = "input: "; // prompt string static ParseFailure failure = new ParseFailure(); // failure addMult(String input) // parser constructor { super(input); // construct LineBuffer } boolean isFailure(Object ob) // test for failure { return ob instanceof ParseFailure; } static boolean prompt() // prompter { System.out.print(promptString); System.out.flush(); return true; } } // class addMult // ParseFailure object is used to indicate a parse failure. class ParseFailure { } // 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); } }