目录
1.词法分析概述
1.1 词法分析原理
高级语言源程序由一系列的句子构成,句子由单词按照一定的规则构成,而单词又由字符按照一定规则构成的。词法分析是编译基础,主要分析源程序中的字符流能否构成正确的单词。
1.2 词法分析程序
执行词法分析的程序称为词法分析程序或扫描器。
1.3 词法分析程序两种实现方式
1.3.1 手工构造
手工构造词法分析程序主要用状态图来表示单词的构成规则,根据状态转换图来进行单词的识别。
1.3.2 自动生成
自动生成词法分析程序需要用到单词及其识别的形式化表示:正则表达式和有穷自动机。
正则表达式 用来表示单词构成模式的表示方法 有穷自动机 对由正则表达式表示的字符串进行识别 1.4 词法分析任务
词法分析任务是:从左到右逐个字符扫描输入的源程序,按照构词规则,检查词法错误,识别出正确的单词,并输出单词的内部表示形式。如果识别过程中发现错误或无法识别的单词,则输出有关错误信息。
1.5 词法分析的目的
识别单词的目的是为了后续阶段的使用。因此为了简化后续阶段的工作,需要对每个单词(或单词类别)进行编码,这个编码称为种别码,又称token值。当识别出一个单词时,就将其种别码及单词自身的值一起输出,把作为字符串输入的源程序改造成单词记号换的中间形式(又称为token串),提交给语法分析程序使用。如下图所示;
1.6 词法分析的工作
词法分析是编译程序时唯一与源程序打交道的部分,其主要工作如下:
1.扫描源程序,从源程序中读入字符流到输入缓冲区汇总。 2.按构词规则识别单词,输出单词本身及其种别码。 3.过滤掉源程序中无用成分,如注释、空格、回车换行等。 4.调用出错处理程序,识别并定位错误。词法错误:如非法字符、违反构词规则等等。 1.7 两种词法分析调用方式
1.7.1 独立执行一遍
将词法分析作为独立的一遍执行完再执行语法分析器,它把以字符串输入的源程序变为统一的中间形式(token)输出到中间文件,作为语法分析程序的输入。这种实现方式结构清晰,易于理解和检查词法错误。
1.7.2 需要时即调用
以语法为核心,需要单词时再调用词法分析器。每次调用时就从源程序文件中读入若干个字符,向语法分析程序返回一个单词。省去了中间文件,在商业化编译程序中经常使用。
2.高级语言中的单词
2.1 单词的分类
1.关键字 2.标识符 用于标识命名的各个对象,以便程序引用。可以代表变量、常量、函数、类、对象等。 3.运算符 用来指明进行的运算类型。
算术运算符:+,-,*,/,%等
逻辑运算符:!,&&,||
关系运算符:<,>,<=,>=,==,!=
4.界符 主要用于分隔单词的。
单界符:空白符号,{,},;等
双界符:/*等
5.常数 固定不变的值,一般分为整型,实型,字符型和字符串型等等。 2.2 单词的种别码
为了简化编译程序后续阶段的工作,优化编译程序的内部处理,需要对每个单词(或单词类别)给定一个种别码,又称为token值。
种别码用于表示单词的种类,通常用整数编码表示。单词如何分类,如何编码,并没有统一的规定。基本原则是不同的单词能够彼此区别并且有唯一的表示。
一般来说,一种程序语言的关键字、界符和运算符是固定的,可以采用一字一种;而标识符一般统归为一种;常数则按照类型分种。如下所示:
类别 单词 编码 类别 单词 编码 关键字 char 101 运算符 ( 201 int 102 ) 202 flaot 103 [ 203 break 104 ] 204 const 105 ! 205 return 106 * 206 void 107 / 207 continue 108 % 208 do 109 + 209 while 110 - 210 if 111 < 211 else 112 <= 212 for 113 > 213 界符 { 301 >= 214 } 302 == 215 ; 303 != 216 , 304 && 217 单词类别 整数 400 || 218 字符 500 = 219 字符串 600 标识符 700 实数(float) 800 例如对以下代码:
if(r>=20) c=2*pi*r
经过词法分析后的输出为:
(111,"if") (201,"(") (700,"r") (214,">=") (800,"2.0") (202,")") (700,"c") (219,"=") (400,"2") (206,"*") (700,"pi") (206,"*") (700,"r")
2.3 单词的识别
2.3.1 状态转换图
状态转换图是描述单词构成规则的一种很好的工具。状态转换图是一张有限方向图,由以下几部分构成:
1.有限个结点,结点用圆圈表示,称为状态。 2.状态之间用带箭头的弧线连接,称为边。 3.状态a到状态b的边上标记的符号表示:使状态从a转换到b的输入字符或字符流。同一个状态出发的多条边上的标记不能有相同或有包含关系。 4.一个初态(又称为开始态),用双箭头=>标识;初始时,状态转换图总是位于初态。 5.至少有一个终态(又称为接受态),用双圈表示。 如下图所示:
其构词规则是:以字母开头,后面跟若干个字母数字的任意组合。如果出现了非字母或数字的符号,状态即转向2.
2.3.2 单词识别程序
有了状态图就可用很容易的识别一个字符串是否符号某个规则,或是识别出来一个符合规则的单词。其识别过程如下所示:
1.初始时,状态转换图位于初态,输入指针指向输入缓冲区的开始。 2.从输入缓冲区读入一个字符,会使状态发生转换,若读入字符为a,当前状态为s,寻找从s出发的、边上标记与a匹配(和a相同或包含a)的边,状态转向该边指向的状态;若没有找到与a匹配的边,表示识别失败。 3.当输入指针不断扫描输入缓冲区中的字符流时,状态转换图中的状态不断地发送转换,直到状态转换到接受状态。 案例:假定输入缓冲区有字符串“ABC+12”,用2.2.1的图来描述识别标识符的过程:
序号 输入 输入指针的变化 状态变化 1 初始时 ABC+12
输入指针指向A
状态为0 2 读入当前字符A ABC+12
输入指针指向B
读入了字母,状态变为1
识别出字符串“A”
3 读入当前字符B ABC+12
输入指针指向C
读入了字母,状态仍为1
识别出字符串“AB”
4 读入当前字符C ABC+12
输入指针指向+
读入了字母,状态仍为1
识别出字符串“ABC”
5 读入当前字符+ ABC+12
输入指针指向1
读入了+,+既不是字母也不是数字
所以状态变为2,此时表示识别出的
字符串“ABC”是一个单词。
6 ...... 将上述过程转换为C语言程序如下所示:
Recognized(char ch){ char state = '0'; //开始状态 while(state != 2){ switch(state != 2){ case '0': if(isLesster(ch)) state='1'; //是字母,则转向状态1 else error(); //否则调用出错处理,识别其他的单词 break; case '1': ch = GetNextChar(); //读取下一个输入字符 if(isLetter(ch) || isDigit(ch)) state='1'; //是字母或数字,状态不变 else state='2'; //其他字符,转向状态2 break; } } index--; //回退一个符号,当前列号-1 return (GetToken()); //返回识别的单词的token值 }
注:有些符号串的识别并不需要返回单词的token值,只需要跳过相应的符号即可,比如出现连续多个空白符号、发现注释等等。
2.3.3 超前搜索技术和双界符的识别
对于双界符来说,读取了一个字符,并不能确定是否识别了单界符和双界符,还需要向前看一个或多个字符才能确定是囊个单词。这种技术就是超前搜索技术,通过提前查看而不读取的方式,只有向前看到某个符号确定了单词或单词类别后才继续读入。
超前搜索技术和状态转换图是两种不同的单词识别方式。对于状态转换图来说,只有继续读入符号,根据后续符号来判断,才能识别单词。
如下图所示超前搜索技术:
如图所示是识别以>开头的四个单词:>、>=、>>、>>=的状态转换图。
1.初始时处于状态0 2.如果读入了>,进入状态1 3.在状态1时如果读入了=,则进入状态2,此时表示识别出了一个单词:>= 4.在状态1时如果读入了>,则进入状态3 5.在状态3时如果读入了=,则进入状态4,此时表示识别出了一个单词:>>= 6.在状态3时如果读入了其他字符,则进入状态4,此时表示识别出了一个单词:>> 7.在状态1时如果读入了其他字符,则进入状态6,此时表示识别出了一个单词:> 2.3.4 数值型常量的识别与状态转换图的合并
2.4 词法分析器的设计
2.4.1 词法分析器的任务
词法分析器的任务就是扫描源程序,识别单词,查找单词的token值,转换并输出token值,输出相应的错误信息。如下图所示:
2.4.2 工作流程
工作流程如下图所示:
流程概述如下:
扫描程序 只执行一次,识别单词,查找token值,插入token表都是反复执行的,知道缓冲区为空,就
输出token串到文件中。
查找token值 根据识别出的单词去表中查找单词的种别码,并对每个单词形成二元式的形式:(token值,单词自身的值)。 插入token表 按照顺序将每个单词以二元形式插入到token表中。 输出token串 识别玩所有单词后,将token串输出到文件供语法分析阶段使用。 2.4.3 识别单词流程
识别单词流程如下所示:
3 正则表达式与有穷机
正则表达式 用数学表达式的形式来描述单词构成模式 有穷机 是状态转换图的形式化描述,可以从识别的观点来判断某个单词是否符合单词构成规则 3.1 符号和符号串
1)符号
一个高级语言程序所能够使用的全体字符构成的集合称为字母表,即该语言的合法字符集。
2)符号串
字母表上的符号串是指由该字母表中的符号构成的有穷序列,又称为字。
3.2 集合的运算及语言的定义
集合的运算有并,连接,方幂,闭包,正闭包。这里有两个集合A,B如下所示:
并:A∪B 其中的符号串包含A与B的全部符号串。 连接:AB 也称为集合A与B的乘积,表示由A中的任一符号串与B中的任一符号串连接构成的符号串集合。 方幂:A的n次方 指的是集合A自身的n次乘积。 闭包:A* 表示将集合A自身连接0到多次得到的集合的并集。A* = A0∪A2∪A3∪A4...... 正闭包:A+ 表示就是在集合A的闭包总去掉A0得到的符号串的集合。A+ = AA* 3.3 正则表达式
3.3.1 正则表达式定义
正则表达式也称为正规式,可以描述所有通过对某个字母表上的符号串集合应用并、连接和闭包运算而得到的语言,是一种描述字符串构成模式的方法。同样运用正则表达式可以来检查一个字符串是否符合匹配某种特征的子模式串。
其表达式如下所示:
| 表示或运算,A|B = A∪B () 代表用括号括起来的子表达式 * 代表对字符串求闭包 · 代表对字符串求连接 3.3.2 优先级
优先顺序从大到小为:*,·,|
3.3.3 正则表达式及正规集
如下所示:
正则表达式 正规集 a {a} a|b {a,b} ab {ab} a* {a,aa,aaa...}(任意个a组成的串) ba* 以b开头后面接任意多个a的串 (a|b)(a|b) {aa,ab,ba,bb} (a|b)* {a,b,aa,ab.ba......}任意个a或b组成的串 (a|b)*(aa|bb)(a|b)* 所有含有两个相继的a或两个相继的b组成的串 3.4 有穷自动机