lang:自制編程語言9——重寫表達式分類機制 + 改進異常機制

前言

[ 落後的分類機制如下!]
之前區分不同種類的語句(賦值與非賦值、計算與字符串、判斷式、邏輯式)是靠下面幾個標誌區分的!!!

  1. exprType 表達式種類(種類有 NumberCalculate五則計算、StringConnect字符串拼接、Compare判斷式、Logic邏輯式
  2. completedName_flag 是否完成變量命名
  3. expr_compare_flag 是否是判斷語句
  4. expr_logic_flag 是否是邏輯語句

後面兩個bool標誌是爲了彌補exprType的作用引入的,因爲在遇到某一個Token的時候,exprType可能變成對應的類型,這樣就丟失了信息!!!

之前所寫的表達式類型的枚舉中,NumberCalculate&StringConnect是並列的,而Compare更高一個級別,Logic又比Compare高一個級別!!!

語句又可能是賦值的、非賦值的,賦值也可能有一個或多個等於號。。。。雖然都是從同一個Expr抽象類中派生下來的,可是各個節點的職能和級別千差萬別,要重新設計一個區分機制!


改進異常機制

正確顯示錯誤的位置

這裏的異常不是語言內的機制,而是自定義的異常顯示,我運行解釋器的時候出錯了我需要接收到什麼樣的異常,需要顯示什麼樣的異常信息!!!

Suatin編程語言這個項目進展到現在,每個語句都是以分號結尾,而不是換行了,甚至換行號我好多天之前就不處理了。所以,打印異常信息時,文件行、文件行內容這兩個都使用不了。

改進之後的異常機制 能夠顯示的信息有:文件路徑、語句的索引、語句內容、運行階段

我設置了一個SuatinRunTimeType,確定了三個解釋器運行階段(詞法期、語法期、解釋期),當前面的階段出錯時,先把當前階段執行完 , 後面的階段就跳過了,免得浪費性能!!!

//Utils.h

...
	enum SuatinRunTimeType {
		SuatinRunTimeType_Lex,
		SuatinRunTimeType_Parse,
		SuatinRunTimeType_Interpret,
	};

	extern std::string g_file_dir;									//文件路徑
	extern int g_statement_index;									//語句索引
	extern bool g_error_lex_flag;									//詞法期錯誤
	extern bool g_error_parse_flag;									//語法期錯誤
	extern bool g_error_interpret_flag;								//解釋期錯誤
	extern SuatinRunTimeType g_run_time;							//運行階段


	...
		
	
	//錯誤類型重寫了
	enum SuatinErrorType {
		SuatinErrorType_Name ,							//命名錯誤,拿沒賦值的NIL變量來操作引起的錯誤
		SuatinErrorType_ZeroDivisor ,					//零是除數
		SuatinErrorType_Syntax ,						//語法錯誤,比如拼寫錯誤,右括號沒匹配,語法樹構造完了還有未處理的節點
		SuatinErrorType_IO ,							//文件讀寫、在終端中輸出輸入……都是IO錯誤
		SuatinErrorType_Value,							//值錯誤
		SuatinErrorType_Type,							//類型錯誤
		SuatinErrorType_OutRange ,						//超出範圍
		SuatinErrorType_Other,							//未知錯誤,這個用不到,畢竟所有的拋出異常函數都是自己寫的,沒理由寫一個未知錯誤類型
	};


	class SuatinExcept : public std::exception
	{
	public:
		SuatinExcept(std::string error) : exception(error.c_str()) {	}
	};


	template<int errorType>
	void ThrowException_func(std::string _errorString,std::string _description) {
	
		//打印錯誤類型和錯誤信息
		switch (errorType) {
		case SuatinErrorType_Name:
			_errorString += "SuatinErrorType_Name : " + _description;
			break;
		case SuatinErrorType_ZeroDivisor:
			_errorString += "SuatinErrorType_ZeroDivisor : " + _description;
			break;
		case SuatinErrorType_Syntax:
			_errorString += "SuatinErrorType_Syntax : " + _description;
			break;
		case SuatinErrorType_IO:
			_errorString += "SuatinErrorType_IO : " + _description;
			break;
		case SuatinErrorType_Value:
			_errorString += "SuatinErrorType_Value : " + _description;
			break;
		case SuatinErrorType_Type:
			_errorString += "SuatinErrorType_Type : " + _description;
			break;
		case SuatinErrorType_OutRange:
			_errorString += "SuatinErrorType_OutRange : " + _description;
			break;
		case SuatinErrorType_Other:
			_errorString += "SuatinErrorType_Other : " + _description;
			break;
		}


		//是否是語法期/解釋期錯誤
		switch (g_run_time) {
		case SuatinRunTimeType_Lex:
			_errorString += " in lex period\n";
			g_error_lex_flag = true;
			break;
		case SuatinRunTimeType_Parse:
			_errorString += " in parse period\n";
			g_error_parse_flag = true;
			break;
		case SuatinRunTimeType_Interpret:
			_errorString += " in interpret period\n";
			g_error_interpret_flag = true;
			break;
		}

		throw SuatinExcept(_errorString);

	}


	//在非Parser類中使用
	template<int errorType>
	void ThrowException(std::string _description) {
		//打印文件路徑和語句索引
		std::string errorString = "Traceback at : File \"" + g_file_dir + "\n";
		ThrowException_func<errorType>(errorString,_description);
	}

	//在Parser類中使用
	template<int errorType>
	void ThrowException(int _start, int _end, std::string _description) {
		//打印文件路徑和語句索引
		std::string errorString = "Traceback at : File \"" + g_file_dir + "\",no. " + std::to_string(g_statement_index) + "\n\t>";

		//打印該語句的內容
		for (int i = _start; i <= _end; ++i) {
			errorString += global_infix[i]->name;
		}
		errorString += "\n";

		ThrowException_func<errorType>(errorString,_description);
	}

後面的有起始位置的函數,是用在Parser內的,每一個Parser類實例處理一句話,所以每一個Parser我給其傳入該語句在全局中綴表達式中的起始位置!!!

除了Parser外,其他位置上沒有這個start,end,甚至global_infx都沒有填入內容,,,,所以又寫了一個沒有起始參數的異常顯示函數。

顯示效果
//test.suatin
TRUE;
 1 +(;
詞法分析 time consumed 68 ms
初始化語言環境 time consumed 1 ms
語法分析·創建語法樹 time consumed 5 ms
[0]
TRUE
[1]
+
├── 1
└── ()
Traceback at : File "C:\Users\LX\Desktop\test.suatin",no. 1
        >1+(;
SuatinErrorType_Syntax : the left didnot matched its right part in parse period
語法分析·打印語法樹 time consumed 21 ms
語法分析·解釋語法樹 time consumed 0 ms
語言環境>
                name   isconst    type      funcPtr     flag             num   str
                 NIL    true       nil     00000000    false               0
              TEST_C    true       int     00000000     true               7
               FALSE    true      bool     00000000    false               0
              TEST_D    true      bool     00000000    false               0
                TRUE    true      bool     00000000     true               0
              TEST_B    true    number     00000000     true     1.22323e+06
              TEST_A    true    string     00000000     true               0   author@Demllie
顯示環境信息 time consumed 176 ms
釋放環境 time consumed 0 ms

在Parse期檢查出所有拼寫問題

上面顯示結果中,可以看出錯誤在打印語法樹時纔出現,但是一般解釋語句不打印語法樹,那樣錯誤就隱藏起來了!所以,需要在構造語法樹的階段,就把錯誤都找出來!

在構造完語法樹,即進行分號處理之後,再過一遍語法檢測,看看是否有未處理的節點,或者是否有未匹配到右括號的左括號節點

//Parser.cpp
	/*在遇到分號之後,構造語法樹操作結束,檢查ast的語法*/
	void Parser::CheckASTreeSyntax() {
		if (expTmp) {//還有待處理的節點
			ThrowException<SuatinErrorType_Syntax>(start,end,"still have signs which didn't handle");
		}		
		if (v_NoMatchedLLeft.size() > 0) {//還有未匹配的左括號
			ThrowException<SuatinErrorType_Syntax>(start, end, "the left didnot matched its right part");
		}
	}
詞法分析 time consumed 191 ms
初始化語言環境 time consumed 2 ms
Traceback at : File "C:\Users\LX\Desktop\test.suatin",no. 1
        >1+(;
SuatinErrorType_Syntax : still have signs which didn't handle in parse period
語法分析·創建語法樹 time consumed 45 ms
[0]
TRUE
[1]
+
├── 1
└── ()
Traceback at : File "C:\Users\LX\Desktop\test.suatin",no. 1
        >1+(;
SuatinErrorType_Syntax : the left didnot matched its right part in parse period
語法分析·打印語法樹 time consumed 22 ms
語法分析·解釋語法樹 time consumed 0 ms
語言環境>
                name   isconst    type      funcPtr     flag             num   str
                 NIL    true       nil     00000000    false               0
              TEST_C    true       int     00000000     true               7
               FALSE    true      bool     00000000    false               0
              TEST_D    true      bool     00000000    false               0
                TRUE    true      bool     00000000     true               0
              TEST_B    true    number     00000000     true     1.22323e+06
              TEST_A    true    string     00000000     true               0   author@Demllie
顯示環境信息 time consumed 729 ms
釋放環境 time consumed 1 ms

[代碼1下載地址]
https://download.csdn.net/download/weixin_41374099/12236918
項目代碼下載地址BDWP
鏈接:https://pan.baidu.com/s/1smQ5HL0LblVk_yQejznogg
提取碼:b2y7
複製這段內容後打開百度網盤手機App,操作更方便哦

重寫語句區分機制

語句、表達式的分類

有這樣的語句: a = not 1 and 2 ~= "hello";
其中的語句、表達式類型如下:

賦值式: a = not 1 and 2 ~= "hello"
邏輯式: not 1 and 2~= "hello"
判斷式: 2 ~= "hello"
簡單表達式:
			1
			2
			"hello"
簡單表達式中的計算式: 
			1
			2
簡單表達式中的字符串拼接式:
			"hello" 
  • 語句要麼是賦值式、要麼是非賦值式
  • 表達式定義爲最後的賦值符號的後面的式子
  • 表達式要麼是簡單表達式,要麼是判斷式,要麼是邏輯式
  • 語句關係和表達式關係有重疊
  • 數字計算和字符串拼接,兩個模式是並列的。都屬於簡單表達式

區分表達式

//Utils.h
...
	//語句類型
	//如果是if、while後面的判斷語句,一定要是非賦值式,但是其表達式類型不要求
	//如果是語句塊中的語句,一定要是賦值式
	enum StatType{
		StatType_NoEval,						//非賦值式
		StatType_Eval,							//賦值式
	};

	
	//表達式類型,和語句類型有交叉。如果是非賦值式的話,那麼語句肯定是三種表達式之一
	//默認表達式爲簡單表達式,遇到判斷符後轉爲判斷式,遇到邏輯符後轉爲邏輯式
	//在一個Parser實例裏,表達式類型的轉換隻有一個方向
	enum ExprType{
		ExprType_Simple,						//簡單表達式
		ExprType_Compare,						//判斷式
		ExprType_Logic,							//邏輯式
	};

	//簡單表達式類型,簡單表達式是表達式的單元,也是語句的單元
	//默認是五則計算,遇到字符串轉變爲字符串拼接
	//字符串拼接模式下,如果又遇到了數字,就報錯!
	//在一個Parser實例裏,簡單表達式類型的轉換隻有一個方向
	enum SimpleExprType{
		SimpleExprType_NumCalc,					//五則計算式
		SimpleExprType_StrLink,           		//字符串拼接式
	};
...
//Parser.h
...
		//語句類型,默認非賦值式
		StatType    m_statType = StatType_NoEval;	
		//表達式類型,默認簡單表達式
		ExprType   m_exprType = ExprType_Simple;	
		//簡單表達式類型,默認五則計算式
		SimpleExprType  m_simpleExprType = SimpleExprType_NumCalc;
...
//Parser.cpp
...
	
	void Parser::interpret() {
	//改了之後清爽多了!!!

		try {
			
			if (m_exprType == ExprType_Simple) { //簡單表達式
				if (m_simpleExprType == SimpleExprType_NumCalc) { //五則計算式
					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) {
			std::cout << e.what();
		}
		catch (...) {
			std::cout << "interpret error";
		}
	}

...

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

項目代碼下載地址BDWP
鏈接:https://pan.baidu.com/s/1FKTvYOvlmG30UJmDYDNRSg
提取碼:33ku
複製這段內容後打開百度網盤手機App,操作更方便哦

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