编译原理之词法分析器(一)

此生再无相见时 提交于 2019-11-29 20:30:32

由于时间太少,偶尔才花点时间谢谢这个,废话不多说,下面来简单讲解下词法分析器的实现过程。

一下内容包括:

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回车:

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!