Expression Trees

Another tree structure, for representing expressions.

Here are two example expression trees:

       +                     *
      / \                   / \
     Y   *                 +   5
        / \               / \
       +   5             Y   +
      / \                   / \
     X   Z                 X   Z

Leaves of an expression tree refer to operands (variables or numbers)

The other nodes describe operations, where the children are operands.

The structure of the tree very much affects the order of operations
(e.g. the order of the addition and multiplication operations above)

Compilers can use expression trees to improve usage of the finite storage with the Central Processing Unit (registers)
e.g. the tree on the left could have these instruction sequences:

     Load Y into a register		Load X into a register
     Load X into a register		Load Z into a register
     Load Z into a register		Add the values of X and Z
     Add the values of X and Z		Multiply the sum by 5
     Multiply the sum by 5		Load Y into a register
     Add Y to the product		Add Y to the product

     Requires 3 machine registers       Requires 2 machine registers






Tree Traversals

A tree traversal is an operation to visit several (probably all) nodes of a tree.

The most common purpose for a complete traversal is to display the tree contents.

For Binary Search Trees, the most likely traversals are inorder traversals,
in which a node value is visited between its children.

There can also pre-order traversals (visit before)
and post-order traversals (visit after).

Traversal is much more interesting for expression trees:

       +         
      / \           pre-order      + Y * + X Z 5
     Y   *       
        / \         post-order     Y X Z + 5 * + 
       +   5     
      / \           in-order       Y + X + Z * 5    (imperfect)
     X   Z       
An implementation for an in-order traversal must insert parentheses:
	( Y + ( ( X + Z ) * 5 )
Once the expression tree is constructed, it can be used to display expressions in any format we desired, as well as giving information on how to evaluate the expression.

The expression tree can also be built by any parsing routine that can understand any of the three expressions.









Inheritance, Polymorphism, and Expression Trees

A couple simple observations regarding expression evaluation:
--- a node that performs addition does not care what its operands are
--- not all leaf nodes are treated in the same way

These two observations should strongly suggest a good use for derived classes.

Consider an abstract class representing an expression tree node,
and derived classes for each different kind of subexpression:
constant value, variable, arithmetic computation, assignment

And allowing derived classes means we are not limited to a binary tree.

	X = ( Y > 0 ? Y : -Y )        - has one child; ?: has 3 children




A couple Simple Examples for a Polymorphic Expression Tree Node

class ExprNode
{
    public:
    virtual string toString() const = 0;	// facilitates << operator
    virtual int evaluate( VarTree &v ) const = 0;  // evaluate this node
};

class Value: public ExprNode
{
    private:
	int value;
    public:
	string toString() const;	// facilitates << operator
	int evaluate( VarTree &v ) const;
	Value(int v)
	{
	    value = v;
	}
};

string Value::toString() const
{
    ostringstream convert;	// output string stream
    convert << value;		// 'output' into the stream
    return convert.str();	// and extract its string equivalent
}

int Value::evaluate( VarTree &v ) const
{
    return value;
}

class Variable: public ExprNode
{
    private:
	string name;
    public:
	string toString() const ;	// facilitates << operator
	int evaluate( VarTree &v ) const;
	Variable(string var)
	{
	    name = var;
	}
};

string Variable::toString() const
{
    return name;
}

int Variable::evaluate( VarTree &v ) const
{
    return v.lookup( name );
}