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;
}
}