当前位置:   article > 正文

编译原理:c语言词法分析器的实现_编译原理词法分析器c语言

编译原理词法分析器c语言

一、前言

词法分析和语法分析是编译原理中必要的部分,是需要花费一定时间去学习理解的,本文简单介绍了使用c语言如何编写c语言的词法分析器。(ps:完整代码的链接在文末)

二、什么是词法分析器

定义

词法分析器的功能输入源程序,按照构词规则分解成一系列单词符号。单词是语言中具有独立意义的最小单位,包括关键字、标识符、运算符、界符和常量等。
(1) 关键字:是由程序语言定义的具有固定意义的标识符。例如begin,end,if,while都是保留字。这些字通常不用作一般标识符。
(2) 标识符:用来表示各种名字,如变量名,数组名,过程名等等。
(3) 常数 :常数的类型一般有整型、实型、布尔型、文字型等。
(4) 运算符:如+、-、*、/等等。
(5) 界符 :如逗号、分号、括号、等等。

输出

有了对词法分析器的定义,我们编写的词法分析器的输出理所当然应当是如下的形式:
(单词,单词属性,id(种别码))
如:(if,关键字,3)
id通常情况下可以自己定义,例如无符号整数的id可以设为1,只要能区别不同的属性即可。(ps:如果在编写时有对应的种别码表,照着写就完事儿了。)

种别码表示例
  • 1
单词符号种别码
NUM0
Letter1
main2

三、实现过程

如何实现

  1. 读到空格则略过,读下一个字符;若读到的是字母,就再接着读,直到读到的既不是字母也不是数字也不是下划线,并将读到的写入到token数组
  2. 若读到的是数字,直到读到的不是数字或小数点,将读到的写入到token数组;
  3. 若读到的是<|>|=,则再读入下一位,若为=,则该运算符为<=|>=|==,若为其他字符,则返回<|>|=的种别码;
  4. 若读到的是/,则读下一位,若为*,则说明之后为多行注释,一直读入直到读入*,并判断下一位是否为/,若是则注释结束,不是继续往下一位读入;若读入\n,则行数加一,若读入的字符与以上都不匹配,则报错,并输出出错行数;
  5. 若读到/时,下一位又读到/,即读到的是单行注释,此时判断下一位是否为\n,若是,则注释结束,不是则继续读入下一位。

部分代码

--建立分析时的缓冲空间
  • 1
char ch =' ';	//存放读入当前的输入字符
int Line_NO;	//纪录行号
  • 1
  • 2
--建立关键字表
  • 1
struct keywords{	//关键字
	char lexptr[MAXBUF];
	int token;
};

struct keywords symtable[MAX];
char str[MAX][10]={"int","char","float","main","double","case","for","if","auto","else","do","while","void","static","return","break","struct","const","union","switch","typedef","enum"};	//记为3~24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
--初始化关键字表
  • 1
void init(){	//关键字表初始化
	int j;
	for(j=0; j<MAX; j++){
	   strcpy(symtable[j].lexptr,str[j]);
	   symtable[j].token=j+3;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
--Iskeyword函数分析关键字
  • 1
int Iskeyword(char * is_res){	//对关键字进行搜索
    int i;
    for(i=0;i<MAX;i++){
    	if((strcmp(symtable[i].lexptr,is_res))==0) 
	 		break;
   }
	if(i<MAX)
   		return symtable[i].token;		
	else
		return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
--IsLetter函数分析字母
  • 1
int IsLetter(char c){	//判断是否为字母
   	if(((c>='a')&&(c<='z'))||((c>='A')&&(c<='Z')))
   		return 1;
    else
		return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
--IsDigit函数分析数字
  • 1
int IsDigit(char c){	//判断是否为数字
   	if(c>='0'&&c<='9')
   		return 1;
   	else
	   	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
--碰到空格、tab跳过
  • 1
if(ch==' '||ch=='\t'){}
else if(ch=='\n')
	Line_NO++;
  • 1
  • 2
  • 3
--忽略大小写
  • 1
if((ch<='A')&&(ch>='Z'))     
	ch=ch+32;
  • 1
  • 2
--字符的处理,包括注释的去除,非法字符
  • 1
switch(ch){	//符号
	case'(' :fprintf(fpout,"%s\t\t%d\t\t分界符\n","(",26);break;
	case')' :fprintf(fpout,"%s\t\t%d\t\t分界符\n",")",27);break;
	case'[' :fprintf(fpout,"%s\t\t%d\t\t分界符\n","[",28);break;
	case']' :fprintf(fpout,"%s\t\t%d\t\t分界符\n","]",29);break;
	case';' :fprintf(fpout,"%s\t\t%d\t\t分界符\n",";",30);break;
	case'.' :fprintf(fpout,"%s\t\t%d\t\t分界符\n",".",31);break;
	case',' :fprintf(fpout,"%s\t\t%d\t\t分界符\n",",",32);break;
	case':' :fprintf(fpout,"%s\t\t%d\t\t分界符\n",":",33);break;
	case'{' :fprintf(fpout,"%s\t\t%d\t\t分界符\n","{",34);break;
	case'}' :fprintf(fpout,"%s\t\t%d\t\t分界符\n","}",35);break;
	case'"' :fprintf(fpout,"%s\t\t%d\t\t分界符\n","\"",36);break;
	case'+' :fprintf(fpout,"%s\t\t%d\t\t运算符\n","+",37);break;
	case'-' :fprintf(fpout,"%s\t\t%d\t\t运算符\n","-",38);break;
	case'*' :fprintf(fpout,"%s\t\t%d\t\t运算符\n","*",39);break;
	case'=' :fprintf(fpout,"%s\t\t%d\t\t运算符\n","=",40);break;
	case'>' :{
		ch=fgetc(fpin);
		if(ch=='=') 
			fprintf(fpout,"%s\t\t%d\t\t运算符\n",">=",41);
		else{
			fprintf(fpout,"%s\t\t%d\t\t运算符\n",">",42);
			fseek(fpin,-1L,SEEK_CUR);
		}
	}break;
	case'<' :{
		ch=fgetc(fpin);
		if(ch=='=')
			fprintf(fpout,"%s\t\t%d\t\t运算符\n","<=",43);
		else{
			fprintf(fpout,"%s\t\t%d\t\t运算符\n","<",44);
			fseek(fpin,-1L,SEEK_CUR);}
	}break;
	case'%' :{
		ch=fgetc(fpin);
	    if(ch=='d'||ch=='f'||ch=='s'||ch=='x')
	        fprintf(fpout,"%s\t\t%d\t\t输出格式符\n","\%",45);
		else
			fprintf(fpout,"%s\t\t%d\t\t运算符\n","\%",46);
	}break;
	case'/' :{
		ch=fgetc(fpin);//出现在/  /之间的全部作为注释部分处理
		if(ch=='*'){
	    	while(ch!='/'&&ch!=EOF)
				ch=fgetc(fpin);
	        if(ch==EOF) 
				fprintf(fpout,"缺少一个'/'");
		}
		else if(ch=='/'){
			while(ch!='\n'&&ch!=EOF)
				ch=fgetc(fpin);
		}
		else{
			fprintf(fpout,"%s\t\t%d\t\t运算符\n","/",47);
			fseek(fpin,-1L,SEEK_CUR);
		}
	}break;
	default :fprintf(fpout,"在第%d行无法识别的字符\t%c\n",Line_NO,ch);	//非法字符 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

四、测试

测试用例

测试文件s.txt

int main(void)
{
   int a1,b;
   a1=103;
   b=2;
   if(a1>=b){/*多行
注释*/
   	a1=a1*b;//单行注释
   }
   printf("%d",a1);
   return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出结果

输出文件r.txt

int		3		关键字
main	6		关键字
(		26		分界符
void	15		关键字
)		27		分界符
{		34		分界符
int		3		关键字
a1		1		标识符
,		32		分界符
b		1		标识符
;		30		分界符
a1		1		标识符
=		40		运算符
103		2		无符号整数
;		30		分界符
b		1		标识符
=		40		运算符
2		2		无符号整数
;		30		分界符
if		10		关键字
(		26		分界符
a1		1		标识符
>=		41		运算符
b		1		标识符
)		27		分界符
{		34		分界符
a1		1		标识符
=		40		运算符
a1		1		标识符
*		39		运算符
b		1		标识符
;		30		分界符
}		35		分界符
printf	1		标识符
(		26		分界符
"		36		分界符
%		45		输出格式符
"		36		分界符
,		32		分界符
a1		1		标识符
)		27		分界符
;		30		分界符
return	17		关键字
0		2		无符号整数
;		30		分界符
}		35		分界符
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

完整代码: https://download.csdn.net/download/yiwanxianyutang/20089286?spm=1001.2014.3001.5501

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/294197
推荐阅读
相关标签
  

闽ICP备14008679号