Introduction

In the previousposts on DynCalc, we explored how to write a simple parser for simple calculations, transforming it from infix to postfix and finally to a tree that can be interpreted or compiled for execution of the calculation.

As of C# 3.0, there's library support for expression trees and compilation of these. In this post, we'll transform our DynCalc sample into an equivalent application using C# 3.0 Expression Trees.

Building the Expression Tree

As illustrated in Part 2: Building an expression tree we take a queue of MathOpOrVal tokens (representing either a mathematical operation or an integer value) and turn it into an expression tree as follows:

In here, the TreeNode was an internal class to represent a tree node consisting of an operation together with a left and right node. All of this can be replaced by the C# 3.0 Expression Trees, as follows:

The signature (line 1) indicates we consume the same queue as we did before, but this time we return an Expression (namespace System.BLOCKED EXPRESSION supplied by the Orcas C# 3.0 Framework. The Expression<Func<int>> indicates that the expression represents a function with return type int. Func<int> is a generic type "instance" of Func<R> in which R stands for "return type". Basically Func<R> is a delegate with signature public delegate R Func<R>();. In case the expression tree would consume a parameter, one could use Expression<Func<int,int>> or Expression<Func<int,int,int>> (2 int parameters).

Instead of building a stack of custom tree nodes, we now build a Stack<Expression> as shown in line 3.

Next, we iterate over the queue in lines 5 to 32.

In case the value of the MathOpOrVal object in the queue is set, we have to add a constant value to the stack, which is constructed via the factory method Expression.Constant in line 8.

Otherwise an operation will be set. In this case, we have to pop both arguments to the binary operation from the stack (lines 11, 12) and do case analysis based on the binary operation desired (lines 13 to 30). To represent a binary operation in the tree, one uses the Expression.<operation>(Expression left, Expression right) factory method.

Finally (line 34), the resulting expression is popped from the stack and wrapped in a lambda expression that can be returned. Notice the use of ParameterExpression[0] to indicate no parameters are required.

The cool thing of the built expression is that it doesn't require manual compilation as we did in Part 3: Compilation to IL. Instead we can just call Compile() on the expression. To illustrate this, the Main method is changed as follows:

As you can see (line 28), execution is piece of cake thanks to the System.Expressions library. Also, printing the tree is relatively straightforward using a StringBuilder (lines 20 to 23). A sample execution of this application is shown below: