【问题标题】:GCC/Clang lexer and parserGCC/Clang 词法分析器和解析器
【发布时间】:2020-01-17 14:57:15
【问题描述】:

我很好奇 C/C++ 词法分析器和解析器如何协同工作。我知道解析器通常需要先行至少一个标记。不过我的问题是,在生产编译器中(比如 gcc 或 clang):

1) 词法分析器是否先运行,对整个文件进行词法分析,然后让解析器生成 AST。这意味着词法分析器会生成一个标记列表。

2) 词法分析器是否只生成一小组标记,足以让解析器完成其工作。这意味着词法分析器和解析器轮流运行。

我绝对认为使用选项 1,因为像 C++ 这样的语言有时需要任意前瞻,因为语法不是上下文无关的,但这会占用大量内存。

【问题讨论】:

  • 这里有一些关于clang的信息:clang.llvm.org/docs/InternalsManual.html;这更像是您的选项 2(解析器从词法分析器请求令牌),但我还不够熟悉,无法实际回答。
  • 第 1 部分。一些事情:虽然 C++ 语言确实不是上下文无关的(例如,需要在使用之前定义变量),但用于解析语言是上下文无关的。怎么会这样?例如,基于 LALR(1) 语法的解析器在执行归约时会执行语义操作。具体来说,当它识别出一个变量声明时,它会将变量定义输入到一个符号表中。当它识别出一个使用变量的表达式时,它可以检查符号表以查看该变量是否存在。
  • 第 2 部分。通常,词法分析和解析与解析器并行运行,因为它需要“下一个标记”来调用词法分析器。但是,这可能涉及词法分析器的大量读取和处理。考虑包含文件的处理,这些文件可能嵌套到许多级别并包含宏定义。我们知道 C/C++ 编译时开关之一是能够只预处理输入并输出新的源文件而不编译。但这不是解析器处理的实际令牌流。您仍然需要对此输出进行词法分析。
  • 既不是 (1) 也不是 (2)。词法分析器根本没有“运行”:每当解析器需要一个新的标记时,它就会被解析器作为一个过程调用。词法分析器一次返回一个标记。 C++ 不需要任意前瞻,而 at 的语法不是上下文无关的事实与此无关。

标签: compiler-construction


【解决方案1】:

传统答案与您的情况 2 接近,但并非完全如此。请注意,词法分析器和解析器通常都作为相对简单的状态机实现。

词法分析状态机可以由以下任一驱动:

  • 给我一个新令牌

(这显然需要获取输入代码并将它们组装成令牌),或者:

  • 这是一个新的输入字符

(最终导致标记从词法分析器中“脱落”)。

解析器状态机可以从任一方向驱动:

  • 给我解析

(然后必须获取标记,直到找到完整的句子),或者:

  • 这是一个新令牌

(然后必须将标记组合成一个句子)。

如果我们使用的解析器算法是这样驱动的,我们会“编译”一个文件:

for all input characters:
    feed character to tokenizer

当标记从标记器中“脱落”时,它们会驱动解析器。整个事情都是自下而上驱动的协程。

传统上,在由 yacc、bison 等生成的解析器中,以及在为它们服务的词法分析器中,我们运行更多的是“自上而下”,即,有人调用 get me a sentence 函数(可以构建一个 AST,或直接发出代码,或介于两者之间的东西——例如,为一个函数或声明构建一个 AST,然后将其转换为中间代码,然后为另一个函数或声明构建另一个 AST,等等)。这使得一切都朝着从词法分析器中提取标记的方向发展——但它仍然相当协程,因为解析器一次只要求一个标记。

这种方法也是手动编写递归下降解析器的明显方法:您的首要功能是“给我一个句子”(或“给我所有句子”或其他什么),最终导致一些函数调用“给我一个令牌”。所以在这两种情况下,算法的实际表达最终都会对词法分析器重复“给我一个令牌”调用。

GCC 有一个手动编码的解析器(和手动编码的词法分析器)以这种方式工作。我没有看过 clang 的内部结构,但我怀疑它是一样的。

至于 C++,嗯,它有一些 非常 讨厌的解析情况;见https://en.wikipedia.org/wiki/Most_vexing_parsePavel Minaev's answerIs there a good Python library that can parse C++?。一些编译器使用 ad-hoc 方法来处理这个问题,例如,提供过度接受的语法并尝试对最终的 AST 进行回补,或者通过 hacks “引导”语法。 (我已经看到 C++ 编译器在这里崩溃:给它们提供语法上有效的标记,这些标记会造成语义上的废话,并且黑客可能会出错。)另一种可以说更好的方法是使用 GLR 解析器。见Ira Baxter's answer here

(我已经很久没有做过任何解析器理论了,在写这个答案时遇到了 sjoerd's comment 关于 2011 年的 GLL 解析器,这很有趣。)

【讨论】:

  • 非常感谢您的回复!这非常有帮助:-)!
猜你喜欢
  • 2013-01-31
  • 1970-01-01
  • 2011-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-30
  • 1970-01-01
  • 2020-08-27
相关资源
最近更新 更多