上一篇我们用二维数组实现了状态机,但是我们可以看到,使用二维数组实现浪费了很多内存,实际上我们可以使用其他的方式来实现状态机,也就是说状态机并不是只有一种实现方式,很多数据结构有很多种实现方式,但对于同一种数据结构基本的思想是相同的。其实学习词法分析实际最重要的是学习状态机的实现过程,代码都是次要的,每种实现过程都有优点和缺点。
本文内容:
1:状态机的另一种实现方式(Switch语句实现)
2:评价该实现的方式优缺点
下面我们使用switch的case结构来实现这一过程:
状态机的状态转移图如下所示:
我们使用的是switch语句实现:
代码如下:
头文件:
/*** * 编写人: * 时 间:2019.11.25 * 文件名: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; LexerPro Analyser(char *sream); #endif
源文件:
/*** * 编写人: * 时 间:2019.11.25 * 文件名:lexer.c * 说 明:词法分析器 ***/ #include "lexer.h" #include "stdio.h" LexerPro Analyser(char *sream) { int state=0; //初始化状态为0 LexerPro Resl={0,""}; int i=0; while(*sream) { Resl.Label[i++]=*sream; switch(state) { case 0: if(*sream==' ') {state=0;break;} else if(*sream=='u') {state=0;break;} else if(*sream=='i') {state=10;break;} else if(*sream=='f') {state=19;break;} else if(*sream=='w') {state=22;break;} else if(*sream=='s') {state=27;break;} else if(*sream=='c') {state=27;break;} else if(*sream=='g') {state=37;break;} else {state=255;break;} case 1: if(*sream=='i') {state=2;break;} else {state=255;break;} case 2: if(*sream=='n') {state=3;break;} else {state=255;break;} case 3: if(*sream=='t') {state=4;break;} else {state=255;break;} case 4: if(*sream=='8') {state=5;break;} else if(*sream=='1') {state=6;break;} else if(*sream=='3') {state=8;break;} else {state=255;break;} case 5: if(*sream==' ') {Resl.id=1;return Resl;} else {state=255;break;} case 6: if(*sream=='6') {state=7;break;} else {state=255;break;} case 7: if(*sream==' ') {Resl.id=2;return Resl;} else {state=255;break;} case 8: if(*sream=='2') {state=9;break;} else {state=255;break;} case 9: if(*sream==' ') {Resl.id=3;return Resl;} else {state=255;break;} case 10: if(*sream=='f') {state=18;break;} else if(*sream=='n') {state=11;break;} else {state=255;break;} case 11: if(*sream=='t') {state=12;break;} else {state=255;break;} case 12: if(*sream=='8') {state=13;break;} else if(*sream=='1') {state=14;break;} else if(*sream=='3') {state=16;break;} else {state=255;break;} case 13: if(*sream==' ') {Resl.id=4;return Resl;} else {state=255;break;} case 14: if(*sream=='6') {state=15;break;} else {state=255;break;} case 15: if(*sream==' ') {Resl.id=5;return Resl;} else {state=255;break;} case 16: if(*sream=='2') {state=17;break;} else {state=255;break;} case 17: if(*sream==' ') {Resl.id=6;return Resl;} else {state=255;break;} case 18: if(*sream==' ') {Resl.id=20;return Resl;} else {state=255;break;} case 19: if(*sream=='o') {state=20;break;} else {state=255;break;} case 20: if(*sream=='r') {state=21;break;} else {state=255;break;} case 21: if(*sream==' ') {Resl.id=21;return Resl;} else {state=255;break;} case 22: if(*sream=='h') {state=23;break;} else {state=255;break;} case 23: if(*sream=='i') {state=24;break;} else {state=255;break;} case 24: if(*sream=='l') {state=25;break;} else {state=255;break;} case 25: if(*sream=='e') {state=26;break;} else {state=255;break;} case 26: if(*sream==' ') {Resl.id=22;return Resl;} else {state=255;break;} case 27: if(*sream=='w') {state=28;break;} else {state=255;break;} case 28: if(*sream=='i') {state=29;break;} else {state=255;break;} case 29: if(*sream=='t') {state=30;break;} else {state=255;break;} case 30: if(*sream=='c') {state=31;break;} else {state=255;break;} case 31: if(*sream=='h') {state=32;break;} else {state=255;break;} case 32: if(*sream==' ') {Resl.id=23;return Resl;} else {state=255;break;} case 33: if(*sream=='a') {state=34;break;} else {state=255;break;} case 34: if(*sream=='s') {state=35;break;} else {state=255;break;} case 35: if(*sream=='e') {state=36;break;} else {state=255;break;} case 36: if(*sream==' ') {Resl.id=24;return Resl;} else {state=255;break;} case 37: if(*sream=='o') {state=38;break;} else {state=255;break;} case 38: if(*sream=='t') {state=39;break;} else {state=255;break;} case 39: if(*sream=='o') {state=40;break;} else {state=255;break;} case 40: if(*sream==' ') {Resl.id=25;return Resl;} else {state=255;break;} default:state=255;break; } sream++; printf("当前状态=%d\n",state); } return Resl; }
主函数:
#include "stdio.h" #include "lexer.h" int main(void) { LexerPro res; char input[20]="while "; res=Analyser(input); if(res.id==0) { printf("id=%d\n",res.id); puts("此单词为标识符"); puts(res.Label); } printf("%s\n",input); if(res.id!=0) { printf("此单词为关键字,关键字索引为 %d",res.id); } return 0; }
测试结果如下:
通过上面的代码可以看到,用switch语句实现会使函数体变得很大,有的复杂状态机可能就会达到上千行,因此我们可以看到该种实现过程的缺点。但是其有点也很明显。其优点就是不浪费内存空间。