Search code examples
.nettranslationexpression-treesparenthesesinorder

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.
}

Solution

  • Try something like this, assuming that node.NodeType is of type NodeType, and that function Precedes exists and returns true if first parameter precedes the second.

    protected override Expression Visit(BinaryExpression node, NodeType parentType)
    {
        bool useParenthesis = Precedes(parentType, node.NodeType);
    
        if (useParenthesis)
            Console.Write("(");
    
        Visit(node.Left, node.NodeType);
        Console.WriteLine(node.NodeType.ToString());
        Visit(node.Right, node.NodeType);
    
        if (useParenthesis)
            Console.Write(")");
    
        return node;
    }