我们可以把它分成两部分。有一个用于括号表达式的规则,另一个用于乘法。
而不是维基百科文章,这是故意为了解释的目的而简化的文章,我会按照Parsing Expressions by Recursive Descent这个更详细的例子来处理括号内的表达式。
这是我用我的解析器可以与隐含乘法工作的代码。我有多字母变量名称,并使用空格来分隔不同的变量,所以你可以有“2 pi r”。
protected void expression() throws ParseException {
prefixSuffix();
Token t = it.peekNext();
while(t!=null) {
if(t.isBinary()) {
pushOp(t);
it.consume();
prefixSuffix();
}
else if(t.isImplicitMulRhs()) {
pushOp(implicitMul);
prefixSuffix();
}
else
break;
t=it.peekNext();
}
while(!sentinel.equals(ops.peek())) {
popOp();
}
}
这需要一些其他功能。
我使用一个单独的标记化步骤,其破坏了输入到离散的令牌。 Tokens
类有许多方法,尤其是Token.isBinary()
测试如果运算符是二元运算符,如+,=,*,/。另一种方法Token.isImplicitMulRhs()
测试令牌是否可以出现在隐式乘法的右侧,这对于数字,变量名称和左括号是成立的。
的Iterator<Token>
用于输入流。 it.peekNext()
查看下一个令牌,并且it.consume()
移至输入中的下一个令牌。
pushOp(Token)
将令牌推入运算符堆栈,popOp
删除一个和。pushOp具有处理不同运算符优先级的逻辑。弹出操作者如果它们具有较低的优先级特别值得注意
protected void pushOp(Token op)
{
while(compareOps(ops.peek(),op))
popOp();
ops.push(op);
}
的是implicitMul
人工令牌具有相同优先级的乘法,其被压入操作栈。
prefixSuffix()
处理表达式,它可以是数字和带有可选前缀的后缀运算符的变量。这将识别“2”,“x”,“-2”,“x ++”,从输入中删除令牌并将其添加到输出/运算符堆栈中。
我们可以认为这个程序在BNF作为
<expression> ::=
<prefixSuffix> (<binaryOp> <prefixSuffix>)* // normal binary ops x+y
| <prefixSuffix> (<prefixSuffix>)* // implicit multiplication x y
处理括号prefixSuffix()
完成。如果检测到左括号,则它将递归调用expression()
。为了检测匹配的右括号,特殊的哨兵令牌被压入操作员堆栈。当在输入中遇到右括号时,主循环会中断,并且操作员堆栈上的所有操作员都会弹出,直到遇到标记并且控制返回到prefixSuffix()
。代码可能类似于
void prefixSuffix() {
Token t = it.peekNext();
if(t.equals('(')) {
it.consume(); // advance the input
operatorStack.push(sentinel);
expression(); // parse until ')' encountered
t = it.peekNext();
if(t.equals(')')) {
it.consume(); // advance the input
return;
} else throw Exception("Unmatched (");
}
// handle variable names, numbers etc
}