编译器的阶段?

4
编译的哪个阶段会识别编程语言的关键字?
我有点困惑:
1. 词法分析。 2. 程序解析。
我曾经用正则表达式在C中编写过一个词法分析器,但它也将“int main(void)”中的“main()”识别为关键字。
基于这些情况,我认为我们需要构建一个解析树来识别关键字。

只是一个提醒:正则表达式不适合用于可以嵌套的模式 - user541686
3
@Mehrdad:完全正确,但是对于标记化(与任何嵌套无关的词法分析器只返回一个平面标记流)来说,它们非常好!(翻译:绝对正确,但对于标记化(与任何嵌套无关的词法分析),它们非常棒!) - user395760
@Mehrdad - 还有基于正则表达式的工具,可以添加有限的处理嵌套的能力 - 足够处理可嵌套的注释等。Ragel是一个很好的例子。请注意,我说的是“基于”(我已经习惯了这里人们会对某些事情进行攻击;-)),我知道一旦支持任意深度的嵌套,整个语法就不再是正则的了。 - user180247
5个回答

3

通常,在编译的词法分析阶段,输入文本被拆分成一系列词元序列,每个词元属于某个特定的标记类型,这在后续的分析中非常有用。因此,在词法分析期间通常首先识别关键字以使解析更容易。由于解析器往往是通过编写基于标记而不是基于词元(即词元的类别而不是内容)的上下文无关语法来实现的,因此在词法分析期间标记关键字会更加容易构建解析器。例如,如果我想让一个解析器将“if”视为关键字,则我可能需要在我的CFG中使用类似以下规则:

Statement ::= 'IF' Expr 'THEN' Expr

如果我不把IFTHEN归为自己的标记类型,那么我的解析器就无法编写上述语句。


3

今年我需要完成一个简单的编译器项目,使用的是Java语言。在词法分析阶段中,我需要识别关键字。这个阶段中,我会读取输入的语言并创建一个带有类型的标记(对于关键字来说,类型为variable_declaration),以及它的值。对于每种情况,如标识符、常量、乘法操作、加法操作等,我都有不同的类型。然后将这些标记传递到队列中,接着进入解析器,检查语法并创建二叉树,最终用于创建输出语言。


1

那将是词法分析。

有些语言有“特殊”的标识符和关键字。这些通常会在解析开始之前添加到标识符表中,并分配已知的常量ID值,以便可以轻松地识别它们。虽然这些对解析器通常没有特殊含义,但应在解析后的抽象语法树(AST)中检测到。

例如,请看一下Oberon语言报告...

http://www-old.oberon.ethz.ch/oreport.html

不是语言推荐 - 只是一个易于获取和简单的语言规范(非常像Wirth的风格)。

无论如何,“词汇和表示”部分包括了一个“运算符和分隔符”的列表,其中包括大多数人会认为是关键字的内容。这些将被词法分析器识别。

在“声明和作用域规则”部分中,有一个预定义标识符列表,例如“ABS”和“BOOLEAN”。我对Oberon不太熟悉,但如果我要编写编译器,我很可能只是预初始化普通标识符表以包含这些预定义标识符。

在C中,“main”在大多数方面只是另一个函数。编译器可能会或可能不会将其视为特殊对象。唯一的“特殊”之处可能只是链接到最终可执行文件的启动代码调用该函数。


1

这很大程度上取决于定义,特别是在扫描器、标记化器、词法分析器和解析器之间划分线的位置。由于这是作业,只有您的教授说它是正确的才算正确:请查看阅读材料中提供的定义。

关于main():您绝对可以说main()与所有其他函数一样,并不是一个关键字,但它是一个标记。标记化器识别子字符串“main”为一个标记,解析器将其设置与其“(…)”和“{…}”部分相关联。此外,对于 main(),解析器将自动生成程序入口点。


1

传统上,关键字是由词法分析器识别出来的(这给你留下了一个由固定一组关键字组成的语言)。但当然你可以在解析过程中完成它。你甚至可以完全摆脱你的词法分析器,使用众多无词法分析器的解析技术之一(例如,PEGs)。这可能有助于避免混淆。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接