// $Id: SimpleArithExpr.java,v 1.1 1999/08/24 04:10:06 leavens Exp $

// Helping procedures for the following grammar.
//   <simple-arith-expr> ::= <number>
//                       | (<op> <simple-arith-expr> <simple-arith-expr>)
//   <op> ::= + | - | *

// To test this, make sure you are either in the directory where
// the class file lives, or have that directory in your CLASSPATH.
// Then execute at the command line:
//       java SimpleArithExpr

package lib;

import java.io.*;
import java.util.StringTokenizer;

public abstract class SimpleArithExpr {

  public boolean isLiteral() { return false; }

  public static SimpleArithExpr makeLiteral(int num) {
    return new LiteralSimpleArithExpr(num);
  }

  public int literalToNumber() throws Exception {
    throw new Exception("literalToNumber called with non-number");
  }

  public boolean isOpCall() { return false; }

  public static SimpleArithExpr makeOpCall(String op,
					   SimpleArithExpr leftArg,
					   SimpleArithExpr rightArg) {
    return new OpCallSimpleArithExpr(op, leftArg, rightArg);
  }

  public String opCallToOp() throws Exception {
    throw new Exception("opCallToOp called with non-op-call");
  }

  public SimpleArithExpr opCallToLeftArg() throws Exception {
    throw new Exception("opCallToLeftArg called with non-op-call");
  }

  public SimpleArithExpr opCallToRightArg() throws Exception {
    throw new Exception("opCallToRightArg called with non-op-call");
  }

  public boolean isSimpleArithExpr() { return true; }

  public static void main(String argv[]) {
    BufferedReader inputreader
      = new BufferedReader(new InputStreamReader(System.in));
    while (true) {
      try {
	System.out.print("? ");
	String input = inputreader.readLine();
	if (input.equals("quit")) {
	  System.exit(0);
	}
	SimpleArithExpr tree = parse(input);
	System.out.println(tree.toString());
      } catch (Exception e) {
	System.out.println(e.getMessage());
      }
    }
  }

  public static SimpleArithExpr parse(String datum) throws Exception {
    StringTokenizer tokens = new StringTokenizer(datum, "() \t\r\n", true);
    SimpleArithExpr ret = parseHelper(tokens);
    return ret;
  }

  private static SimpleArithExpr parseHelper(StringTokenizer tokens)
       throws Exception {
	 if (tokens.hasMoreTokens()) {
	   String lookahead = nextLispToken(tokens);
	   if (lookahead.equals("(")) {
	     return parseOpCall(tokens);
	   } else if (lookahead.length() >= 1
		      && Character.isDigit(lookahead.charAt(0))) {
	     return makeLiteral(Integer.parseInt(lookahead));
	   } else {
	     throw new Exception("bad expression syntax: " + lookahead);
	   }
	 }
	 throw new Exception("bad expression syntax `'");
  }

  private static SimpleArithExpr parseOpCall(StringTokenizer tokens)
       throws Exception {
	 String permittedOps[] = {"+", "-", "*", "/" };
	 if (tokens.hasMoreTokens()) {
	   String opArg = nextLispToken(tokens);
	   if (!member(opArg, permittedOps)) {
	     throw new Exception("illegal operator name : " + opArg);
	   }
	   if (tokens.hasMoreTokens()) {
	     SimpleArithExpr leftArg = parseHelper(tokens);
	     if (tokens.hasMoreTokens()) {
	       SimpleArithExpr rightArg = parseHelper(tokens);
	       if (tokens.hasMoreTokens()
		   && nextLispToken(tokens).equals(")")) {
		 return makeOpCall(opArg, leftArg, rightArg);
	       }
	     }
	   }
	 }
	 throw new Exception("bad syntax in op call");
  }

  private static String nextLispToken(StringTokenizer tokens)
       throws Exception {
    while (tokens.hasMoreTokens()) {
      String lookahead = tokens.nextToken();
      if (lookahead.length() >= 1 &&
	  !Character.isWhitespace(lookahead.charAt(0))) {
	return lookahead;
      }
    }
    throw new Exception("unexpected end of input");
  }

  private static boolean member(String sought, String arr[]) {
    for (int i = 0; i < arr.length; i++) {
      if (sought.equals(arr[i])) {
	return true;
      }
    }
    return false;
  }
}

class LiteralSimpleArithExpr extends SimpleArithExpr {
  private int n;
  public boolean isLiteral() { return true; }
  public LiteralSimpleArithExpr(int num) { n = num; }
  public int literalToNumber() throws Exception { return n; }
  public String toString() { return Integer.toString(n); }
}

class OpCallSimpleArithExpr extends SimpleArithExpr {

  private String name;
  private SimpleArithExpr left, right;
  public boolean isOpCal() { return true; }

  public boolean isOpCall() { return true; }

  public OpCallSimpleArithExpr(String op,
			       SimpleArithExpr leftArg,
			       SimpleArithExpr rightArg) {
     name = op;
     left = leftArg;
     right = rightArg;
  }

  public String opCallToOp() throws Exception { return name; }

  public SimpleArithExpr opCallToLeftArg() throws Exception {
    return left;
  }
  public SimpleArithExpr opCallToRightArg() throws Exception {
    return right;
  }

  public String toString() {
    return "(" + name + " " + left.toString() + " " + right.toString() + ")";
  }
}
