词法分析的作用
词法分析是编译器的第一阶段。
主要任务
读入源程序的输入字段,并将它们组成词素,最终生成并输出一个词法单元序列。
输出的词法单元序列被输出到语法分析器进行语法分析。
与符号表的交互
词法分析器通常都需要和符号表交互,当词法分析器发现了一个标识符的词素时,它将把这个词素添加到符号表中。
交互关系
一般情况下,交互是由语法分析器调用词法分析器来实现的,如图所示:
注意图中的命令getNextToken,其调用者是语法分析器,通过这个命令使得词法分析器从它的输入不断读取字符,直到识别出下一个词素位置。词法分析器根据这个词素生成一个词法单元返回给语法分析器。
同时我们也可以注意到,词法分析器和语法分析器都需要和符号表交互。
其他作用
通过以上分析,我们知道词法分析器的主要用途就是读取源程序,因此它还会完成以下其他任务:
-
过滤源程序中的注释和空白(例如空格,换行符等等)
-
将编译器生成的错误消息和源程序的位置联系起来
其中第二点的实现思路是词法分析器记录遇到的换行符个数,以此给每个出错的消息赋予一个行号。
词法分析和语法分析
书中对编译过程的分析部分拆分为词法分析和语法分析的原因做了3个原因的解释。
简化编译器的设计
将词法分析器和语法分析器分离使得我们至少可以简化其中的一项任务,这里体现了“分而治之”的设计思想。
提高编译器的效率
把词法分析器独立出来,使得开发者可以使用专用于词法分析任务、不进行语法分析的技术,做到专事专做,单点优化。
增强编译器的可移植性
单独分离的模块,使得输入设备相关的特殊性可以被限制在词法分析器中,从而接上不同的语法分析等步骤。
词法单元、模式和词素
在讨论词法分析时,有3个需要明确的术语。
词法单元
词法单元是由一个词法单元名和一个可选的属性值组成的。例如我在文章《《编译原理》阅读笔记01之编译器的几个概念》中所举例的<id, 1>,就是一个词法单元,是由词素result映射来的。
词法单元的名字是语法分析器处理的输入符号。
模式
模式描述了一个词法单元的词素可能具有的形式。
当词法单元是一个关键字的时候,它的模式就是组成这个关键字的字符序列。
对于标识符或者其他词法单元,模式是一个更加负责的结构,可以和很多符号串匹配。
词素
词素是源程序中的一个字符序列,例如伪代码a + b里,a、+、b都是词素,它和某个词法单元的模式匹配。
词法单元的属性
属性存在的意义
对于代码分析器而言,至关重要的事情是知道在源程序中找到了哪个词素,然而多个词素可以和同一个模式进行匹配。因此,词法分析器不仅仅向语法分析器返回一个词法单元的名字,同时还会返回一个描述该词法单元的词素的属性值。
属性值的本质
一般来说,和一个标识符有关的信息,例如它的词素、类型、第一次出现的位置,都保存在符号表中。因此,一个标识符的属性值是一个指向符号表中该标识符对应条目的指针。
词法错误
词法分析器无法发现错误
当词法分析器作为一个单独的组件时,是很难发现源码中的错误的。
例如我们写出如下代码:
fi (a == b) {
...
}
当词法分析器第一次遇到fi的时候,它无法指出fi究竟是关键字if还是一个错误的未声明的函数标识符。由于fi是标识符id的一个合法词素,因此词法分析器必须向语法分析器输出这个id的词法单元,而编译器会在语法分析的时候处理这个因为字母颠倒而引起的错误。
“恐慌模式”恢复策略
当出现所有的词法单元的模式都无法和剩余输入的某个前缀匹配的时候,此时词法分析器就不能继续处理输入了。出现这种情况时,最简单的错误恢复策略就是“恐慌模式”。
这个策略的基本逻辑是:我们从剩余的输入中不断删除字符,直到词法分析器中能够在剩余输入的开头发现一个正确的词法单元为止。
发表评论