Demllie>编译语言实现的核心问题,就是这里不明白让我白白写6000行垃圾

前言

核心问题就是分析时的迭代,因为不懂怎么迭代,我白白写了好多垃圾。下面做一个计算器,弄明白这个东西。

Token定义


typedef enum  {
     
	NIL,
	ADD,
	SUB,
	MUL,
	DIV,
	MOD,
	NUM,
	LB,
	RB
}TokenType;


typedef union {
     
	char c;
	double v;
}Var;


class Token {
     
private:
	static int counter;//类变量,不能再此处初始化
	int num;
	TokenType type = TokenType::NIL;
	Var value;
public:
	Token(char _value) {
     
		num = counter++;
		switch (_value) {
     
		case '+':
			type = TokenType::ADD;
			break;
		case '-':
			type = TokenType::SUB;
			break;
		case '*':
			type = TokenType::MUL;
			break;
		case '/':
			type = TokenType::DIV;
			break;
		case '%':
			type = TokenType::MOD;
			break;
		case '(':
			type = TokenType::LB;
			break;
		case ')':
			type = TokenType::RB;
			break;
		default:
			cout << "违法字符>" << _value << endl;
			exit(1);
			break;
		}

		value.c = _value;
	}
	Token(double _value) {
     
		num = counter++;
		type = TokenType::NUM;
		value.v = _value;
	}


	TokenType GetTokenType()const {
     
		return type;
	}
	double GetTokenValue()const {
     
		if (type == TokenType::NUM)return value.v;
		return 0;
	}
};

int Token::counter = 0;//类变量初始化


主要是两种,一种是单个char的操作符,另一种是多个字符的整数!!!

next找到下一个Token

定义几个全局变量,line数组是读取的一行字符串,index是其中的索引,currentToken是当前的Token


static Token* currentToken=NULL;
static int index = 0;
static char line[256] = {
      0 };

就是轮询一边,遇到空字符就跳过,遇到数字就结合起来,然后遇到非数字就把结合的整个数字变成一个Token。
遇到操作符就直接变成Token。


//获取下一个token
void next() {
     

	char name[256] = {
      0 };
	char c;
	double v=0;
	
	while ((c=line[index++]) != '\0') {
     
		if (c == ' ')continue;
		else if (c<='9'&&c>='0'   ) {
     
			v = v * 10 + c - '0';
			//
			char nextChar = line[index];
			if (!(nextChar <= '9' && nextChar >='0')) {
     
				if (currentToken != NULL) {
     
					delete currentToken;
					currentToken = NULL;
				}
				currentToken = new Token((double)v);
				break;
			}
		}
		else if (c == '(' || c==')' ) {
     
			if (currentToken != NULL)
				delete currentToken;
			currentToken = new Token((char)c);
			break;
		}
		else if (c == '+' || c == '-' || c == '*' || c == '\\' || c == '%'||c=='/') {
     
			if (currentToken != NULL) {
     
				delete currentToken;
				currentToken = NULL;
			}
			currentToken = new Token((char)c);
			break;
		}
	}


}

factor

factor、term、expr是核心问题。

expr是加减表达式或者一个数,形式是

expr ::= term [ ('+'|'-') term ]

term是乘除取余表达式或者一个数,形式是

term ::= factor [ ('*'|'/'|'%') factor]

factor是元素或者括号表达式,形式是

factor::= NUM | '(' expr ')'



#define ENUM_TYPE(type)   (type==0?"NIL":  \
												  (type==1?"ADD": \
											      (type==2?"SUB": \
												  (type==3?"MUL": \
												  (type==4?"DIV":  \
												  (type==5?"MOD": \
												  (type==6?"NUM": \
												  (type==7?"LB":"RB"))))))))


//终结符,是最底层的元素
double factor() {
     

	double value = 0;
	if (currentToken->GetTokenType() == TokenType::NUM) {
     
		value = currentToken->GetTokenValue();
	}
	else if (currentToken->GetTokenType() == TokenType::LB) {
     
		next();
		double ans = expr();//获取括号内的answer


		//错误处理
		if (currentToken->GetTokenType() != TokenType::RB) {
     
			cout << "语法分析出错,右括号不匹配!";
			if (currentToken->GetTokenType() == TokenType::NUM){
     
				cout << "value > "<<currentToken->GetTokenValue() << endl;
			}
			else {
     
				cout << "value > " << ENUM_TYPE(currentToken->GetTokenType()) << endl;
			}
		}

		value = ans;//终结符为表达式asnwer
		
	}

	next();//离开的时候别忘了移动一下token
	cout << "factor>" << value << endl;
	return value;
}

term


//乘除取余表达式
double term() {
     
	
	double value = 0;
	double left = factor();//乘除的上一级是单个元素或者表达
	double right = 0;
	value = left;


	//暂时保存current token前两个token的类型和值,即前一个OP
	Token tempToken = *currentToken;


	

	while (tempToken.GetTokenType() == TokenType::MOD || tempToken.GetTokenType() == TokenType::MUL  || tempToken.GetTokenType() == TokenType::DIV) {
     
		next();

		right = factor();//乘除的上一级是单个元素或者表达
		switch (tempToken.GetTokenType() ) {
     
		case TokenType::MOD:
			value = (int)value % (int)right;
			break;
		case TokenType::MUL:
			value = value * right;
			break;
		case TokenType::DIV:
			if (right == 0) {
     
				cout << "除数不能为零"<< endl;
				exit(1);
				return -1;
			}
			value = value / right;
			break;
		}
		tempToken = *currentToken;//移动token

	}
	cout << "term>" << value << endl;
	return value;
}

expr

和term是类似的。


//加减表达式
double expr() {
     

	double value = 0;
	double left = term();//加减的上一级是乘除取余
	double right = 0;
	value = left;


	//暂时保存current token前两个token的类型和值,即前一个OP
	Token tempToken = *currentToken;

	

	while (tempToken.GetTokenType() == TokenType::ADD || tempToken.GetTokenType() == TokenType::SUB ) {
     
		next();

		right = term(); //加减的上一级是乘除取余
		switch (tempToken.GetTokenType()) {
     
		case TokenType::ADD:
			value = value + right;
			break;
		case TokenType::SUB:
			value = value - right;
			break;
		}
		tempToken = *currentToken;//移动token

	}
	cout << "expr>" << value << endl;
	return value;
}

从文件中读取字符串


int main(void) {
     

	FILE* file = NULL;
	int a = fopen_s(&file, "test.txt", "r");
	if (a != 0) {
     
		cout << "文件打不开!" << endl;
		return 1;
	}

	fgets(line, 256, file);
	cout << line << endl;
	next();//取得第一个token
	cout << "ans="<<expr()<< endl;//迭代下去,返回answer
	

	return 0;
}

建一个文本文件test.txt,输入公式运行结果如下:

1+2*3/4 + (1+3*4)*4*5/2*12 - 0 - 100 + 120*2

factor>1
term>1
factor>2
factor>3
factor>4
term>1.5
factor>1
term>1
factor>3
factor>4
term>12
expr>13
factor>13
factor>4
factor>5
factor>2
factor>12
term>1560
factor>0
term>0
factor>100
term>100
factor>120
factor>2
term>240
expr>1702.5
ans=1702.5

你可能感兴趣的:(#,编译原理学习,编译原理,编译器,解释器,词法分析,语法分析)