前言
[ 落後的分類機制如下!]
之前區分不同種類的語句(賦值與非賦值、計算與字符串、判斷式、邏輯式)是靠下面幾個標誌區分的!!!
- exprType 表達式種類(種類有 NumberCalculate五則計算、StringConnect字符串拼接、Compare判斷式、Logic邏輯式)
- completedName_flag 是否完成變量命名
- expr_compare_flag 是否是判斷語句
- 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,操作更方便哦