这里有很多答案,但他们把事情弄糊涂了。是的,有 LL 和 LR 解析器,但这不是你的选择。
你有语法。有一些工具可以在给定语法的情况下自动为您创建解析器。可敬的Yacc和Bison就是这样做的。他们创建了一个 LR 解析器(实际上是 LALR)。还有一些工具可以为您创建 LL 解析器,例如ANTLR。像这样的工具的缺点是它们不灵活。他们自动生成的语法错误消息很糟糕,错误恢复很困难,而且旧的鼓励你以一种方式分解你的代码——这恰好是错误的方式。正确的方法是让你的解析器吐出一个抽象语法树,然后让编译器从中生成代码。这些工具希望您混合解析器和编译器代码。
当您使用这样的自动化工具时,LL、LR 和 LALR 之间的功率差异确实很重要。你不能“欺骗”来扩大他们的权力。(在这种情况下,强大意味着能够为有效的上下文无关文法生成解析器。有效的上下文无关文法是为每个输入生成唯一的、正确的解析树,或者正确地说它与文法不匹配。)我们目前没有可以为每个有效语法创建解析器的解析器生成器。然而,LR 可以处理比任何其他类型更多的语法。无法处理语法并不是一场灾难,因为您可以以解析器生成器可以接受的形式重写语法。然而,如何做到这一点并不总是很明显,
存在 LL、LALR 和 LR 解析器的原因是很久以前,生成 LR 解析器的工作在时间和内存方面对现代计算机来说都是一项繁重的工作。(注意这是它需要生成解析器,这仅在您编写它时发生。生成的解析器运行得非常快。)但那是很久以前的事了。对于中等复杂的语言,生成 LR(1) 解析器所需的 RAM 远少于 1GB,而在现代计算机上则需要不到一秒钟的时间。出于这个原因,您最好使用 LR 自动解析器生成器,例如Hyacc。
另一种选择是您编写自己的解析器。在这种情况下,只有一个选择:LL 解析器。当这里的人说写 LR 很难时,他们低估了情况。人类几乎不可能手动创建 LR 解析器。您可能认为这意味着如果您编写自己的解析器,您将被限制使用 LL(1) 语法。但这并不完全正确。由于您正在编写代码,因此您可以作弊。您可以前瞻任意数量的符号,并且因为在您准备好并准备好之前您不必输出任何内容,因此抽象语法树不必匹配您正在使用的语法。这种作弊的能力弥补了 LL 和 LR(1) 之间失去的所有权力,而且通常还有一些。
手写解析器当然有其缺点。无法保证您的解析器实际上匹配您的语法,或者就此而言不检查您的语法是否有效(即识别您认为的语言)。它们更长,而且更不鼓励您将解析代码与编译代码混合使用。它们显然也只用一种语言实现,而解析器生成器经常用几种不同的语言输出它们的结果。即使他们不这样做,LR 解析表也可以用只包含常量的数据结构(比如 JSON)来表示,而实际的解析器只有 100 行代码左右。但是手写解析器也有好处。因为您编写了代码,所以您知道发生了什么,因此更容易进行错误恢复并生成合理的错误消息。
最后,权衡通常是这样的:
- 对于一次性工作,使用 LR(1) 解析器生成器要好得多。生成器会检查你的语法,节省你的工作量,现代的会直接拆分抽象语法树,这正是你想要的。
- 对于像 mcc 或 gcc 这样高度完善的工具,请使用手写的 LL 解析器。无论如何,您将编写大量单元测试来保护自己,错误恢复和错误消息更容易正确处理,并且它们可以识别更大类别的语言。
我唯一的另一个问题是:为什么是 C?编译器通常不是时间关键代码。如果你愿意让你的编译器运行得慢一点——例如我自己的Lrparsing ,有非常好的解析包可以让你在 1/2 的代码中完成工作。请记住,这里的“相当慢”的意思是“人类几乎察觉不到”。我想答案是“我正在处理的作业指定 C”。为了给你一个想法,当你放宽要求时,从你的语法到解析树变得多么简单。这个程序:
#!/usr/bin/python
from lrparsing import *
class G(Grammar):
Exp = Ref("Exp")
int = Token(re='[0-9]+')
id = Token(re='[a-zA-Z][a-zA-Z0-9_]*')
ActArgs = List(Exp, ',', 1)
FunCall = id + '(' + Opt(ActArgs) + ')'
Exp = Prio(
id | int | Tokens("[]", "False True") | Token('(') + List(THIS, ',', 1, 2) + ')' |
Token("! -") + THIS,
THIS << Tokens("* / %") << THIS,
THIS << Tokens("+ -") << THIS,
THIS << Tokens("== < > <= >= !=") << THIS,
THIS << Tokens("&&") << THIS,
THIS << Tokens("||") << THIS,
THIS << Tokens(":") << THIS)
Type = (
Tokens("", "Int Bool") |
Token('(') + THIS + ',' + THIS + ')' |
Token('[') + THIS + ']')
Stmt = (
Token('{') + THIS * Many + '}' |
Keyword("if") + '(' + Exp + ')' << THIS + Opt(Keyword('else') + THIS) |
Keyword("while") + '(' + Exp + ')' + THIS |
id + '=' + Exp + ';' |
FunCall + ';' |
Keyword('return') + Opt(Exp) + ';')
FArgs = List(Type + id, ',', 1)
RetType = Type | Keyword('void')
VarDecl = Type + id + '=' + Exp + ';'
FunDecl = (
RetType + id + '(' + Opt(FArgs) + ')' +
'{' + VarDecl * Many + Stmt * Some + '}')
Decl = VarDecl | FunDecl
Prog = Decl * Some
COMMENTS = Token(re="/[*](?:[^*]|[*][^/])*[*]/") | Token(re="//[^\n\r]*")
START = Prog
EXAMPLE = """\
Int factorial(Int n) {
Int result = 1;
while (n > 1) {
result = result * n;
n = n - 1;
}
return result;
}
"""
parse_tree = G.parse(EXAMPLE)
print G.repr_parse_tree(parse_tree)
产生这个输出:
(START (Prog (Decl (FunDecl
(RetType (Type 'Int'))
(id 'factorial') '('
(FArgs
(Type 'Int')
(id 'n')) ')' '{'
(VarDecl
(Type 'Int')
(id 'result') '='
(Exp (int '1')) ';')
(Stmt 'while' '('
(Exp
(Exp (id 'n')) '>'
(Exp (int '1'))) ')'
(Stmt '{'
(Stmt
(id 'result') '='
(Exp
(Exp (id 'result')) '*'
(Exp (id 'n'))) ';')
(Stmt
(id 'n') '='
(Exp
(Exp (id 'n')) '-'
(Exp (int '1'))) ';') '}'))
(Stmt 'return'
(Exp (id 'result')) ';') '}'))))