package A1; import java.util.InputMismatchException; import java.util.Scanner; /** This class contains an evaluate method that evaluates boolean expressions represented as Strings, provided that they are legal expressions with respect to the language defined by the grammar given below. If not, the method will throw an InputMismatchException that contains an appropriate error message. 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 <expr> and productions
    <expr> ::= <operand> | ~<expr> | ( <expr> <operator> <expr> )
    <operator> ::= ^ | v 
    <operand> ::= T | F | true | false
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; /** Instances of this class represent syntax trees (analogs of parse trees that are used for expression evaluation). Their methods are abstract, since different subclasses are used for leaves and nonleaves. */ private abstract class ExprTree { abstract public boolean evaluate(); abstract public String getOperator(); abstract protected String toString(String str); } // end ExprTree class /** Instances of this class represent syntax trees with single nodes representing operands. */ private class ExprLeaf extends ExprTree { // the value of the operand private boolean value; /** This constructor takes a token and constructs a corresponding syntax tree of one node. @param token one of the four legal operand tokens permitted by the current grammar (T, F, true, false). Illegal operand tokens are given the value false. */ public ExprLeaf(String token) { if (token.equals("T") || token.equals("true")) value = true; else value = false; } /** @return the value of the syntax tree */ public boolean evaluate() { return value; } /** @return a dummy value for the tree's top-level operator, since trees of 1 node have no operators. */ public String getOperator() { return null; } // overrides inherited method public String toString() { return Boolean.toString(value); } public String toString(String indent) { return indent + value; } } // end ExprLeaf class /** Instances of this class represent syntax trees with more than one node. Roots represent operators, and children represent operands. Operators may be unary or binary. Unary operators are assumed to have null second children. Currently the only operators supported are the logical operators ~, ^, and v. */ private class ExprInternalNode extends ExprTree { // the top-level operator private String operator; // the first operand private ExprTree firstChild; // the second operand (null for a unary operator) private ExprTree secondChild; /** A constructor that takes as arguments the three desired components of the new tree @param op the top-level operator, represented as a String @param first the first operand @param second the second operand */ public ExprInternalNode(String op, ExprTree first, ExprTree second) { operator = op; firstChild = first; secondChild = second; } /** @return the boolean value obtained by evaluating the tree @throws InputMismatchException if an illegal operator is present */ // The evaluation algorithm is straightforward recursive evaluation, // equivalent to a postorder traversal public boolean evaluate() { if (operator.equals("~")) return !firstChild.evaluate(); else if (operator.equals("^")) return (firstChild.evaluate() && secondChild.evaluate()); else if (operator.equals("v")) return (firstChild.evaluate() || secondChild.evaluate()); else throw new InputMismatchException( "illegal operand"); } /** @return the top-level operator token, represented as a string */ public String getOperator() { return operator; } // overrides inherited method public String toString() { return toString(""); } /** @param indent a string of spaces which is printed before the contents of the root. That is, the length of this string specifies how far the contents of the root are to be indented. @return a string representing the expression rooted at the node The node contents are printed in preorder. The contents of each node is indented an amount proportional to its distance from the root. */ /* The algorithm is the straightforward recursive one for preorder traversal, with a fixed-length string of spaces appended to the parameter at each recursive call. */ protected String toString(String indent) { StringBuffer outputBuffer = new StringBuffer(indent); outputBuffer.append(operator); outputBuffer.append(System.getProperty("line.separator")); outputBuffer.append(firstChild.toString(indent+" ")); if (secondChild != null) { outputBuffer.append(System.getProperty("line.separator")); outputBuffer.append(secondChild.toString(indent+" ")); } return new String(outputBuffer); } } // end ExprInternalNode class /** Evaluate a boolean expression consistent with the given grammar @param input the input string representing the expression @return the boolean value @throws InputMismatchException for ill-formed input strings */ public boolean evaluate(String input) { return parse(input).evaluate(); } /** Evaluate a boolean expression consistent with the given grammar, but print a traversal of the parse tree before returning the value. @param input the input string representing the expression @return the boolean value @throws InputMismatchException for ill-formed input strings */ public boolean evaluateShowingTraversal(String input) { ExprTree parseTree = parse(input); System.out.println(parseTree); return parseTree.evaluate(); } /** Parse a string that is assumed to represent a boolean expression that can be generated by the given grammar @param input the input string @return a syntax tree for the expression, represented as an ExprTree @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 // This method is private, since the ExprTree class is?? private ExprTree parse(String input) { if (input == null) throw new InputMismatchException( "input string cannot be null"); tokenizer = new Scanner(input + " $"); lookahead = tokenizer.next(); ExprTree result = parseExpr(); tokenizer.close(); if (!lookahead.equals("$")) throw new InputMismatchException( "symbols remain after end of expression"); return result; } ///////////// The parsing methods for particular nonterminals //////////////// /** Parse a string that is assumed to be generable from the <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 second rule if (lookahead.equals("~")) { lookahead = tokenizer.next(); ExprTree child = parseExpr(); return new ExprInternalNode("~", child, null); } // try the third rule else if (lookahead.equals("(")) { lookahead = tokenizer.next(); ExprTree child1 = parseExpr(); ExprTree operator = parseOperator(); ExprTree child2 = parseExpr(); if (lookahead.equals("$")) throw new InputMismatchException( "premature end of input -- ) expected"); else if (!lookahead.equals(")")) throw new InputMismatchException( ") expected -- " + lookahead + " found"); else lookahead = tokenizer.next(); return new ExprInternalNode( operator.getOperator(), child1, child2); } // try the first rule // this method will check for premature end of input // and illegal symbols else return parseOperand(); } /** Parse a string that is assumed to be generable from the <operator> 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 with the operator in the root and null children @throws InputMismatchException for ill-formed input strings */ private ExprTree parseOperator() { if (lookahead.equals("^") || lookahead.equals("v")) { String operator = lookahead; lookahead = tokenizer.next(); return new ExprInternalNode(operator, null, null); } else if (lookahead.equals("$")) throw new InputMismatchException( "premature end of input -- operator expected"); else throw new InputMismatchException( "operator expected -- " + lookahead + " found"); } /** Parse a string that is assumed to be generable from the <operand> 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 that is a leaf @throws InputMismatchException for ill-formed input strings */ private ExprTree parseOperand() { if (lookahead.equals("T") || lookahead.equals("true") || lookahead.equals("F") || lookahead.equals("false")) { String operand = lookahead; lookahead = tokenizer.next(); return new ExprLeaf(operand); } else if (lookahead.equals("$")) throw new InputMismatchException( "premature end of input at beginning of expression"); else throw new InputMismatchException( "operand expected -- " + lookahead + " found"); } }