import java.io.*;
  
// ----------------------------------------------------------------------------
// Abstract Syntax, Evaluation, Printing 
// ----------------------------------------------------------------------------
//
// Each kind of expression is a distinct subclass of Exp.
//
abstract class Exp {
  abstract int eval();
  // abstract void print();
}

class IntExp extends Exp {
  int i;
  IntExp (int iarg) { 
    i = iarg;
  }
  int eval() {
    return i;
  }
  public String toString() {
    return Integer.toString(i);
  }
}

class AddExp extends Exp {
  Exp e1;
  Exp e2;
  AddExp(Exp e1,Exp e2) {
    this.e1 = e1;
    this.e2 = e2;
  }
  int eval() {
    int v1 = e1.eval();
    int v2 = e2.eval();
    return v1 + v2;
  }
  public String toString() {
    return "(+ " + e1.toString() + " " + e2.toString() + ")";
  }
}

class SubExp extends Exp {
  Exp e1;
  Exp e2;
  SubExp(Exp e1,Exp e2) {
    this.e1 = e1;
    this.e2 = e2;
  }
  int eval() {
    int v1 = e1.eval();
    int v2 = e2.eval();
    return v1 - v2;
  }
  public String toString() {
    return "(- " + e1.toString() + " " + e2.toString() + ")";
  }
}

class MulExp extends Exp {
  Exp e1;
  Exp e2;
  MulExp(Exp e1,Exp e2) {
    this.e1 = e1;
    this.e2 = e2;
  }
  int eval() {
    int v1 = e1.eval();
    int v2 = e2.eval();
    return v1 * v2;
  }
  public String toString() {
    return "(* " + e1.toString() + " " + e2.toString() + ")";
  }
}

class DivExp extends Exp {
  Exp e1;
  Exp e2;
  DivExp(Exp e1,Exp e2) {
    this.e1 = e1;
    this.e2 = e2;
  }
  int eval() {
    int v1 = e1.eval();
    int v2 = e2.eval();
    return v1 / v2;
  }
  public String toString() {
    return "(/ " + e1.toString() + " " + e2.toString() + ")";
  }
}

// ----------------------------------------------------------------------------
// Parsing
// ----------------------------------------------------------------------------
//
class Parser {

  // Failed parses throw the ParseFailed exception.
  static class ParseFailed extends Exception {
    ParseFailed(String w,int lineno) {
      super("Line " + lineno + ": " + w);
    }
  }

  // Lexical analysis
    
  // Token objects
  static private abstract class Token {
    int lineno;
  }
  static private class EofToken extends Token {
    EofToken(int l) {
      lineno = l;
    }
  }
  static private class LpToken extends Token {
    LpToken(int l) {
      lineno = l;
    }
  }
  static private class RpToken extends Token {
    RpToken(int l) {
      lineno = l;
    }
  }
  static private class NumToken extends Token {
    int num;
    NumToken(int n,int l) {
      num = n;
      lineno = l;
    }
  }
  static private class OperToken extends Token {
    String str;
    OperToken(String s, int l) {
      str = s;
      lineno = l;
    }
    static boolean isOperChar(char c) {
      return (c == '+' || c == '-' || c == '*' || c == '/');
    }
  }

  // Lexical analysis
  static private class Tokenizer {
    PushbackInputStream in;
    int lineno;
    
    Tokenizer(InputStream inarg) {
      in = new PushbackInputStream(inarg);
      lineno = 1;
    }

    static private final char EOF = (char) 0;

    private char readc() {
      int c;
      try {
	c = in.read();
      } catch (IOException exn) {
	throw new Error("IOException:" + exn.getMessage());
      }
      if (c == -1)
	return EOF;
      else 
	return (char)c;
    }
      
    private void unreadc(char c) {
      try {
	in.unread((int) c);
      } catch (IOException exn) {
	throw new Error("IOException:" + exn.getMessage());
      }
    }

    Token next() throws ParseFailed {
      Token result = null;
      int comment_level = 0;
      while (result == null) {
	char c = readc();
	if (c == EOF) {
	  if (comment_level > 0)
	    throw new ParseFailed ("Unmatched open comment",lineno);
	  result = new EofToken(lineno);
	} else if (c == '\n') {
	  lineno++;
	  continue;
	} else if (c == '{') {
	  comment_level++;
	  continue;
	} else if (c == '}') {
	  if (comment_level > 0)
	    comment_level--;
	  else 
	    throw new ParseFailed ("Unmatched close comment",lineno);
	  continue;
	} else if (comment_level > 0)
	  continue;
	else if (Character.isWhitespace(c)) 
	  continue;
	else if (c == '(')
	  result = new LpToken(lineno);
	else if (c == ')')
	  result = new RpToken(lineno);
	else if (Character.isDigit(c)) {
	  String s = "" + c;
	  while (Character.isDigit((c = readc())))
	    s = s + (char)c;
	  unreadc(c);
	  try {
	    int v = Integer.parseInt(s);
	    result = new NumToken(v,lineno);
	  } catch (NumberFormatException exn) {
	    throw new ParseFailed ("Invalid number: " + s,lineno);
	  }
	} else if (OperToken.isOperChar(c)) {
	  String s = "" + c;
	  while (OperToken.isOperChar(c = readc()))
	    s = s + c;
	  unreadc(c);
	  result = new OperToken(s,lineno);
	} else // invalid character
	  throw new ParseFailed ("Invalid character: " + c,lineno);
      }
      return result;
    }
  }

  // The parser itself.
    
  // Parse expression.
  static private Exp parseExp(Tokenizer tokens) throws ParseFailed { 
    Exp result = null;
    Token tok = tokens.next();
    if (tok instanceof NumToken) 
      result = new IntExp(((NumToken)tok).num);
    else if (tok instanceof LpToken) {
      tok = tokens.next();
      if (tok instanceof OperToken) {
	String oper = ((OperToken)tok).str;
	Exp e1 = parseExp(tokens);
	Exp e2 = parseExp(tokens);
	if (oper.equals("+")) 
	  result = new AddExp(e1,e2);
	else if (oper.equals("-")) 
	  result = new SubExp(e1,e2);
	else if (oper.equals("*"))
	  result = new MulExp(e1,e2);
	else if (oper.equals("/"))
	  result = new DivExp(e1,e2);
	else
	  throw new ParseFailed ("Invalid operator: " + oper,tok.lineno);
      } else
	throw new ParseFailed ("Missing or invalid expression",tok.lineno);
      tok = tokens.next();
      if (!(tok instanceof RpToken))
	throw new ParseFailed("Missing )",tok.lineno);
    } else
      throw new ParseFailed ("Missing or invalid expression",tok.lineno);
    return result;
  }      

  // Parse program.
  static Exp parse(InputStream is) throws ParseFailed {
    Tokenizer tokens = new Tokenizer(is);
    Exp exp = parseExp(tokens);
    Token tok = tokens.next();
    if (!(tok instanceof EofToken))
      throw new ParseFailed ("Extraneous characters at end of program",tok.lineno);
    return exp;
  }
}

// ----------------------------------------------------------------------------
// Top-level class and main function.
// ----------------------------------------------------------------------------

class Interp {

  public static void main (String args[]) throws IOException {
    FileInputStream is = new FileInputStream(args[0]);
    try {
      Exp e = Parser.parse(is);
      System.out.println("Expression: " + e.toString()); 
      int result = e.eval();
      System.out.println ("Evaluates to: " + result);
    } catch (Parser.ParseFailed e) {
      System.out.println ("Parse failed: " + e.getMessage());
    }
    is.close();
  }

}
    
