Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I infer the usage of parentheses when translating an expression tree?

I am working on translating an expression tree to a format that resembles infix notation; I am not evaluating the tree or executing its operations. The tree contains both logical and relational operations, and I would like to emit parentheses in an intelligent manner during the translation.

To illustrate, consider the following contrived expression:

a < x & (a < y | a == c) & a != d

If I walk the expression tree produced by this expression in-order, then I will print out the following expression, which is incorrect.

a < x & a < y | a == c & a != d
// equivalent to (a < x & a < y) | (a == c & a != d)

Alternatively, I can again perform an in-order traversal but emit parentheses before and after processing a binary expression. This will produce the following correct expression, but with several redundant parentheses.

(((a < x) & ((a < y) | (a == c))) & (a != d))

Is there an expression tree traversal algorithm that produces optimally-parenthesized expressions?

For reference, here is a snippet of the ExpressionVisitor I am using to inspect the tree.

class MyVisitor : ExpressionVisitor
{
    protected override Expression VisitBinary(BinaryExpression node)
    {
        Console.Write("(");

        Visit(node.Left);
        Console.WriteLine(node.NodeType.ToString());
        Visit(node.Right);

        Console.Write(")");

        return node;
    }

    // VisitConstant, VisitMember, and VisitParameter omitted for brevity.
}
like image 598
Steve Guidi Avatar asked Aug 21 '12 14:08

Steve Guidi


People also ask

What are the rules to construct expression trees?

Construction of Expression Tree: Now For constructing an expression tree we use a stack. We loop through input expression and do the following for every character. If a character is an operator pop two values from the stack make them its child and push the current node again.

What happens if an expression tree reads the symbol in the form of an operand?

Q. What happens if an expression tree reads the symbol in the form of an Operand? a. One node tree is created and a pointer is pushed towards it on the stack.

In what order should an expression tree be traversed to evaluate the expression?

Evaluation of Expression Tree This strategy of calling left subtree, the root node, and right subtree are eventually called in order traversal method. Apart from this, you can also use the post-order traversal strategy where the left subtree is printed first, then the right subtree, and lastly the root node operator.

How does an expression tree work?

Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation such as x < y . You can compile and run code represented by expression trees.


1 Answers

I've accepted the answer of Dialecticus as it provides a good basis for implementing this algorithm. The only issue with this answer is that it requires that the VisitBinary() method know about its parent caller as a method argument, which is not feasible since these methods are overloads of a base method.

I provide the following solution, which uses a similar algorithm, but applies the check to emit parentheses in the parent call for the child nodes of the expression tree.

class MyVisitor : ExpressionVisitor
{
    private readonly IComparer<ExpressionType> m_comparer = new OperatorPrecedenceComparer();

    protected override Expression VisitBinary(BinaryExpression node)
    {
        Visit(node, node.Left);
        Console.Write(node.NodeType.ToString());
        Visit(node, node.Right);

        return node;
    }

    private void Visit(Expression parent, Expression child)
    {
        if (m_comparer.Compare(child.NodeType, parent.NodeType) < 0)
        {
            Console.Write("(");
            base.Visit(child);
            Console.Write(")");
        }
        else
        {
            base.Visit(child);
        }
    }

    // VisitConstant, VisitMember, and VisitParameter omitted for brevity.
}

The precedence comparison function is implemented as an IComparer<ExpressionType>, which applies the C# rules of operator precedence.

class OperatorPrecedenceComparer : Comparer<ExpressionType>
{
    public override int Compare(ExpressionType x, ExpressionType y)
    {
        return Precedence(x).CompareTo(Precedence(y));
    }

    private int Precedence(ExpressionType expressionType)
    {
        switch(expressionType) { /* group expressions and return precedence ordinal * }
    }
}
like image 82
Steve Guidi Avatar answered Oct 24 '22 13:10

Steve Guidi