我为计算器找到了这个语法:
<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(和其他工具),但我想在没有它们的情况下这样做。
有人可以伸手吗?
答案 0 :(得分:0)
在手工制作的顶级下降解析器中处理左递归并不容易。解决问题的解析器生成器有多年的工作。这有理论上的原因。
如果您不想使用工具,最佳解决方案是消除左递归。问题,如果你这样做&#34;通过书&#34;就是你会得到一个丑陋的语法和难以使用的丑陋的解析器。
但是还有另一个解决方案。您可以添加足够的规则来表示运算符的优先级层次结构,这是您必须要做的事情,除非您希望冒a+b*c
将其解析为(a+b)*c
。
Web上的表达式有很多非左递归语法的例子,特别是在SO中。我建议你带一个,然后从那里开始。