编译原理 Compiler Principles 第三章 词法分析 湖南大学信息科学与工程学院 软件工程系 杨金民 2019/02.

Slides:



Advertisements
Similar presentations

Advertisements

3 的倍数的特征 的倍数有 : 。 5 的倍数有 : 。 既是 2 的倍数又是 5 的倍数有 : 。 12 , 18 , 20 , 48 , 60 , 72 , , 25 , 60 ,
2 和 5 的倍数的特征 运动热身 怎样找一个数的倍数? 从小到大写出 2 的倍数( 10 个): 写出 5 的倍数( 6 个) 2 , 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18 , 20 5 , 10 , 15 , 20 , 25 , 30.
Tool Command Language --11级ACM班 金天行.
编译原理和技术.
《高等数学》(理学) 常数项级数的概念 袁安锋
单元辅导(二)   词法分析与有穷自动机.
第三章 词法分析 赵建华 南京大学计算机系 2009年2月.
一、原函数与不定积分 二、不定积分的几何意义 三、基本积分公式及积分法则 四、牛顿—莱布尼兹公式 五、小结
第九章 字符串.
EBNF 请用扩展的 BNF 描述 C语言里语句的结构; 请用扩展的 BNF 描述 C++语言里类声明的结构;
上次课主要内容 正规式的简化形式与辅助定义; 记号的识别:有限自动机 NFA的定义:M =(S,∑,move,s0,F)
Last Lecture Revisited
Lexical analyzer generator
第二章 词法分析 词法记号及属性 词法记号的描述与识别 有限自动机 DFA构建 DFA化简 词法模式的识别过程 子集构造法.
编译原理与技术 词法分析(1) 2018/11/28 《编译原理与技术》讲义.
编译原理与技术 2018/11/30 《编译原理与技术》讲义.
编译原理 第三章 词法分析.
第三章 词法分析.
Part3词法分析 授课:胡静.
Last Lecture Revisited
走进编程 程序的顺序结构(二).
元素替换法 ——行列式按行(列)展开(推论)
第2次课 上下文无关文法
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
编译原理与技术 词法分析 (2) 2018/12/30 《编译原理与技术》讲义.
字符串(1) 字母表: 任意非空有穷集合 (字符)串: 连接: ={0,1} ={a,b,c,…,x,y,z,空格}
第二、三章习题讲解
第二章 Java语言基础.
2.3 正则表达式 主要内容: 1.正则表达式和正则集; 2.正则表达式和有限自动机的相互转化。.
中国科学技术大学计算机系 陈香兰(0551- ) Spring 2009
《编译原理与技术》 期末复习 计算机科学与技术学院 郑启龙 李 诚 25/12/2018.
绿色圃中小学教育网 比例 比例的意义 绿色圃中小学教育网
多媒体技术 中南大学信息科学与工程学院 黄东军.
宁波市高校慕课联盟课程 与 进行交互 Linux 系统管理.
第二章 词法分析4 词法分析程序实现 构造词法分析器步骤 单词的形式化描述 词法分析程序的实现.
SOA – Experiment 2: Query Classification Web Service
第4章 非线性规划 4.5 约束最优化方法 2019/4/6 山东大学 软件学院.
数列.
C语言程序设计 主讲教师:陆幼利.
EBNF与操作语义 请用扩展的 BNF 描述 javascript语言里语句的结构;并用操作语义的方法描述对应的语义规则
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
学习目标 1、什么是字符集 2、字符集四个级别 3、如何选择字符集.
6.4不等式的解法举例(1) 2019年4月17日星期三.
编译原理总结-1 第3~5章.
自动机与形式语言 报告人:姜勇刚 郑奘巍.
项目二:HTML语言基础.
成绩是怎么算出来的? 16级第一学期半期考试成绩 班级 姓名 语文 数学 英语 政治 历史 地理 物理 化学 生物 总分 1 张三1 115
第4章 Excel电子表格制作软件 4.4 函数(一).
第九节 赋值运算符和赋值表达式.
iSIGHT 基本培训 使用 Excel的栅栏问题
3.16 枚举算法及其程序实现 ——数组的作用.
词法分析
1.2 子集、补集、全集习题课.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
学习目标 1、了解基本运算符 2、运算符优先级.
基于规则抽取的时间表达式识别 -英文Ⅲ 高冠吉.
本节内容 C语言的汇编表示 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
主讲教师 欧阳丹彤 吉林大学计算机科学与技术学院
基于列存储的RDF数据管理 朱敏
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
第四节 向量的乘积 一、两向量的数量积 二、两向量的向量积.
第三章:词法分析 戴新宇 南京大学 计算机科学与技术系.
第三节 数量积 向量积 混合积 一、向量的数量积 二、向量的向量积 三、向量的混合积 四、小结 思考题.
顺序结构程序设计 ——关于“字符串”和数值.
编译原理实践 6.程序设计语言PL/0.
编译原理实践 --词法分析程序的自动生成器LEX
§2 自由代数 定义19.7:设X是集合,G是一个T-代数,为X到G的函数,若对每个T-代数A和X到A的函数,都存在唯一的G到A的同态映射,使得=,则称G(更严格的说是(G,))是生成集X上的自由T-代数。X中的元素称为生成元。 A变, 变 变, 也变 对给定的 和A,是唯一的.
第二章 简单数据类型 §2.1 数据类型概述 §2.2 变量和常量 §2.3 简单数据类型 §2.4 简单数据类型的相互转换
Presentation transcript:

编译原理 Compiler Principles 第三章 词法分析 湖南大学信息科学与工程学院 软件工程系 杨金民 2019/02

第三章 词法分析 1. 词法分析的含义; 2. 词法分析的基本概念; 3. 正则表达式——词法单元模式的表达; 4. 状态转换图; 第三章 词法分析 1. 词法分析的含义; 2. 词法分析的基本概念; 3. 正则表达式——词法单元模式的表达; 4. 状态转换图; 5. 词法分析器构造工具; 6. 有穷状态自动机; 7. 从正则表达式到NFA,DFA的映射方法;

词法分析 词法分析/扫描(lexical analysis, scanning) 高级语言程序的源文件(文本文件),可将其看作字符流。 对源程序文件中的字符流进行扫描,切分成为词素流; 例如:return (initial<=10)?100:(position+initial**2); return ( initial <= 10 ) ? 100 : ( position + initial ** 2 ) ;

语言中的词法单元 词素流 (标识符流) 文本文件 (字符流) 词法分析程序 return ( initial <= 10 ) ? 100 : ( position + initial ** 2 ) ; forward begin 目标:高效能:一次扫描解决

语言中的词法单元 id 词法 预定义符 自定义符 保留字 运算符 标点符 特义符 变量 常量 ; “ ... if while int 数据变量 函数变量 预定义符:相互独立,没有相交性。 语法主要通过预定义符来体现和标识。

词法分析 例如:return (initial<=10)?100:(position+initial**2); 词素流:符号表: row_id name type class row_num 1 return 预留 保留字 120 2 ( 特义符 3 initial 自定义 变量 4 <= 运算符 5 10 常量 6 ? 标点符号

词法分析要解决的问题 要解决的问题:如何从字符流切分识别为词素流。 return ( initial <= 10 ) ? 100 : ( position + initial ** 2 ) ; forward begin 词法单元的模式:从左到右的字符序列,在其组成上的约束条件。 如果一个字符串匹配某个词法单元的模式,就说它是该词法单元的一个实例,称之为词素 如何描述词法单元的模式?  正则表达式

语法分析中的三个概念 词法单元(token):词素的类概念,例如,自定义符; 词素(lexme):字符序列; 词法单元的实例;例如,上例中的position,initial。 词法单元的模式(pattern) 词法单元的构成规约;

程序语言中词法单元的模式 customed identifier: 变量:以字母或者下划线开头,接任意多个字母、下划线, 数字;例如: i ; _if ; if3; 数值常量:例如:0.1; 32; 49.8; 3.6E8; 0.5E-2; 8.9E+5; 字符常量:引号括起来的字符串 ; reserved keywords: if; else; do; while; for; else if +; - ; * ; / ; ** ; ++; +- =; ==; <; <=; >; >= ; != ; , “ ” ‘ ’ blank; tab; newline

模式表达中的概念 字符(character):字符集,例如,ASCII字符集:字母集,数字集,标点符号集; 串(string): 某个字符集下的字符串。注意:串不是集合。 串s的长度记为|s|。空串用ε表示。 语言(language):串的集合。 在语言中,串有语义。 字,词,句,段,文都是串。 例如:C语言, Java语言,英语,汉语。,{a}是语言。 串s的前缀(prefix), 后缀(suffix), 子串(substring), 子序列(subsequence): 去掉了一些其中的字符。

串的运算 串的连接运算:也叫“乘积”运算,串x连接串y记为xy。结果还是为串。 例如, x=my, y =house, 那么xy =myhouse; 自然有:sε = εs = s 串的“指数”运算: si = si-1s; s0 = ε 例如, x=my, 那么x3 =mymymy; 表达串接特性 常量合并,复写传播 表达重复串接性

语言的运算 并运算,联接,闭包三种运算: 结果还是为语言。语言L和M 并运算:LM = L | M = {s| sL 或者 sM}; 联接运算: LM = {st | sL , tM}; 闭包运算:L的Kneene闭包: L的正闭包: 可选性 串接性 串接的重复出现性 例如:个位整数:1|2|3|4|5|6|7|8|9 两位整数:(1|2|3|4|5|6|7|8|9) (0|1|2|3|4|5|6|7|8|9) 整数:(1|2|3|4|5|6|7|8|9) (0|1|2|3|4|5|6|7|8|9)*

思考的问题 L= {A, B,...,Z, a,b, ...., z}, D = {0,1,2,3,4,5,6,7,8,9} LD ,LD中包含的串元素的个数各为多少? L0={ε}。L4, L*, D+, L(LD)*分别是什么?

语言运算的例子 L= {A, B,...,Z, a,b, ...., z}, D = {0,1,2,3,4,5,6,7,8,9} LD = {A, B,...,Z, a,b, ...., z, 0,1,2,3,4,5,6,7,8,9}; (LD)2 = (LD)(LD) = {AA, AB, Az, A0, A9,...., 9A,9B, 99},其中的串元素各个数为: (LD)* = {ε}  (LD)  (LD)2  ...  (LD) 因此,L(LD)*是以字母开头,由字母和数字组成的串的集合。 注意:(LD)n和(LD)*,(LD)+是完全不同的概念

正则表达式—描述串接约束—词法单元的模式 语言运算表达式就称作正则表达式(Regular expression): 表达式中通常是变量(斜体字)运算表达式. 并运算符:或|。 语言运算的结合律和交换律,不言而喻;但(a|b)* ≠ a*| b* 三种运算的优先级:*(+) > 联接 > 或| 正则表达式也可给它取一个名字,随后用于其它正则表达式: 例如,C语言的自定义标示符的一个正则定义可以是: letter_ → A | B | ... | Z | a | b | ... | z | _ digit → 0 | 1 | 2 | ... | 9 id → letter_ (letter_ | digit)*

正则表达式表达词法单元的模式 digit → 0 | 1 | 2 | ... | 9 digits → digit digit* = digit+ optionalFraction → . digits | ε optionalExponent → (E(+ | - | ε ) digits) | ε number → digits optionalFraction optionalExponent 问题:01匹配number吗? 1.0匹配number吗? 1. 匹配number吗?

正则表达式的标记符扩展 基本运算符:并、连接、Kleene闭包(*) 扩展的标记符: 0个或多个实例: (r)* = r*r , r* = r+ | ε 1个或多个实例:(r)+ = rr* = r*r , r* = r+ | ε 0个或1个实例:ε | r = r? 字符类:连续的字符中的任选其一,例如 a1 | a2 | …| an = [a1a2…an] = [a1- an] a | b | c | d | e = [a - e] 仅仅只是使正则表达式更简洁。

简洁的正则表达式例子 letter_ → [A- Z a - z _ ] digit → [0 - 9] id → letter_ (letter_ | digit)* digits → digit+ number → digits (.digits )?( E [+ - ]? digits)?

正则表达式的特性——描述串接性问题 字符串在构成上的串接约束;这些串接约束必须相互独立,不能重贴交叉: 例如:asdfgghjkweyr38839ssdf 只能 不会 约束1 约束2 约束3 id → letter_ (letter_ | digit)* digits → digit+ number → digits (. digits )?( E [+ - ]? digits)?

程序语言的词法单元及其正则表达式 letter_ → [A- Z a - z _ ] digit → [0 - 9] digits → digit+ customed identifier: id → letter_ (letter_ | digit)* number → digits (. digits )?( E [+ - ]? digits)? reserved keywords: if→ if else → else; operator→ = | < | > | == | <= | >= | != punctuation →; |,|“ |” |‘ |’ space →( blank | tab | newline )+

随堂测试1 character → [A- Z a - z 0-9] SQL语言中可模糊查询,比如: LIKE “zhang%”, 为模糊 查询,其中%可以在任意位置,请用正则表达式表达该类 模糊查询的模式。 IP地址例子为1.192.21.0,即0~255的四个数用点隔开。请 写出IP地址得正则表达式。

正则表达式例子 digit → [0 - 9] id → letter_ (letter_ | digit)* letter_ → [A- Z a - z _ ] digit → [0 - 9] id → letter_ (letter_ | digit)* digits → digit+ number → digits (.digits )?( E [+ - ]? digits)?

词法分析算法 return ( initial <= 10 ) ? 100 : ( position + initial ** 2 ) ; forward lexemeBegin string s= ε;  i, RE[i].math = true; //所有词法单元的正则表达式 while ( i, RE[i].math == true) { s = s + 下一个字符;  i, if (RE[i].math == true) if s does not math RE[i] RE[i].math = false; } Java的编译方式有两种,一种是和C++等语言一样的,把源代码编译成和本地机器平台相关的机器语言,叫即时编译。另一种是编译成一种中间的字节码,与机器平台无关的,这种也是常用的,叫解释型的。 得出了词素,也得出了它所属的词法单元

正则表达式的状态转换图 将字符流切分成词素流,表达了一个词素的获取过程。 < 2 4 return operator. <= 5 start = < > 1 other 2 3 8 9 6 7 4 * 5 return operator. > return operator. >= return operator. < return operator. <= return operator. = return operator. ==

词法单元的正则表达式的状态转换图 将字符流切分成词素流,表达了一个词素的获取过程。 < 2 4 start = < > 1 other 2 3 8 9 6 7 4 * 5 return operator. > return operator. >= return operator. < return operator. <= return operator. = return operator. == 状态转换图

正则表达式的状态转换图 正则表达式: id → letter_ (letter_ | digit)* start letter_ 10 . letter_ 10 . other 11 letter_ | digit * 正则表达式: if → if Java的编译方式有两种,一种是和C++等语言一样的,把源代码编译成和本地机器平台相关的机器语言,叫即时编译。另一种是编译成一种中间的字节码,与机器平台无关的,这种也是常用的,叫解释型的。 not letter_ | digit i . * f start 23 24 25

正则表达式的状态转换图 正则表达式: number → digits (. digits )?( E [+ - ]? digits)? E other 20 * start digit 13 . 14 15 16 +|- 17 18 19 21 Java的编译方式有两种,一种是和C++等语言一样的,把源代码编译成和本地机器平台相关的机器语言,叫即时编译。另一种是编译成一种中间的字节码,与机器平台无关的,这种也是常用的,叫解释型的。

正则表达式的兼容性vs最长匹配原则 正则表达式: id → letter_ (letter_ | digit)* start letter_ letter_ 10 . other 11 letter_ | digit * 正则表达式: if → if Java的编译方式有两种,一种是和C++等语言一样的,把源代码编译成和本地机器平台相关的机器语言,叫即时编译。另一种是编译成一种中间的字节码,与机器平台无关的,这种也是常用的,叫解释型的。 not letter_ | digit i . * f start 23 24 25

基于状态转换图的词法分析体系结构 if(area>=len*h/2-2) forward lexemeBegin state = 0; while (1) { c = Char(forward); swith(state) { case 0: if (c is letter_ ) // id state =10; else if (c = '<') //operator state = 2; else if (c is digit) // number state =12; forward++; break; case 10: if (!(c is not letter_ or digit)) forward; find_id(lexemeBegin, forward -1); lexemeBegin = forward; } else forward++; if(area>=len*h/2-2) forward lexemeBegin letter_ 10 . other 11 letter_ | digit * 状态,以及输入字符驱动下的状态变迁

随堂测试2 1. 写出(a|b)*a(a|b)(a|b)的状态转换图。 2.对正则表达式: number → digits (. digits )?( E [+ - ]? digits)? E other 20 * start 12 digit 13 . 14 15 16 +|- 17 18 19 21 Java的编译方式有两种,一种是和C++等语言一样的,把源代码编译成和本地机器平台相关的机器语言,叫即时编译。另一种是编译成一种中间的字节码,与机器平台无关的,这种也是常用的,叫解释型的。 写出该状态转换图的词法分析程序。

3.5 词法分析器生成工具Lex 词法单元的正则表达式 → 状态转换图 → 词法分析代码 工具Lex 表达 生成 lex.l lex工具 词法单元的正则表达式 → 状态转换图 → 词法分析代码 工具Lex 表达 生成 lex.l lex工具 lex.yy.c C编译器 a.out 源程序的字符流 a.out 词素流

3.5 词法分析器生成工具Lex 词法单元的正则表达式 → 状态转换图 → 词法分析源程序 工具Lex 表达 生成 lex.l lex工具 词法单元的正则表达式 → 状态转换图 → 词法分析源程序 工具Lex 表达 生成 lex.l lex工具 lex.yy.c C编译器 a.out 源程序的字符流 a.out 词素流

词法分析器构造接下来要解决的问题 从正则表达式  状态转换图,在前面是手工构造的, 能否有一种方法(算法),以正则表达式表达式为输入,状态 转换图为输出呢? 状态转换图有哪些类型?各种类型之间的关系如何?

正则表达式的状态转换图的分类 正则表达式: number → digits (. digits )?( E [+ - ]? digits)? other 20 * start 12 digit 13 . 14 15 16 +|- 17 18 19 21 a b start 1 2 3 a Java的编译方式有两种,一种是和C++等语言一样的,把源代码编译成和本地机器平台相关的机器语言,叫即时编译。另一种是编译成一种中间的字节码,与机器平台无关的,这种也是常用的,叫解释型的。 (a|b)*abb

另一个例子 语言L((aa*|bb*)的状态转换图: 开始状态:一个 a 接受状态:可多个 转换函数 1 2 ε 边上的标号 start b 1 2 a ε 开始状态:一个 接受状态:可多个 转换函数 边上的标号 3 4

3.6 有穷自动机 将状态转换图形式化,上升为一种理论知识,被称作有穷自动机(finite automata)。为两类: 非确定有穷自动机NFA(Nondeterministic Finite Automata)。图中的某一状态结点,一个驱动符号(即边上的标号),可引出多条边, ε 可以是边上的标号。 确定性有穷自动机DFA( Deterministic Finite Automata):图中的状态结点,一个驱动符号,有且仅有一条出边。驱动符号不包含ε 。

NFA 正则表达式(a|b)*abb的NFA表示。NFA的图表示 开始状态:一个 a 接受状态:可多个 转换函数; 边上的标号。 start 1 2 3 NFA的表表示法:转换表

NFA的另一个例子 语言L((aa*|bb*)的NFA: 开始状态:一个 a 接受状态:可多个 转换函数 1 2 ε 边上的标号 start 1 2 a ε 开始状态:一个 接受状态:可多个 转换函数 边上的标号 3 4

3.6.2. DFA匹配(DFA模拟) state = 0; ; c = nextChar(); while (c != eof ) { state = move(state, c); } if (state 在 接受状态集合F) return true; else return false;

对NFA中ε变迁的认识:ε-closure()函数 7 8 b 10 ε a start 1 2 3 4 5 6 9 开始状态的集合:S = ε-closure( {0} ) = {0,1,2,4, 7}; 其中0是自己; 1,7是直接的ε后继; 2和4是间接的ε后继; ε-closure(S) = sS ε-closure(s) = {0,1,2,4, 7}= S;

NFA中状态集S在字符a驱动下的的后继状态集 7 8 b 10 ε a start 1 2 3 4 5 6 9 开始状态的集合:S = ε-closure(0) = {0,1,2,4, 7}; move(S,a) = {3,8}

NFA中状态集S在字符a驱动下的的后继状态集{3,8} 的 ε-closure() 7 8 b 10 ε a start 1 2 3 4 5 6 9 状态集合S在字符a的驱动下的后继状态集合: DTran[S,a] = ε-closure(move(S,a) )= ε-closure( {3, 8}) ={1, 2, 3, 4, 6, 7, 8}

NFA中状态集S在字符b驱动下的的后继状态集 7 8 b 10 ε a start 1 2 3 4 5 6 9 开始状态的集合:S = {0,1,2,4, 7}; move(S,b) = {5}; DTran[S,b] = ε-closure(move(S,b) )=ε-closure( {5} )

NFA中状态集S在字符b驱动下的的后继状态集{5} 的 ε-closure() 7 8 b 10 ε a start 1 2 3 4 5 6 9 DTran[S,b] = ε-closure(move(S,b) )=ε-closure( {5} ) ={1,2,4,5,6,7};

DFA和NFA的匹配对比 state = 0; c = nextChar(); while (c != eof ) { state = move(state, c); } if (state接受状态集F!=) return true; else return false; states = ε-closure(0) ; c = nextChar(); while (c != eof ) { states = ε-closure(move(states,c); } if (states接受状态集F!=) return true; else return false; DFA匹配 NFA匹配

总的思路与框架 词法分析程序 正则表达式 NFA DFA 状态数最小化后的DFA

正则表达式的NFA实例 7 8 b 10 ε a start 1 2 3 4 5 6 9 对于正则表达式r= (a|b)*abb

正则表达式的基本元素 正则表达式中仅只有三种运算: r= s|t r= st r= s* 作用域类比:势力范围

3.7 简单正则表达式的NFA 对于单个字符的表达式r = a,构建其NFA: 对于表达式 r= ε,构建其NFA: f start i a

并运算与NFA组合的对应 对于表达式r =s | t ,构建其NFA: ft start it t ir is fs s ε fr ft

联接运算与NFA组合的对应 对于表达式r =s t ,构建其NFA: s t start is fs it ft ε s t start

+运算的NFA画法 对于表达式r =s+,构建其NFA: start is fs s ε start is fs s

*运算与NFA组合的对应 对于表达式r =s* =  | s+, 构建其NFA: ε start is fs s start f i ε ir fr ε is fs s f i

*运算与NFA组合的对应 对于表达式r =s* =  | s+, 构建其NFA: start ir is fs s fr ε

其它运算的NFA画法 对于表达式:r =s? = s |  ft start it ε ir is fs s fr fs s start

正则表达式的NFA构建例子 对于正则表达式r= (a|b)*abb 1 ε 3 b 2 a 4 5 6 start 2 a 3 b 5 4

正则表达式的NFA构建例子 a 3 ε 2 start 1 6 b 5 4 a 3 2 start ε 7 1 6 b 5 4 对于正则表达式r= (a|b)*abb

正则表达式的NFA构建例子 7 ε b start 1 2 3 a 4 5 6 7 a 8 ε a 2 3 ε ε start ε a ε 7 a 8 ε a 2 3 ε ε start ε a ε 1 6 7 8 b ε ε 5 ε 4 对于正则表达式r= (a|b)*abb

正则表达式的NFA构建例子 7 8 b 10 ε a start 1 2 3 4 5 6 9 对于正则表达式r= (a|b)*abb

对正则表达式的NFA构建——错误认识1 对于正则表达式(a|b)* 引起的状态变迁:尽管是空变迁,但是有方向。 7 ε b start 1 2 3 a 4 5 6 错误的画法: b start 1 ε 2 3 a 4 5 6 导致a或b驱动的变迁失去意义:1状态 = 6状态

正则表达式的NFA构建——错误认识2 对于正则表达式(a|b)* ε a 3 2 ε ε start ε ε 7 1 6 b ε ε 5 ε 1 6 7 b ε ε 5 ε 4 错误的画法: ε 没有区分驱动字符:a或b,与,它俩有相同的结果状态。 a 2 3 ε ε start ε 1 6 b ε ε 5 4 ε

正则表达式的NFA构建——错误认识3 对于正则表达式(a|b)* ε a 3 2 ε ε start ε ε 1 6 7 b ε ε 5 ε 1 6 7 b ε ε 5 ε 4 错误的画法: 7 ε b start 1 2 3 a 4 5 6 对开始状态与变迁后状态混为一谈; (a|b)* =  | (a|b)+

对正则表达式的处理顺序 过程分解: 正则表达式r= (a|b)*abb, 先构建其语法分析树: a b r1 r2 r3 | ( ) r4 作用域类比:势力范围

NFADFA 对正则表达式的NFA,并不能用于词法分析器的构造; 怎么解决? idea:使用穷举,把所有可能情况列举出来,把不确定性转变成确定性  实现 NFA  DFA

NFADFA:子集构造法 7 8 b 10 ε a start 1 2 3 4 5 6 9

NFA中的开始状态0的ε闭包 7 8 b 10 ε a start 1 2 3 4 5 6 9 开始状态的集合:S = ε-closure(0) = {0,1,2,4, 7}; 其中0是自己; 1,7是直接的ε后继; 2和4是间接的ε后继; ε-closure(S) = sS ε-closure(s) = {0,1,2,4, 7};

NFA中的状态集,在字符驱动下的变迁  后继状态集 7 8 b 10 ε a start 1 2 3 4 5 6 9 开始状态的集合:S = ε-closure(0) = {0,1,2,4, 7}; move(S,a) = {3,8}

S在字符b的驱动下的后继状态 7 8 b 10 ε a start 1 2 3 4 5 6 9 状态集合S在字符a的驱动下的后继状态集合: 状态集合S在字符a的驱动下的后继状态集合: DTran[S,a] = ε-closure(move(S,a) )= ε-closure( {3, 8}) ={1, 2, 3, 4, 6, 7, 8}

S在字符b的驱动下的后继状态 7 8 b 10 ε a start 1 2 3 4 5 6 9 开始状态的集合:S = {0,1,2,4, 7}; move(S,b) = {5}; DTran[S,b] = ε-closure(move(S,b) )=ε-closure( {5} )

NFA中状态集{5}的ε闭包 7 8 b 10 ε a start 1 2 3 4 5 6 9 DTran[S,b] = ε-closure(move(S,b) )=ε-closure( {5} ) ={1,2,4,5,6,7};

观察 S = {0,1,2,4, 7} DTran[S,a] =ε-closure( {3,8} ) = {1,2,3,4,6,7,8} = S1,1; DTran[S,b] =ε-closure( {5} ) = {1,2,4,5,6,7} = S1,2;

S1,1在字符a的驱动下的后继状态 7 8 b 10 ε a start 1 2 3 4 5 6 9 已知状态的集合:S1,1 = {1,2,3,4,6,7,8}; DTran[S1,1, a] = ε-closure(move(S1,1, a) )= ε-closure( {3, 8} ) = S1,1;

S1,1在字符b的驱动下的后继状态 7 8 b 10 ε a start 1 2 3 4 5 6 9 已知状态的集合:S1,1 = {1,2,3,4,6,7,8}; DTran[S1,1, b] = ε-closure(move(S1,1, b) )= ε-closure( {5, 9} )

S1,1在字符b的驱动下的后继状态 7 8 b 10 ε a start 1 2 3 4 5 6 9 已知状态的集合:S1,1 = {1,2,3,4,6,7,8}; DTran[S1,1, b] = ε-closure(move(S1,1, b) )= ε-closure( {5, 9} ) = {1,2,4,5,6,7,9} = S2,1 新状态!!!

S1,2在字符a的驱动下的后继状态 ε a ε 2 3 ε ε ε a start b b 1 6 7 8 9 10 b ε 5 ε ε 4 1 6 7 8 9 10 b ε 5 ε ε 4 已知状态的集合:S1,2 = {1,2,4,5,6,7}; DTran[S1,2, a] = ε-closure(move(S1,2, a) )= ε-closure( {3, 8} ) = S1,1

S1,2在字符b的驱动下的后继状态 7 8 b 10 ε a start 1 2 3 4 5 6 9 已知状态的集合:S1,2 = {1,2,4,5,6,7}; DTran[S1,2, b] = ε-closure(move(S1,2, b) )= ε-closure( {5} ) = S1,2

S2,1在字符a的驱动下的后继状态 7 8 b 10 ε a start 1 2 3 4 5 6 9 已知状态的集合:S2,1 = {1,2,4,5,6,7,9}; DTran[S2,1, a] =ε-closure(move(S2,1, a) )= ε-closure( {3, 8} ) = S1,1

S2,1在字符b的驱动下的后继状态 7 8 b 10 ε a start 1 2 3 4 5 6 9 已知状态的集合:S2,1 = {1,2,4,5,6,7,9}; DTran[S2,1, b] = ε-closure(move(S2,1, b) )= ε-closure( {5, 10} ) = {1,2,4,5,6,7,10} = S3,1 新状态!!!

S3,1在字符a的驱动下的后继状态 7 8 b 10 ε a start 1 2 3 4 5 6 9 已知状态的集合:S3,1= {1,2,4,5,6,7,10}; DTran[S3,1, a] =ε-closure(move(S3,1, a) )= ε-closure( {3, 8} ) = S1,1

S3,1在字符b的驱动下的后继状态 7 8 b 10 ε a start 1 2 3 4 5 6 9 已知状态的集合:S3,1= {1,2,4,5,6,7,10}; DTran[S3,1, b] =ε-closure(move(S3,1, b) )= ε-closure( {5} ) = S1,2

总结——DFA D的转换表 NFA状态集 DFA状态 a b {0,1,2,4, 7} S S1,1 S1,2 {1,2,3,4,6,7,8} S2,1 {1,2,4,5,6,7} {1,2,4,5,6,7,9} S3,1 {1,2,4,5,6,7,10}

3.7.1 由NFA转化成DFA(子集构造法) 正则表达式r= (a|b)*abb,构建其NFA,然后转化为DFA: S1,2 b start S S1,1 a S1,2 b S2,1 S3,1

3.9.6 最小化DFA的状态数量 对于DFA中的状态,分为两个集合S和F: start S T a U b Z Q

3.9.6 最小化DFA的状态数量 对S集合中的任一元素s,如果move(s,a)或者move(s,b)不在S中,则将其从S脱离出来; U start S T a U b Z Q

3.9.6 最小化DFA的状态数量 start S T a U b Z Q

3.9.6 最小化DFA的状态数量 对于表达式r =s t ,构建其NFA: start S T a U b Z Q

3. 9.6 最小化DFA的状态数量 对于表达式r =s t ,构建其NFA: start S T a U b Z Q

3. 9.6 最小化DFA的状态数量 对于表达式r =s t ,构建其NFA: start S T a U b Z Q

3.9.6 最小化DFA的状态数量 b U b b start S T a Z b Q start S T a b Z Q

例题1:正则表达式(0?0?1) *0?0?的NFA start 6 7 1 2 3 5 4  S0={0,1,2,3,5,6,7} 3  6 5 7 4 S0={0,1,2,3,5,6,7} move(S0,1) = {4} ; ε-closure({4}) = {1,2,3,4,5,6,7} =S1 move(S0,0) = {2,3,6,7} ; ε-closure({2,3,6,7}) ={2,3,6,7} = S2 move(S1,1) = {4} = S1 move(S1,0) = {2,3,6,7} = S2 ; move(S2,1) = {4} = S1 move(S2,0) = {3,7} ; ε-closure({3,7}) ={3,7} = S3 move(S3,1) = {4} =S1; move(S3, 0) = 

正则表达式(0?0?1)*0?0?的DFA S1 1 start start S3 S0 S2 S3 S0 1 S2 S3 1 S1 S0 start S0 S2 S3 1 注意:上述状态S3对于字符0没有变迁。意思是说在状态S3,如果当前输入符号为0,就直接得出结论:该字符串就与该正则表达式不匹配。例如”000”就不匹配。 start S0 1 上述DFA不能化简为:

例题2:number → digits (.digits )?( E [+ - ]? digits)? digit → [0 - 9] digits → digit+ number → digits (.digits )?( E [+ - ]? digits)? digit → [0 - 9] digits → digit+ number → digits ( | (.digits )) ( | ( E(( | [+ - ]) digits)

digits → digit+  start 1 [0-9]

digits ( | (.digits)) start [0-9] . 2 3 4 1   start . [0-9] 3 4 5 2  start 1 [0-9] 4 3 2 . 5

( | [+ - ])   [+-] 2 3 4 5 

E(( | [+ - ]) digits  E   [0-9] 6 [+-] 1 2 3 4 5 

( | ( E(( | [+ - ]) digits)  5 [0-9] 6 1 E 2 3 [+-] 4 7

number → digits ( | (.digits )) ( | ( E(( | [+ - ]) digits) 4 3 [0-9] 2 . 5 start 1 [0-9] 5  10 [0-9] 11 6 E 7 8 [+-] 9 12

DFA D状态转换表 NFA状态集 DFA状态 [0-9] . E [+-] {0} S0 S1,1  {0,1,2,5,6,12} {3} S3,1 {7,8,10} S3,2 S3,3 {3,4,5,6,12} {10,11,12} {9,10}

DFA的图形表示法 正则表达式r= number → digits (.digits )?( E [+ - ]? digits)? [0-9] [0-9] S3,1 S2,1 [0-9] . E start [0-9] E [0-9] S0 S1,1 S2,2 S3,2 [+-] [0-9] [0-9] S3,3

语言中的词法单元的模式的正则表达式表达 id 词法 预定义符 自定义符 保留字 运算符 标点符 特义符 变量 常量 ; “ ... if while int 数据变量 函数变量 预定义符:相互独立,没有相交性。 变量与保留字的冲突,采用最长匹配办法解决

软件构造方法学:用软件编软件——词法分析器生成工具Lex 词法单元的正则表达式 → NFA → DFA → 词法分析代码 工具Lex 表达 生成 lex.l lex工具 lex.yy.c C编译器 a.out 源程序的字符流 a.out 词素流

随堂测试 对正则表达式:b*(a|ab)*, 画出其NFA,得出DFA D的转换表,画出DFA,给出待匹配的字符串例子3个,对给出的每个例子,要求长度>5,并在匹配过程中,走遍DFA中所有状态,并且最终分别结束在接受状态,非接受状态,无变迁边(即无须进一步匹配,直接得出不匹配结论)

小结1:词法分析 字符 字符串 语言:既有类概念,也有实例概念 词法单元(类概念) 字符串的三种运算 正则表达式 模式 词素(实例概念) 语言的类概念:词法单元的集合; 匹配 语言的实例概念:词素的集合; 字符串的三种运算 正则表达式

小结2 语言的词法分析程序 正则表达式 NFA DFA 状态数的最小化

随堂测试 对正则表达式:((ε|a)b*)*, 画出其DFA. 证明它与(a|b)*等价。

第二章作业 3.1.1,3.1.2 3.3.2, 3.3.5(1-3, 8-9),3.3.12 3.4.1,3.4.2 3.6.2, 3.6.3, 3.6.4 3.7.1, 3.7.3

第一次讨论课 第一次讨论课的主题: 词法分析。小主题: 1) 正则表达式转换NFA; 2) NFA确定化为DFA; 5) TINY编译器与TM虚拟机; 6) 词法分析器自动工具(如:FLEX) 自己定题,也可以选择如下内容: 1) Unix/Linux中的grep指令嵌套使用的典型应用案例; 2) 练习3.3.5(4,5,6,8)中的任何一个; 3) 其它习题也可以;

谢 谢 谢 谢!