public abstract class Expr { abstract Expr eval(); abstract Expr call(String method, Expr[] es); public static void main(String[] s) { Expr e1 = new Call("+", new Call("*", new Int(1), new Expr[] {new Int(2)}), new Expr[] {new Int(3)}); System.out.println("Eval " + e1 + " = " + e1.eval()); Expr e2 = new Call("+", new Int(1), new Expr[] { new Call("*", new Int(2), new Expr[] {new Int(3)})}); System.out.println("Eval " + e2 + " = " + e2.eval()); } String toStringWithParent() { return "(" + this + ")"; } } abstract class Obj extends Expr { Expr eval() { return this; } } class Int extends Obj { int x; Int(int x) { this.x = x; } Expr call(String method, Expr[] es) { if ("+".equals(method)) { Expr e = es[0].eval(); if (e instanceof Int) { return new Int(x + ((Int) e).x); } } if ("*".equals(method)) { Expr e = es[0].eval(); if (e instanceof Int) { return new Int(x * ((Int) e).x); } } return null; } public String toString() { return "" + x; } public String toStringWithParent() { return toString(); } } class Call extends Expr { String method; Expr obj; Expr[] params; Call(String method, Expr obj, Expr[] params) { this.method = method; this.obj = obj; this.params = params; } Expr eval() { Expr[] evals = new Expr[params.length]; for (int i = 0; i < evals.length; i++) { evals[i] = params[i].eval(); } return obj.eval().call(method, evals); } Expr call(String method, Expr[] es) { return null; } public String toString() { String res = obj.toStringWithParent() + " " + method; for (int i = 0; i < params.length; i++) { res += " " + params[i].toStringWithParent(); } return res; } }