next up previous
Next: 1.13 Unusual Situations versus Up: 1.12 The Visitor Pattern Previous: 1.12.3 Polymorphic Visitors

1.12.4 Polymorphic Visitors with Arguments

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 Visitor {
  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 Finish the visitor-based implementation of expression evaluation, including the definition of the Environment class, by yourself. Can you think of other operations on expressions that the visitor pattern might help you implement?


next up previous
Next: 1.13 Unusual Situations versus Up: 1.12 The Visitor Pattern Previous: 1.12.3 Polymorphic Visitors
Corky Cartwright
2000-01-07