词法分析总结

本文深入探讨词法分析的基本任务与过程,解析状态转换图、正规表达式及有限自动机在词法分析中的应用,揭示正规集、正规式与自动机之间的联系,以及它们在识别单词符号中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先要了解词法分析的任务。词法分析的任务:从左至右逐个字符的对源程序进行扫描,产生一个个的单词符号,把作为字符串的源程序改造成为由单词符号串组成的程序。那么以词法分析为功能,词法分析器是将一个字母作为分析目标,根据该字母的下一个字母来判断是否应该判定该字母所属类型。它的输入是源程序,输出是单词符号。

 

如何识别单词符号呢?根据读来的第一个字符的种类分别转到各种子程序处理。这些子程序功能就是识别以相应字符开头的各种单词。分析的过程是这样的:通过判断下一个语句字符是否已经读过来,如果已经读过来,判断是字母、数字或者其他的,进入不同的子程序;如果没有读进来这个字符,就要读下一个字符。

什么是状态转换图?状态转换图是一张有限方向图,它的功能是识别或者接受一定的符号串或者说单词。有三个部分:结点、箭弧、箭弧上的标记。一个完整的状态转换图是有n个状态的,其中有一个状态,至少有一个终态。

关于正规表达式和有限自动机。把具有相同特征的字放在一起组成一个集合,即所谓的正规集
然后使用一种形式化的方法来表示正规集,即所谓的正规式。正规式是描述单词结构的一种形式;

正规集是该类单词的全集。

对于符号集合∑={a,b},有:
- 正则表达式a表示语言{a};
- 正则表达式a|b表示语言{a,b};
- 正则表达式(a|b)(a|b)表示语言{aa,ab,ba,bb};
- 正则表达式a*表示语言{ε,a,aa,aaa,…};
- 正则表达式(a|b)*表示语言{ε,a,b,aa,ab,ba,bb,aaa,…};

- 正则表达式a|a*b表示语言{a,b,ab,aab,aaab,…}。

什么时候两个正规式相等?若两个正规式U和V所表示的正规集相同,则认为二者等价,记为:U = V。证明:b(ab)* = (ba)*b

证明:因为,L(b(ab)*)=L(b)L((ab)*)
    ={b} · {ε,ab, abab, …}
    ={b, bab, babab, …}
又因为, L((ba)*b)=L((ba)*) L(b)
={ε, ba, baba, …} · {b}
={b, bab, babab, …}
因此, L(b(ab)*)= L((ba)*b)
所以, b(ab)*=(ba)*b
正规式的性质:设U,V,W是上的∑正规式,则
(1) U | V = V | U    或的交换律
(2) U | ( V|W ) = ( U|V ) | W    或的结合律
(3) U ( VW ) = ( UV ) W    连接积的结合律
(4) U ( V | W ) = ( UV ) | ( UW )    分配律
     ( V | W ) U = VU | WU

(5) εU = Uε = U

把状态转换图再形式化一下就是有限自动机(确定的有限自动机,非确定的有限自动机)。需要注意的是,所谓的自动机不是指一台实际的机器,而是一种数学模型(集合,函数,序列…),利用它模拟计算机识别的功能。所谓确定性是指,f(s, a) = s’ 是单值函数。 对任何状态s∈S,和输入符号 a∈∑ , f(s, a) 唯一的确定下一个状态。所谓有限性是指,S是一个有限的状态集合,并且∑是一个有限的输入符号的字母表。用上述5条,来定义一个DFA,来完成识别一个序列是否被机器所接受。

eg. 设DFA M = ({0,1,2,3},{a, b}, f, 0, {3}),其中

f: f(0, a) = 1,    f(0, b) = 2 。  f(1, a) = 3,    f(1, b) = 2 。 f(2, a) = 1,    f(2, b) = 3 。   f(3, a) = 3,    f(3, b) = 3

若M的初态结点同时又是终态节点,则空字可被M识别。DFA M所能识别的字的全体记为L(M)。如果一个DFA M的输入字母表为∑,则我们称M是∑上的一个DFA。若V是∑上的一个正规集,当且仅当存在一个∑上的DFA M,使得V = L(M)。

对任何两个有限的自动机M1和M2,若有L(M1)=L(M2),则称M1与M2等价。 对于任何∑上NFA M都可构造一个∑上的正规式V,使得  L(V) = L(M) 。

使用替换规则逐步消去M’的所有结点。替换规则如下:


还有关于闭包的概念。假定I是M’的状态集的子集,a ∈ ∑,定义Ia =ε_CLOSURE(J)。其中,J是所有那些可从I中的某一状态结点出发经过一条a弧而到达的状态结点的全体。

关于确定有限自动机的化简(最少化)。寻找一个状态比DFA M少的DFA M’,使得    L(M’) = L(M)。两个状态等价:设s和t是M两个不同的状态,从s出发能读出某个字而停于终态,那么同样,从t出发也能读出同一个字而停在终态,反之亦可。DFA化简算法:把M的状态集分割为一些不相交的子集,使得任何不同的两个子集状态都是可区别的,而同一个子集中的任何状态都是等价的,最后让每个子集选一个代表,同时消去其他等价状态。化简步骤:(1) 检查状态转换函数是否为全函数。(2) 用化简算法进行化简。(3) 去掉死状态。
 

设计思想 (1)程序主体结构部分: 说明部分 %% 规则部分 %% 辅助程序部分 (2)主体结构的说明 在这里说明部分告诉我们使用的LETTER,DIGIT, IDENT(标识符,通常定义为字母开头的字母数字串)和STR(字符串常量,通常定义为双引号括起来的一串字符)是什么意思.这部分也可以包含一些初始化代码.例如用#include来使用标准的头文件和前向说明(forward ,references).这些代码应该再标记"%{"和"%}"之间;规则部分>可以包括任何你想用来分析的代码;我们这里包括了忽略所有注释中字符的功能,传送ID名称和字符串常量内容到主调函数和main函数的功能. (3)实现原理 程序中先判断这个句语句中每个单元为关键字、常数、运算符、界符,对与不同的单词符号给出不同编码形式的编码,用以区分之。 PL/0语言的EBNF表示 <常量定义>::=<标识符>=<无符号整数>; <标识符>::=<字母>={<字母>|<数字>}; <加法运算符>::=+|- <乘法运算符>::=*|/ <关系运算符>::==|#|<|<=|>|>= <字母>::=a|b|…|X|Y|Z <数字>::=0|1|2|…|8|9 三:设计过程 1. 关键字:void,main,if,then,break,int,Char,float,include,for,while,printfscanf 并为小写。 2."+”;”-”;”*”;”/”;”:=“;”:”;”<“;”<=“;”>“;”>=“;”<>“;”=“;”(“;”)”;”;”;”#”为运算符。 3. 其他标记 如字符串,表示以字母开头的标识符。 4. 空格符跳过。 5. 各符号对应种别码 关键字分别对应1-13 运算符分别对应401-418,501-513。 字符串对应100 常量对应200 结束符# 四:举例说明 目标:实现对常量的判别 代码: digit [0-9] letter [A-Za-z] other_char [!-@\[-~] id ({letter}|[_])({letter}|{digit}|[_])* string {({letter}|{digit}|{other_char})+} int_num {digit}+ %% [ |\t|\n]+ "auto"|"double"|"int"|"struct"|"break"|"else"|"long"|"switch"|"case"|"enum"|"register"|"typedef"|"char"|"extern"|"return"|"union"|"const"|"float"|"short"|"unsigned"|"continue"|"for"|"signed"|"void"|"default"|"goto"|"sizeof"|"do"|"if"|"static"|"while"|"main" {Upper(yytext,yyleng);printf("%s,NULL\n",yytext);} \"([!-~])*\" {printf("CONST_string,%s\n",yytext);} -?{int_num}[.]{int_num}?([E][+|-]?{int_num})? {printf("CONST_real,%s\n",yytext);} "0x"?{int_num} {printf("CONST_int,%s\n",yytext);} ","|";"|"("|")"|"{"|"}"|"["|"]"|"->"|"."|"!"|"~"|"++"|"--"|"*"|"&"|"sizeof"|"/"|"%"|"+"|"-"|">"|"<"|">="|"<="|"=="|"!="|"&"|"^"|"|"|"&"|"||"|"+="|"-="|"*="|"/="|"%="|">>="|"<<="|"&="|"^="|"|="|"=" {printf("%s,NULL\n",yytext);} {id} {printf("ID,%s\n",yytext);} {digit}({letter})+ {printf("error1:%s\n",yytext);} %% #include <ctype.h> Upper(char *s,int l) { int i; for(i=0;i<l;i++) { s[i]=toupper(s[i]); } } yywrap() { return 1; } 五:DFA:数据测试 七:心得体会 其实匹配并不困难,主要是C++知识要求相对较高,只要把握住指针就好了。 附源程序: #include<iostream.h> #include<stdio.h> #include<stdlib.h> #include<string.h> int i,j,k,flag,number,status; /*status which is use to judge the string is keywords or not!*/ char ch; char words[10] = {" "}; char program[500]; int Scan(char program[]) { char *keywords[13] = {"void","main","if","then","break","int", "char","float","include","for","while","printf", "scanf"}; number = 0; status = 0; j = 0; ch = program[i++]; /* To handle the lettle space ands tab*/ /*handle letters*/ if ((ch >= &#39;a&#39;) && (ch <= &#39;z&#39; )) { while ((ch >= &#39;a&#39;) && (ch <= &#39;z&#39; )) { words[j++]=ch; ch=program[i++]; } i--; words[j++] = &#39;\0&#39;; for (k = 0; k < 13; k++) if (strcmp (words,keywords[k]) == 0) switch(k) { case 0:{ flag = 1; status = 1; break; } case 1:{ flag = 2; status = 1; break; } case 2:{ flag = 3; status = 1; break; } case 3:{ flag = 4; status = 1; break; } case 4:{ flag = 5; status = 1; break; } case 5:{ flag = 6; status = 1; break; } case 6:{ flag = 7; status = 1; break; } case 7:{ flag = 8; status = 1; break; } case 8:{ flag = 9; status = 1; break; } case 9:{ flag = 10; status = 1; break; } case 10:{ flag = 11; status = 1; break; } case 11:{ flag = 12; status = 1; break; } case 12:{ flag = 13; status = 1; break; } } if (status == 0) { flag = 100; } } /*handle digits*/ else if ((ch >= &#39;0&#39;) && (ch <= &#39;9&#39;)) { number = 0; while ((ch >= &#39;0&#39; ) && (ch <= &#39;9&#39; )) { number = number*10+(ch-&#39;0&#39;); ch = program[i++]; } flag = 200; i--; } /*opereation and edge handle*/ else switch (ch) { case &#39;=&#39;:{ if (ch == &#39;=&#39;) words[j++] = ch; words[j] = &#39;\0&#39;; ch = program[i++]; if (ch == &#39;=&#39;) { words[j++] = ch; words[j] = &#39;\0&#39;; flag = 401; } else { i--; flag = 402; } break; } case&#39;>&#39;:{ if (ch == &#39;>&#39;) words[j++] = ch; words[j] = &#39;\0&#39;; ch = program[i++]; if (ch == &#39;=&#39;) { words[j++] = ch; words[j] = &#39;\0&#39;; flag = 403; } else { i--; flag = 404; } break; } case&#39;<&#39;:{ if (ch == &#39;<&#39;) words[j++] = ch; words[j] = &#39;\0&#39;; ch = program[i++]; if (ch == &#39;=&#39;) { words[j++] = ch; words[j] = &#39;\0&#39;; flag = 405; } else { i--; flag = 406; } break; } case&#39;!&#39;:{ if (ch == &#39;!&#39;) words[j++] = ch; words[j] = &#39;\0&#39;; ch = program[i++]; if (ch == &#39;=&#39;) { words[j++] = ch; words[j] = &#39;\0&#39;; flag = 407; } else { i--; flag = 408; } break; } case&#39;+&#39;:{ if (ch == &#39;+&#39;) words[j++] = ch; words[j] = &#39;\0&#39;; ch = program[i++]; if (ch == &#39;=&#39;) { words[j++] = ch; words[j] = &#39;\0&#39;; flag = 409; } else if (ch == &#39;+&#39;) { words[j++] = ch; words[j] = &#39;\0&#39;; flag = 410; } else { i--; flag = 411; } break; } case&#39;-&#39;:{ if (ch == &#39;-&#39;) words[j++] = ch; words[j] = &#39;\0&#39;; ch = program[i++]; if (ch == &#39;=&#39;) { words[j++] = ch; words[j] = &#39;\0&#39;; flag = 412; } else if( ch == &#39;-&#39;) { words[j++] = ch; words[j] = &#39;\0&#39;; flag = 413; } else { i--; flag = 414; } break; } case&#39;*&#39;:{ if (ch == &#39;*&#39;) words[j++] = ch; words[j] = &#39;\0&#39;; ch = program[i++]; if (ch == &#39;=&#39;) { words[j++] = ch; words[j] = &#39;\0&#39;; flag = 415; } else { i--; flag = 416; } break; } case&#39;/&#39;:{ if (ch == &#39;/&#39;) words[j++] = ch; words[j] = &#39;\0&#39;; ch = program[i++]; if (ch == &#39;=&#39;) { words[j++] = ch; words[j] = &#39;\0&#39;; flag = 417; } else { i--; flag = 418; } break; } case&#39;;&#39;:{ words[j] = ch; words[j+1] = &#39;\0&#39;; flag = 501; break; } case&#39;(&#39;:{ words[j] = ch; words[j+1] = &#39;\0&#39;; flag = 502; break; } case&#39;)&#39;:{ words[j] = ch; words[j+1] = &#39;\0&#39;; flag = 503; break; } case&#39;[&#39;:{ words[j] = ch; words[j+1] = &#39;\0&#39;; flag = 504; break; } case&#39;]&#39;:{ words[j] = ch; words[j+1] = &#39;\0&#39;; flag = 505; break; } case&#39;{&#39;:{ words[j] = ch; words[j+1] = &#39;\0&#39;; flag = 506; break; } case&#39;}&#39;:{ words[j] = ch; words[j+1] = &#39;\0&#39;; flag = 507; break; } case&#39;:&#39;:{ words[j] = ch; words[j+1] = &#39;\0&#39;; flag = 508; break; } case&#39;"&#39;:{ words[j] = ch; words[j+1] = &#39;\0&#39;; flag = 509; break; } case&#39;%&#39;:{ if (ch == &#39;%&#39;) words[j++] = ch; words[j] = &#39;\0&#39;; ch = program[i++]; if (ch == &#39;=&#39;) { words[j++] = ch; words[j] = &#39;\0&#39;; flag = 510; } else { i--; flag = 511; } break; } case&#39;,&#39;:{ words[j] = ch; words[j+1] = &#39;\0&#39;; flag = 512; break; } case&#39;#&#39;:{ words[j] = ch; words[j+1] = &#39;\0&#39;; flag = 513; break; } case&#39;@&#39;:{ words[j] = &#39;#&#39;; flag = 0; break; } default:{ flag = -1; break; } } return flag; } main() { i=0; printf("please input a program end with @"); do { ch = getchar(); program[i++] = ch; }while(ch != &#39;@&#39;); i = 0; do{ flag = Scan(program); if (flag == 200) { printf("(%2d,%4d)",flag,number); } else if (flag == -1) { printf("(%d,error)",flag); } else { printf("(%2d,%4s)",flag,words); } }while (flag != 0); system("pause"); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值