我在编写自上而下的解析器时遇到了这个答案:Is there an alternative for flex/bison that is usable on 8-bit embedded systems?但我对几点感到困惑。
说我有这个语法:
Expr = Id | Id '(' Expr ')'
Stmt = Id '=' Expr | Expr
我不确定这是否完全必要,但我想我们可以留下语法因素:
Expr = Id ExprRest
ExprRest = ϵ | '(' Expr ')'
Stmt = Id '=' Expr ';' | Expr ';'
我究竟如何编写能够将foo(x);
正确解析为Stmt
的代码?如果我们编写Stmt
解析代码,如:
func ParseStmt() {
if ParseId() {
return ParseTerminal('=') && ParseExpr() && ParseTerminal(';');
} else {
return ParseExpr() && ParseTerminal(';');
}
}
我们会看到Id
foo
,假设我们处于第一种情况,然后失败,因为我们无法在=
之后找到foo
。
这个语法不是LL(1)吗?
答案 0 :(得分:1)
该语法不是LL(1)
,因为LL(1)
语法需要能够仅在解析堆栈和前瞻令牌的情况下确定要扩展的生产。使用空的解析堆栈,在{1}}前瞻令牌的情况下,您无法分辨两个Stmt
生产中的哪一个。你可以留下它,但它很烦人。
Id
创建的可执行文件虽然占用了很多C行,但实际上非常紧凑。你的语法由bison编译,产生1559行C,但编译成4K的目标文件,其中(我相信)仅略多于1K对应于实际的代码和数据。 (当然,那是没有行动的。)
如果你真的想手工编写解析器的表达式语法,我建议你使用自下而上的'Shunting Yard'算法或自上而下的'Pratt解析器'(两者都可以随时搜索与谷歌),处理运算符优先级之类的东西比严格的LL语法。但你可能会发现,野牛生成的语法具有相当的大小和速度,以及更好的可读性和可维护性。 (或者你可能不会。口味不同。)
答案 1 :(得分:0)
您的问题是ID(xxx)可以出现在语句和表达式上下文中。
正如另一个答案所说,你必须考虑更多因素。这并不难。 写下你的语法:
Stmt = Id ( '=' Expr ';' |
RestOfExpression );
RestOfExpression = '(' Expr ')' |
ϵ );
Expr = Id RestOfExpression;
代码应该是显而易见的。