遍历具有复杂条件表达式的ast以生成linq表达式

时间:2012-01-28 15:53:02

标签: c# parsing abstract-syntax-tree irony

我正在使用Irony.net从源代码生成解析树。本质上我使用ExpressionEvaluatorGrammer就像语法一样用于二进制表达式(算术,关系和逻辑/条件)。我想通过遍历它将得到的解析树转换为Linq表达式。但是,树似乎没有可直接转换为linq条件表达式的形式。这种表达的假设例子:

1 == 1 && 4 - 1 == 3
为了简洁,

生成(伪xml树):

<binary>
  <binary>
    <binary>
      <literal>1</literal>
      <op>==</op>
      <literal>1</literal>
    </binary>
    <op>&&</op>
    <binary>
      <literal>4</literal>
      <op>-</op>
      <literal>1</literal>
    </binary>
  </binary>
  <op>==</op>
  <literal>3</literal>
</binary>

在上面的树中,算术表达式(4 - 1)成为&amp;&amp;和&amp;逻辑操作,因为父节点在它之后关闭。在理想世界中,它应该是表示“== 3”的节点的左表达式。

如何遍历这样一棵树以产生正确的操作?或者,有没有办法以我想要的形式生成树?

编辑:这是语法(部分)定义。我从IronY.interpreter附带的ExpressionEvaluatorGrammer中获取了它。

RegisterOperators(15, "&", "&&", "|", "||");
RegisterOperators(20, "==", "<", "<=", ">", ">=", "!=");
RegisterOperators(30, "+", "-");
RegisterOperators(40, "*", "/");
Expr.Rule = Term
Term.Rule = number | ParExpr | stringLit | FunctionCall | identifier | MemberAccess | IndexedAccess;
ParExpr.Rule = "(" + Expr + ")";
BinExpr.Rule = Expr + BinOp + Expr;
BinOp.Rule = ToTerm("+") | "-" | "*" | "/" | "**" | "==" | "<" | "<=" | ">" | ">=" | "!=" | "&&" | "||" | "&" | "|";

2 个答案:

答案 0 :(得分:1)

你不能通过以神奇/特殊的方式遍历树来解决这个问题。你的解析器不正确!可能它只是配置错误。你绝对需要从中获取正确的树,以便进一步处理它。

可能你的运营商优先级规则有错误。它看起来至少如此。尝试添加括号以查看它是否修复了树。

答案 1 :(得分:0)

假设运算符优先级正确,您应该使用访问者模式递归地遍历树,并在每个级别返回Expression

XName xBinary = "binary";
XName xLiteral = "literal";
Expression Visit(XElement elt)
{
    if (elt.Name == xBinary)
    {
        return VisitBinary(elt);
    }
    else if (elt.Name == xLiteral)
    {
        return VisitLiteral(elt);
    } // ...

    throw new NotSupportedException();
}

现在您已拥有Visit结构,只需编写每个特定访问者即可使用您的主Visit

Expression VisitLiteral(XElement elt)
{
    Debug.Assert(elt.Name == xLiteral);
    return Expression.Constant((int)elt);
}

Expression VisitBinary(XElement elt)
{
    Debug.Assert(elt.Name == xBinary);
    Debug.Assert(elt.Elements().Count() >= 3);

    var lhs = elt.Elements().ElementAt(0);
    var op = elt.Elements().ElementAt(1);
    var rhs = elt.Elements().ElementAt(2);

    switch((string)op)
    {
    case "+":
        // by chaining LHS and RHS to Visit we allow the tree to be constructed
        // properly as Visit performs the per-element dispatch
        return Expression.Add(Visit(lhs), Visit(rhs));
    case "&&":
        return Expression.AndAlso(Visit(lhs), Visit(rhs));
    default:
        throw new NotSupportedException();
    }
}