编译原理实验一:源程序的预处理及词法分析程序设计

实验一 源程序的预处理及词法分析程序设计

一、 实验目的
设计并实现一个包含预处理功能的词法分析程序,加深对编译中词法分析过程的理解。

二、 实验要求
1、实现预处理功能
源程序中可能包含有对程序执行无意义的符号,要求将其剔除。
首先编制一个源程序的输入过程,从键盘、文件或文本框输入若干行语句,依次存入输入缓冲区(字符型数据);然后编制一个预处理子程序,去掉输入串中的回车符、换行符和跳格符等编辑性文字;把多个空白符合并为一个;去掉注释。
2、实现词法分析功能
输入:所给文法的源程序字符串。
输出:二元组(syn,token或sum)构成的序列。其中,
syn为单词种别码。
Token为存放的单词自身字符串。
Sum为整型常量。
具体实现时,可以将单词的二元组用结构进行处理。
3、待分析的C语言子集的词法
1)关键字

main  if  then  while  do  
static  int  double  struct  
break  else  long  switch  
case  typedef  char  return  
const  float  short  continue  
for  void   sizeof   

所有的关键字都是小写。
2)运算符和界符

-    *  /   +    =    <   <=  >  >=   ; (  )  #

3)其他标记ID和NUM
通过以下正规式定义其他标记:

ID→letter(letter|digit)*
NUM→digit digit*
letter→a||z|A||Z
digit→0||9

4)空格由空白、制表符和换行符组成
空格一般用来分隔ID、NUM、专用符号和关键字,词法分析阶段通常被忽略。
4、各种单词符号对应的种别码
表1 各种单词符号的种别码

单词符号   种别码       单词符号   种别码       
main       1             ;           41     
if         2             (           42      
then       3             )           43      
while      4             int         7 
do         5             double      8   
static     6             struct      9   
ID         25            break       10  
NUM        26            else        11  
+          27            long        12  
-          28            switch      13  
*          29            case        14     
/          30            typedef     15
**         31            char        16
==         32            return      17
<          33            const       18
!=         34            float       19
<=         35            short       20
>          36            continue    21
>=         37            for         22
=          38            void        23
 [          39           sizeof      24
]          40              #         0 
++         44             --         45
{          46              }         47
,          48              &         49
%          50

5、 词法分析程序的主要算法思想
算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到的单词符号的第一个字符的种类,拼出相应的单词符号。

其中初值包括如下两方面:
(1) 关键字表初值
关键字作为特殊标识符处理,把它们预先安排在一张表格中(称为关键字表),当扫描程序识别出标识符时,查关键字表。如能查到匹配的单词,则该单词为关键字,否则为一般标识符。关键字表为一个字符串数组,其描述如下:

char *rwtab[27]={“main”,if,”then”,while,do,static,int,double,struct,break,else,long,switch,case,typedef,char,return,const,float,short,continue,for,void,sizeof}

(2) 程序中需要用到的主要变量:syn,token和sum。

代码如下:

#include <bits/stdc++.h>
using namespace std;

//@author lyj
 #define N 10005
 string rwtab[27]={" ","main","if","then","while","do",
"static","int","double","struct","break",
"else","long","switch","case","typedef",
"char","return","const","float","short",
"continue","for","void","sizeof"};//关键字表 


//预处理 
void init(){
	//判断测试源文件是否存在 
	FILE *p1,*p2;
	if((p1=fopen("testinput.txt","r"))==NULL){
		cout<<"栓Q!文件不存在或文件错误!"<<endl; 
	}
	//读入测试文件中的字符 
	int i=0,j=0;
	char str[N],str1[N],c;
	while((c=fgetc(p1))!=EOF){
		str[i]=c;
		i++;
	}
	fclose(p1);
	str[i]='\0';
	
	//进行处理 
	for(i=0;i<strlen(str);i++){
		//判断单行注释,处理 
		if(str[i]=='/'&&str[i+1]=='/'){
			while(str[i]!='\n'){
				i++;
			}
		}
		//判断多行注释,处理 
		else if(str[i]=='/'&&str[i+1]=='*'){
			while(!(str[i]=='*'&&str[i+1]=='/')){
				i++;
			}
			i+=2;
		}
		//判断多个空格,处理 
		else if(str[i]==' '&&str[i+1]==' '){
			while(str[i]==' '){
				i++;
			}
			i--;
			if(str1[j-1]!=' '){
				str1[j++]=' ';
			}
		}
		//判断回车、换行、跳格等编辑性字符,处理 
		else if(str[i]=='\r'||str[i]=='\n'||str[i]=='\t'){
			if(str1[j-1]!=' '){
				str1[j++]=' ';
			}
		}
		//其他字符处理 
		else
		str1[j++]=str[i];
	}
	str1[j]='\0';
	
	//将预处理后得到的字符存储到文本文件 
	if((p2=fopen("testoutput.txt","w"))==NULL){
		cout<<"预处理文件存储失败!"<<endl;
	}
	puts(str1);
	fputs(str1,p2);
	fclose(p2);
}

//扫描子程序 
void scanner(){
	FILE *p1,*p2;
	
	int syn,sum;
    char token[200];
    char str[N];
    char c;
    int i=0;
	int j=0;
    int k;
    
    //打开预处理后的文件,读取字符 
	 if((p1=fopen("testoutput.txt","r"))==NULL){
	 	cout<<"栓Q!文件不存在!"<<endl;
	 }
	 while((c=fgetc(p1))!=EOF){
	 	str[i]=c;
	 	i++;
	 }
	 fclose(p1);
	 str[i]='\0';
	 //创建结果文件 
	 if((p2=fopen("analysis.txt","w"))==NULL){
	 	cout<<"栓Q!出错了!"<<endl;
	 }
	 
	 //开始扫描 
	 for(i=0;str[i]!='\0';){
	 	j=0;
	 	sum=-1;//标志非数字 

	 //处理关键字、标识符 
	 if((str[i]>='a'&&str[i]<='z')||(str[i]>='A'&&str[i]<='Z')||str[i]=='_'){
	 	while((str[i]>='a'&&str[i]<='z')||(str[i]>='A'&&str[i]<='Z')||(str[i]>='0'&&str[i]<='9')||str[i]=='_'){
	 		token[j++]=str[i++];
		 }
		 token[j]='\0';
		 for(k=1;k<27;k++){
		 	if(rwtab[k]==token)
		 	    break;//跳出循环时,k=关键字的种别码 
		 } 
		 
		if(k<=24){
			syn=k;
		}
		else{
			syn=25;//非关键字,种别码为25 
		}
	 }
	 //以数字开头的非法标识符处理 
	 else if((str[i]>='0'&&str[i]<='9')&&((str[i+1]>='a'&&str[i+1]<='z')|| (str[i+1]>='A'&&str[i+1]<='Z')||str[i+1]=='_')){
		    sum=-1;
			syn = -1;//标志非法标识符 
            while((str[i]>='a'&&str[i]<='z')||(str[i]>='A'&&str[i]<='Z')||str[i]=='_'||(str[i]>='0'&&str[i]<='9')){
               token[j++] = str[i++]; 
			   }
			 i--;
        }
	 //数字处理	
	 else if(str[i]>='0'&&str[i]<='9') {	 
        	while(str[i]>='0'&&str[i]<='9'){
			 token[j++]=str[i++];
		    }
		 token[j]='\0';
		 sum=atoi(token);//字符串转换为数字
		 if(sum>=0)
		    syn=26;
	 }
	 //空格的处理
	else if(str[i]==' '){
	 	i++;//遇到空格跳过 
	 	syn=-2;//空格标志 
	 }
	 //运算符、界符等处理 
	 else{
	 	//连续两个符号组成的运算符 
	 	if(str[i]=='+'&&str[i+1]=='+'){
	 		syn=44;
	 		token[j++]=str[i++];
	 		token[j++]=str[i++];
		 } 
		 else if(str[i]=='-'&&str[i+1]=='-'){
		 	syn=45;
		 	token[j++]=str[i++];
	 		token[j++]=str[i++];
		 }
		 else if(str[i]=='*'&&str[i+1]=='*'){
		 	syn=31;
		 	token[j++]=str[i++];
	 		token[j++]=str[i++];
		 }
		 else if(str[i]=='='&&str[i+1]=='='){
		 	syn=32;
		 	token[j++]=str[i++];
	 		token[j++]=str[i++];
		 }
		 else if(str[i]=='!'&&str[i+1]=='='){
		 	syn=34;
		 	token[j++]=str[i++];
	 		token[j++]=str[i++];
		 }
		 else if(str[i]=='<'&&str[i+1]=='='){
		 	syn=35;
		 	token[j++]=str[i++];
	 		token[j++]=str[i++];
		 }
		 else if(str[i]=='>'&&str[i+1]=='='){
		 	syn=37;
		 	token[j++]=str[i++];
	 		token[j++]=str[i++];
		 }
		 //单个运算符号及界符等 
		 else{
		 	switch(str[i]){
		 		case '+':syn=27;break;
		 		case '-':syn=28;break;
		 		case '*':syn=29;break;
		 		case '/':syn=30;break;
		 		case '<':syn=33;break;
		 		case '>':syn=36;break;
		 		case '=':syn=38;break;
		 		case '[':syn=39;break;
		 		case ']':syn=40;break;
		 		case ';':syn=41;break;
		 		case '(':syn=42;break;
		 		case ')':syn=43;break;
		 		case '{':syn=46;break;
		 		case '}':syn=47;break;
		 		case ',':syn=48;break;
				case '&':syn=49;break;
				case '%':syn=50;break;
				case '#':syn=0;break;
		 		default:syn=-3;break;
				 }
		 	token[j++]=str[i++];
		 }	
		 	 
		token[j]='\0';	 
		 } 
		 
		 
		 //输出结果 
		 if(sum!=-1){
		 	printf("%d : %d\n",sum,syn);//输出数字 
		 	fprintf(p2,"%d : %d\n",sum,syn);
		 }
		 else if(syn==-1){
		 	printf("栓Q!%s 是非法标识符\n",token);//输出非法标识符 
		 	fprintf(p2,"栓Q!%s 是非法标识符\n",token);
		 }
		 else if(syn==-3){
		 	printf("%s :栓Q!无法识别它!\n",token);//输出无法识别的 
		 	fprintf(p2,"%s :栓Q!无法识别它!\n",token);
		 }
		 else if(syn!=-1&&syn!=-2&&syn!=-3){
		 	printf("%s : %d\n",token,syn);//输出可以识别的
		 	fprintf(p2,"%s : %d\n",token,syn);	
		 }
		 	 
	 } 
	 
	 fclose(p2);
}



int main(){
	//进行预处理
	init();
	//调用扫描子程序 
	scanner();
}

测试数据

在这里插入图片描述
结果
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全糖去冰不加料

打赏一块钱💰也是钱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值