目錄 `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 採用兩階段型別檢查:

  1. 候選型別填充:由 fill_candidate_datatypes 實現,為每個節點收集所有可能的型別
  2. 型別窄化:由 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 處理時,流程如下:

  1. 填充候選型別

    • fill_candidate_datatypes_c 遍歷 AST
    • 為每個節點建立可能的型別列表
  2. 窄化型別

    • narrow_candidate_datatypes_c 根據上下文約束縮小型別範圍
    • 選擇最終型別
  3. 錯誤檢查與報告

    • print_datatypes_error_c 檢查型別相容性
    • 報告任何型別不匹配或其他語意錯誤
  4. 其他檢查

    • 執行陣列範圍檢查
    • 常數折疊優化
    • 變數作用域和初始化檢查

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 種授權類型的類似代碼