To make our visitor example more interesting and realistic, let us include variables in the type ArithExpr.
class Var extends ArithExpr { String name; Var(String n) { name = n; } public String toString() { return name; } Object accept(Visitor v) { return v.forVar(this); } }Then the visitor interface for ArithExprs has the form:
interface IVisitor { Object forConst(Const c); Object forSum(Sum c); Object forProd(Prod p); Object forVar(Var v); }
The concrete visitor class that implements expression evaluation is:
class EvalVisitor implements Visitor { Env env; // an environment for looking up variables EvalVisitor(Env e) { env = e; } Object forConst(Const c) { return c; } Object forSum(Sum s) { return new Const( ((Const)s.left.accept(this)).value + ((Const)s.right.accept(this)).value ); } Object forProd(Prod p) { return new Const( ((Const)p.left.accept(this)).value) * ((Const)p.right.accept(this)).value)); } Object forVar(Var v) { return env.lookup(v.name); } }The environment env, which was an explicit parameter of the eval method in our method-based implementation for evaluation, is now a field of the visitor. As before, it is directly used only for evaluating instances of Var, but now we don't need to explicitly pass the environment through method argument lists.
Since we are programming in a functional style, the forConst method need only return its argument as the result, rather than allocating a copy. The forSum and forProd methods are mostly straightforward, evaluating the subexpressions first and combining the results. The only subtlety is that since accept now returns an instance of Object rather than an int, we need to perform an explicit type cast to get the values for the left and right subexpressions. For example, to obtain the value for the left subexpression in a Sum, we have
((Const)s.left.accept(this)).value
The expression
s.left.accept(this)computes a Const whose value field is the value of the expression s.left. But the declared return type for accept is Object, and since an Object has no field named value, we cannot extract the value directly. Since we know that the Object is in fact a Const, we can insert an explicit type cast to Const, and then extract the value.
The forVar method looks up the value of the variable in the current environment. The environment is passed in when an EvalVisitor is created, and is presumably given bindings for the existing variables beforehand.
Finger Exercise 1.13.4.1
Extend your solution to exercise 1.13.3.1 to support
variables. You will need to define an Environment class
in addition to the ArithExpr classes.