abstract class ArithExpr { abstract double eval(Env enf); abstract String exprString(); } class Const extends ArithExpr { double value; double eval(Env env) { return value; } String exprString() { return Double.toString(value); } } class Var extends ArithExpr { String name; double eval(Env env) { return env.lookup(name); } String exprString() { return name; } } class Sum extends ArithExpr { ArithExpr left, right; double eval(Env env) { return left.eval(env) + right.eval(env); } String exprString() { return "(" + left.exprString() + " + " + right.exprString() + ")"; } } class Diff extends ArithExpr { ArithExpr left, right; double eval(Env env) { return left.eval(env) - right.eval(env); } String exprString() { return "(" + left.exprString() + " - " + right.exprString() + ")"; } } class Prod extends ArithExpr { ArithExpr left, right; double eval(Env env) { return left.eval(env) * right.eval(env); } String exprString() { return "(" + left.exprString() + " * " + right.exprString() + ")"; } } class Quot extends ArithExpr { ArithExpr left, right; double eval(Env env) { return left.eval(env) / right.eval(env); } String exprString() { return "(" + left.exprString() + " / " + right.exprString() + ")"; } } abstract class Env { abstract Double lookup(String name); Env cons(String vn, double v) { return new ConsEnv(vn, v, this); } } class EmptyEnv extends Env { static EmptyEnv ONLY = new EmptyEnv(); private EmptyEnv() { } Double lookup(String name) { return null; } } class ConsEnv extends Env { String varName; double value; Env rest; Double lookup(String name) { if (name.equals(varName)) return value; else return rest.lookup(name); } }