/* * CS431 Summer 2004 * Project 1 * Group Members: Lo, Wing; Brown, Shallon; Lee, Myung; Luu, Huy; Shimko, Spencer * Explanation: This is a recursive descent parser. It recurses through * tokens passed to it from JLex's classes, parses, and evaluates * each token in turn. It uses a 1 token lookahead. * * EBNF form: * S := VARIABLE '=' S | E | EVAL VARIABLE * E := ( '+' | '-' ) E T | T * T := ( '*' | '/' ) T F | F * F := EXP '(' F G ')' * G := VARIABLE | DOUBLE | '(' E ')' | ('arc')? TRIG '(' E ')' * TRIG := 'cos' | 'sin' | 'tan' * EVAL := 'eval' */ package Parser; import java.lang.System; import java.math.*; import java.util.Vector; // Class: Parser // Purpose: Execute our main public class Parser { public static void main(String argv[]) throws java.io.IOException { Tokenizer tok = new Tokenizer ( ); Grammar g = new Grammar ( tok ); Yytoken tokN; // run through until $ while ( tok.lookAhead() != null ){ // should be a full ErrorParseTreeNode to prevent // this try/catch hack... but not enough time try { g.A(); } catch ( java.lang.NullPointerException e ){ } } } } // Class: Grammar // Purpose: Recurse through productions using the tokenizer // passed in during contruction class Grammar { static Tokenizer tok; static Yytoken tokN; static Yytoken trash; static varMap varM = new varMap (); // enumerations static final int ADD = 0; static final int SUB = 1; static final int MUL = 2; static final int DIV = 3; static final int EQ = 4; static final int LPAR = 5; static final int RPAR = 6; static final int ARC = 8; static final int COS = 9; static final int SIN = 10; static final int TAN = 11; static final int RADS = 12; static final int EVAL = 13; static final int VAR = 15; static final int DUB = 14; static final int EXP = 16; Grammar ( Tokenizer t ){ tok = t; } // the rest of the methods in the Grammar class are the productions public ParseTreeNode A( ) throws java.io.IOException { tokN = tok.peek ( ); // System.out.println("PROD A: " + tokN ); switch ( tokN.getType() ) { case VAR: if ( tok.lookAhead().getType() != EQ ){ return (E()); } else { String vn = tokN.getText(); // obtain variable name trash = tok.get(); trash = tok.get(); // remove varname and equals sign // add variable to varmap varM.varCreate( vn, A() ); //trash = tok.get(); break; } case EVAL: trash = tok.get(); EVAL( ); trash = tok.get();break; default: return (E( )); } return null; } public ParseTreeNode E( ) throws java.io.IOException { tokN = tok.peek ( ); // System.out.println("PROD E: " + tokN ); switch ( tokN.getType() ) { case ADD: trash = tok.get(); return (new PlusPTN ( E( ), T( ) )); case SUB: trash = tok.get(); return (new SubPTN ( E( ), T( ) )); case RPAR: trash = tok.get(); break; default: return ( T( ) ); } return null; } public ParseTreeNode T( ) throws java.io.IOException { tokN = tok.peek ( ); // System.out.println("PROD T: " + tokN ); switch ( tokN.getType() ) { case MUL: trash = tok.get (); return ( new MulPTN ( T( ), F( ) )); case DIV: trash = tok.get (); return ( new DivPTN ( T( ), F( ) )); default: return ( F( ) ); } } public ParseTreeNode F( ) throws java.io.IOException { tokN = tok.peek ( ); // System.out.println("PROD F: " + tokN ); switch ( tokN.getType() ) { case EXP: trash = tok.get(); if ( tok.peek().getType() != LPAR ){ System.out.println("Missing open paren in trig function!"); return null; } trash = tok.get(); return ( new ExpPTN ( F( ), G ( ) )); case LPAR: trash = tok.get(); return ( E() ); default: return ( G ( ) ); } } public ParseTreeNode G( ) throws java.io.IOException { tokN = tok.peek ( ); // System.out.println("PROD G: " + tokN ); switch ( tokN.getType() ) { case VAR: return ( VARIABLE ( ) ); case DUB: dubPTN tmp = new dubPTN ( tok.peek().getText() ); trash = tok.get(); return ( tmp ); case LPAR: trash = tok.get();return (E ( )); // case RPAR: case ARC: return (INV ( )); case RADS: trash = tok.get(); if ( tok.peek().getType() != LPAR ){ System.out.println("Missing open paren in trig function!"); return null; } trash = tok.get(); return ( new dubPTN ( String.valueOf( Math.toRadians( Double.parseDouble(E().evaluate()) )))); default: return(TRIG()); } } public ParseTreeNode TRIG( ) throws java.io.IOException { tokN = tok.peek ( ); trash = tok.get(); if ( tok.peek().getType() != LPAR ){ System.out.println("Missing open paren in trig function!"); return null; } trash = tok.get(); switch ( tokN.getType() ) { // a lot of type casting is needed since all doubles are stored as strings case SIN: return ( new dubPTN( String.valueOf ( Math.sin( Double.parseDouble(E().evaluate()))))); case COS: return ( new dubPTN( String.valueOf ( Math.cos( Double.parseDouble(E().evaluate()))))); case TAN: return ( new dubPTN( String.valueOf ( Math.tan( Double.parseDouble(E().evaluate()))))); default: System.out.println( "TRIG error" ); return ( new dubPTN("-1") ); } } public ParseTreeNode INV( ) throws java.io.IOException { trash = tok.get ( ); tokN = tok.peek ( ); trash = tok.get(); if ( tok.peek().getType() != LPAR ){ System.out.println("Missing open paren in trig function!"); return null; } trash = tok.get(); switch ( tokN.getType() ) { // a lot of type casting is needed since all doubles are stored as strings case SIN: return ( new dubPTN( String.valueOf ( Math.asin(Double.parseDouble(E().evaluate()))))); case COS: return ( new dubPTN( String.valueOf ( Math.acos(Double.parseDouble(E().evaluate()))))); case TAN: return ( new dubPTN( String.valueOf ( Math.atan( Double.parseDouble(E().evaluate()))))); default: System.out.println(" INV error") ; return ( new dubPTN ("-1")); } //return ( new dubPTN (String.valueOf ((1 / Double.parseDouble (TRIG().evaluate())) ); } public void EVAL( ) throws java.io.IOException { String s = tok.peek().getText (); String val = varM.varLookup ( s ); if ( val == null ){ System.out.println ("eval " + tok.peek().getText() + " is uninitialized" ); } else { System.out.println( "Variable \"" + s + "\" evaluates to " + val ); } } public ParseTreeNode VARIABLE( ) throws java.io.IOException { dubPTN tmp = new dubPTN( varM.varLookup ( tok.peek().getText () ) ); trash = tok.get(); return ( tmp ); } } // Class: varMap // Purpose: Store and lookup variables class varMap { static Vector variables; varMap(){ variables = new Vector(); } public String varLookup(String s){ int index = variables.indexOf(s); if ( index == -1 ){ System.out.println("ERROR! Unknown variable " + s); return null; } return ( String.valueOf(variables.elementAt(index+1)) ); } public void varCreate( String s, ParseTreeNode val ){ int index = variables.indexOf(s); if ( index == -1 ){ variables.add(s); variables.add(val.evaluate()); } else { //variables.setElementAt( val.evaluate(), index+1 ); variables.add( (index+1), val.evaluate()); } } } // Class: Tokenizer // Purpose: Provide buffered access to the JLEX tokens class Tokenizer { // buffered and current token static Yytoken buffer; static Yytoken tokN; static int tokNum; // lexer static Lexer yy = new Lexer(System.in); Tokenizer ( ) throws java.io.IOException { // "prime" the buffer while ( true ){ try { tokN = yy.yylex(); buffer = yy.yylex(); tokNum += 2; break; } catch ( java.lang.Error e ){ System.out.println( "Tokenization error on line #0 token #0" ); } } } public static Yytoken get ( ) throws java.io.IOException { // get next token and update buffer tokN = buffer; while ( true ){ try { buffer = yy.yylex(); break; } catch ( java.lang.Error e ){ System.out.println( "Tokenization error on line " + yy.getLine ( ) + " token #" + yy.getTokCnt ( ) ); } } tokNum++; return tokN; } public static Yytoken peek ( ){ return tokN; } public static Yytoken lookAhead ( ){ return buffer; } public static int getTokCnt ( ){ return tokNum; } } // Class: Yytoken // Purpose: All token objects are of this type class Yytoken { Yytoken ( int type, String text, int line, int charBegin, int charEnd) { m_type = type; m_text = new String(text); m_line = line; m_charBegin = charBegin; m_charEnd = charEnd; } public boolean match ( int type ){ return ( m_type == type ); } public int m_type; public String m_text; public int m_line; public int m_charBegin; public int m_charEnd; public String toString() { return "Token type #"+m_type+": "+m_text+" (line "+m_line+")"; } public String getText ( ){ return m_text; } public int getType ( ){ return m_type; } } // InterfacE: ParseTreeNode // Purpose: Provide an abstraction for other PTN's to implement interface ParseTreeNode{ String evaluate(); } // The *PTN classes are specialized ParseTreeNodes that evaluate // as required class PlusPTN implements ParseTreeNode{ ParseTreeNode E; ParseTreeNode T; PlusPTN ( ParseTreeNode e, ParseTreeNode t ){ E = e; T = t; } public String evaluate ( ){ return String.valueOf ( Double.parseDouble (E.evaluate()) + Double.parseDouble (T.evaluate()) ); } } class SubPTN implements ParseTreeNode{ ParseTreeNode E; ParseTreeNode T; SubPTN ( ParseTreeNode e, ParseTreeNode t ){ E = e; T = t; } public String evaluate ( ){ return String.valueOf ( Double.parseDouble (E.evaluate()) - Double.parseDouble (T.evaluate()) ); } } class DivPTN implements ParseTreeNode{ ParseTreeNode E; ParseTreeNode T; DivPTN ( ParseTreeNode e, ParseTreeNode t ){ E = e; T = t; } public String evaluate ( ){ if ( Double.parseDouble (T.evaluate()) == 0.0 ){ System.out.println("ERROR: Divide by zero goes to infinity (dumbass)"); } return String.valueOf ( Double.parseDouble (E.evaluate()) / Double.parseDouble (T.evaluate()) ); } } class MulPTN implements ParseTreeNode{ ParseTreeNode E; ParseTreeNode T; MulPTN ( ParseTreeNode e, ParseTreeNode t ){ E = e; T = t; } public String evaluate ( ){ return String.valueOf ( Double.parseDouble (E.evaluate()) * Double.parseDouble (T.evaluate()) ); } } class dubPTN implements ParseTreeNode{ String S; dubPTN ( String s ){ S = s; } public String evaluate ( ){ return S; } } class trigPTN implements ParseTreeNode{ Yytoken PTN_Tok; public String evaluate ( ){ return null; } } class ExpPTN implements ParseTreeNode{ ParseTreeNode E; ParseTreeNode T; ExpPTN ( ParseTreeNode e, ParseTreeNode t ) { E = e; T = t; } public String evaluate ( ){ return String.valueOf ( Math.pow(Double.parseDouble (E.evaluate()), Double.parseDouble (T.evaluate()) ) ); } } // unimplemented at this point class ErrorPTN implements ParseTreeNode{ String err; ErrorPTN ( String S ){ err = S; } public String evaluate(){ return err; } } %% %{ private int tokCount = 0; private int curLine = 0; public int getLine ( ){ return curLine; } public int getTokCnt ( ){ return tokCount; } private void updateStats ( ){ if ( yyline != curLine ){ tokCount = 0; curLine = yyline; } else { tokCount++; } } %} %line %char %type Yytoken %class Lexer ALPHA=[A-Za-z_] DIGIT=[0-9] NONNEWLINE_WHITE_SPACE_CHAR=[\ \t\b\012] WHITE_SPACE_CHAR=[\n\ \t\b\012] STRING_TEXT=(\\\"|[^\n\"]|\\{WHITE_SPACE_CHAR}+\\)* %% "+" { updateStats ( ); return (new Yytoken(0,yytext(),yyline,yychar,yychar+1)); } "-" { updateStats ( ); return (new Yytoken(1,yytext(),yyline,yychar,yychar+1)); } "*" { updateStats ( ); return (new Yytoken(2,yytext(),yyline,yychar,yychar+1)); } "/" { updateStats ( ); return (new Yytoken(3,yytext(),yyline,yychar,yychar+1)); } "=" { updateStats ( ); return (new Yytoken(4,yytext(),yyline,yychar,yychar+1)); } "(" { updateStats ( ); return (new Yytoken(5,yytext(),yyline,yychar,yychar+1)); } ")" { updateStats ( ); return (new Yytoken(6,yytext(),yyline,yychar,yychar+1)); } "arc" { updateStats ( ); return (new Yytoken(8,yytext(),yyline,yychar,yychar+1)); } "exp" { updateStats ( ); return (new Yytoken(16,yytext(),yyline,yychar,yychar+1)); } "cos" { updateStats ( ); return (new Yytoken(9,yytext(),yyline,yychar,yychar+1)); } "sin" { updateStats ( ); return (new Yytoken(10,yytext(),yyline,yychar,yychar+1)); } "tan" { updateStats ( ); return (new Yytoken(11,yytext(),yyline,yychar,yychar+1)); } "rads" { updateStats ( ); return (new Yytoken(12,yytext(),yyline,yychar,yychar+1)); } "eval" { updateStats ( ); return (new Yytoken(13,yytext(),yyline,yychar,yychar+1)); } {NONNEWLINE_WHITE_SPACE_CHAR}+ { } {ALPHA}({ALPHA}|{DIGIT}|_)* { updateStats ( ); return (new Yytoken(15,yytext(),yyline,yychar,yychar + yytext().length())); } ("-"|"+")?{DIGIT}+("."{DIGIT}+)? { updateStats ( ); return (new Yytoken(14,yytext(),yyline,yychar,yychar + yytext().length())); } "."[^ \t\b\012]* { java.lang.System.out.println("Invalid number format '" + yytext() + "' on line " + yyline + " token " + (getTokCnt()+1)); } . { java.lang.System.out.println("Unmatched input '" + yytext() + "' on line " + yyline+ " token " + (getTokCnt()+1)); }