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

一、前言

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

二、什么是词法分析器

定义

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

输出

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

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

三、实现过程

如何实现

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

部分代码

--建立分析时的缓冲空间
char ch =' ';	//存放读入当前的输入字符
int Line_NO;	//纪录行号
--建立关键字表
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
--初始化关键字表
void init(){	//关键字表初始化
	int j;
	for(j=0; j<MAX; j++){
	   strcpy(symtable[j].lexptr,str[j]);
	   symtable[j].token=j+3;
	}
}
--Iskeyword函数分析关键字
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;
}
--IsLetter函数分析字母
int IsLetter(char c){	//判断是否为字母
   	if(((c>='a')&&(c<='z'))||((c>='A')&&(c<='Z')))
   		return 1;
    else
		return 0;
}
--IsDigit函数分析数字
int IsDigit(char c){	//判断是否为数字
   	if(c>='0'&&c<='9')
   		return 1;
   	else
	   	return 0;
}
--碰到空格、tab跳过
if(ch==' '||ch=='\t'){}
else if(ch=='\n')
	Line_NO++;
--忽略大小写
if((ch<='A')&&(ch>='Z'))     
	ch=ch+32;
--字符的处理,包括注释的去除,非法字符
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);	//非法字符 
}

四、测试

测试用例

测试文件s.txt

int main(void)
{
   int a1,b;
   a1=103;
   b=2;
   if(a1>=b){/*多行
注释*/
   	a1=a1*b;//单行注释
   }
   printf("%d",a1);
   return 0;
}

输出结果

输出文件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		分界符

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

  • 11
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值