/* This Console applet simulates the stand-alone program SimpleParser5.java. */ import java.util.HashMap; public class SimpleParser5Console extends ConsoleApplet { static class ParseError extends Exception { // Represents a syntax error found in the user's input. ParseError(String message) { super(message); } } // end nested class ParseError HashMap symbolTable = new HashMap(); // The symbolTable contains information about the // values of variables. When a variable is assigned // a value, it is recorded in the symbol table. // The key is the name of the variable, and the // value is an object of type Double that contains // the value of the variable. (The wrapper class // Double is used, since a HashMap cannot contain // objects belonging to the primitive type double.) protected String getTitle() { return "Symbol Table Demo"; } protected void program() { // To start, add variables named "pi" and "e" to the // symbol tables. Their values are the usual // mathematical constants. symbolTable.put("pi", new Double(Math.PI)); symbolTable.put("e", new Double(Math.E)); console.putln("\n\nEnter commands; press return to end."); console.putln("Commands must have the form:\n"); console.putln(" print "); console.putln(" or"); console.putln(" let = "); while (true) { console.put("\n? "); skipBlanks(); if ( console.peek() == '\n' ) break; try { String command = console.getWord(); if (command.equalsIgnoreCase("print")) doPrintCommand(); else if (command.equalsIgnoreCase("let")) doLetCommand(); else throw new ParseError("Command must begin with 'print' or 'let'."); console.getln(); } catch (ParseError e) { console.putln("\n*** Error in input: " + e.getMessage()); console.putln("*** Discarding input: " + console.getln()); } } console.putln("\n\nDone."); } // end main() void skipBlanks() { // Skip past any spaces and tabs on the current line of input. // Stop at a non-blank character or end-of-line. while ( console.peek() == ' ' || console.peek() == '\t' ) console.getAnyChar(); } void doLetCommand() throws ParseError { // Process a command of the form let = . // When this method is called, the word "let" has already // been read. Read the variable name and the expression, and // store the value of the variable in the symbol table. skipBlanks(); if ( ! Character.isLetter(console.peek()) ) throw new ParseError("Expected variable name after 'let'."); String name = readWord(); // The name of the variable. skipBlanks(); if ( console.peek() != '=' ) throw new ParseError("Expected '=' operator for 'let' command."); console.getChar(); double val = expressionValue(); // The value of the variable. skipBlanks(); if ( console.peek() != '\n' ) throw new ParseError("Extra data after end of expression."); symbolTable.put(name, new Double(val)); // Add to symbol table. console.putln("ok"); } void doPrintCommand() throws ParseError { // Process a command of the form print . // When this method is called, the word "print" has already // been read. Evaluate the expression and print the value. double val = expressionValue(); skipBlanks(); if ( console.peek() != '\n' ) throw new ParseError("Extra data after end of expression."); console.putln("Value is " + val); } double expressionValue() throws ParseError { // Read an expression from the current line of input and // return its value. skipBlanks(); boolean negative; // True if there is a leading minus sign. negative = false; if (console.peek() == '-') { console.getAnyChar(); negative = true; } double val; // Value of the expression. val = termValue(); // An expression must start with a term. if (negative) val = -val; // Apply the leading minus sign skipBlanks(); while ( console.peek() == '+' || console.peek() == '-' ) { // Read the next term and add it to or subtract it from // the value of previous terms in the expression. char op = console.getAnyChar(); double nextVal = termValue(); if (op == '+') val += nextVal; else val -= nextVal; skipBlanks(); } return val; } // end expressionValue() double termValue() throws ParseError { // Read a term from the current line of input and // return its value. skipBlanks(); double val; // The value of the term. val = factorValue(); // A term must start with a factor. skipBlanks(); while ( console.peek() == '*' || console.peek() == '/' ) { // Read the next factor, and multiply or divide // the value-so-far by the value of this factor. char op = console.getAnyChar(); double nextVal = factorValue(); if (op == '*') val *= nextVal; else val /= nextVal; skipBlanks(); } return val; } // end termValue() double factorValue() throws ParseError { // Read a factor from the current line of input and // return its value. skipBlanks(); double val; // Value of the factor. val = primaryValue(); // A factor must start with a primary. skipBlanks(); while ( console.peek() == '^' ) { // Read the next primary, and exponentiate // the value-so-far by the value of this primary. console.getChar(); double nextVal = primaryValue(); val = Math.pow(val,nextVal); if (Double.isNaN(val)) throw new ParseError("Illegal values for ^ operator."); skipBlanks(); } return val; } // end termValue() double primaryValue() throws ParseError { // Read a primary from the current line of input and // return its value. A primary must be a number, // a variable, or an expression enclosed in parentheses. skipBlanks(); char ch = console.peek(); if ( Character.isDigit(ch) ) { // The factor is a number. Read it and // return its value. return console.getDouble(); } else if ( Character.isLetter(ch) ) { // The factor is a variable. Read its name and // look up its value in the symbol table. If the // variable is not in the symbol table, an error // occurs. (Note that the values in the symbol // table are objects of type Double.) String name = readWord(); Object symbolTableEntry = symbolTable.get(name); if (symbolTableEntry == null) throw new ParseError("Unknown variable \"" + name + "\""); Double val = (Double)symbolTableEntry; return val.doubleValue(); } else if ( ch == '(' ) { // The factor is an expression in parentheses. // Return the value of the expression. console.getAnyChar(); // Read the "(" double val = expressionValue(); skipBlanks(); if ( console.peek() != ')' ) throw new ParseError("Missing right parenthesis."); console.getAnyChar(); // Read the ")" return val; } else if ( ch == '\n' ) throw new ParseError("End-of-line encountered in the middle of an expression."); else if ( ch == ')' ) throw new ParseError("Extra right parenthesis."); else if ( ch == '+' || ch == '-' || ch == '*' || ch == '/') throw new ParseError("Misplaced operator."); else throw new ParseError("Unexpected character \"" + ch + "\" encountered."); } String readWord() { // Reads a word from input. A word is any sequence of // letters and digits, starting with a letter. When // this subroutine is called, it should already be // known that the next character in the input is // a letter. String word = ""; // The word. char ch = console.peek(); while (Character.isLetter(ch) || Character.isDigit(ch)) { word += console.getChar(); // Add the character to the word. ch = console.peek(); } return word; } } // end class SimpleParser5