`simple_spec_init_c` 節點的型別檢查機制深入探討 - benchen2001/matiec GitHub Wiki
simple_spec_init_c 節點的型別檢查機制深入探討
這段程式碼是 matiec 編譯器中 Stage 3 階段的型別檢查邏輯,專門處理變數初始化語句的型別相容性檢查。讓我們深入分析它的工作原理、調用流程和實際應用。
一、程式碼功能說明
print_datatypes_error_c::visit(simple_spec_init_c *symbol) 函數負責檢查 simple_spec_init_c 類型的 AST 節點,這類節點代表 IEC 61131-3 中的變數初始化語句,如:
MyVar : INT := 10; // 這是一個 simple_spec_init_c 節點
該函數執行三個關鍵檢查:
- 型別有效性檢查:確認變數宣告的型別是否有效
- 初始值型別相容性檢查:確認初始值的型別與變數型別相容
- 最終型別檢查:確保整個初始化語句的型別有效(這是一個安全檢查)
二、調用流程
1. 調用時機
這個函數在 Stage 3 階段被調用,具體流程如下:
main() -> stage3() -> type_safety() -> print_datatypes_error_c(tree_root) -> tree_root->accept()
當 AST 遍歷到 simple_spec_init_c 節點時,會自動呼叫這個函數。
2. 實際調用範例
考慮以下 IEC 61131-3 代碼:
PROGRAM Example
VAR
count : INT := 10; // 合法: INT 與 10 型別相容
status : BOOL := 5; // 錯誤: BOOL 與數字 5 型別不相容
temp : REAL; // 合法: 沒有初始值
sensor : WRONG_TYPE; // 錯誤: 無效型別
END_VAR
END_PROGRAM
編譯過程中,AST 遍歷器會為上述每個變數宣告呼叫 visit(simple_spec_init_c *symbol):
範例 1: count : INT := 10;
symbol->simple_specification->datatype是INT(有效型別)symbol->constant->datatype是INT(整數常數 10)- 檢查通過,不報錯
範例 2: status : BOOL := 5;
symbol->simple_specification->datatype是BOOL(有效型別)symbol->constant->datatype是INT(整數常數 5)- 型別不相容,觸發錯誤: "Initial value has incompatible data type."
範例 3: temp : REAL;
symbol->simple_specification->datatype是REAL(有效型別)symbol->constant為NULL(沒有初始值)- 檢查通過,不報錯
範例 4: sensor : WRONG_TYPE;
symbol->simple_specification->datatype為無效型別- 觸發錯誤: "Invalid data type."
三、程式碼深入分析
讓我們逐行分析這個函數:
void *print_datatypes_error_c::visit(simple_spec_init_c *symbol) {
// 檢查 1: 確認宣告的型別是否有效
if (!get_datatype_info_c::is_type_valid(symbol->simple_specification->datatype)) {
// 如果型別無效,報告錯誤
STAGE3_ERROR(0, symbol->simple_specification, symbol->simple_specification, "Invalid data type.");
}
// 檢查 2: 如果有初始值,確認初始值型別與變數型別相容
else if (NULL != symbol->constant) {
if (!get_datatype_info_c::is_type_valid(symbol->constant->datatype))
STAGE3_ERROR(0, symbol->constant, symbol->constant, "Initial value has incompatible data type.");
}
// 檢查 3: 最終確認整個初始化語句的型別是否有效
else if (!get_datatype_info_c::is_type_valid(symbol->datatype)) {
ERROR; // 這應該不會發生,因為如果發生則前面的檢查應該會捕獲
}
return NULL;
}
關鍵函數與巨集
-
get_datatype_info_c::is_type_valid():- 這個函數檢查型別是否有效
- 有效型別必須在先前的 Stage 3 階段 (
fill_candidate_datatypes和narrow_candidate_datatypes) 中被正確識別 - 無效型別可能是未宣告的型別、型別不匹配或型別歧義
-
STAGE3_ERROR巨集:- 格式化錯誤訊息並輸出至標準錯誤
- 記錄錯誤位置 (文件名、行號、列號)
- 增加全域錯誤計數
error_count - 參數
0表示錯誤嚴重性 (0 = 最嚴重)
-
ERROR巨集:- 這是一個調試巨集,用於標記不應該發生的情況
- 在發布版本中可能會觸發中止或記錄嚴重錯誤
四、與其他模組的關係
這個函數與其他 Stage 3 模組密切相關:
-
fill_candidate_datatypes_c:- 在型別檢查前填充可能的型別候選列表
- 為
simple_spec_init_c節點確定可能的型別
-
narrow_candidate_datatypes_c:- 從候選型別列表中選擇最合適的型別
- 設置
datatype欄位,供print_datatypes_error_c檢查
-
stage3.cc:
- 協調整個 Stage 3 階段的處理流程
- 確保型別檢查在適當時機執行
五、實際應用範例
範例: 分析標準 IEC 61131-3 程式
考慮以下程式碼:
FUNCTION_BLOCK AVERAGE
VAR_INPUT
RUN : BOOL;
XIN : REAL;
N : INT := 100; // 初始化值型別相容
END_VAR
VAR_OUTPUT
XOUT : REAL;
END_VAR
VAR
SUM : REAL := 0.0; // 初始化值型別相容
FIFO : DELAY; // 沒有初始值
error_var : INT := TRUE; // 型別不相容錯誤
END_VAR
// 函數體...
END_FUNCTION_BLOCK
編譯時,會針對每個變數宣告調用 visit(simple_spec_init_c *symbol):
- 對於
N : INT := 100;,檢查通過,因為 INT 型別與整數常數 100 相容 - 對於
SUM : REAL := 0.0;,檢查通過,因為 REAL 型別與實數常數 0.0 相容 - 對於
FIFO : DELAY;,檢查通過,因為 DELAY 是有效型別且沒有初始值 - 對於
error_var : INT := TRUE;,檢查失敗,因為 INT 型別與布林常數 TRUE 不相容
會輸出錯誤訊息:
[file.st:15] error: Initial value has incompatible data type.
六、小結
print_datatypes_error_c::visit(simple_spec_init_c *symbol) 函數是 matiec 編譯器中執行型別檢查的關鍵部分,特別針對變數初始化語句。它確保:
- 宣告的變數型別是有效的
- 初始值(如果有)與變數型別相容
- 整個初始化語句的型別檢查無誤
這是 Stage 3 階段(語意分析)的重要組成部分,即使語法正確的程式碼,如果型別不相容,也會在這個階段被檢測並報錯。這種嚴格的型別檢查確保了 IEC 61131-3 程式的型別安全性,防止運行時出現未定義行為。