虽然文章的标题是词法分析,但首先还是要从编译原理说开来。编译原理应该很多人都听说过,虽 然不一定会有多么了解。
简单的说,编译原理就是研究如何进行编译——也就如何从代码(*.cs 文件)转换为计 算机可以执行的程序(*.exe 文件)。当然也有些语言如 JavaScript 是解释执行的,它的代码是直接 被执行的,不需要生成可执行程序。
编译过程是很复杂的,它涉及到很多步骤,直接拿《编译原理》(Compilers: Principles, Techniques and Tools,红龙书)上的图来看:
图 1 编译器的各个步骤,其实是我根据书上的图综合了一下后画的
这里给出了 7 个步骤(后面的优化步骤是可选的),其中前 4 个步骤是分析部分(也被称为前端 front end),是把源程序分解为多个组成要素,并在这些要素上加上语法结构,最后把信息存放在符 号表(symbol table)中。后三个步骤是综合部分(也成为后端 back end),它们根据中间表示和符 号表中的信息构造期待的目标程序。
将编译器分为这么多步骤,其好处就是使得每个步骤更加简单,从而使编译器更加容易设计,也可 以利用很多现有的工具——例如词法分析器可以用 Lex 或 Flex 生成,语法分析器可以用 Yacc 或 Bison 生成,几乎不用做太多编码工作就能得到一颗语法树,前端的工作也就完成的差不多了 。而至于后端,也有很多现有的技术可以使用,例如现成的虚拟机(CLR 或 Java,只要翻译成相应的 IL 就可以了)。
这个系列的文章,说的就是编译原理的第一步:语法分析。大部分算法和理论都来自《编译原理》 ,其余的部分则是自己搞出来的,或者是参考了 Flex 的实现(这里的 Flex 是指 fast lexical analyzer generator,一个著名的提供词法分析的程序,而不是 Adobe 的 Flex)。
我会尽量完整的介绍词法分析器的编写过程,包括一些细节的实现。当然,目前只能根据正则表达 式定义得到一个可以用来进行词法分析的对象,要想达到 Flex 那样直接根据词法定义文件生成词法分 析器源代码,还有很多工作要做,不是短期内能够搞定的。
本篇文章作为系列的第一篇,将会对词法分析做综合的概述,介绍一下其中用到的技术和大致的流 程。
一、词法分析介绍
词法分析(lexical analysis)或扫描(scanning)是编译器的第一个步骤。词法分析器读入组成 源程序的字符流,并且将它们组织成有意义的词素(lexeme)的序列,并对每个词素产生词法单元 (token)作为输出。
简单的来说,词法分析就是将源程序(可以认为是一个很长的字符串)读进来,并且“切 ”成小段(每一段就是一个词法单元 token),每个单元都是有具体的意义的,例如表示某个特 定的关键词,或者代表一个数字。而这个词法单元在源程序中对应的文本,就叫做“词素” 。
以计算器来举例,12+34*9 这一段“源程序”的词法分析过程如下所示:
图 2 算式的词法分析过程