刘坚 编译原理基础 学习笔记(1)词法分析

1.记号,模式与单词。

  • 模式(pattern):产生/识别单词的规则
  • 记号(token):按照某个模式(或规则)识别出的元素
  • 单词(lexeme):被识别出的元素的值

2.词法分析器的作用

  1. 滤掉无用成分,比如注释,回车,空格
  2. 处理与具体平台有关的输入。比如文件结束符可能有不同表示,需要具体分析
  3. 识别记号并交给语法分析器
  4. 调用符号表管理器和出错处理器,进行相关处理

        词法分析器的两种工作模式:(1) 作为语法分析器的子程序;(2) 词法分析器单独进行一遍扫描;(3) 与语法分析器并行的工作模式


3.字符串的基本概念

表示、术语意义
|S|字符串S的长度
ε空串
Sⁿ表示S自身连接n次 (注意:S⁰=ε)
S的前缀X去掉S尾部字符形成的字符串 (例如:S=“abc”,子串为"ε","a","ab","abc")
S的后缀X类似上文
S的子串X类似上文
S的真前缀(后缀,子串)XX是S的前缀/后缀/子串,X≠S且|X|>0
S的子序列X不同于子串,去掉的字符可以不连续 (例如:S="abc",X="ac")

闭包运算:

  •         X=L* —— X是集合L的闭包,X=L⁰∪L¹∪L²∪L³.....
  •         X=L⁺ —— X是集合L的正闭包,X=L¹∪L²∪L³.....

4.正规式&正规集

        定义:令Σ是一个有限字母表,则Σ上的 正规式 及其表示的集合递归定义如下:

        1. ε是正规式,它表示集合 L(ε) = {ε}

        2. 若a是Σ上的字符,则a是正规式,它表示集合L(a)={a}

        3. 若正规式r和s分别表示集合L(r)和L(s),则

  •                 r|s是正规式,表示集合L(r)∪L(s);表示或运算
  •                 rs是正规式,表示集合L(r)L(s);表示连接运算
  •                 r*是正规式,表示集合(L(r))*。表示闭包运算

        示例:

  •         括号可以规定运算的先后次序。
  •         运算的高低优先级为:闭包 > 连接 > 或
  •         若正规式子P,Q表示了同一个正规集,则称P和Q是等价的,记作P=Q
正规式的代数性质
公理公理
r|s=s|r(rs)t=r(st)
r|(s|t)=(r|s)|tεr=r=rε
r(s|t)=rs|rtr*=(r⁺|ε)
(s|t)r=sr|trr**=r*

        简化正规式描述

  1. 一个或多个实例。正规表达式a⁺表示由-一个或多个a构成的所有串的集合。操作符⁺和操作符*具有同样的优先级和结合性。代数恒等式:r*=r|ε 和 r⁺=rr*。
  2. 零个或一个实例。一元后缀操作符?的意思是“零个或一个实例”。r?是r|ε的缩写形式。如果r是正规表达式,则(r)?是表示语言L(r)U{ε}的正规表达式。
  3. 字符类。[abc] (其中a、b和c是字母表中的符号)表示正规表达式alb|c。缩写的字符类[a-z]表示正规表达式alb...|z。

         


5.记号的识别

        (1) 不确定的有限自动机 NFA

  •         利用直观的方式表示有限自动机:状态转换图&状态转换矩阵
  •         NFA的特点是不确定性,在当前状态下可能有多个下一状态的转移。
  •         从NFA的初态开始,对于输入序列的每一个字符,寻找它的下一状态转移,直到没有下一状态转移为止。若此时所处状态是终态,则识别,否则原路返回并探索下一路径。如果找不到下一状态,或者达不到终态,则输入序列不合法。

  •  对于转换图,其中初态是没有前驱的节点,末态是加粗的圆圈或者双圆
  • 对于转换矩阵其中第一行对应的状态为初态,末态需要特别指出

        (2) 确定的有限自动机

  •         DFA是NFA的特例,在当前状态下,对于同一字符最多有一个下一状态转移。

         (3) 有限自动机的等价

  •         对于任何一个NFA,均可以找到一个与它等价的DFA。

    6.Thompson算法构造NFA

 推导:https://www.bilibili.com/video/BV17W41187gL?p=15


7.子集法构造DFA

https://www.bilibili.com/video/BV17W41187gL?p=19

比如对于如图所示NFA,先构造:
A={0,1,2,4,7}
给A输入一个a,得到B={1,2,3,4,6,7,8}
给A输入一个b,得到C={1,2,4,5,6,7}
给B输入一个a,得到B
给B输入一个b,得到D={1,2,3,4,5,6,7,8,9}
给C输入一个a,得到B
给C输入一个b,得到C
给D输入一个a,得到B
给D输入一个b,得到E={1,2,3,4,5,6,7,8,9,10}
给E输入一个a,得到B
给E输入一个b,得到C 

以输入为边,以ABCDE为定点,得到结果


8.最小化DFA的状态数

        一开始划分成终态组与非终态组,然后以上题为例:

  1. 先划分成接受状态组(E)和非接受状态组(ABCD)
  2. 考虑(ABCD)。对于输入a,这些状态都转换到B,因此分组(ABCD)不变;但对于输人b, A、B和C都转换到状态组(ABCD)的一个成员,而D转换到另-组的成员E。于是状态组(ABCD)必须分裂成两个新组(ABC)和(D)。
  3. 对(ABC)输人a上仍然没有分裂,但对输人b. (ABC)还要划分,因为A和C都转到C.但B转到D,而C和D不在个组中,于是分成(AC)(B)(D)(E)。
  4. 现在只有(AC)有划分的可能。但是对于输入a, A和c都转换到B,对输人b,它们都转换到状态C,因而不必再划分。故结果为(AC)(B)(D)(E)

另一个示例:https://www.bilibili.com/video/BV17W41187gL?p=22


9.由DFA构造词法分析器

        方法(1):将DFA转换图用矩阵表示,词法分析驱动代码一边接受字符一边查表。通常采用的最长匹配原则(比如“result:=”应该被识别成result而不是re或res)。以下是伪代码描述

char table[M][N];
table[0]['a']=1;
table[1]['b']=1;
table[1]['c']=1;//表中其他内容为ERR0R

nextToken(){
	state=0;
	stack=[];//空栈
	while(state!=ERR0R){
		c=getchar();
		if(state is ACCEPT)clear(stack);
		push(state);//给栈中送state 
		state=table[state][c];
	} //end while 
	while(state is not ACCEPT){
		state=pop();
		rollback;
	}//end while 
}

        方法(2):不需要表指导,直接用goto模拟DFA的行为。以下是伪代码描述,用例与上相同。

nextToken(){
	state=0;
	stack=[]
	goto q0;
} 

q0:
	{
		c=getchar();
		if(state is ACCEPT)clear(stack);
		push(state);
		if(c=='a')goto q1;
	}

q1:
	{
		c=getchar();
		if(state is ACCEPT)clear(stack);
		push(state);
		if(c=='b'||c=='c')goto q1;
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值