目錄 `stage3` 完整探討與說明 - benchen2001/matiec GitHub Wiki
我現在會深入探討 matiec 編譯器中的 stage3 目錄,該目錄是負責語意分析與型別檢查的重要部分。
目錄 stage3 完整探討與說明
stage3 目錄是 matiec 編譯器的語意分析階段,在解析語法(stage1_2)之後執行,主要負責型別檢查、變數解析和語意驗證。這個階段確保 IEC 61131-3 程式在語意上正確,例如檢查變數是否使用前先宣告、運算式型別是否相容、功能塊呼叫參數是否正確等。
1. 主要功能與任務
stage3 階段負責以下核心任務:
- 型別檢查:確保所有運算式、賦值和函式呼叫中的型別都相容
- 變數解析:確保所有變數在使用前都已宣告
- 功能塊與函式驗證:檢查函式和功能塊的呼叫參數是否符合定義
- 陣列索引檢查:驗證陣列索引是否在有效範圍內
- 列舉值驗證:確保列舉值使用在正確的上下文
2. 主要檔案與功能
2.1 型別檢查相關
-
fill_candidate_datatypes.cc/hh:- 為每個 AST 節點填充可能的型別列表
- 使用 Visitor 模式遍歷整個 AST
- 為變數宣告、運算式、函式呼叫等註解可能的型別
-
narrow_candidate_datatypes.cc/hh:- 根據上下文約束,縮小每個節點的型別候選列表
- 解決型別歧義性,例如
+運算符可以是整數加法或實數加法 - 確定每個節點的最終型別
-
print_datatypes_error.cc/hh:- 當型別檢查失敗時,產生明確的錯誤訊息
- 報告不相容的型別、不允許的轉換等
- 指出問題所在的程式位置
-
datatype_functions.cc/hh:- 提供型別相關的輔助函式
- 檢查型別是否相容
- 執行型別轉換和提升
2.2 陣列與初始化檢查
-
array_range_check.cc/hh:- 檢查陣列範圍是否有效
- 計算陣列維度
- 驗證陣列索引是否在宣告的範圍內
-
constant_folding.cc/hh:- 在編譯時計算常數表達式
- 減少運行時開銷
- 支援初始化中的常數運算
2.3 變數與作用域處理
-
lvalue_check.cc/hh:- 檢查賦值語句左側是否為有效的 lvalue
- 防止賦值給常數或表達式
-
implicit_variable_decls.cc/hh:- 處理隱式宣告的變數
- 在某些上下文中自動宣告變數(例如 FOR 迴圈)
3. 技術實現與設計模式
3.1 兩階段型別檢查機制
stage3 採用兩階段型別檢查:
- 候選型別填充:由
fill_candidate_datatypes實現,為每個節點收集所有可能的型別 - 型別窄化:由
narrow_candidate_datatypes實現,根據上下文限制消除不可能的型別
這種設計允許處理複雜的型別推導和多態性。
3.2 Visitor 模式
整個 stage3 階段大量使用 Visitor 設計模式:
- 各種型別檢查器實現了
visitor介面 - 通過遍歷 AST 訪問每個節點
- 每個節點型別有對應的
visit方法
3.3 錯誤處理
print_datatypes_error 提供詳細的錯誤報告:
- 指出錯誤的確切位置
- 解釋錯誤原因
- 提供型別不匹配的詳細資訊
4. 重要類別詳解
4.1 fill_candidate_datatypes_c
這個類別為 AST 節點填充可能的型別列表:
- 遍歷 AST 的每個節點
- 根據 IEC 61131-3 標準的型別規則,確定每個節點可能的型別
- 處理基本型別、陣列、結構、列舉等
例如,處理 simple_spec_init_c(類似 enable : BOOL := 10;)時:
void *fill_candidate_datatypes_c::visit(simple_spec_init_c *symbol) {
return fill_spec_init(symbol, symbol->simple_specification, symbol->constant);
}
這個方法會檢查 BOOL 類型與 10 是否相容。
4.2 narrow_candidate_datatypes_c
這個類別根據上下文約束縮小型別範圍:
- 使用 "最佳匹配" 原則選擇最合適的型別
- 解決運算符重載(如
+可用於多種型別) - 傳播型別約束(如賦值語句中左側的型別約束右側的表達式)
4.3 print_datatypes_error_c
負責產生錯誤訊息,如遇到 enable : BOOL := 10; 這種型別不相容情況:
void *print_datatypes_error_c::visit(simple_spec_init_c *symbol) {
if (!get_datatype_info_c::is_type_valid(symbol->simple_specification->datatype)) {
STAGE3_ERROR(0, symbol->simple_specification, symbol->simple_specification, "Invalid data type.");
} 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.");
}
// ...
}
這就是當你有 enable : BOOL := 10; 這種型別不相容代碼時會產生錯誤的部分。
5. 執行流程
當程式經過 stage3 處理時,流程如下:
-
填充候選型別:
fill_candidate_datatypes_c遍歷 AST- 為每個節點建立可能的型別列表
-
窄化型別:
narrow_candidate_datatypes_c根據上下文約束縮小型別範圍- 選擇最終型別
-
錯誤檢查與報告:
print_datatypes_error_c檢查型別相容性- 報告任何型別不匹配或其他語意錯誤
-
其他檢查:
- 執行陣列範圍檢查
- 常數折疊優化
- 變數作用域和初始化檢查
6. 小結
stage3 目錄實現了 matiec 編譯器的語意分析階段,負責確保 IEC 61131-3 程式在語意上正確。它採用兩階段型別檢查機制和 Visitor 設計模式,提供強大的型別檢查和錯誤報告功能。
對於 enable : BOOL := 10; 這種情況,正是 stage3 中的 print_datatypes_error_c::visit(simple_spec_init_c *symbol) 方法負責檢測到這種型別不相容錯誤並報告。這確保了 IEC 61131-3 程式的型別安全,防止在執行時出現意外行為。
找到 1 種授權類型的類似代碼