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

📄 scanner.cpp

📁 完整的S语言词法分析器
💻 CPP
字号:
// Scanner.cpp : Defines the entry point for the console application.
//
/************************************************************
词法分析程序
*************************************************************/
//#include "stdafx.h"

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define LENGTH 61
#define N 100
/***********************数据存储结构*************************/
//token文件记录结构
typedef struct token
{
	int  label;		//单词序号
	char name[30];		//单词自身值
	int  code;			//单词机内码(种别码)
	int  addr;			//单词在符号表中的入口地址,但关键字以-1表示
}token;
//关键字表记录结构
typedef struct KeyWord
{
	char name[30];
	int  code;
}KeyWord;
//符号(标识符/实常数/整常数/字符常量)表记录结构
typedef struct symble	
{
	int  number;			//序号
	int  type;				//类型
	char name[30];			//名字
}symble;
/*************************************************************/
char ch;
int var_count;		//符号表项计数器
int label_count;	//单词序号计数器
//int addr_count;	//地址计数器
int LineOfCurrent;	//当前源码行
int LineOfPro;		//已处理的源码行数计数器
int error_count;	//错误计数器

char filename[30];	
FILE *KeyFin;		//指向关键字文件的指针
FILE *SourceFin;	//指向源文件的指针
FILE *TokenFout;	//指向token输出文件指针
FILE *SymbleFout;	//指向符号表输出文件指针
token  CurrentToken;	//当前单词符号
symble CurrentSymble;	//查填符号表时的当前项
symble SymbleList[N];	//符号(变量,常量)表
KeyWord  key[LENGTH];	//关键字表
/**********************************************************/
void Scanner();
void ScannerInit();
void IsAlpha();
void IsNumber();
void IsAnotation();
void IsChar();
void IsOther();
void OutPut();
void Error(int a);
int WordHave();
int strcmp(char*s,char *t)/*两个字符串的精确比较,相等时返回0,否则返回1*/
{ 
	for(;*s==*t;s++,t++)
		if(*s==0)
			return 0;
	return 1;
}
/***************************  主程序  *********************************/
int main()
{
	int i=0,j=0;
	LineOfPro=0;
	var_count=0;
	//addr_count=1;
	label_count=1;
	for(i=0;i<N;i++){
		SymbleList[i].number=0;
  		SymbleList[i].type=0;
  		for(j=0;j<30;j++)
			SymbleList[i].name[j]='\0';
 	}
	Scanner();
	return 0;
}

/*************************   扫描程序  *******************************/
void Scanner()
{
	int i=0;
	error_count=0;
	ScannerInit();
	printf("**********************************************\n");
	printf("              S语言词法分析器                \n");
	printf("**********************************************\n");
	printf("输入源文件名(提示是s.txt):");
	for(;;){
		scanf("%c",&filename[i]);
		if(filename[i]==10)
			break;		//回车符
		i++;
	}
	filename[i]='\0';					//最后加上'\0'
	strcpy(filename,"s.txt");
	if((SourceFin=fopen(filename,"r"))==NULL){
		printf("无法打开文件%s.\n",filename);
		exit(1);
	}
	if((TokenFout=fopen("token.txt","w+"))==NULL){
		printf("无法打开文件token.txt.\n");
		exit(1);
	}
	if((SymbleFout=fopen("symble.txt","w+"))==NULL){
		printf("无法打开文件symble.txt.\n");
		exit(1);
	}
	ch=fgetc(SourceFin);   /*读取第一个字符*/
	while(ch!=EOF)			/*循环处理字符串*/
	{
		for(i=0;i<30;i++)	
			CurrentToken.name[i]='\0';
		if((ch>47)&&(ch<58))		//判断是否为数字
			IsNumber();
		else
		{
			if(((ch>64)&&(ch<90))||((ch>96)&&(ch<123))||ch=='_')	//判断是否为大小写字符或者是空格
				IsAlpha();	//标识符处理
			else
			{
				if(ch=='/') 
					IsAnotation();	//注释处理
				else
					if(ch=='\'')   //单引号
						IsChar();	//字符串(字符常量)处理
					else
						IsOther();//算术与关系运算符,(,),:,:=,.,;,逗号处理
			}
		}
	}
	fclose(SourceFin);
	fclose(TokenFout);
	fclose(SymbleFout);
	printf("源文件共%d行,现已全部分析完毕.\n",LineOfPro);
}

/********************    初始化    ************************/
void ScannerInit()
{
	int i=1;
	int k=0;
	if((KeyFin=fopen("key.txt","rb"))==NULL)			//预先检查是否存在key.txt文件,即关键字文件
	{
		printf("cannot open key.txt\n");
		exit(1);
	}
	for(i=0;i<60;i++)	{							//初始化清空
		for(k=0;k<30;k++) {
			key[i].name[k]='\0';			
		}
	}

	//修改读取操作
	/*for(i=0;i<60;i++){								//读入编码表
		fread(&key[i],sizeof(struct KeyWord),1,KeyFin);
	}*/
	//修改如下:
	for (i=0;i<60;i++) {
		fscanf(KeyFin,"%s",&key[i].name);
		fscanf(KeyFin,"%d",&key[i].code);
	}
	fclose(KeyFin);
}

/****************   数字处理   **************************/
void IsNumber()
{
	int k=0;			//索引
	int flag=0;			//实数标志
	char ch1;			//保存读取的符号
	while(((ch>47)&&(ch<58)))					//是否为数字
	{ 
		CurrentToken.name[k++]=ch;
		ch=fgetc(SourceFin);					//读取字符
		if(ch=='.')
		{
			flag=1;								//flag=1表示实数
			//ch=fgetc(SourceFin);
			break;
		}	
	}
	if (!flag) 
		CurrentToken.code=28;					//整常数
 
	//CurrentToken.addr=addr_count++;
	//CurrentToken.addr=var_count;
	CurrentToken.label=label_count++;			//单词序号加1
	
	if(flag)									//实数
	{ 
		ch1=fgetc(SourceFin);
		if((ch1>47)&&(ch1<58))
			CurrentToken.name[k++]=ch;			//记录小数点号
		else 
			Error(2);
		ch=ch1;
		while((ch>47)&&(ch<58))					//添加小数点后数字
		{
			CurrentToken.name[k++]=ch;
			ch=fgetc(SourceFin);
		}
		CurrentToken.code=29;					//标记为实数
		if(ch=='.')								//还有小数点,则舍去尾部
		{
			Error(2);
			ch=fgetc(SourceFin);
			while((ch>47)&&(ch<58))
				ch=fgetc(SourceFin);
		}
	}
	if(((ch>64)&&(ch<90))||((ch>96)&&(ch<123)))	//当前字符为字母,舍去尾部
	{
		Error(2);
		while(((ch>64)&&(ch<90))||((ch>96)&&(ch<123)))
		{ 
			ch=fgetc(SourceFin);
			while((ch>47)&&(ch<58))
				ch=fgetc(SourceFin);
		}
	}
	OutPut();
}

/***************    字母处理    *****************************/
void IsAlpha()
{
	int i,h;
	h=0;
	i=0;
	while(((ch>64)&&(ch<90))||((ch>96)&&(ch<123))||((ch>47)&&(ch<58))||(ch=='_'))
	{
		CurrentToken.name[i++]=ch;
		ch=fgetc(SourceFin);
	}
	for(i=0;i<LENGTH;i++)    /*判断输入字符是否为保留字*/
	{
		h=strcmp(CurrentToken.name,key[i].name);
		if(!h)
			break;			//是保留字
	}    
	if(!h) {				//是保留字
		CurrentToken.code=key[i].code;
		CurrentToken.addr=-1;
	}
	else {
		CurrentToken.code=27;
//		CurrentToken.addr=addr_count++;
	}
	CurrentToken.label=label_count++;
	OutPut();
}

/******************    字符串常量处理    ********************/
void IsChar()
{	
	int i=0;
	for(;;)
	{
		ch=fgetc(SourceFin);
		CurrentToken.code=30;
		if(ch!='\'') 
			CurrentToken.name[i++]=ch;
		else 
			break;
	}
	//CurrentToken.addr=addr_count++;
	CurrentToken.label=label_count++;
	OutPut();
	ch=fgetc(SourceFin);
}

/***************    注释处理    *********************/
void IsAnotation()
{	
	char ch1;
	ch1=ch;
	ch=fgetc(SourceFin);
	if(ch=='*')
	{
		LineOfCurrent=LineOfPro+1;				//处理行数加1
		for(;;)
		{
			ch=fgetc(SourceFin);
			if(ch==10) LineOfPro++;				//换行
			if(ch==EOF)
			{
				Error(3);break;	
			}									//注释不正常结束
	 		if(ch=='*')
			{
				ch1=ch;
				ch=fgetc(SourceFin);
				if(ch=='/')
				{
					ch=fgetc(SourceFin);break;}
				}
			}
	}
	else{    /*不是注释,当作除号处理*/
		 CurrentToken.name[0]='/';
		 CurrentToken.code=39;
		 CurrentToken.addr=-1;
		 CurrentToken.label=label_count++;
		 OutPut();
	}
}

/******************  其他情况的处理  *******************/
void IsOther()
{
	char ch1;
	int i;
	for(i=0;i<30;i++)								//清空
		CurrentToken.name[i]='\0';
	switch(ch)
	{
		case '(':
			CurrentToken.name[0]='(';
			CurrentToken.code=32;
			CurrentToken.addr=-1;
			CurrentToken.label=label_count++;
			OutPut();
			ch=fgetc(SourceFin);
			break;
		case ')':
			CurrentToken.name[0]=')';
			CurrentToken.code=33;
			CurrentToken.addr=-1;
			CurrentToken.label=label_count++;
			OutPut();
			ch=fgetc(SourceFin);
			break;
		case '*':
			CurrentToken.name[0]='*';
			CurrentToken.code=34;
			CurrentToken.addr=-1;
			CurrentToken.label=label_count++;
			OutPut();
			ch=fgetc(SourceFin);
			break;
		case '+':
			CurrentToken.name[0]='+';
			CurrentToken.code=35;
			CurrentToken.addr=-1;
			CurrentToken.label=label_count++;
			OutPut();
			ch=fgetc(SourceFin);
			break;
		case ',':
			CurrentToken.name[0]=',';
			CurrentToken.code=37;
			CurrentToken.addr=-1;
			CurrentToken.label=label_count++;
			OutPut();
			ch=fgetc(SourceFin);
			break;
		case '-':
			CurrentToken.name[0]='-';
			CurrentToken.code=36;
			CurrentToken.addr=-1;
			CurrentToken.label=label_count++;
			OutPut();
			ch=fgetc(SourceFin);
			break;
		case '.':
			CurrentToken.name[0]='.';
			CurrentToken.code=38;
			CurrentToken.addr=-1;
			CurrentToken.label=label_count++;
			OutPut();
			ch=fgetc(SourceFin);
			break;
		case ':':
			ch1=ch;
			ch=fgetc(SourceFin);
			if(ch!='='){
				CurrentToken.name[0]=':';
				CurrentToken.code=40;
				CurrentToken.addr=-1;
				CurrentToken.label=label_count++;
				OutPut();
				}
			else{
				CurrentToken.name[0]=':';
				CurrentToken.name[1]='=';
				CurrentToken.code=41;
				CurrentToken.addr=-1;
				CurrentToken.label=label_count++;
				OutPut();
				ch=fgetc(SourceFin);}
			break;
		case ';' :
			CurrentToken.name[0]=';';
			CurrentToken.code=42;
			CurrentToken.addr=-1;
			CurrentToken.label=label_count++;
			OutPut();
			ch=fgetc(SourceFin);
			break;
		case '<':
			ch1=fgetc(SourceFin);
			if(ch1=='='){
				CurrentToken.name[0]='<';
				CurrentToken.name[1]='=';
				CurrentToken.code=44;
				CurrentToken.addr=-1;
				CurrentToken.label=label_count++;
				OutPut();
				ch1=fgetc(SourceFin);
				}
			else{
				if(ch1=='>'){
					CurrentToken.name[0]='<';
					CurrentToken.name[1]='>';
					CurrentToken.code=45;
					CurrentToken.addr=-1;
					CurrentToken.label=label_count++;
					OutPut();
					ch1=fgetc(SourceFin);
					}
				else{
					CurrentToken.name[0]='<';
					CurrentToken.code=43;
					CurrentToken.addr=-1;
					CurrentToken.label=label_count++;
					OutPut();
					}
				}
			ch=ch1;
			break;
		case '=':
			CurrentToken.name[0]='=';
			CurrentToken.code=46;
			CurrentToken.addr=-1;
			CurrentToken.label=label_count++;
			OutPut();
			ch=fgetc(SourceFin);
			break;
		case'>':
			ch1=fgetc(SourceFin);
			if(ch1=='='){
				CurrentToken.name[0]='>';
				CurrentToken.name[1]='=';
				CurrentToken.code=48;
				CurrentToken.addr=-1;
				CurrentToken.label=label_count++;
				OutPut();
				ch1=fgetc(SourceFin);
				}
			else{
				CurrentToken.name[0]='>';
				CurrentToken.code=47;
				CurrentToken.addr=-1;
				CurrentToken.label=label_count++;
				OutPut();
				}
			ch=ch1;
			break;
		case 10:	
			LineOfPro++;
			ch=fgetc(SourceFin);
			break;
		case 13:
			LineOfPro++;
			ch=fgetc(SourceFin);
			break;
		case' ':
			ch=fgetc(SourceFin);
			break;
		case EOF:
			Error(4);
			break;
		default:
			Error(1);
			ch=fgetc(SourceFin);
			break;
		}//end switch(ch)
}

/***************    输出模块    *************************/
void OutPut()
{
	int flag,i=0;
	int k;
 
	//如果是 27=标识符 28=整常数 29=实常数 30=字符常数,查填符号表SymbleList
	if((CurrentToken.code==27)||
	 (CurrentToken.code==28)||
	 (CurrentToken.code==29)||
	 (CurrentToken.code==30))
	{
		CurrentSymble.number=CurrentToken.addr;
		CurrentSymble.type=CurrentToken.code;
		strcpy(CurrentSymble.name,CurrentToken.name);
     
		flag=WordHave();//查表

		if (flag==1)								//原来不在表中,现插入到表的最后,同时写符号表文件
		{
			//插入到表的最后
			SymbleList[var_count].number=CurrentSymble.number;
			SymbleList[var_count].type=CurrentSymble.type;
			strcpy(SymbleList[var_count].name,CurrentSymble.name);
		 		 
			//写符号表文件
			fprintf(SymbleFout,"%10d%10s%10d\n",
				CurrentSymble.number,
				CurrentSymble.name,
				CurrentSymble.type);
		}
	}
 
	/*写token表文件*/
	for(;;) 
		if(CurrentToken.name[i++]=='\0') 
			break;							//找到name字符串结束位置
	for(k=30-i;k>0;k--)
		CurrentToken.name[i]='\0';
		//CurrentToken.name[k]='\0';

	fprintf(TokenFout,"%10d%10s%10d%10d\n",
		 CurrentToken.label,
		 CurrentToken.name,
		 CurrentToken.code,
		 CurrentToken.addr);

	printf("%10d%10s%10d%10d\n",
		 CurrentToken.label,
		 CurrentToken.name,
		 CurrentToken.code,
		 CurrentToken.addr);
 }

void Error(int a)
{
	error_count++;
	switch(a)
	{
		case 1:printf("error%2d非法字符于第%3d行.\n",error_count,LineOfPro+1);break;
		case 2:printf("error%2d实常数出错于第%3d行.\n",error_count,LineOfPro+1);break;
		case 3:printf("error%2d第%3d行的注释没有与之匹配的注释符'*/'. \n",
			    error_count,LineOfCurrent);break;
		case 4:printf("error%2d非正常结束!\n",error_count);break;
		default:break;
	}
	return;
}

/**********查填符号表************/
int WordHave()
{
	int flag,i=0;
	//查表
	for(i=1;i<var_count+1;i++) 
	{
		flag=strcmp(CurrentSymble.name,SymbleList[i].name);
		if(flag==0){										//找到,设置当前符号的符号表入口地址,返回0
			CurrentToken.addr=SymbleList[i].number;
			CurrentSymble.number = SymbleList[i].number;
			return 0;
		}
	}
	//没找到,返回1
	var_count++;	 
	CurrentToken.addr=var_count;
	CurrentSymble.number = var_count ;
	return 1;
}

⌨️ 快捷键说明

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