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

📄 2.cpp

📁 编译表达式计算
💻 CPP
字号:
#include<iostream>
#include<setjmp.h>
#include<fstream>
#include<sstream>
   
#define EXP_LEN    100                       //定义输入字符缓冲区的长度
/*------------出错代码的宏定义--------------*/
#define INVALID_CHAR_TAIL 0          //表达式后跟有非法字符
#define CHAR_AFTER_RIGHT 1           //右括号后连接非法字符
#define LEFT_AFTER_NUM  2            //数字后非法直接连接左括号
#define INVALID_CHAR_IN  3           //表达式中含有非法字符
#define NO_RIGHT   4                 //缺少右括号
#define EMPTY_BRACKET  5             //括号内无表达式
#define UNEXPECTED_END  6            //预期外的算术表达式结束
using namespace std;
const string ErrCodeStr[]=                   //表达式出错信息
{
 "表达式后跟有非法字符!",
 "右括号后连接非法字符!",
 "数字后非法直接连接左括号!",
 "表达式中含有非法字符!",
 "缺少右括号!",
 "括号内无表达式或表达式不完整!",
 "表达式非法结束或表达式不完整!"
};
static char expr[EXP_LEN];                 //算术表达式输入字符缓冲区
static int pos;                            //字符指示器标志:用来保存正在分析的字符的位置
static jmp_buf errjb;                      //出错跳转缓冲器
//********以下是函数声明*********
//产生式“E -> T+E | T-E | T”的函数,用来分析加减算术表达式。
int E_AddSub(); 
//产生式“T -> F*T | F/T | F”的函数,用来分析乘除算术表达式。
int T_MulDiv(); 
//产生式“F -> i | (E)”的函数,用来分析数字和括号内的表达式。
int F_Number();
//出错处理函数,可以指出错误位置,出错信息。
void Error(int ErrCode);
int main()
{
 int ans;                                   //保存算术表达式的计算结果
 char opt,quit;
 bool esc=false;                               //是否退出计算
MENU:
	cout<<"请选择程序执行方式:"<<endl;
	cout<<"[1]从键盘输入表达式;"<<endl;	
	cout<<"[2]处理文件中表达式;"<<endl;
	cout<<"[Q]退出本程序。"<<endl;
while(1)
{
	cin>>opt;
	if(opt=='1')
	{
	
		//在此设定一个跳转目标,如果本程序的其他函数调用longjmp,
		//执行指令就跳转到这里,从这里继续执行。
		OPT:if(setjmp(errjb)==0)                       //如果没有错误
			{
				pos=0;									//初始化字符指示器为0,即指向输入字符串的第一个字符。
				cout<<"请输入一个算术表达式:"<<endl;
				cin>>expr;
		
				//调用推导式“E -> T+E | T-E | T”的函数,
				//从起始符号“E”开始推导。
				ans=E_AddSub();
				//此时,程序认为对表达式的语法分析已经完毕,下面判断出错的原因:
				//如果表达式中的某个右括号后直接跟着数字或其他字符,
				//则报错,因为数字i不属于FOLLOW())集。
				if(expr[pos-1]==')'&&expr[pos]!='\0')
				Error(CHAR_AFTER_RIGHT);
				//如果表达式中的某个数字或右括号后直接跟着左括号,
				//则报错,因为左括号不属于FOLLOW(E)集。
				if(expr[pos]=='(')
				Error(LEFT_AFTER_NUM);
				//如果结尾有其他非法字符
				if(expr[pos]!='\0')
				Error(INVALID_CHAR_TAIL);
				cout<<"计算得出表达式的值为:"<<ans<<endl;
			}
			else
			{
				//setjmp(errjb)!=0的情况:
				cout<<"请重新输入!"<<endl;
			}
			cout<<"表达式处理完毕:"<<endl;
			cout<<"键入“q”回到主选单:"<<endl;
			cout<<"键入任意键继续计算:"<<endl;
			cout<<endl;
			cin>>quit;
			if(quit=='q')goto MENU;
			else goto OPT;
	
	}
	else if(opt=='2')
	{
		ifstream in("input.txt");
		int line=0;
		for(string s;getline(in,s);line++)
		{
			for(istringstream sin(s);sin>>expr;)
				{
	
					if(setjmp(errjb)==0)                       //如果没有错误
					{
						pos=0;									//初始化字符指示器为0,即指向输入字符串的第一个字符。
					
						//调用推导式“E -> T+E | T-E | T”的函数,
						//从起始符号“E”开始推导。
						ans=E_AddSub();
						//此时,程序认为对表达式的语法分析已经完毕,下面判断出错的原因:
						//如果表达式中的某个右括号后直接跟着数字或其他字符,
						//则报错,因为数字i不属于FOLLOW())集。
						if(expr[pos-1]==')'&&expr[pos]!='\0')
						{
							cout<<"行"<<line<<"---";
							Error(CHAR_AFTER_RIGHT);
						}
						//如果表达式中的某个数字或右括号后直接跟着左括号,
						//则报错,因为左括号不属于FOLLOW(E)集。
						if(expr[pos]=='(')
						{
							cout<<"行"<<line<<"---";
							Error(LEFT_AFTER_NUM);
						}
						//如果结尾有其他非法字符
						if(expr[pos]!='\0')
						{
							cout<<"行"<<line<<"---";
							Error(INVALID_CHAR_TAIL);
						}
    
						cout<<"行"<<line<<"---计算得出表达式的值为:"<<ans<<endl;
					
					}
					else
					{
						//setjmp(errjb)!=0的情况:
						cout<<"行"<<line<<"---该行表达式不合法!"<<endl;
					}
				}
		}
		cout<<"文件处理完毕"<<endl;
		cout<<endl;
		goto MENU;
	}
	else if(opt=='Q'||opt=='q')
		break;
	else
		cout<<"输入有误,请从新输入!"<<endl;
}
return 0;
}
//产生式“E -> T+E | T-E | T”的函数,用来分析加减算术表达式。
//返回计算结果
int E_AddSub()
{
	int rtn=T_MulDiv();              //计算加减算术表达式的左元
	while(expr[pos]=='+'||expr[pos]=='-')
	{
		int op=expr[pos++];             //取字符缓冲区中当前位置的符号到op
		int opr2=T_MulDiv();            //计算加减算术表达式的右元
		//计算求值
		if(op=='+')                     //如果是"+"号
			rtn+=opr2;                     //则用加法计算
		else                            //否则(是"-"号)
			rtn-=opr2;                     //用减法计算
	}
	return rtn;
}
//推导式“T -> F*T | F/T | F”的函数,用来分析乘除算术表达式。
//返回计算结果
int T_MulDiv()
{
	int rtn=F_Number();          //计算乘除算术表达式的左元
	while(expr[pos]=='*'||expr[pos]=='/')
	{
		int op=expr[pos++];        //取字符缓冲区中当前位置的符号到op
		int opr2=F_Number();      //计算乘除算术表达式的右元
		//计算求值
		if(op=='*')                       //如果是"*"号
			rtn*=opr2;                     //则用乘法计算
		else                              //否则(是"\"号)
			rtn/=opr2;                     //用除法计算
	}
	return rtn;
}
//产生式“F -> i | (E)”的函数,用来分析数字和括号内的表达式。
int F_Number()
{
	int rtn;                            //声明存储返回值的变量
	//用产生式F->(E)推导:
	if(expr[pos]=='(')              //如果字符缓冲区当前位置的符号是"("
	{
		pos++;                         //则指示器加一指向下一个符号
		rtn=E_AddSub();           //调用产生式“E -> T+E | T-E | T”的分析函数
		if(expr[pos++]!=')')         //如果没有与"("匹配的")"
		Error(NO_RIGHT);        //则产生错误
		return rtn;
	} 

	if(isdigit(expr[pos]))        //如果字符缓冲区中当前位置的字符为数字
	{
		//则用产生式F -> i推导
		//把字符缓冲区中当前位置的字符串转换为整数
		rtn=atoi(expr+pos);//把字符串转换成整型数 用法: int atoi(const char *nptr);
		//改变指示器的值,跳过字符缓冲区的数字部分,找到下一个输入字符。
		while(isdigit(expr[pos]))
		pos++;
	}
	else                                      //如果不是数字则产生相应的错误
	{
		if(expr[pos]==')')                    //如果发现一个")"
			Error(EMPTY_BRACKET);    //则是括号是空的,即括号内无算术表达式。
		else if(expr[pos]=='\0')           //如果此时输入串结束
			Error(UNEXPECTED_END);  //则算术表达式非法结束
		else
			Error(INVALID_CHAR_IN);    //否则输入字符串中含有非法字符
	}
	return rtn;
}
//出错处理函数,输入错误代码,可以指出错误位置,出错信息。
void Error(int ErrCode)
{
	cout<<'\r';                       //换行
	while(pos--)
	cout<<' ';                      //打印空格,把指示错误的"^"移到输入字符串的出错位置
	cout<<"^ 语法错误 !!!  "<<ErrCodeStr[ErrCode].c_str()<<endl;
	longjmp(errjb,1); //跳转到main函数中的setjmp调用处,并设置setjmp(errjb)返回值为1
	//longjmp(errjb,r);表示跳转到setjmp的调用处,并设置setjmp(errjb)返回值为r
}

⌨️ 快捷键说明

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