由于时间太少,偶尔才花点时间谢谢这个,废话不多说,下面来简单讲解下词法分析器的实现过程。
一下内容包括:
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