package a5;
import java.util.InputMismatchException;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.LinkedList;
/**
This class contains an evaluate method that interprets
programs in the simple functional language defined by the grammar
given below. In case of syntactic or semantic error, the method
will throw an InputMismatchException that contains an
appropriate error message.
It assumes that static scoping is in use.
So this is the EXTRA CREDIT VERSION of Assignment 5
The program representation is as a string of tokens separated
by whitespace characters as recognized by the
Character.isWhitespace predicate.
There is no explicit constructor; the default constructor is sufficient.
The grammar for boolean expressions has start symbol <Program> and productions
Program := Def* E
E := ( Fname E E )
E := Number
E := Id
Def := define Id ( Id Id ) Def* E end
Fname := Id | + | *
Id := Letter+
Number := Digit+
Note that recursive descent parsing is appropriate for this grammar.
*/
public class Interpreter
{
// used to divide the program into tokens
// and to retrieve the next token
private Scanner tokenizer;
// the next unprocessed token of the program
private String lookahead;
/// CLASSES FOR REPRESENTING PARSED PROGRAMS & PROGRAM SEGMENTS ///
/**
This class represents parsed programs in the given language.
*/
private class Program
{
// a list of the function blocks in the program
public LinkedListString
@param first the first operand
@param second the second operand
*/
// this class has no mutators and returns no mutable
// values, so cloning the constructor's arguments
// is unnecessary
public ExprInternalNode(String op, ExprTree first, ExprTree second) {
operator = op;
firstChild = first;
secondChild = second; }
/**
Evaluates the represented expression in a given environment
@param e the environment to be used for evaluation
@param caller the name of the caller
(used for error messages only)
@return the integer value obtained by evaluating the tree
@throws InputMismatchException if an undefined function or
variable is present
*/
// The evaluation algorithm is a recursive evaluation that's
// equivalent to a postorder traversal
// It looks up the operator and the operands in the environment
// and applies the operator to the operands
// Note that processing of + and * is hard-coded into the method.
// However the ordering of the cases in the current version of
// this method could handle the situation where these operators
// can be redefined by the user.
public int evaluate(Environment e, String caller) {
Def d = e.getDef(operator);
if (d != null) {
String fname = d.getName();
return d.apply(firstChild.evaluate(e, fname),
secondChild.evaluate(e, fname), e); }
if (operator.equals("+")) {
return firstChild.evaluate(e,"+")
+ secondChild.evaluate(e,"+"); }
if (operator.equals("*")) {
return firstChild.evaluate(e,"*")
* secondChild.evaluate(e,"*"); }
else
throw new InputMismatchException(
"undefined function " + operator + " in " + caller); }
} // end ExprInternalNode class
///////////////////// ENVIRONMENTS //////////////////////
/**
This class is used to represent environments in which
expressions may be evaluated using dynamic scoping
*/
// No global frame is assumed -- the treatment of + and *
// is hard-coded into the "evaluate" method
// The default constructor suffices in the 0-argument case
private class Environment
{
// Here we assume that frames have their own link fields,
// so that it's enough to specify only the top frame
private Frame topFrame;
public Environment() {
topFrame = new Frame("main",null,0,null,0,null,null); }
/**
Adds a given frame, representing a function block,
to the environment
@param nm the name of the function whose block
corresponds to the frame (for debugging only)
@param p1 the first formal parameter of the function
@param v1 the argument bound to this parameter
@param p2 the first formal parameter of the function
@param v2 the argument bound to this parameter
@param d the immediately nested function blocks for
this function
*/
public void addFrame(String nm, String p1, int v1,
String p2, int v2, Frame definingFrame) {
Frame f = new Frame(nm, p1, v1, p2, v2, topFrame,
definingFrame);
topFrame = f; }
/**
Evaluates all the definitions in a given list.
That is, enters them into the current frame
and initializes their envrionment references.
@param defs the list of definitions
@param definingEnv the environment in which
the definitions were defined
*/
// may assume that there is at least one frame on the stack
public void evaluateDefs(LinkedListExprTree
@throws InputMismatchException for ill-formed input strings
*/
// The parsing algorithm is recursive descent.
// A dummy symbol is appended to each input string so
// that lookahead never fails
// The input string is entered into a tokenizer,
// which really ought to be closed as in Assignment 2
private Program parse(String input) {
if (input == null)
throw new InputMismatchException(
"input string cannot be null");
tokenizer = new Scanner(input + " $end-of-file$");
lookahead = tokenizer.next();
Program result = parseProgram();
tokenizer.close();
if (!lookahead.equals("$end-of-file$"))
throw new InputMismatchException(
"symbols remain after end of expression");
return result; }
//////// parsing methods for particular nonterminals /////////
/**
Parse a string that is assumed to be generable from the
<program> symbol in the given grammar
The string is assumed to be a prefix of the contents
of the tokenizer. After parsing, all tokens of the
string will have been removed from the tokenizer.
@return a Program representation for the string
(which is thus assumed to represent a legal program)
@throws InputMismatchException for ill-formed input strings
*/
private Program parseProgram() {
LinkedList<def> symbol in the given grammar
The string is assumed to be a prefix of the contents
of the tokenizer. After parsing, all tokens of the
string will have been removed from the tokenizer.
@return a Def representation for the string
(which is thus assumed to represent a function block)
@throws InputMismatchException for ill-formed input strings
*/
private Def parseDef() {
// could remove this test -- lookahead is already checked
if (!lookahead.equals("define"))
throw new InputMismatchException(
"define expected -- " + lookahead + " found");
lookahead = tokenizer.next();
String name = parseId();
if (!lookahead.equals("("))
throw new InputMismatchException(
"( expected in " + name + " -- " + lookahead + " found");
lookahead = tokenizer.next();
String param1 = parseId();
String param2 = parseId();
if (!lookahead.equals(")"))
throw new InputMismatchException(
") expected in " + name + " -- " + lookahead + " found");
lookahead = tokenizer.next();
LinkedList<expr> symbol in the given grammar
The string is assumed to be a prefix of the contents
of the tokenizer. After parsing, all tokens of the
string will have been removed from the tokenizer.
@return a syntax tree for the expression, represented as
an ExprTree
@throws InputMismatchException for ill-formed input strings
*/
private ExprTree parseExpr() {
// try the 3rd rule
if (lookahead.equals("(")) {
lookahead = tokenizer.next();
String f = parseFunctionName();
ExprTree child1 = parseExpr();
ExprTree child2 = parseExpr();
if (!lookahead.equals(")"))
throw new InputMismatchException(
") expected in " + f + " -- " + lookahead + " found");
lookahead = tokenizer.next();
return new ExprInternalNode(f, child1, child2); }
// try the 1st rule
else if (Character.isLetter(lookahead.charAt(0))) {
return new ExprLeaf(parseId(), null); }
// try the 2nd rule
else if (Character.isDigit(lookahead.charAt(0))) {
String numberAsString = parseNumber();
return new ExprLeaf(numberAsString,
Integer.parseInt(numberAsString)); }
else
throw new InputMismatchException(
"( expected -- " + lookahead + " found"); }
/**
Parse a string that is assumed to be generable from the
<id> symbol in the given grammar
The string is assumed to be a prefix of the contents
of the tokenizer. After parsing, all tokens of the
string will have been removed from the tokenizer.
@return a String representing the token
@throws InputMismatchException for ill-formed input strings
*/
private String parseId() {
for (int i=0; i