由于时间太少,偶尔才花点时间谢谢这个,废话不多说,下面来简单讲解下词法分析器的实现过程。
一下内容包括:
1:讲解简单词法分析器的实现
2:用C语言验证
注意:词法分析器可以用在命令解释器上,原理是一样的。
首先词法分析器的任务就是识别单词的属性,比如在编程语言中是关键字还是标识符或者是数字等等,这些工作就是词法分析需要做的。
下面我们来通过一个非常简单的例子来说明如何让构建。
假设现在又一下关键字需要识别,其id号已经做了下面规定,如果待检测的单词不在其中,则视为标识符,并保存其标识符,如果在其中,则输出id号。
关键字 ID uint8 1 uint16 2 uint32 3 int8 4 int16 5 int32 6 if 20 for 21 while 22 switch 23 case 24 goto 25
那么如何来识别出单词呢?
一个简单粗暴的方法就是直接判断,我们将其全部定义为字符串,一个一个判断,这种方式最简单易懂,但是这种方式的效率极低,效率低的原因就是每次对其进行遍历,从头开始匹配,如果匹配返回,如果不匹配,继续下一个,这样时间并不确定,而且在最坏情况下的时间复杂度为O(N)。如果将其在嵌入式上面用过命令解释器,基本不可靠。所以下面来说在利用状态机实现该过程,时间复杂度为O(1),即不管输入那种类型,只需要一次便可得到输出结果,并不需要反复遍历,这就是下面要介绍的状态机解决方案。
首先,我们先来简单识别几个单词:
例如uint8,uint16,uint32,int8,int16,int32。
第一步:构建状态机。状态机的构建使用状态图表示:
状态图接收状态应该用双圈表示,这里用不同颜色表示。
上面只涉及到了六个关键字的判断。
现在的问题是如何将其进行程序描述出来,有一个比较粗暴的方式,就是利用二维数组直接表示。如下图所示:
可以发现该方式的坏处就是非常浪费内存,很多元素都没有用到。所以下面用另一种方式表示:
这样就大大降低的内存的浪费。
下面简单说下上面的思路:二维数组的行下标表示当前的状态,列下标表示可进入的下一个状态号,但是可进入的下一个状态号有多个,因此还需要记录输入字符(也就是状态图上的边),所以必须定义一个数组来存储要匹配的字符。
由于时间原因,这里不多叙述了。
下面是设计好的状态图:
下面是代码
/*** * 编写人: * 时 间:2019.11.7 * 文件名:lexer.h * 说 明:词法分析器 ***/ /** 下面是所有能识别的关键字 关键字 ID uint8 1 uint16 2 uint32 3 int8 4 int16 5 int32 6 if 20 for 21 while 22 switch 23 case 24 goto 25 **/ /********************** 下面定义状态表 ***********************/ #ifndef __MYLAXER_ #define __MYLAXER_ typedef struct Lexer { int id; char Label[20]; }LexerPro; void StatusInit(void); LexerPro Analyser(char *sream); #endif
/*** * 编写人: * 时 间:2019.11.7 * 文件名:lexer.c * 说 明:词法分析器 ***/ #include "lexer.h" #include "stdio.h" int Status[41][7]; char TokenTab[41]="-uint813int813fforwhileswitchcasegoto6262"; void StatusInit(void) { /**S0下一状态******/ Status[0][0]=1; //u Status[0][1]=8; //i Status[0][2]=15; //f Status[0][3]=18; //w Status[0][4]=23; //s Status[0][5]=29; //c Status[0][6]=33; //f /**S1下一状态******/ Status[1][0]=2; //i /**S2下一状态******/ Status[2][0]=3; //n /**S3下一状态******/ Status[3][0]=4; //t /**S4下一状态******/ Status[4][0]=5; //8 Status[4][1]=6; //1 Status[4][2]=7; //3 /**S5下一状态******/ Status[5][0]=255; //接收状态标志255 Status[5][1]=1; //uint8 /**S6下一状态******/ Status[6][0]=37; //6 /**S7下一状态******/ Status[7][0]=38; //2 /**S8下一状态******/ Status[8][0]=9; //n Status[8][1]=14; //f /**S9下一状态******/ Status[9][0]=10; //t /**S10下一状态******/ Status[10][0]=11; //8 Status[10][1]=12; //1 Status[10][2]=13; //3 /**S11下一状态******/ Status[11][0]=255; //接收状态标志255 Status[11][1]=4; //int8 /**S12下一状态******/ Status[12][0]=39; //6 /**S13下一状态******/ Status[13][0]=40; //2 /**S14下一状态******/ Status[14][0]=255; //接收状态标志255 Status[14][1]=20; //if /**S15下一状态******/ Status[15][0]=16; //o /**S16下一状态******/ Status[16][0]=17; //r /**S17下一状态******/ Status[17][0]=255; //接收状态标志255 Status[17][1]=21; //for /**S18下一状态******/ Status[18][0]=19; //h /**S19下一状态******/ Status[19][0]=20; //i /**S20下一状态******/ Status[20][0]=21; //l /**S21下一状态******/ Status[21][0]=22; //e /**S22下一状态******/ Status[22][0]=255; //接收状态标志255 Status[22][1]=22; //while /**S23下一状态******/ Status[23][0]=24; //w /**S24下一状态******/ Status[24][0]=25; //i /**S25下一状态******/ Status[25][0]=26; //t /**S26下一状态******/ Status[26][0]=27; //c /**S27下一状态******/ Status[27][0]=28; //h /**S28下一状态******/ Status[28][0]=255; //接收状态标志255 Status[28][1]=23; //switch /**S29下一状态******/ Status[29][0]=30; //a /**S30下一状态******/ Status[30][0]=31; //s /**S31下一状态******/ Status[31][0]=32; //e /**S32下一状态******/ Status[32][0]=255; //接收状态标志255 Status[32][1]=24; //case /**S33下一状态******/ Status[33][0]=34; //o /**S34下一状态******/ Status[34][0]=35; //t /**S35下一状态******/ Status[35][0]=36; //o /**S36下一状态******/ Status[36][0]=255; //接收状态标志255 Status[36][1]=25; //goto /**S37下一状态******/ Status[37][0]=255; //接收状态标志255 Status[37][1]=2; //uint16 /**S38下一状态******/ Status[38][0]=255; //接收状态标志255 Status[38][1]=3; //uint32 /**S39下一状态******/ Status[39][0]=255; //接收状态标志255 Status[39][1]=5; //int16 /**S40下一状态******/ Status[40][0]=255; //接收状态标志255 Status[40][1]=6; //int32 } int NxteStatus(char inChar,int CurrentStatus) { int i; for(i=0;i<7;i++) { if(inChar==TokenTab[Status[CurrentStatus][i]]) { return Status[CurrentStatus][i];//返回下一个状态 } } return -1; //返回非接收状态 } LexerPro Analyser(char *sream) { LexerPro resul; int error=0; int StartSta=0; //状态 int TemtSta=0; //临时状态 int count=0; StatusInit(); while(*(sream)!='\0'&&(*(sream)!=' ')) { resul.Label[count++]=*(sream); if(!error) { TemtSta=NxteStatus(*sream,StartSta); } printf("当前状态=%d\n",TemtSta); if(Status[TemtSta][0]==255) //表示达到接收状态 { error=1; //禁止进入下个状态 if((*(sream+1)==' ')||(*(sream+1)=='\0')) //如果下一个字符为空格,表示接收 { resul.id=Status[TemtSta][1]; //返回id break; } else //如果下一个字符为不为空格,表示包含关键字但不是关键字 { resul.id=0; TemtSta=0; } } if(TemtSta==-1) { error=1; TemtSta=0; } if(TemtSta>0&&TemtSta<41) { StartSta=TemtSta; //更新状态 } sream++; } resul.Label[count++]='\0'; return resul; }
#include "stdio.h" #include "lexer.h" int main(void) { LexerPro res; char input[20]=""; gets(input); res=Analyser(input); if(res.id==0) { printf("id=%d\n",res.id); puts("此单词为标识符"); puts(res.Label); } if(res.id!=0) { printf("此单词为关键字,关键字索引为 %d",res.id); } return 0; }
运行后如下所示:输入for后回车
重新运行,输入fori回车:
来源:https://www.cnblogs.com/listenscience/p/11815127.html