Upload
others
View
33
Download
0
Embed Size (px)
Citation preview
湖南大学信息科学与工程学院
软件工程系 杨金民
2018
编译原理Compiler Principles
第三章 词法分析
第三章 词法分析
• 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 ) ;
词法分析
• 词素有类概念:预定义符和自定义符;
• 预定义符有:保留字,运算符,标点符,特义符;
• 词素流:可以表达在符号表:row_id name type class v_class row_num
1 return 预留 保留字 120
2 ( 预留 特义符 120
3 initial 自定义 局部变量 int 120
4 <= 预留 运算符 float 120
5 10 自定义 常量 数值 1206 ? 预留 标点符号 120
语法分析中的三个概念
词法单元(token):词素的类概念,例如,自定义符;
词素(lexme):字符序列;
– 词法单元的实例;例如,上例中的position,initial。
词法单元的模式(pattern)
– 词法单元的构成规约;
词法分析要解决的问题
• 要解决的问题:如何从字符流切分识别为词素流。
return ( initial <= 10 ) ? 100 : ( position + initial ** 2 ) ;
forward
lexemeBegin
如何描述词法单元的模式? 正则表达式
词法单元的模式:从左到右的字符序列,在其组成上的约束条件。如果一个字符串匹配某个词法单元的模式,就说它是该词法单元的一个实例,称之为词素
模式表达中的概念
• 字符(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
并运算:LM = L | M = {s| sL 或者 sM};
联接运算: LM = {st | sL , tM};
闭包运算:L的Kneene闭包:
L的正闭包:
L= {A, B,...,Z, a,b, ...., z}, D = {0,1,2,3,4,5,6,7,8,9}
LD ,LD中包含的串元素的个数各为多少?
L0={ε}。L4, L*, D+, L(LD)*分别是什么?
ii LL 0
*
ii LL
1
可选性
串接的可搭配性
重复次数的可随意性
语言运算的例子
• L= {A, B,...,Z, a,b, ...., z}, D = {0,1,2,3,4,5,6,7,8,9}
• LD = {A, B,...,Z, a,b, ...., z, 0,1,2,3,4,5,6,7,8,9};
• (LD)2 = (LD)(LD) = {AA, AB, Az, A0, A9,...., 9A,9B, 99},
其中的串元素各个数为:
• (LD)* = {ε} (LD) (LD)2 ... (LD)
• 因此,L(LD)*是以字母开头,由字母和数字组成的串的集合。
2162
162 62CC
注意:(LD)n和(LD)*,(LD)+是完全不同的概念
正则表达式—描述串接约束—词法单元的模式
语言运算表达式就称作正则表达式(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
约束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→ digitselse → digits;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地址得正则表达式。
正则表达式例子
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;}
得出了词素,也得出了它所属的词法单元
词法单元的正则表达式的状态转换图
将字符流切分成词素流,表达了一个词素的获取过程。
start0 =
<
>
1 =
other
2 =
other
3 =
other
8
9
6
7
4
*
*
5 *
return operator. >
return operator. >=
return operator. <
return operator. <=
return operator. =
return operator. ==
词法单元的正则表达式的状态转换图
将字符流切分成词素流,表达了一个词素的获取过程。
start0 =
<
>
1 =
other
2 =
other
3 =
other
8
9
6
7
4
*
*
5 *
return operator. >
return operator. >=
return operator. <
return operator. <=
return operator. =
return operator. ==
状态转换图
正则表达式的状态转换图
start 0letter_
10.other 11
letter_ | digit
*
正则表达式: id → letter_ (letter_ | digit)*
start 0i
23.not letter_ | digit
25*f
24
正则表达式: if → if
正则表达式的状态转换图
Eother20 *
start 0digit
13.
14digit
15E
16+|-
17digit
18other
19
digit digit digit
digitother
21 *
正则表达式: number → digits (. digits )?( E [+ - ]? digits)?
正则表达式的兼容性vs最长匹配原则
start 0letter_
10.other 11
letter_ | digit
*
正则表达式: id → letter_ (letter_ | digit)*
start 0i
23.not letter_ | digit
25*f
24
正则表达式: if → if
基于状态转换图的词法分析体系结构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; state = 0; }else forward++;break;
if(area>=len*h/2-2)
forward
lexemeBegin
0letter_
10.other11
letter_ | digit
*
状态,以及输入字符驱动下的状态变迁
随堂测试2
Eother20 *
start 12digit
13.
14digit
15E
16+|-
17digit
18other
19
digit digit digit
digitother
21 *
2.对正则表达式: number → digits (. digits )?( E [+ - ]? digits)?
写出该状态转换图的词法分析程序。
1. 写出(a|b)*a(a|b)(a|b)的状态转换图。
3.5 词法分析器生成工具Lex
词法单元的正则表达式 → 状态转换图 → 词法分析代码
工具Lex
表达 生成 生成
lex.l lex工具 C编译器 lex.yy.c a.out
a.out 源程序的字符流 词素流
3.5 词法分析器生成工具Lex
词法单元的正则表达式 → 状态转换图 → 词法分析代码
工具Lex
表达 生成 生成
lex.l lex工具 C编译器 lex.yy.c a.out
a.out 源程序的字符流 词素流
词法分析器构造接下来要解决的问题
从正则表达式 状态转换图,在前面是手工构造的,
能否有一种方法(算法),以正则表达式表达式为输入,状态
转换图为输出呢?
状态转换图有哪些类型?各种类型之间的关系如何?
正则表达式的状态转换图的分类
Eother20 *
start 12digit
13.
14digit
15E
16+|-
17digit
18other
19
digit digit digit
digitother
21 *
正则表达式: number → digits (. digits )?( E [+ - ]? digits)?
b
start 0 1 2b
3a b(a|b)*abb
a
另一个例子
语言L((aa*|bb*)的状态转换图:
bstart 0
1 2a
εa 开始状态:一个
接受状态:可多个
转换函数
边上的标号3 4
b
ε
3.6 有穷自动机
将状态转换图形式化,上升为一种理论知识,被称作有穷自动
机(finite automata)。为两类:
– 非确定有穷自动机NFA(Nondeterministic Finite
Automata)。图中的某一状态结点,一个驱动符号(即边上
的标号),可引出多条边, ε 可以是边上的标号。
– 确定性有穷自动机DFA( Deterministic Finite
Automata):图中的状态结点,一个驱动符号,有且仅有
一条出边。驱动符号不包含ε 。
NFA 正则表达式(a|b)*abb的NFA表示。NFA的图表示
a
b
start 0 1 2b
3a b
开始状态:一个
接受状态:可多个
转换函数;
边上的标号。
NFA的表表示法:转换表
NFA的另一个例子
语言L((aa*|bb*)的NFA:
bstart 0
1 2a
εa 开始状态:一个
接受状态:可多个
转换函数
边上的标号3 4
b
ε
3.6.2. DFA匹配(DFA模拟)
state = 0; ;c = nextChar();while (c != eof ) { state = move(state, c); c = nextChar();}if (state 在 接受状态集合F) return true;else return false;
NFA中ε-closure()函数
开始状态0:
ε-closure()函数:S = ε-closure(0) = {0,1,2,4, 7} ; 其中0是
自己; 1,7是直接的ε后继状态; 2和4是通过1的ε间接后继;
ε-closure(S) = sS ε-closure(s) = {0,1,2,4, 7}= S;
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
εε
ε
S0在字符a驱动下的后继状态集
开始状态的集合:S0 = ε-closure(0) = {0,1,2,4, 7};
状态集合S0在字符a的驱动下的后继状态集合T:
S'0,a = move(S0,a) = {3, 8};
S1,1 = ε-closure(S'0,a) = {3,6,1,2,4,7,8};
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
S0在字符b驱动下的后继状态集
开始状态的集合:S0 = {0,1,2,4, 7};
状态集合S0在字符b的驱动下的后继状态集合:
S'0,b = move(S0,b) = {5};
S1,2 = ε-closure(S'0,b) = {5,6,1,2,4,7};
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
DFA和NFA的匹配对比
state = 0; c = nextChar();while (c != eof ) { state = move(state, c); c = nextChar();}if (state接受状态集F!=) return true;else return false;
states = ε-closure(0) ;c = nextChar();while (c != eof ) { states=ε-closure(move(states,c); c = nextChar();}if (states接受状态集F!=) return true;else return false;
DFA匹配 NFA匹配
3.7 从正则表达式到NFA
• 正则表达式r= (a|b)*abb, 先构建其语法分析树:
a br1 r2
r3
|
( )r4 *
r5 r6
r7
a
r8
r9
b
r10
r11
b仅只有三种运算:
r= s|t
r= st
r= s* (s+)
正则表达式的NFA实例
对于正则表达式r= (a|b)*abb
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
εε
ε
3.7 简单正则表达式的NFA
对于表达式 ε,构建其NFA:
对于表达式(例如 (a|b)*abb) 中的单个字符a,构建其NFA:
fstart
iε
fstart
ia
并运算与NFA组合的对应
对于表达式r =s| t ,构建其NFA:
ftstart
it
t
startir
startis fs
s
ε fr
ε
ε
ε
ftit
t
startir
is fs
s
ε fr
ε
ε
ε
联接运算与NFA组合的对应
对于表达式r =s t ,构建其NFA:
ftstart
it
tstartis fs
s
εstart
startis ftfsit
ts
*运算与NFA组合的对应
对于表达式r =s*,构建其NFA:ε
startir
startis fs
sfr
ε
ε
ε
startir is fs
sfr
ε
ε
ε
ε
正则表达式的NFA构建例子
对于正则表达式r= (a|b)*abb
1ε 3
b
2a
4 5ε6
start
ε
ε
b4 5
2a 3
正则表达式的NFA构建例子
对于正则表达式r= (a|b)*abb
7ε
b
start1
ε 2 3a
4 5ε60 ε
ε
εε
ε
1ε 3
b
2a
4 5ε6
start
ε
ε
正则表达式的NFA构建例子
对于正则表达式r= (a|b)*abb
7 8ε a
b
start1
ε 2 3a
4 5ε60 ε
ε
εε
ε
7ε
b
start1
ε 2 3a
4 5ε60 ε
ε
εε
ε
7a 8
正则表达式的NFA构建例子
对于正则表达式r= (a|b)*abb
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0 ε
ε
εε
ε
随堂测试3
• 对正则表达式:((ε|a)b*)*, 画出其NFA
NFA中的开始状态和后继状态
开始状态的集合:S = ε-closure(0) = {0,1,2,4, 7}; 其中0是自
己; 1,7是直接的ε后继状态; 2和4是通过1的ε间接后继;
ε-closure(S) = sS ε-closure(s) = {0,1,2,4, 7};
状态集合S在字符a的驱动下的后继状态集合T:
T = ε-closure(move(S,a) )= ε-closure(sS move(s,a)) ={3, 8};
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
εε
ε
NFA中开始状态的ε闭包
开始状态的集合:S = ε-closure(0) = {0,1,2,4, 7}; 其中0是
自己; 1,7是直接的ε后继状态; 2和4是通过1的ε间接后继;
ε-closure(S) = sS ε-closure(s) = {0,1,2,4, 7};
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
NFA中状态集的后继状态集(a)
开始状态的集合:S0 = ε-closure(0) = {0,1,2,4, 7};
状态集合S0在字符a的驱动下的后继状态集合T:
T = ε-closure(move(S,a) )= ε-closure(sS move(s,a)) ={3, 8};
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
状态集{3,8}的ε闭包
开始状态的集合:S0 = {0,1,2,4, 7}的后继状态集合:
S1,1= S0→a = ε-closure( {3, 8} ) = {1,2,3,4,6,7,8};
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
S0的后继状态(b)
开始状态的集合:S0 = {0,1,2,4, 7};
S1,2= S0→b = ε-closure(move(S,b) )=ε-closure( {5} )
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
NFA中状态集{5}的ε闭包
S1,2= S0→b = ε-closure( {5} ) ={1,2,4,5,6,7};
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
NFA中的状态集S1,1的后继状态(a)
已知状态的集合:S1,1 = {1,2,3,4,6,7,8};S2,1= S1,1→a = = ε-closure(move(S1,1, a) )= ε-closure( {3, 8} )
= S1,1;
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
NFA中状态集S1,1的后继状态(b)
已知状态的集合:S1,1 = {1,2,3,4,6,7,8};S2,2= S1,1→b = = ε-closure(move(S1,1, a) )= ε-closure( {5, 9} )
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
NFA中状态集{5,9}的ε闭包
已知状态的集合:S1,1 = {1,2,3,4,6,7,8};S2,2= S1,1→b = = ε-closure(move(S1,1, a) )= ε-closure( {5, 9} )
= {1,2,4,5,6,7,9}
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
新状态!!!
求S1,2的后继状态集(a)
已知状态的集合:S1,2 = {1,2,4,5,6,7};S2,3= S1,2→a = = ε-closure(move(S1,2, a) )= ε-closure( {3, 8} )
= S1,1
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
求S1,2的后继状态(b)
已知状态的集合:S1,2 = {1,2,4,5,6,7};S2,4= S1,2→b = = ε-closure(move(S1,2, b) )= ε-closure( {5} )
= S1,2
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
求S2,2 的后继状态(a)
已知状态的集合:S2,2 = {1,2,4,5,6,7,9};S3,1= S2,2→a = ε-closure(move(S1,1, a) )= ε-closure( {3, 8} )
= S1,1
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
求S2,2 的后继状态(b)
已知状态的集合:S2,2 = {1,2,4,5,6,7,9};S3,2= S2,2→b = ε-closure(move(S1,1, a) )= ε-closure( {5, 10} )
= {1,2,4,5,6,7,10}
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
新状态!!!
求S3,2 的后继状态(a)
已知状态的集合:S3,2= {1,2,4,5,6,7,10};S4,1= S3,2→a = ε-closure(move(S1,1, a) )= ε-closure( {3, 8} )
= S1,1
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
求S3,2 的后继状态(b)
已知状态的集合:S3,2= {1,2,4,5,6,7,10};S4,2= S3,2→b = ε-closure(move(S1,1, a) )= ε-closure( {5} )
= S1,2
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
3.7.1 由NFA转化成DFA(子集构造法)
正则表达式r= (a|b)*abb,构建其NFA,然后转化为DFA:
startS0 S1,1
a
S1,2
b a
a
b
S2,2b S3,2
ba
a
b
随堂测试4
• 对正则表达式:((ε|a)b*)*, 在上一随堂测试的基础上
(画出了其NFA),将其NFA转化为DFA。
3.9.6 最小化DFA的状态数量
对于DFA中的状态,分为两个集合S和F:
startS T
a
Ub a
a
b
Zb
Qb
aa
b
3.9.6 最小化DFA的状态数量
对S集合中的任一元素s,如果move(s,a)或者move(s,b)不在S
中,则将其从S脱离出来;
startS T
a
Ub a
a
b
Zb
Qb
aa
b
3.9.6 最小化DFA的状态数量
对于表达式r =s t ,构建其NFA:
startS T
a
Ub a
a
b
Zb
Qb
aa
b
3.9.6 最小化DFA的状态数量
对于表达式r =s t ,构建其NFA:
startS T
a
Ub a
a
b
Zb
Qb
aa
b
3. 9.6 最小化DFA的状态数量
对于表达式r =s t ,构建其NFA:
startS T
a
Ub a
a
b
Zb
Qb
aa
b
3. 9.6 最小化DFA的状态数量
对于表达式r =s t ,构建其NFA:
startS T
a
Ub a
a
b
Zb
Qb
aa
b
3.9.6 最小化DFA的状态数量
Ub
b
b
startS T
aa
a
Zb
Qb
aa
startS T
a
b
a
Zb
Qb
aa
b
复习:空串ε在表达和衔接上很有用
对于表达式r =s| t ,构建其NFA:
ftstart
it
t
startir
startis fs
s
ε fr
ε
ε
ε
ftit
t
startir
is fs
s
ε fr
ε
ε
ε
复习:空串ε在表达和衔接上很有用
对于表达式r =s*,构建其NFA:ε
startir
startis fs
sfr
ε
ε
ε
startir is fs
sfr
ε
ε
ε
ε
复习:最小化DFA的状态数量
S1,2
b
b
b
startS0 T
aa
a
Zb
Qb
aa
startS T
a
b
a
Zb
Qb
aa
b
为什么S0和S1,2可以合并:它们等价
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
S0
S1,2
复习:软件构造方法学:用软件编软件——词法分析器生成工具Lex
词法单元的正则表达式 → 状态转换图 → 词法分析代码
工具Lex
表达 生成 生成
lex.l lex工具 C编译器 lex.yy.c a.out
a.out 源程序的字符流 词素流
3.9 从正则表达式到DFA
• 正则表达式r= (a|b)*abb
• 构建其语法分析树:
a b
|
*a
。
。
。
b
b
3.9 从正则表达式到DFA
• 正则表达式r= (a|b)*abb
• 构建其语法分析树。标识每一个叶结点。
实例:abbaabbbabbbaabb
a b
|
*a
。
#
。
。
。
b
b
1 2
3
4
5
6
只有一个实例:a,它的:
第一个字符来自位置1: firstPos = 1;
最后一个字符来自位置1: lastPos = 1;
3.9 正则表达式的第一个字符,和最后一个字符函数
a b
|
*a
。
#
。
。
。
b
b
1 2
3
4
5
6
看其实例的第一个字符,和最后一个字符。
ε不是其实例
3.9 正则表达式的firstPos, lastPos
a b
|
*a
。
#
。
。
。
b
b
1 2
3
4
5
6
1#叶的实例的第一个字符,和最后一个字符。
只有一个实例:a,它的:
firstPos = 1; lastPos = 1;
ε不是其实例,于是
{1} {1}nullable = false
nullable = false
3.9 正则表达式的firstPos, lastPos
a b
|
*a
。
#
。
。
。
b
b
1 2
3
4
5
6
2#叶的的第一个字符,和最后一个字符。
{1} {1}nullable = false
nullable = false{2} {2}
只有一个实例:b,它的:
firstPos = 2;
lastPos = 2;
3.9 正则表达式的firstPos, lastPos
a b
|
*a
。
#
。
。
。
b
b
a:firstPos = 1,lastPos = 1;b:firstPos = 2,lastPos = 2;
1 2
3
4
5
6
结点a|b的只有2个实例:
ε不是其实例
{1,2} {1,2}nullable = false
{1} {1} {2} {2}
3.9 正则表达式的firstPos, lastPos
a b
|
*a
。
#
。
。
。
b
b
a:firstPos = 1,lastPos = 1;b:firstPos = 2,lastPos = 2;ab:firstPos = 1,lastPos = 2;ba:firstPos = 2,lastPos = 1;
1 2
3
4
5
6
结点(a|b)*的实例:
ε是其实例
{1,2} {1,2}nullable = true
{1,2} {1,2}
{1} {1} {2} {2}
3.9 正则表达式的firstPos, lastPos
a b
|
*a
。
#
。
。
。
b
b
a:firstPos = 3,lastPos = 3;ba:firstPos = 2,lastPos = 3;aa:firstPos = 1,lastPos = 3;aba:firstPos = 1,lastPos = 3;
1 2
3
4
5
6
结点(a|b*a的实例:
ε不是其实例 {1,2,3} {3}nullable = false
{1,2} {1,2}
{1,2} {1,2}
{1} {1} {2} {2}
{3} {3}
3.9 正则表达式的firstPos, lastPos
a b
|
*a
。
#
。
。
。
b
b
abb#:firstPos = 3,lastPos = 6;aabb#:firstPos = 1,lastPos = 6;babb#:firstPos = 2,lastPos = 6;
1 2
3
4
5
6
结点(a|b*abb#实例:nullable =false
{1,2,3} {6}
3.9 正则表达式的firstPos, lastPos
• 对语法分析树中的每一结点n:
• nullable(n);
• firstPos(n);
• lastPos(n);
a b
|
*a
。
#
。
。
。
{1} {1} {2} {2}
{3} {3}
b{4} {4}
b{5} {5}
{6} {6}
{1,2} {1,2}
{1,2} {1,2}
{1,2,3} {3}
{1,2,3} {4}
{1,2,3} {5}
{1,2,3} {6}
3.9 正则表达式的firstPos, lastPos
语法分析树中,自底向上的三个函数求解方法:
结点n nullable(n) firstPos(n) lastPos(n)为ε的叶结点 true 序号为i的叶结点 false {i} {i}
| 运算结点:n = c1 | c2
nullable(c1)OR nullable(c2)
firstPos(c1) firstPos(c2) lastPos(c1) lastPos(c2)
cat运算结点:n = c1c2 nullable(c1)AND
nullable(c2)
if nullable(c1)firstPos(c1) firstPos(c2)elsefirstPos(c1)
if nullable(c2)lastPos(c1) lastPos(c2)elselastPos(c2)
*运算结点:n = c1*
true firstPos(c1) lastPos(c1)
对于叶结点i——定义函数followPos(i)
实例:aabb:第一个a的位置序号为1,紧接其后的标号a的位置序号为3;
ababb:第一个a的位置序号为1,紧接其后的标号b的位置序号为2;
aaabb:第一个a的位置序号为1;紧接其后的标号a的位置序号为1;
b
|
*
。
。
。
。
a1 2
a3
b4
b5
#6
followPos(1)={1,2,3}
所有可能接在其后
对于叶结点i——定义函数followPos(i)
实例:babb:第一个b的位置序号为2,紧接其后的标号a的位置序号为3;
baabb:第一个b的位置序号为2,紧接其后的标号a的位置序号为1;
abbabb:第一个b的位置序号为2;紧接其后的标号b的位置序号为2;
b
|
*
。
。
。
。
a1 2
a3
b4
b5
#6
followPos(2)={1,2,3}
所有可能接在其后
函数followPos(i)的求法
对于cat运算的结点n:其左子结点为c1, 右子结点为c2,ilastPos(c1): followPos(i) = { j | jfirstPos(c2)};
b
|
*
。
。
。
。
a1 2
a3
b4
b5
#
6
{1,2} {1,2} {3} {3}
{4} {4}
{5} {5}{1,2,3} {3}
{1,2,3} {4}
{1,2,3} {5}
{1,2,3} {6}
{6} #{6}
结点n followPos(n)
1 {3}
2 {3}
3 {4}
4 {5}
5 {6}
6
函数followPos(i)的求法
对于*运算的结点n:jlastPos(n): followPos(j) = { i | ifirstPos(n)};
b
|
*
。
。
。
。
a1 2
a3
b4
b5
#6
{1,2} {1,2} {3} {3}
{4} {4}
{5} {5}{1,2,3} {3}
{1,2,3} {4}
{1,2,3} {5}
{1,2,3} {6}
{6}
结点n followPos(n)
1 {1,2, 3}
2 {1,2, 3}
3 {4}
4 {5}
5 {6}
6
函数followPos(i)的求法
实例:a:没有followPosb:没有followPos
b
|
*
。
。
。
。
a1 2
a3
b4
b5
#6
{1,2} {1,2} {3} {3}
{4} {4}
{5} {5}{1,2,3} {3}
{1,2,3} {4}
{1,2,3} {5}
{1,2,3} {6}
{6}
对于|运算的结点n:不存在followPos判断的基础
S1,2 ={ j | iS0,char(i) =b, j followPos(i)}
从正则表达式直接构建DFA
S1,1 = { j | iS0,char(i) =a, j followPos(i)} = {1,2,3,4};
b
|*
。
。
。
。
a1 2
a3
b4
b5
#6
{1,2} {1,2} {3} {3}
{4} {4}
{5} {5}{1,2,3} {3}
{1,2,3} {4}
{1,2,3} {5}
{1,2,3} {6}
{6}
结点n followPos(n)
1 {1,2, 3}
2 {1,2, 3}
3 {4}
4 {5}
5 {6}
S0=firstPos(树根)={1,2,3};
= {1,2,3} = S0;
从正则表达式直接构建DFA
S2,1 = { j | iS1,1,char(i) =a, j followPos(i)}={1,2,3,4} =S1,1;
b
|
*
。
。
。
。
a1 2
a3
b4
b5
#6
{1,2} {1,2} {3} {3}
{4} {4}
{5} {5}{1,2,3} {3}
{1,2,3} {4}
{1,2,3} {5}
{1,2,3} {6}
{6}
结点n followPos(n)
1 {1,2, 3}
2 {1,2, 3}
3 {4}
4 {5}
5 {6}
S2,2 = { j | iS1,1,char(i) =b, j followPos(i)}
= {1,2,3,5}
从正则表达式直接构建DFA
S3,1 = { j | iS2,2,char(i) =a, j followPos(i)}={1,2,3,4} =S1,1;
b
|
*
。
。
。
。
a1 2
a3
b4
b5
#6
{1,2} {1,2} {3} {3}
{4} {4}
{5} {5}{1,2,3} {3}
{1,2,3} {4}
{1,2,3} {5}
{1,2,3} {6}
{6}
结点n followPos(n)
1 {1,2, 3}
2 {1,2, 3}
3 {4}
4 {5}
5 {6}
S3,2 = { j | iS2,2,char(i) =b, j followPos(i)}
= {1,2,3,6}
从正则表达式直接构建DFA
S4,1 = { j | iS3,2,char(i) =a, j followPos(i)}={1,2,3,4} =S1,1;
b
|
*
。
。
。
。
a1 2
a3
b4
b5
#6
{1,2} {1,2} {3} {3}
{4} {4}
{5} {5}{1,2,3} {3}
{1,2,3} {4}
{1,2,3} {5}
{1,2,3} {6}
{6}
结点n followPos(n)
1 {1,2, 3}
2 {1,2, 3}
3 {4}
4 {5}
5 {6}
S4,2 = { j | iS3,2,char(i) =b, j followPos(i)}
= {1,2,3}= S0
从正则表达式直接构建DFA的含义
startS0 S1,1a
b
a
S2,2b S3,2
ba
a
b
123
1234 1235 1236
1(a)2(b)3(a)
可以,可能,所有可能(意思是还没有发生,还在等待发生)
一旦发生,来了个a,那就变迁到下一个(可以,可能,所有可能)
穷举策略
从正则表达式直接构建DFA
startS0 S1,1a
b
a
S2,2b S3,2
ba
a
b
123
1234 1235 1236
正因为结点是表示“可以,可能,所有可能,还没有发生,还在等待”,所以S2,2(1235)并不表示是接受状态。而S3,2(1236)则表示5(b)已经发生了,自然是接受状态。
这就是为什么要在树中加一个“6(#)”的原因。
有穷状态机中状态的含义
① 表明它有个历史;即以前已经接受了哪些字符。但历
史可能不唯一。
② 表明它当前“可以,可能,所有可能,还没有发生,
还在等待”的字符。
③ 一旦接受了某个字符,就会转到下一个“可以,可能,
所有可能,还没有发生,还在等待”的状态。
小结1:词法分析
语言:既有类概念,也有实例概念
词法单元(类概念) 模式 词素(实例概念)
字符 字符串
语言的类概念:词法单元的集合;
语言的实例概念:词素的集合;
匹配
字符串的三种运算 正则表达式
小结2
正则表达式 DFA 语言的词法分析程序
NFA DFA 状态数的最小化
随堂测试5
• 对正则表达式:((ε|a)b*)*, 画出其DFA.
• 证明它与(a|b)*等价。
第二章作业
3.1.1,3.1.2
3.3.2, 3.3.5(1-3,9), 3.3.11, 3.3.12
3.4.1,3.4.2,成绩好的学生选做3.4.3
3.6.2, 3.6.3, 3.6.4
3.7.1, 3.7.3
3.9.1
3.9.2
3.9.4
第一次讨论课
第一次讨论课的主题: 词法分析。小主题:
1) 正则表达式转换NFA;
2) NFA确定化为DFA;
3) DFA最小化和词法分析器的状态图最小化;
4) DFA代码化;
5) TINY编译器与TM虚拟机;
6) 词法分析器自动工具(如:FLEX)
自己定题,也可以选择如下内容:
1) Unix/Linux中的grep指令嵌套使用的典型应用案例;
2) 练习3.3.5(4,5,6,8)中的任何一个;
3) 其它习题也可以;
NFA的化简——基于迁移来减少NFA的状态数
7 8b
10ε a
b
start1
ε 2 3a
4 5ε6 9
b0
ε
ε
ε
ε
ε
0 8b
10a
bstart a
9b
0
ε
ε
7start
b
a
正则表达式(0?0?1) *0?0?的NFA
S0={0,1,2,3,4,5} S0,1={3} ; ε-closure({3}) =S0
S0,0={1,2,4,5} ; ε-closure({1,2,4,5}) ={1,2,4,5} =S1
S1,1={3} = S0
S1,0={2,5} ; ε-closure({2,5}) ={2,5} =S2
S2,1={3} = S0
S2,0={} ;
start0 1
02
0 31 4
0 50
正则表达式(0?0?1) *0?0?的DFA
S0={0,1,2,3,4,5} S0,1={3} ; ε-closure({3}) =S0
S0,0={1,2,4,5} ; ε-closure({1,2,4,5}) ={1,2,4,5} =S1
S1,1={3} = S0
S1,0={2,5} ; ε-closure({2,5}) ={2,5} =S2
S2,1={3} = S0
S2,0={} ;
startS0 S1
0S2
0
1
11
谢 谢!谢 谢