DFA与NFA.docx
《DFA与NFA.docx》由会员分享,可在线阅读,更多相关《DFA与NFA.docx(25页珍藏版)》请在冰豆网上搜索。
![DFA与NFA.docx](https://file1.bdocx.com/fileroot1/2023-2/3/8275be46-89db-480f-bc0b-1236b317422b/8275be46-89db-480f-bc0b-1236b317422b1.gif)
DFA与NFA
DFA与NFA
词法分析是编译的第一个时期,前面简介中也谈到过词法分析器的义务确实是:
字符流------>词法记号流
那个地点词法分析和语法分析会交错进行,也确实是说,词法分析器可不能读取所有的词法记号再应用语法分析器来处理,平日情形下,每取一个词法记号,就送入语法分析器进行分析,图解:
词法分析器是编译器中与源法度榜样直截了当接触的部分,是以词法分析器能够做诸如
1).去掉落注释,主动生成文档(c#中的///注释)
2).供给缺点地位(能够经由过程记录行号来供给),当字符流变成词法记号流今后,就没有了行的概念
3).完成预处理,比如宏定义
1.词法记号,词法单位(lexeme),模式
模式是一种规矩
每个词法单位都有一个特定记号
比如inta=3,那个地点int,a,=,3差不多上词法单位,每个词法单位都属于某个词法记号,比如3确实是"num"那个词法记号的一个词法单位,而模式规定了什么样的字符串的词法记号是什么样的(模式是一种规矩)
某一特定模式规定了某个词法记号下的一类词法单位,比如:
模式:
用字母开首的包含字母和数字的串
上面模式的词法记号:
id(所有相符上面模式的字符串的记号差不多上id)
词法单位:
a123或者aabc等
词法记号举例(简称为记号):
1)每个的关键字都有属于本身的一个记号,比如关键字for,它能够应用记号for;关键字int,能够应用记号int
2)所有的关系运算符只有一个记号,比如>=,<=都用记号relation
3)所有的标识符只有一个记号,比如a123,aab应用记号id
4)所有的常数只有一个记号,比如123,22,32.3,23E10应用记号num
5)所有的字符串只有一个记号,比如"123","ab1"应用记号literal
在实际的编译器设计中,词法记号,一样用一个整形数字表示
词法记号的属性:
我们爱好用<词法记号,属性>那个二元组来描述一个词法单位,比如,关于源代码:
position:
=initial+rate*60
关于词法单位+,我们能够应用来表示。
有些情形,加倍复杂一点,比如关于position,我们表示是如许的,,具体来说应当是如许的,假定属性是一个字符串,那么id将指向如许一个字符串"position\0",我们把存放那个字符串的处所叫做符号表。
有些时刻,属性是不须要的,比如:
=,表示赋值,我们能够应用如许的表示那个词法单位,只是那个显得有些多于,因为assign_op和词法单位是一对一的,也确实是assign_op只对应了:
=,因此额外信息(属性)就显得余外的了
词法缺点:
词法分析器是专门难(有些缺点照样能够检测)检测缺点的,因为词法分析器的目标是产生词法记号流,它没有才能去分析法度榜样构造,是以无法检测到和法度榜样构造有关的缺点,比如:
fi(a==b)
词法分析器可不能找到那个缺点,它认为fi是一个标识符,而不是一个关键字,只有在后面的时期中,那个缺点才会被发明,这是一个与法度榜样构造有关的缺点
词法分析器,只能检测到词法单位上的问题,比如12.ab,作为一个词法单位,却不没有对应的模式,那么确实是产生一个缺点。
2.正规式:
前面说过模式是一种规矩,为了应用,我们须要一种规范的方法来表达模式,这确实是正规式
1)串和说话
字符类(又叫字母表):
关于字符的有限集合
串:
字符类上字符的有穷序列,串那个概念,具体来说是,某个字符类上的串
串的长度:
串中字符的个数,比如串s=abc,那么串的长度为3,用|s|表示串的长度
空串:
用ε表示
说话:
某字符类上的串的集合,属于说话的串,成为说话的句子或字
比如:
{abc,a}这确实是一个说话,abc和a确实是句子。
别的空集也是属于说话
连接:
x是串,y是串,x和y连接,成果确实是xy那个串。
假如x是串,x^3为xxx。
关于x^n(n>=0),x^0=ε
说话的运算(假定L和M是说话):
1.LUM={s|s属于L或者M},例如:
L={1,2}M={3,4}那么LUM={1,2,3,4}
2.LM={st|s属于L且t属于M},例如:
L={a,b}M={1,2}那么LM={a1,a2,b1,b2} ML={1a,1b,2a,2b}
3.L^n=LLL...LLL(n个L),例如:
L={a,b}那么L^3={aaa,aab,aba,abb,baa,bab,bbb,bba}
留意n可认为0,L^0={ε}
4.L*=L^0UL^1UL^2UL^3U...
L*表示,说话L中,所有的句子(串)以随便率性数量随便率性次序构成的句子的集合,包含ε,例如:
{a,b}*={ε,a,b,ab,ba,aab,aba,baa,bba,bab,abb,aaa,bbb...}
L*叫做L的闭包
5.L+=L^1UL^2UL^3U...
L+表示,说话L中,所有的句子(串)以随便率性数量随便率性次序构成的句子的集合,然则不包含ε
L+中的句子和L*中的句子比拟少一个ε
那么,我们经由过程上面的常识就能够表示一个标识符了,我们明白一样说话规定标识符是由字母开首,后接若干个字母或数字,我们能够如许来表示:
L={a-zA-Z}N={0-9},那么标识符确实是L(LUN)*
2)正规式
正规式又叫正规表达式,正规式是模式得一种规范的表达情势,正规式描述了一个集合,那个集合是由串构成的,事实上那个集合确实是我们前面说过的说话,只是那个地点大年夜家爱好应用正规集那个术语。
正规式r表示正规集L(r)
正规式的运算:
1.闭包运算,运算优先级最高,(r)*表示(L(r))*
2.连接运算,运算优先集合低于闭包,(r)(s)表示(L(r))(L(s))
3.或运算,运算优先集合最低,(r)|(s)表示(L(r))U(L(s))
例如:
a|b表示集合(说话,正规集){a,b}
(a|b)(a|b)表示集合(说话,正规集){aa,ab,ba,bb}
a*表示由一切a字符构成的集合(说话,正规集),包含ε
(a|b)表示由a,b构成的集合(说话,正规集),包含ε
等价的正规式:
(a|b)=(b|a)
正规式的代数性质:
1.r|s=s|r
2.r|(s|t)=(r|s)|t
3.(rs)t=r(st)
4.r(s|t)=rs|rt
5.εr=r
6.r**=r*
7.r*=(r|ε)*
留意,rs!
=sr因为连接运确实是有次序的,记住并明白得2个最全然的运算:
a|b表示{a,b},ab表示{ab}
3.正规定义
我们能够应用名字->正规式这种表示,来说明一个等价的代替,比如:
dight->0|1|2|3|4|5|6|7|8|9
那个地点,我们就能够应用名字digit来代替后面的正规表达式
我们能够对某个串集进行正规定义,比如我们对标识符集合进行正规定义:
letter->A|B|...|Z|a|b|...|z
dight->0|1|2|3|4|5|6|7|8|9
id->letter(letter|dight)*
请经由过程上面的例子明白得正规定义。
在我们表达正规表达式的时刻,能够应用一些符号使得表达简化
1)+,表示一个或者多个实力,比如,a+表示{a,aa,aaa,aaaa,...}。
差别一下*,他们的关系是那个地点r+=r*|ε
2)字符组,[abc]表示a|b|c,还能够如许表示[a-zA-Z]表示字母表中的字符
4.状况转换图
状况转换图是对词法分析器进行分析过程的描述,我们看一个确信关系运算的状况转化图:
1)图中圆圈表示状况
2)箭头叫做边。
X状况的边,一样指的是由X状况动身,指向其他状况的边
3)边上的符号叫做标记
若何来应用那个图?
假定输入字符串是<=,那么辨认开端时,发明<和状况0与状况1间的边上的标记一样,那么就进入1状况,下一个输入字符为=,将进入2状况,辨认停止,返回二元组
上图中2,3,4,5,7,8状况,他们表示辨认了一个关系运算符,那个状况叫做接收状况
状况4上面有一个*,表示说,输入指针须要回移。
所谓的输入指针,确实是指向输入字符串中现在被读入的字符的地位,4状况会多读取一个字符,因此须要回移,也确实是要留意的是,辨认完成之后,输入指针指向的是被辨认对象的最后一个字符,而不是待辨认对象的第一个字符,如许的规定在实现词法分析器时,是有必定的意义,举例说明:
输入字符串为:
a>b
识其余时刻,从>开端,读入下一个字符b时,进入4状况,那个时刻,输入指针指向b,这时刻须要回移
我们在须要回移的状况上加一个*
每个状况后面有一个return(relop,XX)那个是状况的行动,那个地点具体来说确实是返回一个二元组的行动,词法分析器分析的成果确实是获得二元组(词法记号和属性的二元组),那个二元组能够表示一个特定的字符串。
事实上上面的*,也是表示行动,也确实是输入指针回移的行动,我们能够看见,只有在接收状况才会有行动显现
对一门典范的说话来说状况可能有几百个
5.若何编写一个词法分析器
1)依照须要写出正规定义
2)依照正规定义画出转换图
3)依照转换图写出词法分析器
那个地点具体评论辩论面向过程的说话来实现一个词法分析器(比如c说话),同时重要评论辩论的是第3步
1)我们须要一个nextchar()函数,取得缓存中下一个等待分析的字符,那个函数完成年2个义务
1. 让输入指针向前移动一位
2. 返回输入指针指向的字符
2)定义一个变量token_beginning,在每个状况转换图开端的时刻,记录输入指针的地位,定义forward变量作为输入指针
3)状况转换图被实现成为代码之后,每个状况都有属于本身的一块代码,这些代码按次序完成以下工作:
1. 读取一个字符,经由过程nextchar()函数
2. 读取的字符(标记),假如它和当前状况的边上的标记雷同,那么状况将转换到边所指向的状况,具体实现只须要一个语句确实是state=xxx(xxx为目标状况);假如当前状况的所有边的标记和那个读取字符不一样,那么表示没有找到token(词法记号),这时刻须要调用fail()函数
3. fail()函数完成如许的功能:
a.指针回移,完成forward=token_beginning的操作b.找到恰当的开端状况(也确实是查找别的一个转换图的开端状况)。
假定所有的转换图都被测验测验过,同时无法匹配,这时刻会调用一个发明缺点的小法度榜样,来申报缺点
4. 请不要随便添加行动到各个状况所持有的代码中,应当以转换图中表示的行动为准
4)定义一个全局变量lexical_value,用于储存一个指针,那个指针由install_id()和install_num()两个函数中的一个返回
5)定义两个整形变量start,state,分别表示一个转换图的开端状况和当前的状况
6)nexttoken(),这是词法分析器的主法度榜样,能够说,我们经由过程调用nexttoken()就完成了词法分析,那个函数必定是如许的格局:
while
(1){
switch(state){
casexx:
...
caseyy:
...
default:
...
}
}
关于具体的设计那个地点就不说了,举例说明一个转换图若何转换成为法度榜样:
这是一个辨认浮点数的例子,看下面的代码:
#include
#include
#include
char*nexttoken();
charnextchar();
voidnext();
voidback();
char*gettoken();
charcbuf[]="12.3*********klj12.2e2jj778";
intforward=-1;
intmain(){
while
(1){
printf("%s\n",nexttoken());
if(forward>=strlen(cbuf)-1){
getchar();
return0;
}
}
}
intstate;
intstart;
char*nexttoken(){
charc;
state=12;
while
(1){
switch(state){
case12:
c=nextchar();
start=forward;
if(isdigit(c)){
state=13;
}else{
next();
}
break;
case13:
c=nextchar();
if(isdigit(c))
state=13;
elseif(c=='e'||c=='E')
state=16;
elseif(c=='.')
state=14;
else
state=19;
break;
case14:
c=nextchar();
if(isdigit(c))
state=15;
break;
case15:
c=nextchar();
if(isdigit(c))
state=15;
elseif(c=='e'||c=='E')
state=16;
else
state=19;
break;
case16:
c=nextchar();
if(isdigit(c))
state=18;
elseif(c=='+'||c=='-')
state=17;
break;
case17:
c=nextchar();
if(isdigit(c))
state=18;
break;
case18:
c=nextchar();
if(isdigit(c))
state=18;
else
state=19;
break;
case19:
back();
returngettoken();
}
}
}
charnextchar(){
forward++;
returncbuf[forward];
}
voidback(){
forward--;
}
voidnext(){
forward++;
}
chartoken_buf[128];
char*gettoken(){
inti,j=0;
for(i=start;i<=forward;i++){
token_buf[j++]=cbuf[i];
}
token_buf[j]='\0';
returntoken_buf;
}
词法分析
(2)---NFA
假定一个输入符号(symbol),能够获得2个或者2个以上的可能状况,那么那个finiteautomaton确实是不确信的,反之确实是确信的。
例如:
这确实是一个不确信的无穷自念头,在symbola输入的时刻,无法确信状况应当转向0,照样1
不论是确信的finiteautomaton照样非确信的finiteautomaton,它们都能够精确的描述正规集(regularsets)
我们能够专门便利的把正规表达式(regularexpressions)转换成为不确信finiteautomaton
2.NFA(NondeterministicFiniteAutomaton)
非确信的无穷自念头,我们用NFA那个术语表示,它是一个数学模型(model):
1. 一个关于状况的集合S
2. 一个关于输入符号(inputsymbols)的集合Σ
3. 函数move:
(状况,符号)->P(S)
4. 一个开端状况s0,是一个独一的状况
5. 一个停止(接收)状况集合F
留意,P(S),表示S的幂集。
在NFA中,inputsymbol可认为ε
转换函数(transitionfunction)的含义确实是,一个确信的状况差不多从那个状况动身的一条边的标签(符号symbol),能够确信它的下一个状况构成的集合,比如上图(那个转换图确实是NFA的一种表示方法),0状况,a符号,确信了一个状况的集合{0,1}
3.转换图(transitiongraph)的表示
我们明白,运算机是无法直截了当表示一个图,我们应当若何来表示一个转换图?
应用表格确实是一个最简单的方法,每行表示一个状况,每列表示一个inputsymbol,这种表格被叫做transtiontable(转换表)
能够说应用表格是最简单的表示方法,然则我们能够留意到在那个图中状况1和inputsymbola,是没有下一个状况的(空集合),也确实是,关于一个大年夜的状况图,我们可能花费大年夜量的空间,而个中空聚聚会会议消费许多空间,然则这种消费又不是必须的,因此,作为最简单的一种实现方法,却不是最优的
说话(language)被NFA定义成为一个inputstring的集合,而那个集合中的元素则是被NFA受接收的所有的字符串(那些能够从开端状况到某接收状况的inputstring)
至于储备的方法,能够尝尝邻接表。
留意,应用什么样的数据构造来储存NFA按情形不合而不合,在一些专门情形下,某些数据构造会变得专门便利应用,而换入其他情形,则弗成以应用了。
词法分析(3)---DFA
1.DFA(DeterministicFiniteautomaton)
DFA确实是确信的有限自念头,因为DFA和NFA关系紧密,我们经常须要把他们拿到一路来讲,NFA能够转化成为一个DFA,DFA依旧是一个数学model,它和NFA有以下差别
1. 不存在ε-transition,也确实是说,不存在ε为inputsymbol的边
2. 关于move函数,move:
(state,symbol)->S,具体来说确实是,一个状况和一个特定的inputsymbol,可不能映射到2个不合的状况。
如许的成果是,每个状况,关于每个特定的inputsymbol,只有一条出边
下图确实是一个DFA:
接收说话(a|b)*ab,留意一下,接收说话(a|b)*ab的DFA我们前面见过,确实是这张图:
2.DFA的行动
我们用一个算法来仿照DFA的行动
s=s0;
c=nextchar();
while(c!
=EOF){
s=move(s,c);
c=nextchar();
}
if(s属于F)
return"yes"
else
return"no"
词法分析(4)---NFA与DFA的转化
1.子集构造(SubsetConstruction)
这是一个转换NFA到DFA的算法。
我们明白NFA和DFA的差别最重要的确实是一个状况和一个inputsymbol是否能够或许确信一个状况的问题,关于NFA,它将确信一个组状况,而DFA将确信一个状况,是以,我们有一个专门好的方法确实是把NFA的状况集对应每个DFA的状况,这确实是subsetconstruction的思惟,只是这只是大年夜概泛泛而论,我们须要加倍明白的熟悉
1)NFA在任何一个inputsymbol下,映射的状况集(经由过程move函数,那个集合平日用T字母表示)应当被明白
2)必须包管1)中状况集都对应了DFA中的一个状况
具体算法:
Input:
一个NFAN
Output:
接收雷同说话的DFAD
Method:
为D构架一个transitiontable(转换表)Dtran,每个DFA的状况是一个NFA的状况集合(那个地点必定要留意前面说过的1)2)两点)。
我们定义一些操作:
s表示NFA的状况,T表示NFA的状况集合,a表示一个inputsymbol
ε-transition(ε转换)确实是说inputsymbol为ε时的transition(转换)
操作(operation)
描述(description)
ε-closure(s)
从NFA的状况s动身,只经由过程ε-transition达到的N