编译原理-词法分析器的原理及实现

1.目的要求

设计、编制并调试一个词法分析程序,分析一段程序的词法,包括:过滤无效字符、数值转换、宏展开、预包含处理等,加深对词法分析原理的理解。

2.单词分类表

单词分类表如表1所示

类别

类别码

类别

类别码

int

1

for

11

float

2

do

12

double

3

continue

13

char

4

break

14

long

5

switch

15

short

6

case

16

void

7

default

17

if

8

return

18

else

9

static

19

while

10

 

 

标识符

41

数字

42

空格,换行,tab

-1

+

61

-

62

?

74

*

63

75

/

64

76

65

{

77

66

}

78

=

67

(

79

|

68

)

80

^

69

#

81

.

70

!

82

:

71

%

83

;

72

&

84

,

73

\

85

[              

86

]

87

++

91

--

92

**

93

==

97

||

98

//

94

预编译

120

宏定义

121

                                                      表 1

3.单词结构描述(正规式或正规文法)

I=A|a|B|b|C|c|D|d|E|e|F|f|G|g|H|h|I|i|J|j|K|k|L|l|M|m|N|n|O|o|P|p|Q|q|R|r|S|s|T|t|

U|u|V|v|W|w|X|x|Y|y|Z|z

D=1|2|3|4|5|6|7|8|9|0

标识符:I(I|D)*

数字:D(D*)

4.算法描述

(1)将标识符,数字,预编译,宏定义一种一类,关键字一词一类,符号一词一类,标号

(2)书写函数以下函数:IsLetter(检查字母),IsDigit(检查数字),IsSpace(检查空格),InImp(检查关键字),InMark(检查是否在标识符表中,如果不在那么在标识符标中加入这个字符串),IsNumber(判断数字是否在已经在数字表中,如果不在那么加入到数字表中),IsSign(各种符号)

(3)读取文件,将文件的字符调入到一个数组text中,

(4)读取数组text中的第一个字符,判断是字母,数字还是特殊字符,若是字母则将字母写入到临时数组strToken中去,读入下一个字符,判断是数字还是字母如果是其中一个那么继续写入到数组strToken中,直到读入非字母非数字的字符,判断strToken是关键字,如果是,那么输出关键的编号,如果不是那么去标识符表中查找,若表中有,则输出标识符的标号,以及输出标识符在表中的位置,若没有,将标识符添加到表中,输出标号和位置。

(5)若第一个字符是数字,将数字读入数组strToken中,再读入下一个字符判断是数字还是“.” ,如果是其中一个那么继续写入到数组,继续读入,如果都不是,那么去判断strToken中“.”的个数,如果个数不合法那么输出提示错误,如果合法,那么判断数字表中是否存在,如果不存在那么将字符串转为数字,存入表中,最后输出数字的标号,以及在数字表中的位置。

(6)读取字符是特殊符号那么输出特殊符号的标号,若特殊符号是#,那么判断下一个字符是不是“d”或者“i”若是“d”那么读入到数组strToken中,直到遇到回车,输出是一个预编译,如果是“d”,那么读入到数组strToken中,输出是宏定义,并且输出其定义内容。

6.程序结构

程序结构如图2所示,图中包括了整个程序的算法结构,包括每个分支,每个模块需要实现的功能内容。

![在这里插入图片描述](https://img-blog.csdnimg.cn/20191221224441199.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDgzMTUzNg==,size_16,color_FFFFFF,t_70)

                                     图 1

7.设计技巧及体会

在设计中,刚开感觉会有点麻烦,无从下手,在找了一些资料以及看了书之后,才渐渐知道自己要做的是一个怎样的东西,才有了自己的想法。从分类到编号,再到输出标号,按照标号去找属于哪一个类别,分别构建每一个函数,以及函数之间的关系,和最后那个进行分析的函数要调那些那些函数,怎么调用,都花了一些时间来构思,在调试阶段,由于是写完一个部分就进行测试,所以这个阶段比较轻松一点,只是注意指向数组的变量的变化。这次实验,我收获了很多,掌握词法分析器的一般原理,对程序书写规范有了更深的了解和认识,希望以后能够再接再厉。

8.代码

#include<stdio.h>
#include<conio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
 
char mark[50][20]; //标识符表 
double number[50];//数字表 
int mar=0;//标识符数组下标 
int num=0;//数字表数组下标 

//关键字 
char imp[50][20]={"int","float","double","char","long","short","void","if","else","while","for","do","continue","break","switch","case","default","return","static"};
 
//预编译 

//检查字母 
bool IsLetter (char ch) {
	if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')){
		return true;
	}
	else return false;
} 

//检查数字 
bool IsDigit(char ch) {
	if(ch>='0'&&ch<='9'){
		return true;
	}
	else{
		return false;
	}
}

//检查空格 
bool IsSpace(char ch){
	if(ch==' '||ch=='\n'||ch=='\t'){
		return true;
	}
	else return false;
}

//检查关键字 
int InImp(char strToken[20]){
	int n;
	for (n = 0; n < 50; n++)
        {
            if (strcmp(strToken, imp[n]) == 0)
            {
                return n+1;
            }
        }
	if(n==50){
		return 0;
	}
} 

//检查是否在标识符表中,如果不在那么在标识符标中加入这个字符串 
 int InMark(char strToken[20]){
 	if (mar!= 0)
        {
            int q = 0;
            while (q<mar)
            {
                if (strcmp(strToken, mark[q++]) == 0)
                {
                    return q; 
                }
            }

        }

        //将该标识符保存到标识符表中
        strcpy(mark[mar], strToken);
        mar++;
        return mar;

 } 
 //为宏定义而备
  
 //判断数字是否在已经在数字表中,如果不在那么加入到数字表中
 int IsNumber(char strToken[20]){
 	int count=0;
 	for(int s=0;s<19;s++){
 		if(strToken[s]=='.'){
 			count++;
 		}
 	}
 	//printf("%d-----------------",count);
 	if(count==0){
 	 int nu = atoi(strToken);
	  //判断该常数是否存在于常数表中
        if (num != 0)
        {   
            
            for(int y=0;y<num;y++)
            {
            	if(number[y]==nu)
            	{
            		return y+1;
				}
			}
        }
        
        //将该常数保存到标识符表中
        number[num]=nu;
        num++;
        return num;	
 	}else if(count==1){
 		double nu = atof(strToken);
 		//判断该常数是否存在于常数表中
        if (num != 0)
        {   
            
            for(int y=0;y<num;y++)
            {
            	if(number[y]==nu)
            	{
            		return y+1;
				}
			}
        }
        
        //将该常数保存到标识符表中
        number[num]=nu;
        num++;
        return num;
 	}else{
 		return 0;
 	}
 	
}
  
 //各种符号
 int  IsSign(char ch){
 	switch(ch){
 		case ' ':
        case '\n':
        case '\t':
            return -1;
        case '+': return 61;
        case '-': return 62;
        case '*': return 63;
        case '/': return 64;
        case '>': return 65;
        case '<': return 66;
        case '=': return 67;
        case '|': return 68;
        case '^': return 69;
        case '.': return 70;
        case ':': return 71;
        case ';': return 72;
        case ',': return 73;
        case '?': return 74;
        case '"': return 75;
        case '\'': return 76;
        case '{': return 77;
        case '}': return 78;
        case '(': return 79;
        case ')': return 80;
        case '#': return 81;
        case '!': return 82;
        case '%': return 83;
        case '&': return 84;
        case '\\': return 85;
        case '[': return 86;
        case ']': return 87;
        
        
 	}
 }
 
 void wordAnaly(char text[1000],int length){
 	char ch; //最新的字符 
	char strToken[20];//找到的字符串 
	int i=0;
	while(i<length){
	
	for(int k=0;k<20;k++){
		strToken[k]='\0';
	}
	ch = text[i];
	//过滤空格 
	while(IsSpace(ch)){
		i++;
		ch = text[i];
	}
	
	//关键字和标识符 
	if(IsLetter(ch)){
		int n = 0;
        strToken[n++] = text[i++];
        while (IsLetter(text[i])||IsDigit(text[i]))
        {
            strToken[n++] = text[i++];
        }
        i--;
        
        int flag = InImp(strToken);
        if(flag==0){
        	flag=InMark(strToken);
        	printf("%s----(%d,%d)	标识符\n",strToken,41,flag);
        }else{
        	printf("%s----(%d,-)	关键字\n",strToken,flag);
        }
	}
	//数字 
	else if(IsDigit(ch)){
		int n = 0;
        strToken[n++] = text[i++];
        while (IsDigit(text[i])||text[i]=='.')
        {
            strToken[n++] = text[i++];
        }
        i--;
        
        int flag=IsNumber(strToken);
        if(flag==0){
        	printf("数字输入有误!\n"); 
        }else {
			printf("%s----(%d,%d)	数字\n",strToken,42,flag);
		} 
	}
	//预编译 
	else if(ch=='#'){
		char word[50];
		for(int w=0;w<50;w++){
			word[w]='\0';
		} 
		int a = 0;
		word[a]='#';
		a++;
		if(text[i+1]=='i'){
			i++;
			while(text[i]!='\n'){
				word[a++]=text[i++];	
			}
			printf("%s----(%d,-)	预包含文件处理\n",word,120);
		}else if(text[i+1]=='d'){
			i++;
			while(text[i]!='\n'){
				word[a++]=text[i++];	
			}
			//得到宏定义内容 
			char cha[20];
			char chr[20];
			for(int w=0;w<50;w++){
			cha[w]='\0';
			chr[w]='\0';
		}
			int fl=0;
			int index=0;
			for(int f=7;f<=a;f++){
				if(IsLetter(word[f])){
					cha[index++]=word[f];
				}
			}
			index=0;
			for(int f=fl+1;f<=a;f++){
				if(IsDigit(word[f])||word[f]=='.'){
					chr[index++]=word[f];
				}
			}
			InMark(cha);
			IsNumber(chr);
			printf("%s----(%d,-)	宏定义  ",word,121);
			printf("将%s赋值为%s\n",cha,chr);
		}
	}
	//特殊符号 
	else if(IsSign(ch)){
		int flag = IsSign(ch);
		if(IsSign(text[i+1])==flag){
			i++;
			printf("%c%c----(%d,-)	特殊符号\n",ch,text[i],flag+30);
		}else{
		printf("%c----(%d,-)	特殊符号\n",ch,flag);	
		}		
	} 
	i++;	
  }
}
 
 int main(){
	int c = 0;
	char text[1000];
    FILE *fp;
    fp=fopen("H:\\test.txt","r");
    if (fp == NULL)
    {
        printf("can't open file!\n");
        exit(0);
    }

    while (!feof(fp))
    {
        text[c++] = fgetc(fp);
    }
//    for(int j=0;j<=c;j++){
//    	printf("%d,%c",j,text[j]);
//    	printf("\n");
//    }
	wordAnaly(text,c-1);
	
	//将数字表number和标识符表mark写入文件a.txt 
	FILE *fp1;
	int j;
	fp=fopen("H:\\a.txt","w");//打开文件以便写入数据
	for (j = 0; j < num; j++) { //将a数组中的整数写入fp指向的c:\a.txt文件
	fprintf(fp,"%f\n",number[j]);
	}
	for (j = 0; j < mar; j++) { //将a数组中的整数写入fp指向的c:\a.txt文件
	fprintf(fp,"%s\n",mark[j]);
	}
	fclose(fp); //写入完毕,关闭文件
	
	
	
	
 }

  • 5
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
词法分析是编译器的第一个阶段,也是最基础的一步,其主要任务是将源代码转换为一个个单词或符号,即“词法单元”,以供后续阶段使用。下面我们来介绍如何设计、编制并调试一个词法分析程序。 1. 设计词法规则 在设计词法规则时,首先需要分析所处理的编程语言的语法结构和语法规则,然后将其转化为正则表达式或有限状态自动机等形式,以识别出程序中的各个词法单元。例如,在C语言中,整型常量的正则表达式为`[1-9][0-9]*|0`,表示可以是以非零数字开头的整数,也可以是0。 2. 编制词法分析程序 根据所设计的词法规则,编制词法分析程序,一般有两种方式实现: (1)手写程序 手写程序可以直接使用编程语言的字符串处理函数,如`substr()`、`charAt()`等,逐个字符进行匹配,判断其是否符合所设定的正则表达式。这种方式相对简单,但是需要大量的代码量和调试工作,不适合处理复杂的语法结构。 (2)使用词法分析生成器 词法分析生成器是一种自动生成词法分析程序的工具,常用的有flex、ANTLR等。使用词法分析生成器可以大大简化程序编写过程,只需要提供所设计的词法规则,即可自动生成词法分析程序。这种方式比较适合处理复杂的语法结构,并且可以提高程序的可维护性。 3. 调试词法分析程序 在编写完词法分析程序后,需要进行调试,以确保程序的正确性。主要的调试方法包括: (1)单步调试 单步调试可以逐步执行程序,查看每一步的执行结果,以确定程序的执行过程和问题所在。在单步调试时,可以使用调试器或者打印输出等方式进行调试。 (2)测试用例 编写测试用例,验证程序的正确性。测试用例应该包含各种情况下的输入输出,包括正常情况、边界情况、异常情况等,以尽可能全面地测试程序。 总之,词法分析是编译器的重要组成部分,设计、编制并调试一个词法分析程序需要仔细分析所处理的编程语言的语法结构和语法规则,并且采用合适的方法进行实现和调试,才能保证程序的正确性和可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值