⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pl_0_word.cpp

📁 PL/0 语言的编译程序的一个部件
💻 CPP
字号:
/*
   南京航空航天大学  计算机系  吴潇
   欢迎与我交流, QQ:	706883830
*/
#include<stdio.h>
#include<memory.h>
#include<conio.h>
//#include<string.h>

//存储单词符号的结构
struct plword{
	int sort;//种别
	int value;//属性
};

int line_count;//行数
//全局变量

//	输入处理:从指定的文件(TXT)中获取一定长度的内容,存入指定的内存中
//	在读取内容的过程中,不隔断单词
//	skip为跳过的字符数 max 为输入缓冲区最大的字符个数
//	返回已读取的字符个数 错误则返回-1
int GetText(char* filename, char* buffer,unsigned int skip,int max)
{
	FILE  *fp=NULL;
	int   i=0;
	char c;

	if(NULL==filename || NULL==buffer)//错误检测
	{
		printf("Error\n未指定正确的文件名或缓冲区指针\n");
		return(0);
	}
	if(max<2)
	{
		printf("缓冲区太小!\n");
		return(-1);
	}
	fp=fopen(filename,"r");//只读方式打开文件

	if(NULL==fp)
	{
		printf("Error\n文件%s不存在,请创建该文件并将PL/0语言的源代码存入该文件\n",filename);
		return(0);
	}

	while(1!=feof(fp))//跳过一定字符,然后读取
	{
		c=fgetc(fp);
		if(skip>0)
		{
			//文件已读完,skip>0
			if(feof(fp))
			{
				printf("字符长度错误!\n");
				return(-1);
			}
			skip--;
		}
		else
		{
			//feof返回1时,本次读取的字符无效
			if(feof(fp))
				break;

			buffer[i]=c;
			if(i==max-2)//(预留最后一个单元存储 \0 )
			{
				//如果读进的不是空白字符,则退还字符,直到读取的为空白字符为止
				while(buffer[i]!=' ' &&
						buffer[i]!='\t' &&
						buffer[i]!='\n' &&
						buffer[i]!='\0')
						i--;
				break;
			}
			i++;
		}
	}
	buffer[i]='\0';

	fclose(fp);

	return(i);
}

//预处理:从输入缓冲区中取数据(不隔断单词),预处理后存入指定的扫描缓冲区
//		  在从缓冲区中取数据的时候,head 总是指向非空白字符;tail总是指向空白字符或者\0(head最大为 maxlength 
//																						 tail最大可取 maxlength+1)
//maxlength 为 input 中字符串的长度 (不包括\0)	//input[maxlength]为最后一个字符——不是 \0
//reset 为1,则表明输入缓冲区已重置
//错误返回 -1 全部完成返回 0 , 输入缓冲区未扫描完则返回 1
int Prepare(char* input, char* buffer,int maxlength,int reset)
{
	static int head=0;//head, tail 指示 buffer 中的位置
	static int tail=120;//head 和 tail 指示输入缓冲区

	int head0,tail0; //用于检测

	int i=head;

	if(NULL==input || NULL==buffer)//错误检测
	{
		printf("Error\n未指定正确的缓冲区指针\n");
		return(0);
	}

	if(reset!=0)//输入缓冲区被更新
	{
		head=0;

		//tail总是指向一个空白字符
		if(maxlength<120)
			tail=maxlength;
		else
			tail=120;
	}

	//检查是否截断了末尾的单词,并使tail指向空白字符
	while(input[tail]!=' '&&
		input[tail]!='\t'&&
		input[tail]!='\n' &&
		input[tail]!='\0')
	{
		tail--;

		if(tail<=head)
		{
			printf("Error: 未知错误 Prepare 函数\n");
			return(0);
		}
	}

	head0=head;
	tail0=tail;
	memcpy(buffer,input+head,tail-head);
	buffer[tail-head]='\0';

	//合并界符
	for(i=0;i<tail-head;i++)
	{
		if(buffer[i]==' '||
			buffer[i]=='\t')// \n作为特殊的界符(涉及到行号,留到后面处理)
		{
			buffer[i]=' ';
			if(buffer[i+1]==' '||
				buffer[i+1]=='\t')
			{
				for(int j=i;j<tail-head;j++)
					buffer[j]=buffer[j+1];
				i--;
			}
		}
	}

	//确定下次的head,tail,使head总不指向空白字符
	head=tail;
	if('\0'==input[head])
		return(0);
	while(input[head]==' '||
			input[head]=='\t')
	{

		head++;

		if(head>maxlength)
		{
			printf("未知错误!\n");
			return(-1);
		}
	}

	if(head>maxlength)
		return(-1);
	else
	{
		tail=head+120;
		if(tail>maxlength)
			tail=maxlength;
	}

	return(1);
}
//标识符 状态转换器
int id_detect(int last_status, char inpt)
{
	int status=-1;// -1 表示无法识别	2* 表示分析成功
	switch(last_status)
	{
	case 0:
		if(inpt==' ')
			status=0;

		if(inpt>=97 &&
			inpt<=122)
			status=1;

		if(inpt>=65 &&
			inpt<=90)
			status=1;

		break;
	case 1:
		if(inpt>=97 &&
			inpt<=122)
			status=1;
		else if(inpt>=65 &&
			inpt<=90)
			status=1;

		else if(inpt>=48 &&
			inpt<=57)
			status=1;
		else
			status=2;
		break;
	default:
		break;
	}
	return(status);
}
//常数识别 状态转换器
int integer_detect(int last_status, char inpt)
{
	int status=-1;// -1 表示无法识别	2* 表示分析成功
	switch(last_status)
	{
	case 0:
		if(inpt==' ')
			status=0;

		if(inpt>=48 &&
			inpt<=57)
			status=1;

		break;
	case 1:
		if(inpt>=48 &&
			inpt<=57)
			status=1;
		else
		{
			status=2;

			//常数识别中出现字母
			if(inpt>=97 && inpt<=122)
				status=3;

			if(inpt>=65 && inpt<=90)
				status=3;
		}
		break;
	default:
		break;
	}
	return(status);
}
//单字符算符识别 状态转换器 (\n也被当作算符处理)
int ssign_detect(int last_status, char inpt)
{
	int status=-1;// -1 表示无法识别	1 表示分析成功
	switch(last_status)
	{
	case 0:
		if(inpt==' ')
			status=0;

		if(inpt==';' ||
			inpt==',' ||
			inpt=='(' ||
			inpt==')' ||
			inpt=='+' ||
			inpt=='-' ||
			inpt=='=' ||
			inpt=='*' ||
			inpt=='/')

			status=1;
		if(inpt=='\n')
			status=1;

		break;
	case 1:
		break;
	default:
		break;
	}
	return(status);
}
//双字符算符识别 状态转换器
int dsign_detect(int last_status, char inpt)
{
	int status=-1;//-1表示无法识别		6* 表示已识别
	switch(last_status)
	{
	case 0:
		if(inpt==' ')
			status=0;
		else if(inpt==':')
			status=1;
		else if(inpt=='<')
			status=3;
		else if(inpt=='>')
			status=7;
		break;
	case 1:
		if(inpt=='=')
			status=2;
		break;
	case 2:
		status=6;
		break;
	case 3:
		if(inpt=='>')
			status=4;
		else if(inpt=='=')
			status=5;
		else
			status=6;
		break;
	case 4:
	case 5:
		status=6;
		break;
	case 7:
		if(inpt=='=')
			status=8;
		else
			status=6;
		break;
	case 8:
		status=6;
		break;
	}
	return(status);
}

int strlen(char* s)
{
	int count=0;
	if(s==NULL)
		return(0);

	while(*(s+count)!='\0')
		count++;
	return(count);
}

//分析器:返回值 0 表示当前扫描缓冲区未完成,1 表示完成(本次单词无效),-1 表示错误
//通过 buffer 末尾的 \0 判断结束
//该模块每当被调用一次,返回一个单词符号的种别码和属性码
int Analysis(char* buffer, int &sort, int &value,int reset)
{
	static int head=0;//【下标】每次调用承接上次的结果
	int offset=0;
	int status;
	char keywords[][10]={"program","const","var","procedure","begin",
						"end","if","then","else","while","call",
						"read","write","odd","do"};

	if(NULL==buffer)
	{
		printf("Error:未指定正确的缓冲区地址!\n");
		return(-1);
	}

	if(reset!=0)//已经更新了扫描缓冲区
		head=0;

	if(buffer[head]=='\0')
		return(1);//扫描缓冲区已全部分析,需要新的输入串


	///////////////检查是否是算符(单字符)/////////////
	offset=0;
	status=0;
	while(status!=1 && status!=-1)
	{
		status=ssign_detect(0,buffer[head+offset]);
		offset++;
	}
	offset--;

	if(status==1)
	{
		int i=0;
		switch(buffer[head+offset])
		{
		case ';':
			i=1;
			value=';';
			break;
		case ',':
			i=2;
			value=',';
			break;
		case '(':
			i=3;
			value='(';
			break;
		case ')':
			i=4;
			value=')';
			break;
		case '+':
			i=5;
			value='+';
			break;
		case '-':
			i=6;
			value='-';
			break;
		case '=':
			i=7;
			value='=';
			break;
		case '*':
			i=8;
			value='*';
			break;
		case '/':
			i=9;
			value='/';
			break;
		}
		sort=99+i;

		if(buffer[head+offset]=='\n')
		{
			line_count++;
			sort=16;
		}
//		printf("(%d,%c)\t",sort,value);
		offset++;

		head+=offset;
		while(buffer[head]==' ')
		{
			if(buffer[head]=='\0')
				return(1);
			head++;
		}
		return(0);
	}
	///////////检查是否是双字符算符/////////////
	status=0;
	offset=0;
	while(status!=6 && status!=-1)
	{
		status=dsign_detect(status,buffer[head+offset]);
		offset++;
	}
	offset--;

	if(status==6)
	{
		int i=0;
		switch(buffer[head])
		{
		case ':':
			i=10;
			value=':';
			break;
		case '<':
			if(buffer[head+1]=='>')
			{
				i=11;
				value='X';
			}
			else if(buffer[head+1]=='=')
			{
				i=12;
				value='l';
			}
			else
			{
				i=13;
				value='<';
			}
			break;
		case '>':
			if(buffer[head+1]=='=')
			{
				i=14;
				value='m';
			}
			else
			{
				i=15;
				value='>';
			}
			break;
		}
		sort=99+i;

//		printf("(%d,%c)\t",sort,value);

		head+=offset;
		while(buffer[head]==' ')
		{
			if(buffer[head]=='\0')
				return(1);
			head++;
		}
		return(0);
	}/////////////////////////////////////////////

	/////////////检查是否是标志符/////////////
	status=0;
	offset=0;
	while(status!=2 && status!=-1)
	{
		status=id_detect(status,buffer[head+offset]);
		offset++;
	}
	offset--;
	if(status==2)
	{
		int j;//关键字下标
		for(j=0;j<15;j++)
		{
			int tag=0;
			char *s=NULL;
			int len;
			s=keywords[j];
			len=strlen(s);

			for(int i=0;i<len;i++)
			{
				if(keywords[j][i]!=buffer[head+i])
				{
					tag=1;
					break;
				}
			}
			if(tag==1)
				continue;
			else if(tag==0)
			{
				sort=j+1;
				value=0;
				break;
			}
		}
		if(j>=15)
		{
			sort=0;//未找到关键字,识别为标志符
			value=0;
		}
//		printf("(%d,%d)\t",sort,value);

		head+=offset;
		while(buffer[head]==' ')
		{
			if(buffer[head]=='\0')
				return(1);
			head++;
		}
		return(0);
	}
	/////////////// 检查是否是常数 /////////////
	status=0;
	offset=0;
	bool d=false;
	while(status!=2 && status!=-1)
	{
		if(d==false)
			status=integer_detect(status,buffer[head+offset]);
		else
			status=id_detect(status,buffer[head+offset]);

		offset++;
		if(status==3)//常数识别到了字母
		{
			d=true;
			status=0;
			offset--;
		}
	}
	offset--;
	if(status==2)
	{
		sort=200;//常数种别码
		value=0;

		if(d==true)
		{
			char temp[50];

			memcpy(temp,buffer+head,offset);
			temp[offset]='\0';

			printf("第 %d 行错误,标志符(%s)不能以数字开头!\n",line_count,temp);
		}

		head+=offset;
		while(buffer[head]==' ')
		{
			if(buffer[head]=='\0')
				return(1);
			head++;
		}
		return(0);

				
	}

	//未识别出
	if(status==-1)
		printf("第 %d 行包含非法字符:\t%c\n",line_count,buffer[head]);

	head++;
	return(0);
}
//输出处理: 返回0/1来表示状态  0  表示缓冲区未满, 1表示满
//参数 bufferlength 为剩余的缓冲区长度
//	   sort 种别码	max		缓冲区最大长度
int Output(struct plword* buffer,int sort, int value,int max, int bufferlength)
{
	plword thisword;
	char*  filename="C:\\pl_0_tempfile.txt";
	FILE*  fp=NULL;

	if(NULL==buffer)
	{
		printf("错误:参数指定不正确!\n");
		return(1);
	}
	if(bufferlength<=0)
		return(1);
//	缓冲区已满!

	thisword.sort=sort;
	thisword.value=value;

	//把当前这个单词拷入缓冲区
	memcpy(buffer+max-bufferlength,&thisword,sizeof(plword));

//	如果这是最后一个单元,则把整个缓冲区的内容输出到文件
	if(1==bufferlength)
	{
		fp=fopen(filename,"ab");
		fseek(fp,0,SEEK_END);
		fwrite(buffer,sizeof(plword),max,fp);
		fclose(fp);
		return(1);
	}

	return(0);
}

void main()
{
	char buffer[1024];	//输入缓冲区(从文件中直接提取的一部分字符)
	int bufferlen;

	char pre[121];	//扫描缓冲区(从输入缓冲区中选取的不割裂单词的字符串)
	int sort,value;	//种别码和属性

	plword pbuffer[100];//单词符号缓冲区(经过扫描识别后的单词符号)

	int tag1=0;
	int tag2=0;
	int tag3=0;
	int flag=0,rst=0;//rst 表示输入缓冲区是否已更新 1 为是, 0 为否

	int skip=0;	//输入缓冲区中已读进的字符数
	int count=sizeof(pbuffer)/sizeof(plword);//单词缓冲区中剩余空间
	FILE*  fp=NULL;
	int read_count=0;

	line_count=1;//行号赋初值
	printf("开始进行词法分析:\n\n");
	//如果临时文件存在,则清空原文件的内容
	fp=fopen("C:\\pl_0_tempfile.txt","w");
	fclose(fp);

	do
	{
		//读取原始的字符串(保证不割裂单词)
		bufferlen=GetText("C:\\code.txt",buffer,skip,1024);
		if(bufferlen<=0)
			break;
		//记录已读取的字符数,以避免下次重复读取
		skip+=bufferlen;

		rst=1;

		do{
			//tag=1表示输入缓冲区未读完
			tag1=Prepare(buffer,pre,bufferlen,rst);
			rst=0;
			flag=1;

			//逐个识别出单词符号,将识别出的单词送Output处理
			do{
				tag2=Analysis(pre,sort,value,flag);
				if(tag2==1)
					break;

				tag3=Output(pbuffer,sort,value,sizeof(pbuffer)/sizeof(plword),count);
				read_count++;
				//tag3=1表示pbuffer中已无剩余空间
				if(tag3==1)
				{
					count=sizeof(pbuffer)/sizeof(plword);
					count++;
				}
				count--;//按字节计算

				flag=0;
			}while(tag2==0);

		}while(tag1==1);
	}while(bufferlen>0);

	//把剩余的单词符号输出
	if(0==tag3)
	{
		char*  filename="C:\\pl_0_tempfile.txt";

		fp=fopen(filename,"a");
		fseek(fp,0,SEEK_END);
		fwrite(pbuffer,sizeof(plword),sizeof(pbuffer)/sizeof(plword)-count,fp);
		fclose(fp);

	}


	printf("词法分析完成!\n\n共 %d 个单词,共 %d 行\n产生文件:C:\\pl_0_tempfile.txt\n",read_count,line_count);
	printf("按任意键继续......\n");
	getch();
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -