next up previous contents index
Next: Syntax of view Up: User defined datatypes: Previous: A first example

Another view example

Suppose we use the following alternative implementation, a Expression class hierarchy to represent an expression in our AST. Note that Exp is an abstract base class inherited by the classes Number, Operator, Identifer, etc.

class Expression
{
public:

   // This class uses a ``wide'' interface
   virtual int  number() const         = 0;  // returns a number
   virtual const char * ident() const  = 0;  // returns an identifier

   // returns the ith subchild
   virtual const Expression * child(int) const = 0;
   // mutable version of above
   virtual Expression *& child(int) = 0;
};

typedef Expression * Exp;

class NullExp  : public Expression { ... };
class Number   : public Expression { ... };
class Operator : public Expression { ... };
class Identifier : public Expression { ... };

In order to provide a suitable interface to Prop's view mechanism, we introduce an additional member function to return a variant tag:

class Expression
{
public:
   enum Variant { None, Int, Ident, Add, Sub, Mul, Div };
   virtual Variant get_variant() const = 0;
}

In addition, we assume the following constructor functions have been implemented:

extern Exp INT(int);
extern Exp IDENT(const char *);
extern Exp ADD(Exp, Exp);
extern Exp SUB(Exp, Exp);
extern Exp MUL(Exp, Exp);
extern Exp DIV(Exp, Exp);

Now, with these definitions in place, we can define a datatype view of the above Expression hierarchy using the following definition:

datatype Exp :: view
   = match (this->get_variant())
     view Expression::None  => NONE
   | view Expression::Int   => INT (int = this->number())
   | view Expression::Ident => ID  (const char * = this->ident())
   | view Expression::Add   => ADD (Exp = this->child(0),
                                    Exp = this->child(1))
   | view Expression::Sub   => SUB (Exp = this->child(0),
                                    Exp = this->child(1))
   | view Expression::Mul   => MUL (Exp = this->child(0),
                                    Exp = this->child(1))
   | view Expression::Div   => DIV (Exp = this->child(0),
                                    Exp = this->child(1))
   ;

An evaluation function can be written using Prop's pattern matching construct:

int eval(Exp e, const Env& env)
{  match (e)
   {  NONE:          { return 0; }
   |  INT i:         { return i; }
   |  ID id:         { return env(id); }
   |  ADD (x,y):     { return eval(x,env) + eval(y,env); }
   |  SUB (x,y):     { return eval(x,env) - eval(y,env); }
   |  MUL (x,y):     { return eval(x,env) * eval(y,env); }
   |  DIV (x,INT 0): { cerr << "Division by zero"; return 0; }
   |  DIV (x,y):     { return eval(x,env) / eval(y,env); }
   }
}
Note that this code works equally well with the tagged union representation defined in section 7.1.

We can also use rewriting to transform an expression tree as follows

void simplify(Exp& e)
{
   rewrite (e) type (Exp)
   {  ADD(INT i, INT j): INT(i+j)
   |  SUB(INT i, INT j): INT(i-j)
   |  MUL(INT i, INT j): INT(i*j)
   |  DIV(INT i, INT j): INT(i/j)
   }
}



Allen Leung
Mon Apr 7 14:33:55 EDT 1997