利用状态图实现词法分析

实验一:词法分析程序

                                                                                              03070020  曹宁

一.  实验目的

基本掌握计算机语言的词法分析程序的开发方法。

二.  实验内容

编制一个能够分析三种整数、标识符、主要运算符和主要关键字的词法分析程序。

三.  实验环境

PC微机

DOS操作系统或 Windows 操作系统

Turbo C 程序集成环境或 Visual C++ 程序集成环境

四.  实验内容

1.    根据以下的正规式,编制正规文法,画出状态图;

标识符

letter(letter|digit)*

letter->A|B|…|Z|a|b|…|z

digit->0|1|2|3|4|…|9

十进制整数

0 | (1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*

八进制整数

0(1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7)*

十六进制整数

0x(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)*

运算符和分隔符

+  -  *  /  >  <  =  (  )  

关键字

if  then  else  while  do  

2.    根据状态图,设计词法分析函数int nextToken(),完成以下功能:

1)              从文件读入数据,分析出一个单词。

2)              返回单词种别(用整数表示),

3)              返回单词属性(不同的属性可以放在不同的全局变量中)。

3.    编写测试程序,反复调用函数int nextToken(),输出单词种别和属性。

五.  实验步骤

1.      根据状态图,设计词法分析算法

标识符          

正规式 

id->letter(letter|digit)*

letter->A|B|…|Z|a|b|…|z

digit->0|1|2|3|4|…|9

正规文法

S->aB’|bB’|…|zB’|AB’|BB’|…|ZB’

B’->0B’|1B’|…|9B’

状态图

             

八进制整数             

正规式 

0(1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7)*

正规文法

S->01B|02B|…|07B

B->0B|1B|…|7B|

十进制整数             

正规式 

0 | (1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*

正规文法

S->0|1B|2B|…|9B

B->0B|1B|…|9B|

十六进制整数             

正规式 

0x(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)*

正规文法

S->0(x|X)(1B’|2B’|…|9B’|aB’|bB’|…|fB’|AB’|…|FB’)

B’->0B’|1B’|…|7B’|

识别这三种数字的状态图

             

运算符和分隔符

状态图

 

2.      采用C语言,设计函数scan( ),实现该算法

程序中的变量和函数声明

//对外函数

extern void initLexer();//打开文件,初始化词法分析器

extern int nextToken();//获得一个token

//对外变量

extern int attr=-1;//是数值的时候存储数值,是标识符时存储在名字表中的位置

extern int lineNo=1;//显示行数

extern char *keyWord[]={

        "if",

        "else",

        "then",

        "while",

        "do"

};

//内部函数

static int fail();//换状态图

static char getAnChar();//在文件中读取一个字符,指针下移一位

static void ungetAnChar();//在文件中指针回退一位

static void getNum(int type);//根据type,获得十进制数值,存储在attr

static int lookup(const char *s);//在符号表中查找ID

static int insert(const char *s);//返回再符号表中的下标位置

static int isKeyWord(char word[]);//判断是否为关键字

//内部变量

static char lexBuf[100];//字符缓存,用来存储当前分析的字

static int state=1, start=1;//当前状态和状态表的开始状态

static int currentPos=0;//文件中当前指针的位置

static int tokenBeginning=0;//进入一个状态表时指针位置,即换状态表时的回退位置。

static FILE *fp;//被编译的文件指针

static int smptableLength=0;//当前符号表的长度

 

int nextToken(){

 

    int length=0;

    char c;

    int keyWordPos=0;//在关键字数组中的下标

 

    state=1;start=1;

    //存储开始位置

    tokenBeginning=currentPos;

    //状态图的实现

    while(1){

 

        switch(state){

        case 1:

            c=getAnChar();

            if(c==' '||c=='/t'||c=='/r'){

                tokenBeginning++;

            }

            else if(c=='/n'){

                lineNo++;

                tokenBeginning++;

            }

            else if(isalpha(c)){

                state=2;

                lexBuf[length++]=c;

            }

            else if(c==EOF) return FILEEND;

            else state=fail();

            break;

        case 2:

            c=getAnChar();

            if(isdigit(c)||isalpha(c)){

                state=2;

                lexBuf[length++]=c;

            }

            else state=3;

            break;

        case 3:

            ungetAnChar();

            lexBuf[length]='/0';

            keyWordPos=isKeyWord(lexBuf);

            if(keyWordPos!=-1){

                //是关键字

                return IF+keyWordPos;

            }

            //不是关键字

            attr=insert(lexBuf);//ID在名字表中的数组下标存储在attr

            return ID;

        case 4:

            c=getAnChar();

            if(c=='0') state=5;

            else if(c>='1' && c<='9'){

                state=12;

                lexBuf[length++]=c;

            }

            else state=fail();

            break;

        case 5:

            c=getAnChar();

            if(c=='x') state=6;

            else if(c>='1'&& c<='8'){

                state=10;

                lexBuf[length++]=c;

            }

 

            else state=13;

            break;

        case 6:

            c=getAnChar();

            if(c>='1' && c<='9' || c>='a'&& c<='f' || c>='A' && c<='B'){

                state=7;

                lexBuf[length++]=c;

            }

            else state=fail();

            break;

        case 7:

            c=getAnChar();

            if(c>='1' && c<='9' || c>='a'&& c<='f' || c>='A' && c<='B'||c=='0'){

                state=7;

                lexBuf[length++]=c;

            }

            else state=8;

            break;

        case 8:

            ungetAnChar();

            lexBuf[length]='/0';

            getNum(INT16);//把数值存在attr

            return INT16;

        case 10:

            c=getAnChar();

            if(c>='0'&& c<='8'){

                state=10;

                lexBuf[length++]=c;

            }

            else state=11;

            break;

        case 11:

            ungetAnChar();

            lexBuf[length]='/0';

            getNum(INT8);//把数值存在attr

            return INT8;

        case 12:

            c=getAnChar();

            if(c>='0' && c<='9'){

                state=12;

                lexBuf[length++]=c;

            }

            else state=13;

            break;

        case 13:

            ungetAnChar();

            lexBuf[length]='/0';

            getNum(INT10);//把数值存在attr

            return INT10;

        case 14:

            c=getAnChar();

            if(c=='+') state=15;

            else if(c=='-') state=16;

            else if(c=='*') state=17;

            else if(c=='/') state=18;

            else if(c=='>') state=19;

            else if(c=='<') state=20;

            else if(c=='=') state=21;

            else if(c=='(') state=22;

            else if(c==')') state=23;

            else if(c==';') state=24;

            else state=fail();

            break;

        case 15:

            return ADD;

        case 16:

            return SUB;

        case 17:

            return MUL;

        case 18:

            return DIV;

        case 19:

            return GT;

        case 20:

            return LT;

        case 21:

            return EQ;

        case 22:

            return LBR;

        case 23:

            return RBR;

        case 24:

            return SEM;

        }

    }    

}

 

3.      编制测试程序(主函数main)。

void main(){

     int token;

     int oldLine=-1;

     initLexer();  

     

     while(1){

         token=nextToken();

         if(token==FILEEND) break;

         if(oldLine!=lineNo) {

             printf("___________Line %d____________/n",lineNo);

             oldLine=lineNo;

         }

        

        if(token==ID){//标识符

            printf("ID:%d,Pos:%d/n",token,attr);

        }

        else {

            switch(token){

                case INT16:case INT8:case INT10://数字

                    printf("%d/t/t%d/n",token,attr);

                    break;

                default: printf("%d/n",token);//关键字

                }

        }

        token=nextToken();

     }

}//*/

 

4.      调试程序:输入一组单词,检查输出结果

    1  92+data>  0x 3f   04  while

1x3 x3 x3 x44   00

以上都是一行的没有出现什么问题,但是当对多行进行词法分析时,总是出错,通过打印读到的字符发现有字符13ASCII),查资料才知是’/t’,把光标移到当前行的起始位置,我不知道为什么会有这个字符,添加滤掉这个字符的逻辑,才使词法分析成功。

while (1) do num=10;

num=11;

if (1) then caoning=0x11;

if (1) than caoning=878;

 

5.      词法分析程序的数据结构与算法

符号表的数据结构:采用的是结构数组

struct ENTYE{

   char word[100];

   float value;

};

int smptableLength=0;

struct ENTYE smptable[200];

在配以int lookup(const char *s)int insert(const char *s)对这个符号表操作。

/*查找s在符号表的位置,没有返回-1*/

int lookup(const char *s){

    int i;

    for (i=0;i<smptableLength;i++){

        if (strcmp(smptable[i].word,s)==0) return i;

    }

    return -1;

}

/*返回s在符号表中的位置*/

int insert(const char *s){

   int i=lookup(s);

   if(i==-1){

        strcpy(smptable[smptableLength].word,s)

       //smptableLength++;

       return smptableLength++;

   }

   return i;

}

六.  心得体会

这次词法分析的实验本身没有什么难度,但是在做这实验之前感觉没谱,所以踏下心仔细的阅读Aho的《编译原理》中前三章,受到很大启发,尤其是利用switch语句把状态图实现的技术,可谓一绝,这也是我学习词法分析的最大的收获。

在做语法分析的时候,对词法分析进行了一些修改,学习了一下不同文件内的函数和变量的使用,在做词法分析的时候架构没有做好,比如哪些函数和变量作为内部的,哪些是提供外部使用的接口,比较混乱,也没有进行大量测试,导致语法分析时受阻重重,最后停下语法分析的编程,对此词法分析修改,使程序清晰易读,并进行了大量的测试,再作语法分析时就容易多了。这也是个教训,以后编程首先得考虑好,再一步一步来。就会省很多麻烦。

 

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
词法分析器是编译器中的一部分,它的主要作用是将源代码分割成一个个的单词(token),并将其转化为一个有意义的符号串。在C语言中,词法分析器需要识别标识符关键字运算符、常量等。 利用自动机理论实现词法分析器可以分为以下几个步骤: 1. 定义词法单元(token)的类型和属性,例如标识符关键字、常量等。 2. 根据C语言的语法规则,设计自动机的状态转移图。 3. 实现自动机的状态转移函数,将输入的字符序列转化为词法单元。 4. 对于每个识别出来的词法单元,需要记录其类型和属性值,可以采用符号表来实现。 5. 最后生成词法单元序列,作为后续语法分析的输入。 下面是一个简单的C语言词法分析器的实现(仅包括标识符关键字、常量和运算符的识别): ```c #include <stdio.h> #include <ctype.h> #define MAX_TOKEN_LEN 100 // 定义词法单元类型 typedef enum { TOKEN_KEYWORD, TOKEN_IDENTIFIER, TOKEN_CONSTANT, TOKEN_OPERATOR, TOKEN_UNKNOWN } TokenType; // 定义词法单元结构体 typedef struct { TokenType type; char value[MAX_TOKEN_LEN + 1]; } Token; // 关键字表 char *keywords[] = {"int", "float", "if", "else", "while", "for"}; // 符号表 Token sym_table[MAX_TOKEN_LEN]; // 当前输入缓冲区 char input_buffer[MAX_TOKEN_LEN]; // 当前输入缓冲区位置 int input_pos = 0; // 获取下一个字符 char next_char() { return input_buffer[input_pos++]; } // 将指针回退一个字符 void unget_char() { input_pos--; } // 判断是否为关键字 int is_keyword(char *str) { int i; for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { if (strcmp(str, keywords[i]) == 0) { return 1; } } return 0; } // 识别标识符 Token get_identifier() { Token token; token.type = TOKEN_IDENTIFIER; int i = 0; while (isalnum(input_buffer[input_pos])) { token.value[i++] = next_char(); } token.value[i] = '\0'; if (is_keyword(token.value)) { token.type = TOKEN_KEYWORD; } return token; } // 识别常量 Token get_constant() { Token token; token.type = TOKEN_CONSTANT; int i = 0; while (isdigit(input_buffer[input_pos])) { token.value[i++] = next_char(); } token.value[i] = '\0'; return token; } // 识别运算符 Token get_operator() { Token token; token.type = TOKEN_OPERATOR; token.value[0] = next_char(); token.value[1] = '\0'; return token; } // 词法分析函数 Token *lex() { Token *tokens = NULL; Token token; while (input_pos < strlen(input_buffer)) { char c = next_char(); if (isalpha(c)) { unget_char(); token = get_identifier(); } else if (isdigit(c)) { unget_char(); token = get_constant(); } else if (c == '+' || c == '-' || c == '*' || c == '/') { token = get_operator(); } else { token.type = TOKEN_UNKNOWN; token.value[0] = c; token.value[1] = '\0'; } sym_table[sizeof(sym_table) / sizeof(sym_table[0])] = token; } return tokens; } int main() { // 读入源代码 fgets(input_buffer, MAX_TOKEN_LEN, stdin); // 进行词法分析 Token *tokens = lex(); // 输出词法单元序列 int i; for (i = 0; i < sizeof(sym_table) / sizeof(sym_table[0]); i++) { if (sym_table[i].type == TOKEN_UNKNOWN) { printf("Unknown token: %s\n", sym_table[i].value); } else { printf("Token type: %d, Token value: %s\n", sym_table[i].type, sym_table[i].value); } } return 0; } ``` 以上代码实现一个简单的C语言词法分析器,可以识别标识符关键字、常量和运算符,并将其存入符号表中。在实际应用中,还需要考虑更多的细节和特殊情况,例如注释、字符串等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值