在编译器中,词法分析通常作为一个模块,即词法分析器,进行工作,其通过恰当的功能划分使编译器以更高效的流程工作。在整个工作流程中,词法分析器的根本任务就是将输入的源程序字符序列转化为词法单元(token)序列输出给语法分析器,每一个词法单元对于语法分析器等同于一个终结符号(上下文无关文法中的一个概念)。
除了主要工作外,词法分析器还需提供一些辅助功能,包括空白和注释的剔除、源程序位置信息的记录等等。
在词法分析之前,首先需要了解三个重要的概念:词法单元、模式和词素,三者之间的关系可以用下图来表示:
从中可以看出一个词法分析器的核心就是模式,也成为词法分析器的规约,用来描述某个词法单元词素形式,本质上是将一个词素识别为一个词法单元的标准。
在如何描述一个词素方面,通常采用的方式就是使用正则表达式(单独一节说明)。
如此我们有了正则表达式来描述一个词法单元,但是如何借助正则表达式来识别词法单元呢,通常使用的方法是使用状态转换图或者有穷自动机(又分为确定有穷自动机DFA和不确定有穷自动机,用于手动构建词法分析器或者构建词法分析器生成工具)。同时,在实践中常常使用分析器生成工具(Lex/Flex)来生成词法分析器,我们所需要的是给出确定的词法单元 正则表达式 语义动作并提供相关的辅助函数,Lex会自动生成一个词法分析器。
这里有几个重要的需要考虑的问题就是:
输入缓冲(预读问题)。对于这个问题,可以使用带有“哨兵标记”的双缓冲区方案来解决,同时使用Lexeme和Forward两个指针进行词素的预读,具体不进行赘述。
标识符和关键字的处理:所有的关键字都符合标识符的模式(正则表达式),但是我们需要将关键字识别为关键字词法单元而非标识符词法单元,这里有两种解决途径,一是利用Lex的冲突解决规则:如果输入字符序列的一个前缀与多个模式相匹配,总是选择Lex程序中先被列出的模式。这样,将每个关键字作为独立的词法单元声明列在标识符的词法单元声明之前,当发现一个词素既满足标识符又满足某一关键字识,会被识别转化为关键字的词法单元。另一种处理方式是将所有的关键字预先存储到符号表中,当某个表示关键字的词素被作为标识符识别时,会访问符号表,然后返回预先存入的关键字条目。
冲突解决规则:所谓冲突解决规则主要解决两方面的问题:一是一个输入字符序列可能存在多个不同长度的前缀和不同或同一模式匹配;二是一个输入字符序列的某一前缀和多个不同模式相匹配。在Lex中,这两种冲突的解决方法是:一,总是选择最长的前缀;二,如果一个前缀与多个模式相匹配,总是选择在Lex程序中先被列出的模式。