lang:自制編程語言10——改進賦值

前言

賦值節點的interpret接口、interpet_str接口、interpret_bool接口不是並列的關係……

這個自制編程語言的項目中,已經遇到太多的不對稱的東西了,明明是相似的接口,相似的類型,但是實現卻特別冗餘!沒辦法把兩個相似的函數放在一起比較,這說明一個問題,我的項目架構不好,有很多辣雞代碼!

現在來解決這個問題!


設置firstId_is_bool_flag標誌

項目中的數據類型:int/number/nil/bool/string
底層數據類型:double/bool/string

項目中的Token有下面三種情況

  1. 數字、數字類型的id
  2. 字符串、字符串類型的id
  3. 布爾類型的id

之前爲了將簡單表達式從語句中抽離出來,就是用and , >= 之類的符號,分割簡單表達式,將其作爲判斷式或邏輯式的單元!!!
爲此引入了firstId_is_string_flag,因爲要靠簡單表達式開頭的數據,區分簡單表達式的模式,是否是字符串拼接模式!!!

a = TRUE; 這樣的語句會調用到EqExpr的interpret接口,還會被當成計算式計算。也就是出現下面這張情況

a = 1 + TRUE;
[result]1

所以,規定

  • 如果表達式是簡單表達式,並且第一個是bool類型的id,那麼,就準備退出——此id後面如果不是分號,就報錯!如果是分號就如願退出!
	void Parser::CreateASTree() {
		//遍歷全局中綴表達式
		try {
			for (int it = start; it <= end; ++it) {
				(this->*funcMap[global_infix[it]->type])(it);//處理Token和keyword

				if (end_flag)break;//遇到分號跳出
				
				if (firstId_is_bool_flag) {//表達式第一個id是bool類型的變量
					if (global_infix[ ++ it]->type != SuatinTokenType_Sem) {//如果下一個不是分號
						ThrowException<SuatinErrorType_Syntax>(start,end,"boolean identifier cannot used for calculate");
						return;
					}
					DealToken_Sem(it);
					break;
				}
			}

			if (root == NULL) ThrowException<SuatinErrorType_Value>(start,end,"abstract syntax tree was none");
			/*
			judgeRoot <-> exprRoot
			logicRoot  <->  exprRoot
			exprRoot  <->  root 
			最後除了root外,其他指針都爲空!
			*/

			CheckASTreeSyntax();

		}
		catch (SuatinExcept& e) {
			std::cout << e.what();
		}
		catch (...) {
			std::cout << "parser analysis error";
		}
	}
	
	void Parser::DealToken_Id(int& _t){
		
		...
		
		//檢查一下,表達式的開頭第一個id的類型,如果是bool類型就準備退出構造語法樹的階段
		if (firstId_is_bool_flag == false && m_exprType == ExprType_Simple) {
			if (SuatinEnv[global_infix[_t]->name]->type == SuatinIDType_bool) {
				firstId_is_bool_flag = true;
			}
		}
	}
	void Parser::interpret() {


		try {

			if (m_exprType == ExprType_Simple) { //簡單表達式
				if (firstId_is_bool_flag) {  //開頭第一個是bool類型的id
					bool result = root == NULL ? true : root->interpret_bool();
					std::cout << "[result]" << BOOL2STR(result) << std::endl;
				}
				else if (m_simpleExprType == SimpleExprType_NumCalc) { //五則計算式
				...
	...

使EqExpr的三個解釋接口並列

因爲五則計算式中只有數字、字符串拼接式中只有字符串、開頭bool或判斷式或邏輯式的返回只有布爾,所以解釋起來,清楚多了!
果然是自己辣雞代碼寫多了!

	//處理數字、數字類型的id--------------------
	double EqExpr::interpret() {
		double rightValue = right->interpret();//遞歸進去
		

		////把右邊的變量的值拷貝給左邊的變量,因爲是對符號表的操作,不改變指針,所以傳入一級指針就好!!!
		//auto copyBlock = [](Expr* _left,Expr* _right) {
		//	IDExpr* dst = dynamic_cast<IDExpr*>(_left);
		//	IDExpr* src = dynamic_cast<IDExpr*>(_right);
		//	CopyID(dst->GetName(), src->GetName());
		//};

		////賦值右邊的表達式只有單個Id
		//if (typeid(*(right->GetClassType())) == typeid(IDExpr)) { //右孩子只有個變量節點!!!
		//	copyBlock(left, right);
		//	return rightValue;
		//}
		//else if (typeid(*(right->GetClassType())) == typeid(EqExpr)) { //右孩子同樣是個賦值節點!!!

		//	SymbolExpr* node_tmp = dynamic_cast<SymbolExpr*>(right);
		//	copyBlock(left, node_tmp->GetLeft());
		//	return rightValue;
		//}
		
		IDExpr* var = dynamic_cast<IDExpr*>(left);
		var->SetNumber(rightValue);
		return rightValue;
	}


	//處理字符串、字符串類型的id------------------
	std::string EqExpr::interpret_str() {
		std::string rightValue = right->interpret_str();//遞歸進去


		////把右邊的變量的值拷貝給左邊的變量,因爲是對符號表的操作,不改變指針,所以傳入一級指針就好!!!
		//auto copyBlock = [](Expr* _left, Expr* _right) {
		//	IDExpr* dst = dynamic_cast<IDExpr*>(_left);
		//	IDExpr* src = dynamic_cast<IDExpr*>(_right);
		//	CopyID(dst->GetName(), src->GetName());
		//};

		////賦值右邊的表達式只有單個Id
		//if (typeid(*(right->GetClassType())) == typeid(IDExpr)) { //右孩子只有個變量節點!!!
		//	copyBlock(left, right);
		//	return rightValue;
		//}
		//else if (typeid(*(right->GetClassType())) == typeid(EqExpr)) { //右孩子同樣是個賦值節點!!!

		//	SymbolExpr* node_tmp = dynamic_cast<SymbolExpr*>(right);
		//	copyBlock(left, node_tmp->GetLeft());
		//	return rightValue;

		//}


		IDExpr* var = dynamic_cast<IDExpr*>(left);
		var->SetString(rightValue);
		return rightValue;
	}

	//bool類型的id--------------------
	bool EqExpr::interpret_bool() {
		bool rightValue = right->interpret_bool();//遞歸進去
		IDExpr* var = dynamic_cast<IDExpr*>(left);
		var->SetBool(rightValue);
		return rightValue;
	}

這樣操作一番後,可以看出,開頭bool、五則計算、字符串拼接這幾個已經是並列的了,可以看做三個模式!

[項目代碼下載地址]
https://download.csdn.net/download/weixin_41374099/12238776

BDWP
鏈接:https://pan.baidu.com/s/1uVVWEC8UAEYB7X1z2r8FBQ
提取碼:85ep

  \;
  \;

去掉開頭bool類型的特殊性

——2020.3.13
之前寫完上面的內容覺得基本單行語句做完了,沒錯誤了!其實不然,我將開頭bool類型的語句單獨拿出來也是有問題的!

a = TRUE == "hello";//error

不再允許上面的寫法,所以,之前所思考的將開頭bool的語句和其他兩種模式並列的想法是完全不行的!!!!!!!!!!!去掉firstId_is_bool_flag,換個其他的方案!!!
也就是除了對EqExpr的解釋接口的修改,上面的其他規定和修改都無效了!!!

在firstId是布爾的情況下

將之前的firstId_is_string_flag改爲firstId_flag,記錄表達式的開始的Id。
在字符串類型的判斷旁加上對布爾的判斷,如果是布爾的,就直接將表達式類型改爲判斷式,因爲簡單表達式中是不允許有布爾變量的!!!

	void DealToken_Id(int& _t){
		...
		//檢查一下,表達式的開頭第一個id的類型
		if (firstId_flag == false) {
			if (SuatinEnv[global_infix[_t]->name]->type == SuatinIDType_string) {
				m_simpleExprType = SimpleExprType_StrLink;//如果是字符串類型就轉變表達式類型爲字符串拼接模式
				firstId_flag = true;
			}
			else if (SuatinEnv[global_infix[_t]->name]->type == SuatinIDType_bool) {
				m_exprType = ExprType_Compare;//如果是布爾類型,表達式類型至少是判斷式。這樣就不會把當做簡單表達式中的計算模式被interpret接口處理了
				firstId_flag = true;
			}
		}		
	}

>,<,>=,<=四種節點情況下

在出現這四種判斷運算符的時候,要做判斷exprRoot,即當前的判斷符前面的表達式是否是一個布爾變量,如果是就報錯!!!

	void DealToken_LesEq(int& _t){
		...
		//不存在待處理的節點------------------
		if (typeid(*(exprRoot->GetClassType())) == typeid(IDExpr)) { //左操作數是一個ID
			IDExpr* node_tmp = dynamic_cast<IDExpr*>(exprRoot);
			if (node_tmp->GetType() == SuatinIDType_bool) {
				ThrowException<SuatinErrorType_Syntax>(start, end, "[<=] boolean identifier cannot used for compare");
				return;
			}
		}
		judgeRoot = new LesEqExpr(exprRoot, NULL);
		exprRoot = NULL;
	}

還有就是邏輯運算符,遇到邏輯運算符後也要對judeRoot進行處理,其中處理前就要先判斷下exprRoot是否是布爾型變量!!!

	void Deal_k_and(int& _t){
		...
		//把exprRoot裝上judgeRoot
		if (judgeRoot) {
			//>,<,>=,<=這幾個節點的情況下,左右操作數不能是布爾
			if (typeid(*(judgeRoot->GetClassType())) == typeid(GreExpr) || typeid(*(judgeRoot->GetClassType())) == typeid(GreEqExpr) || typeid(*(judgeRoot->GetClassType())) == typeid(LesExpr) || typeid(*(judgeRoot->GetClassType())) == typeid(LesEqExpr)) {
				if (typeid(*(exprRoot->GetClassType())) == typeid(IDExpr)) { //右操作數是一個ID
					IDExpr* node_tmp = dynamic_cast<IDExpr*>(exprRoot);
					if (node_tmp->GetType() == SuatinIDType_bool) {
						ThrowException<SuatinErrorType_Syntax>(start, end, "[or] boolean identifier cannot used for compare");
						return;
					}
				}
			}
			SymbolExpr* node_tmp = dynamic_cast<SymbolExpr*>(judgeRoot);
			node_tmp->SetRight(exprRoot);
			...
		...
	}

在最後遇到分號後,對judgeRoot裝上右孩子時也要進行這種判斷!!!

	void DealToken_Sem(int& _t){
		
		...
		
		auto judge_chunk = [this](Expr** _node,Expr** _exprRoot) {
			//>,<,>=,<=這幾個節點的情況下,左右操作數不能是布爾
			if (typeid(*((*_node)->GetClassType())) == typeid(GreExpr) || typeid(*((*_node)->GetClassType())) == typeid(GreEqExpr) || typeid(*((*_node)->GetClassType())) == typeid(LesExpr) || typeid(*((*_node)->GetClassType())) == typeid(LesEqExpr)) {
				if (typeid(*( (*_exprRoot)->GetClassType())) == typeid(IDExpr)) { //右操作數是一個ID
					IDExpr* node_tmp = dynamic_cast<IDExpr*>((*_exprRoot));
					if (node_tmp->GetType() == SuatinIDType_bool) {
						ThrowException<SuatinErrorType_Syntax>(start, end, "[sem] boolean identifier cannot used for compare");
						return;
					}
				}
			}

			//替換judgeRoot
			SymbolExpr* node_tmp = dynamic_cast<SymbolExpr*>((*_node));
			node_tmp->SetRight((*_exprRoot));

			//確定解釋接口類型(~=/==右邊)
			if (typeid(*((*_node)->GetClassType())) == typeid(NeqExpr) || typeid(*((*_node)->GetClassType())) == typeid(EqEqExpr)) {
				InterfaceTypeExpr* node_tmp = dynamic_cast<InterfaceTypeExpr*>((*_node));
				node_tmp->SetRight_InterfaceType(GetTree_InterfaceType(&(*_exprRoot)));
			}

			(*_exprRoot) = (*_node);//根節點上移
			(*_node) = NULL;

		};
		
		...
		
	}

檢查待處理節點的右孩子是否是布爾

	void DealToken_Id(int& _t){
		...
		//存在待處理的節點------------------
		if (expTmp) {
			//五則計算模式下,他類型的Id不做處理
			if (SuatinEnv[global_infix[_t]->name]->type != SuatinIDType_number) { 
				ThrowException<SuatinErrorType_Syntax>(start, end, "[Id] boolean identifier cannot used for calculate");
				return;
			}
			...
		...
	}

在五則計算的模式下,當存在待處理節點時,這時候會判斷待處理節點的右孩子是否是布爾變量!!!所以 a = 1+ TRUE; 這種代碼也是能檢查出錯的,但是 a=TRUE + 1 檢查不出來了!!!
在這裏插入圖片描述

^,*,/,+,-五種函數開頭——這一步是錯的

這樣在遇到 a= TRUE+1; 這種語句的時候就會報錯了!!!
這樣會出現新的問題,當語句編程判斷式或邏輯式後,後面的表達式就不能進行計算了!
允許布爾計算這個BUG,之後再解決!

	void DealToken_Pow(int&){
		if (m_exprType != ExprType_Simple) { //表達式類型爲判斷式或邏輯式
			ThrowException<SuatinErrorType_Syntax>(start, end, "[pow] wrong type identifier in expression");
			return;
		}
		...
	}

在這裏插入圖片描述
不幸的是,上面的錯都找出來後,正常編寫的第6條語句,也報錯了!!!

初始化表達式

每次遇到判斷符或邏輯符,都意味着下一個表達式要開始了,所以每個邏輯符、判斷符方法裏都要初始一些變量

		//初始化表達式(簡單表達式的模式、firstId)
		m_simpleExprType = SimpleExprType_NumCalc;
		firstId_flag = true;

解釋NIL要報錯

一般計算式子裏如果有NIL變量,都是當做值爲0的數算的,這不行,所以要在解釋時,將這種NIL類型的變量找出來,並拋出異常!!!(找出來也許是不必要的)

//Parser.cpp
	void Parser::interpret() {

		try {

			if (m_exprType == ExprType_Simple) { //簡單表達式
				if (m_simpleExprType == SimpleExprType_NumCalc) { //五則計算式
					//先判斷類型
					if (typeid(*(root->GetClassType())) == typeid(IDExpr)) { //表達式爲單個ID
						IDExpr* node_tmp = dynamic_cast<IDExpr*>(root);
						if (node_tmp->GetType() == SuatinIDType_nil) {
							std::cout << "[result]nil\n";
							return;
						}
						else if (node_tmp->GetType() == SuatinIDType_bool) {
							std::cout << "[result]" << BOOL2STR(node_tmp->GetBool()) << std::endl;
							return;
						}
					}					
					double result = root == NULL ? 0 : root->interpret();
					std::cout << "[result]" << result << std::endl;
				}
				else if (m_simpleExprType == SimpleExprType_StrLink) { //字符串拼接式
					std::string result = root == NULL ? "" : root->interpret_str();
					std::cout << "[result]" << result << std::endl;
				}
			}
			else { //判斷式或邏輯式
				bool result = root == NULL ? true : root->interpret_bool();
				std::cout << "[result]" << BOOL2STR(result) << std::endl;
			}
		}
		catch (SuatinExcept& e) {
			PrintException(e.what());
		}
		catch (...) {
			PrintException("interpret error\n");
		}
	}

//Expr.cpp

	//IDExpr的解釋接口
	double IDExpr::interpret() {
		if (GetType() == SuatinIDType_nil) ThrowException<SuatinErrorType_Value>("nil type identifier cannot used");
		return GetNumber();
	}

	std::string IDExpr::interpret_str() {
		if (GetType() == SuatinIDType_nil) ThrowException<SuatinErrorType_Value>("nil type identifier cannot used");
		return GetString();
	}
	bool IDExpr::interpret_bool() {
		if (GetType() == SuatinIDType_nil) ThrowException<SuatinErrorType_Value>("nil type identifier cannot used");
		return GetBool();
	}

在這裏插入圖片描述

  • 解釋出NIL異常的時候,不能得到打印出錯誤語句!!!——有一種拆了東牆補西牆的感覺,解決這個問題要重寫異常函數了!!!

[項目代碼下載地址2]
https://download.csdn.net/download/weixin_41374099/12245756

BDWP
鏈接:https://pan.baidu.com/s/1FgjPtVjND8mnUvSsJoTgvA
提取碼:7slg

打印出計算了NIL的語句

再定義兩個全局變量g_statement_start,g_statement_end,在Parser類的構造函數裏初始化,因爲解釋語法樹是從Parser類內部操作的,所以需要全局變量拿到內部的語句開始和結尾的信息!!!
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章