编译原理 词法分析

词法分析

词法分析的双重含义:
规定单词形成的规则(构词规则,词法规则)
根据构词规则识别输入序列(词法分析)

记号、模式和单词

基本分类:关键字(保留字)、标识符、字面量、特殊符号
模式:产生和识别单词的规则
记号:按照某个模式(规则)识别出的元素。

记号至少包含两个信息:一个是记号的类别,另一个是记号的值
一般我们将记号的类别简称为记号,记号的其他信息记为记号的属性
记号的类别标识一类记号
记号的类别加属性标识一个记号实例

单词:指被识别出的元素自身的值

如果把记号看做是一个类型的话,那么单词就是一个类型的实例。词法分析器识别出的序列称为记号流


词法分析器的作用和工作方式

词法分析器是编译器中唯一与源程序打交道的部分

作用:

  1. 滤掉源程序中的无用成分,如注释,空格,回车等
  2. 处理与具体平台有关的输入
  3. 识别记号,并交给语法分析器(这是词法分析器最主要的任务)
  4. 调用符号表管理器或出错处理器,进行相关处理(词法错误往往不是由词法分析器检查出来的,而是由语法分析器发现的)

工作方式:

  1. 词法分析器作为语法分析器的子程序。语法分析器每需要一个记号,就调用词法分析器一次
    在这里插入图片描述
  2. 词法分析器单独进行一遍扫描(以源程序为输入,输出是记号流形式表示的源程序)
    在这里插入图片描述
  3. 与语法分析器并行工作,两者同时工作。(采用队列的结构,词法分析器将记号输出到队列中,语法分析器从队列中取得记号,只要队列中有记号且未满,两者就可同时工作)
    在这里插入图片描述
输入缓冲区

词法分析器是编译器中读入源程序字符序列的唯一阶段
输入缓冲区的安排一般采用单缓冲区或双缓冲区(缓冲区对)的方式。


字符串与语言

语言L是有限字母表∑上有限长度字符串的集合;
从词法分析角度看程序语言,它是由记号组成的集合
语言是一个集合,集合中的元素是字符串。字符串的长度是有限的。
字母表是有限的,即字母表中的元素是有限多个;
字符串的长度是有限的,即字符串中字符的个数时有限多个;


字符串的基本概念字符串集合的基本运算
长度空集合 Φ
空串空串 {ε}
连接并:X=L∪M
n次方交:X=L∩M
前缀连接:X=LM
后缀闭包:X=L*
子串正闭包X=L+
真前缀差:X=L-M
真后缀
真子串
子序列

正规式与正规集(很无聊的定义,看不看无所谓)

令Σ是一个有限字母表,则Σ上的正规式及其表示的集合递归定义如下:
1. ε是正规式,它表示集合L(ε)={ε}
2. 若a是Σ上的字符,则a是正规式,它表示集合L(a)={a}
3. 若正规式r和s分别表示集合L( r)和L(s),则
(a) r|s是正规式,表示集合L( r)∪L(s),
(b) rs是正规式,表示集合L( r)L(s),
(c) r*是正规式,b表示集合(L( r ))*
(d)( r)是正规式,表示的集合仍然是L( r)。(加括弧改变优先级、结合性)
可用正规式描述的语言称为正规语言或正规集。

正规集是一个集合,而正规式是表示正规集的一种方法。
不同的正规式也可以表示同一个正规集,即正规式与正规集之间是多对一的关系。
如果正规式P和Q表示了同一个正规集,则称P和Q是等价的,记为P=Q

例如:

正规式正规集
a,b,c{a},{b},{c}
a(a或b)*{a,aa,bb,ab,aba,abb,aab,···}
a或b{a,b}

正规式的代数性质
在这里插入图片描述

记号的说明

正规式的表示方法:
举个栗子:
relation = < | <= | <> | > | >= | =
正规式的说明记号的公式为:
记号 = 正规式
还可以引入辅助定义式来简化正规式的书写
比如:
char = [a-zA-Z]
digit = [0-9]


有限自动机

有限自动机:自动机的状态数是有限的。
若有限自动机M和N识别同一个正规集,那么称M和N是等价的。

有限自动机分为:
确定的有限自动机(DFA)
不确定的有限自动机(NFA)

M=(S, ∑, move, S0, F)
S:有限个状态的集合
求和:有限个输入字符的集合(包括ε)
move:状态转移函数的集合
S0:唯一的初态(开始状态)
F:终态集合(接受状态集),是S的子集,包含所有终态

有限自动机可以用直观的方式表示:
状态转换图
在这里插入图片描述
状态转移矩阵
在这里插入图片描述

NFA的特点:
不确定性,即它的下一个状态是不确定的,有多个。
NFA存在的问题:

  1. 只有尝试了所有可能的路径,才能确定一个输入序列不可被接受
  2. 识别过程中需要大量的回溯,算法复杂。
    造成这一问题存在的原因就是:NFA的不确定性。

DFA的特定:
确定性,即它的下一个状态是确定的,最多仅有一个。
DFA无需回溯。

对于任何一个NFA,都可以构造一个DFA来识别它。
正规式描述正规集
自动机识别正规集


构造词法分析器的一般方法和步骤

  1. 用正规式对模式进行描述
  2. 为每个正规式构造一个NFA,它识别正规式所表示的正规集
  3. 将构造出的NFA转换成等价的DFA(确定化)
  4. 优化DFA(最小化)
  5. 根据优化后的DFA构造词法分析器

由正规式构造NFA而不是构造DFA的原因是正规式到NFA有规范的一对一的构造算法
由DFA而不是由NFA构造词法分析器的原因是DFA识别记号的方法优于NFA识别记号的方法


从正规式到NFA

Thompson算法
将正规式r 分解成最基本的正规式。分解从正规式的最右端开始,然后按照规则构造。
构造一个新的NFA最多增加两个状态
规则如下:
规则不给描述了,能看懂。看不懂看书
ε
a
P|Q
PQ
P*

从NFA到DFA

将不确定的下一状态确定化
如果从当前状态出发经c可能到达不止一个状态,则将所有这些状态组成一个集合。显然,从当前状态出发经c到达的这一状态集是唯一的。

S和T分别表示状态集。
smove(S,a):从当前状态集S中的任何状态s出发,经字符a可直接到达状态的全体。
ε_闭包(T):表示从状态集T出发,经ε可以到达的所有状态。

模拟DFA的构造

S = ε_闭包({ s0 })
a = nextchar;

while a != EOF
	S = ε_闭包(smove(S, a));
	a = nextchar;

if S&&F==0
	return NO
else 
	return YES

ε_闭包

即,将从该状态集中的每一个状态,经ε可以到达的状态,并入一个集合。该新集合即为ε_闭包求得的下一个集合。
在经过ε一次到达一个状态A后,该状态经ε还可以到达一个状态B,那么就把A,B都并入。

子集构造算法

ε_闭包({s0})是Dstates仅有的状态,且尚未标记

while Dstates有尚未标记的状态T
	标记T
	for(每一个输入的字符a)
		U= ε_闭包(smove(T,a))
		if U not in Dstates
			push U as 未被标记 in Dstates
	Drtan[T,a]=U

由于DFA的一个状态是NFA全体状态的子集,所以在最坏的情况下,有n个状态的NFA,其等价DFA的状态数可能是O(2n)的

最小化DFA

将一个DFA等价变换为另一个状态数最少的DFA的过程称为最小化DFA

对于任意两个状态 t 和 s, 若从一状态出发接受输入字符串w,而从另一状态出发不接受w,或者从t出发和从s出发到达不同的接受状态,则称w对状态t和s是可区分的。

算法思想:
一开始:将非终态集合和终态集合划分
经过一系列区分,把可区分的状态分离出来,直到不可区分为止,所有不可区分的状态可以合并为一个状态。

对G进行划分,G中的两个状态s和t被划分在同一组的充要条件:对于任何输入字符a,move(s,a)和move(t,a)在同一组中。

步骤:
划分集合
终结态与非终结态
然后根据每个集合里,对于每个输入序列,看下一个状态是否仍在这个集合中,如果在,那么就把这些仍处在同一集合的S划分在一起;
如果不在,那么就把不在的划分出去。把划分的组做成新的标记,并对move(S,a)的a进行记录,对新集合进行改造。
消除可能的死状态和不可到达的状态。

由DFA构造词法分析器

  1. 表驱动型词法分析器
    在这里插入图片描述
  2. 直接编码型词法分析器
    在这里插入图片描述
  3. 两者的区别:
    在表驱动的词法分析器中,DFA是被动的,需要一个驱动器来模拟DFA的行为,以实现对输入序列的分析。
    直接编码的词法分析器,将DFA和DFA识别输入序列的过程合并在一起,直接用程序代码模拟DFA识别输入序列的过程。
    在这里插入图片描述
    一般来说,直接编码型的词法分析器适用于词法比较简单的情况。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
词法分析 一、实验目的: 通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。 编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的内部编码及单词符号自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示) 二、实验说明 1、 词法分析器的功能和输出格式 词法分析器的功能是输入源程序,输出单词符号。词法分析器的单词符号常常示成以下的二元式(单词种别码,单词符号的属性值)。本实验中,采用的是一类符号一种别码的方式。 2、 单词的BNF示 -> ->|| |ε -> -> |ε -> + -> - -> > -> >= 三、实验要求 (一)准备: 1.阅读课本有关章节,明确语言的语法,写出基本保留字、标识符、常数、运算符、分隔符和程序例。 2.初步编制好程序。 3.准备好多组测试数据。 (二)上课上机: 将源代码拷贝到机上调试,发现错误,再修改完善。 第二次上机调试通过。 (三)程序要求: 程序输入/输出示例: 如源程序为C语言。输入如下一段: main() { int a,b; a = 10; b = a + 20; } 要求输出如下: (2,”main”) (5,”(“) (5,”)“) (5,”{“) (1,”int”) (2,”a”) (5,”,”) (2,”b”) (5,”;”) (2,”a”) (4,”=”) (3,”10”) (5,”;”) (2,”b”) (4,”=”) (2,”a”) (4,”+”) (3,”20”) (5,”;”) (5,”}“) 要求: 识别保留字:if、int、for、while、do、return、break、continue; 单词种别码为1。 其他的都识别为标识符;单词种别码为2。 常数为无符号整形数;单词种别码为3。 运算符包括:+、-、*、/、=、>、=、<=、!= ; 单词种别码为4。 分隔符包括:,、;、{、}、(、); 单词种别码为5。 以上为参考,具体可自行增删。 (四)程序思路 这里以开始定义的C语言子集的源程序作为词法分析程序的输入数据。在词法分析中,自文件头开始扫描源程序字符,一旦发现符合“单词”定义的源程序字符串时,将它翻译成固定长度的单词内部示,并查填适当的信息。经过词法分析后,源程序字符串(源程序的外部示)被翻译成具有等长信息的单词串(源程序的内部示),并产生两个格:常数和标识符,它们分别包含了源程序中的所有常数和所有标识符。 0.定义部分:定义常量、变量、数据结构。 1.初始化:从文件将源程序全部输入到字符缓冲区中。 2.取单词前:去掉多余空白。 3.取单词后:去掉多余空白(可选,看着办)。 4.取单词:利用实验一的成果读出单词的每一个字符,组成单词,分析类型。(关键是如何判断取单词结束?取到的单词是什么类型的单词?)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值