Partial solutions to the programming assignments ------------------------------------------------ Grammar with the 'let' construct and power (^) operator: <expr> -> 'let' ID '=' <expr> | <term> <term_tail> <term> -> <factor> <factor_tail> <term_tail> -> <add_op> <term> <term_tail> | empty <factor> -> '-' <factor> | <power> <factor_tail> -> <mult_op> <factor> <factor_tail> | empty <power> -> '(' <expr> ')' | '(' <expr> ')' '^' <power> | ID | ID ^ <power> | NUM | NUM ^ <power> Alternative grammar: <expr> -> 'let' ID '=' <expr> | <term> <term_tail> <term> -> <factor> <factor_tail> <term_tail> -> <add_op> <term> <term_tail> | empty <factor> -> '-' <factor> | <power> <factor_tail> -> <mult_op> <factor> <factor_tail> | empty <power> -> <simple> '^' <power> | <simple> <simple> -> '(' <expr> ')' | ID | NUM The <expr> productions and semantic rules implemented as a recursive descent parser in Java: // expr - parse <expr> -> 'let' ID '=' <expr> | <term> <term_tail> private static int expr(Hashtable exprin, Hashtable exprout) throws IOException { if (token == tokens.TT_WORD && tokens.sval.equals("let")) { getToken(); String id = tokens.sval; getToken(); // advance to '=' if (token != (int)'=') System.out.println("'=' expected"); getToken(); // advance to <expr> int val = expr(exprin, exprout); exprout.put(id, new Integer(val)); return val; } else { Hashtable termout = new Hashtable(); int subtotal = term(exprin, termout); return term_tail(subtotal, termout, exprout); } } The <power> productions (of the first grammar above) and semantic rules implemented as a recursive descent parser in Java: // power - parse <power> -> '(' <expr> ')' | '(' < expr> ')' '^' <power> | ID | ID '^' <power> | NUM | NUM '^' <power> private static int power(Hashtable powerin, Hashtable powerout) throws IOException { Hashtable power1 = powerin; int val = 0; if (token == (int)'(') { getToken(); val = expr(powerin, power1); if (token == (int)')') getToken(); else System.out.println("closing ')' expected"); } else if (token == tokens.TT_WORD) { String id = tokens.sval; getToken(); if (powerin.containsKey(id)) val = ((Integer)powerin.get(id)).intValue(); else System.out.println("identifier '" + id + "' unassigned"); } else if (token == tokens.TT_NUMBER) { val = (int)tokens.nval; getToken(); } else System.out.println("power expected"); if (token == (int)'^') { getToken(); return (int)Math.pow(val, power(power1, powerout)); } else { powerout.putAll(power1); return val; } } } The main method should set up the Hashtable variables and invoke expr: // set up hashtables Hashtable exprin = new Hashtable(); Hashtable exprout = new Hashtable(); // parse expression and get calculated value: int value = expr(exprin, exprout); Don't forget to copy the Hashtable contents when necessary with the putAll method of Hashtable, as in term_tail for example: // term_tail - parse <term_tail> -> <add_op> <term> <term_tail> | empty private static int term_tail(int subtotal, Hashtable tailin, Hashtable tailout) throws IOException { if (token == (int)'+') { getToken(); Hashtable termout = new Hashtable(); int termvalue = term(tailin, termout); return term_tail(subtotal + termvalue, termout, tailout); } else if (token == (int)'-') { getToken(); Hashtable termout = new Hashtable(); int termvalue = term(tailin, termout); return term_tail(subtotal - termvalue, termout, tailout); } else { tailout.putAll(tailin); return subtotal; } }