1、DFA与NFADFA与NFA词法分析是编译的第一个时期,前面简介中也谈到过词法分析器的义务确实是:字符流-词法记号流那个地点词法分析和语法分析会交错进行,也确实是说,词法分析器可不能读取所有的词法记号再应用语法分析器来处理,平日情形下,每取一个词法记号,就送入语法分析器进行分析,图解:词法分析器是编译器中与源法度榜样直截了当接触的部分,是以词法分析器能够做诸如1). 去掉落注释,主动生成文档(c#中的/注释)2). 供给缺点地位(能够经由过程记录行号来供给),当字符流变成词法记号流今后,就没有了行的概念3). 完成预处理,比如宏定义1. 词法记号,词法单位(lexeme),模式模式是一种规矩每
2、个词法单位都有一个特定记号比如 int a=3,那个地点 int,a,3差不多上词法单位,每个词法单位都属于某个词法记号,比如3确实是num那个词法记号的一个词法单位,而模式规定了什么样的字符串的词法记号是什么样的(模式是一种规矩)某一特定模式规定了某个词法记号下的一类词法单位,比如:模式:用字母开首的包含字母和数字的串上面模式的词法记号:id(所有相符上面模式的字符串的记号差不多上id)词法单位:a123 或者 aabc 等词法记号举例(简称为记号):1) 每个的关键字都有属于本身的一个记号,比如关键字for,它能够应用记号for;关键字int,能够应用记号int2) 所有的关系运算符只有一
3、个记号,比如 =,=都用记号relation3) 所有的标识符只有一个记号,比如a123,aab应用记号id4) 所有的常数只有一个记号,比如123,22,32.3,23E10应用记号num5) 所有的字符串只有一个记号,比如123,ab1应用记号literal在实际的编译器设计中,词法记号,一样用一个整形数字表示词法记号的属性:我们爱好用那个二元组来描述一个词法单位,比如,关于源代码:position := initial + rate * 60关于词法单位 +,我们能够应用 来表示。有些情形,加倍复杂一点,比如关于 position,我们表示是如许的,具体来说应当是如许的,假定属性是一个字
4、符串,那么id将指向如许一个字符串position0,我们把存放那个字符串的处所叫做符号表。有些时刻,属性是不须要的,比如 := ,表示赋值,我们能够应用 如许的表示那个词法单位,只是那个显得有些多于,因为assign_op和词法单位是一对一的,也确实是assign_op只对应了:=,因此额外信息(属性)就显得余外的了词法缺点:词法分析器是专门难(有些缺点照样能够检测)检测缺点的,因为词法分析器的目标是产生词法记号流,它没有才能去分析法度榜样构造,是以无法检测到和法度榜样构造有关的缺点,比如:fi(a = b)词法分析器可不能找到那个缺点,它认为 fi 是一个标识符,而不是一个关键字,只有在后
5、面的时期中,那个缺点才会被发明,这是一个与法度榜样构造有关的缺点词法分析器,只能检测到词法单位上的问题,比如 12.ab ,作为一个词法单位,却不没有对应的模式,那么确实是产生一个缺点。2. 正规式:前面说过模式是一种规矩,为了应用,我们须要一种规范的方法来表达模式,这确实是正规式1) 串和说话字符类(又叫字母表):关于字符的有限集合串:字符类上字符的有穷序列,串那个概念,具体来说是,某个字符类上的串串的长度:串中字符的个数,比如串 s = abc ,那么串的长度为3,用|s|表示串的长度空串:用 表示说话:某字符类上的串的集合,属于说话的串,成为说话的句子或字比如:abc, a这确实是一个说
6、话,abc和a确实是句子。别的空集也是属于说话连接:x是串,y是串,x和y连接,成果确实是 xy 那个串。假如 x 是串,x3为 xxx。关于 xn (n=0),x0 = 说话的运算(假定L和M是说话):1. L U M = s|s属于L或者M,例如:L=1,2 M=3,4 那么 L U M = 1,2,3,42. LM = st|s属于L且t属于M,例如:L=a,b M=1,2 那么 LM = a1,a2,b1,b2 ML=1a,1b,2a,2b3. Ln = LLL.LLL (n个L),例如:L=a,b 那么 L3 = aaa,aab,aba,abb,baa,bab,bbb,bba留意 n
7、 可认为0,L0 = 4. L* = L0 U L1 U L2 U L3 U .L*表示,说话L中,所有的句子(串)以随便率性数量随便率性次序构成的句子的集合,包含 ,例如:a,b* = ,a,b,ab,ba,aab,aba,baa,bba,bab,abb,aaa,bbb.L*叫做L的闭包5. L+ = L1 U L2 U L3 U.L+表示,说话L中,所有的句子(串)以随便率性数量随便率性次序构成的句子的集合,然则不包含 L+中的句子和 L*中的句子比拟少一个 那么,我们经由过程上面的常识就能够表示一个标识符了,我们明白一样说话规定标识符是由字母开首,后接若干个字母或数字,我们能够如许来表示
8、: L=a-z A-Z N=0-9,那么标识符确实是 L(L U N)*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)(
9、a | b) 表示集合(说话,正规集) aa,ab,ba,bba* 表示由一切a字符构成的集合(说话,正规集),包含 (a | b) 表示由a,b构成的集合(说话,正规集),包含 等价的正规式:(a | b) = (b | a)正规式的代数性质:1. r|s = s|r2. r|(s|t) = (r|s)|t3. (rs)t = r(st)4. r(s|t) = rs|rt5. r = r6. r* = r*7. r* = (r|)*留意,rs != sr 因为连接运确实是有次序的,记住并明白得2个最全然的运算:a|b表示a,b,ab表示ab3. 正规定义我们能够应用 名字 - 正规式这种表示
10、,来说明一个等价的代替,比如:dight - 0|1|2|3|4|5|6|7|8|9那个地点,我们就能够应用名字 digit 来代替后面的正规表达式我们能够对某个串集进行正规定义,比如我们对标识符集合进行正规定义:letter - A|B|.|Z|a|b|.|zdight - 0|1|2|3|4|5|6|7|8|9id - letter(letter|dight)*请经由过程上面的例子明白得正规定义。在我们表达正规表达式的时刻,能够应用一些符号使得表达简化1) + ,表示一个或者多个实力,比如,a+ 表示 a,aa,aaa,aaaa,.。差别一下*,他们的关系是那个地点 r+ = r* | 2
11、) 字符组,abc表示a|b|c,还能够如许表示a-zA-Z表示字母表中的字符4. 状况转换图状况转换图是对词法分析器进行分析过程的描述,我们看一个确信关系运算的状况转化图:1) 图中圆圈表示状况2) 箭头叫做边。X状况的边,一样指的是由X状况动身,指向其他状况的边3) 边上的符号叫做标记若何来应用那个图?假定输入字符串是 = ,那么辨认开端时,发明 和状况0与状况1间的边上的标记一样,那么就进入1状况,下一个输入字符为=,将进入2状况,辨认停止,返回二元组上图中2,3,4,5,7,8状况,他们表示辨认了一个关系运算符,那个状况叫做接收状况状况4上面有一个*,表示说,输入指针须要回移。所谓的输
12、入指针,确实是指向输入字符串中现在被读入的字符的地位,4状况会多读取一个字符,因此须要回移,也确实是要留意的是,辨认完成之后,输入指针指向的是被辨认对象的最后一个字符,而不是待辨认对象的第一个字符,如许的规定在实现词法分析器时,是有必定的意义,举例说明:输入字符串为: ab识其余时刻,从开端,读入下一个字符b时,进入4状况,那个时刻,输入指针指向b,这时刻须要回移我们在须要回移的状况上加一个*每个状况后面有一个return(relop,XX)那个是状况的行动,那个地点具体来说确实是返回一个二元组的行动,词法分析器分析的成果确实是获得二元组(词法记号和属性的二元组),那个二元组能够表示一个特定的
13、字符串。事实上上面的*,也是表示行动,也确实是输入指针回移的行动,我们能够看见,只有在接收状况才会有行动显现对一门典范的说话来说状况可能有几百个5. 若何编写一个词法分析器1) 依照须要写出正规定义2) 依照正规定义画出转换图3) 依照转换图写出词法分析器那个地点具体评论辩论面向过程的说话来实现一个词法分析器(比如c说话),同时重要评论辩论的是第3步1) 我们须要一个 nextchar() 函数,取得缓存中下一个等待分析的字符,那个函数完成年2个义务1. 让输入指针向前移动一位 2. 返回输入指针指向的字符 2) 定义一个变量 token_beginning,在每个状况转换图开端的时刻,记录输
14、入指针的地位,定义forward变量作为输入指针3) 状况转换图被实现成为代码之后,每个状况都有属于本身的一块代码,这些代码按次序完成以下工作:1. 读取一个字符,经由过程nextchar()函数 2. 读取的字符(标记),假如它和当前状况的边上的标记雷同,那么状况将转换到边所指向的状况,具体实现只须要一个语句确实是 state = xxx(xxx为目标状况);假如当前状况的所有边的标记和那个读取字符不一样,那么表示没有找到token(词法记号),这时刻须要调用 fail() 函数 3. fail() 函数完成如许的功能:a.指针回移,完成 forward token_beginning 的操
15、作 b.找到恰当的开端状况(也确实是查找别的一个转换图的开端状况)。假定所有的转换图都被测验测验过,同时无法匹配,这时刻会调用一个发明缺点的小法度榜样,来申报缺点 4. 请不要随便添加行动到各个状况所持有的代码中,应当以转换图中表示的行动为准 4) 定义一个全局变量 lexical_value,用于储存一个指针,那个指针由 install_id() 和 install_num() 两个函数中的一个返回5) 定义两个整形变量 start,state,分别表示一个转换图的开端状况和当前的状况6) nexttoken(),这是词法分析器的主法度榜样,能够说,我们经由过程调用nexttoken()就完
16、成了词法分析,那个函数必定是如许的格局:while(1) switch(state) case xx: . case yy: . default: . 关于具体的设计那个地点就不说了,举例说明一个转换图若何转换成为法度榜样:这是一个辨认浮点数的例子,看下面的代码:#include #include #include char *nexttoken();char nextchar();void next();void back();char* gettoken();char cbuf=12.3*klj12.2e2jj778;int forward = -1;int main() while(1)
17、 printf(%sn,nexttoken(); if(forward = strlen(cbuf)-1) getchar(); return 0; int state;int start;char* nexttoken() char c; state = 12; while(1) switch(state) case 12: c = nextchar(); start = forward; if(isdigit(c) state = 13; else next(); break; case 13: c = nextchar(); if(isdigit(c) state = 13; else
18、if(c = e|c = E) state = 16; else if(c = .) state = 14; else state = 19; break; case 14: c = nextchar(); if(isdigit(c) state = 15; break; case 15: c = nextchar(); if(isdigit(c) state = 15; else if(c = e| c = E) state = 16; else state = 19; break; case 16: c = nextchar(); if(isdigit(c) state = 18; els
19、e if(c = + | c = -) state = 17; break; case 17: c = nextchar(); if(isdigit(c) state = 18; break; case 18: c = nextchar(); if(isdigit(c) state = 18; else state = 19; break; case 19: back(); return gettoken(); char nextchar() forward +; return cbufforward;void back() forward -;void next() forward +;ch
20、ar token_buf128;char* gettoken() int i,j=0; for(i = start; i P(S) 4. 一个开端状况s0,是一个独一的状况 5. 一个停止(接收)状况集合F 留意,P(S),表示S的幂集。在NFA中,input symbol可认为 转换函数(transition function)的含义确实是,一个确信的状况差不多从那个状况动身的一条边的标签(符号symbol),能够确信它的下一个状况构成的集合,比如上图(那个转换图确实是NFA的一种表示方法),0状况,a符号,确信了一个状况的集合0,13. 转换图(transition graph)的表示我们
21、明白,运算机是无法直截了当表示一个图,我们应当若何来表示一个转换图?应用表格确实是一个最简单的方法,每行表示一个状况,每列表示一个input symbol,这种表格被叫做 transtion table(转换表)能够说应用表格是最简单的表示方法,然则我们能够留意到在那个图中状况1和input symbol a,是没有下一个状况的(空集合),也确实是,关于一个大年夜的状况图,我们可能花费大年夜量的空间,而个中空聚聚会会议消费许多空间,然则这种消费又不是必须的,因此,作为最简单的一种实现方法,却不是最优的说话(language)被NFA定义成为一个input string的集合,而那个集合中的元素
22、则是被NFA受接收的所有的字符串(那些能够从开端状况到某接收状况的input string)至于储备的方法,能够尝尝邻接表。留意,应用什么样的数据构造来储存NFA按情形不合而不合,在一些专门情形下,某些数据构造会变得专门便利应用,而换入其他情形,则弗成以应用了。词法分析(3)-DFA1. DFA(Deterministic Finite automaton)DFA确实是确信的有限自念头,因为DFA和NFA关系紧密,我们经常须要把他们拿到一路来讲,NFA能够转化成为一个DFA,DFA依旧是一个数学model,它和NFA有以下差别1. 不存在-transition,也确实是说,不存在为input
23、symbol的边 2. 关于move函数,move : (state, symbol) - S,具体来说确实是,一个状况和一个特定的input symbol,可不能映射到2个不合的状况。如许的成果是,每个状况,关于每个特定的input symbol,只有一条出边下图确实是一个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 y
24、eselse return no词法分析(4)-NFA与DFA的转化1. 子集构造(Subset Construction)这是一个转换NFA到DFA的算法。我们明白NFA和DFA的差别最重要的确实是一个状况和一个input symbol是否能够或许确信一个状况的问题,关于NFA,它将确信一个组状况,而DFA将确信一个状况,是以,我们有一个专门好的方法确实是把NFA的状况集对应每个DFA的状况,这确实是subset construction的思惟,只是这只是大年夜概泛泛而论,我们须要加倍明白的熟悉1) NFA在任何一个input symbol下,映射的状况集(经由过程move函数,那个集合平日
25、用T字母表示)应当被明白2) 必须包管1)中状况集都对应了DFA中的一个状况具体算法:Input : 一个NFA NOutput : 接收雷同说话的DFA DMethod : 为D构架一个transition table(转换表) Dtran,每个DFA的状况是一个NFA的状况集合(那个地点必定要留意前面说过的1)2)两点)。我们定义一些操作:s 表示NFA的状况,T 表示NFA的状况集合,a表示一个input symbol-transition(转换)确实是说input symbol为时的transition(转换) 操作(operation)描述(description)-closure(s)从NFA的状况s动身,只经由过程-transition达到的N
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1