如何解决这种语法递归?

时间:2014-07-30 19:47:12

标签: recursion grammar

我为计算器找到了这个语法:

<Expression> ::= <ExpressionGroup> | <BinaryExpression> | <UnaryExpression> | <LiteralExpression>
<ExpressionGroup> ::= '(' <Expression> ')'
<BinaryExpression> ::= <Expression> <BinaryOperator> <Expression>
<UnaryExpression> ::= <UnaryOperator> <Expression>
<LiteralExpression> ::= <RealLiteral> | <IntegerLiteral>
<BinaryOperator> ::= '+' | '-' | '/' | '*'
<UnaryOperator> ::= '+' | '-'
<RealLiteral> ::= <IntegerLiteral> '.' | <IntegerLiteral> '.' <IntegerLiteral>
<IntegerLiteral> ::= <Digit> <IntegerLiteral> | <Digit>
<Digit> ::= '0' | '1' |'2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'

来源:here

看起来很棒。所以我写了lexer并启动了解析器。现在有一个无限的递归,我无法在Expression和BinaryExpression之间解决。 我的表达代码:

boolean isExpression() {
        if (isExpressionGroup() || isBinaryExpression() || isUnaryExpression() || isLiteralExpression()) {
            println("Expression!");
            return true;
        }
        println("Not expression.");
        return false;
}

对于二进制表达式:

boolean isBinaryExpression() {
    if (isExpression()) {
        peek(1);
        if (currentLex.token == Token.BINARY_OPERATOR) {
            peek(2);
            if (isExpression()) {
                peek(3);
                println("Binary expression!");
                return true;
            } else peek(0);
        } else peek(0);
    } else peek(0);
    return false;
}

所以peek(int)只是一个向前看而不消耗任何词位的函数。所以我的问题是:我的输入是&#39; 2 * 3&#39; 。 isExpression()被调用。 isExpressionGroup()失败,因为没有&#39;(&#39;。然后调用isBinaryExpression(),调用isExpression()。isExpressionGroup()再次失败,再次调用isBinaryExpression()。依此类推,直到堆栈溢出。

我知道,有ANTLR和JavaCC(和其他工具),但我想在没有它们的情况下这样做。

有人可以伸手吗?

1 个答案:

答案 0 :(得分:0)

在手工制作的顶级下降解析器中处理左递归并不容易。解决问题的解析器生成器有多年的工作。这有理论上的原因。

如果您不想使用工具,最佳解决方案是消除左递归。问题,如果你这样做&#34;通过书&#34;就是你会得到一个丑陋的语法和难以使用的丑陋的解析器。

但是还有另一个解决方案。您可以添加足够的规则来表示运算符的优先级层次结构,这是您必须要做的事情,除非您希望冒a+b*c将其解析为(a+b)*c

Web上的表达式有很多非左递归语法的例子,特别是在SO中。我建议你带一个,然后从那里开始。