# 在 `iec_bison.yy` 中 ASSIGN 賦值的型別檢查機制 - benchen2001/matiec GitHub Wiki

iec_bison.yy 中 ASSIGN 賦值的型別檢查機制

一、類型相容性檢查的基本原理

首先,需要明確一個關鍵概念:在 matiec 編譯器中,iec_bison.yy 本身不直接處理型別檢查。這是編譯器設計中的基本原則 - 職責分離:

  • iec_bison.yy(Stage 1-2):負責語法分析,確保程式碼結構符合 IEC 61131-3 語法規則
  • 語義分析階段(Stage 3):負責型別檢查和其他語義規則驗證

對於賦值語句 (variable := expression),型別不匹配的檢測是在 Stage 3 進行的,而不是在 iec_bison.yy 中。以下是完整的檢測流程:

二、具體檢測流程

1. 階段一和二:語法分析(iec_bison.yy

在這個階段,iec_bison.yy 只關注語法結構,不關心型別:

assignment_statement
    : variable ASSIGN expression ';'
      {
        $$ = new_assignment_statement($1, $3);
        /* 設置位置資訊 */
      }
    ;

這裡產生了 assignment_statement_c 類型的 AST 節點,包含左值(LHS)和右值(RHS)兩個子節點。重要的是:此時沒有任何型別檢查!

2. 階段三:語義分析

型別檢查在 Stage 3 中執行,通過以下關鍵文件實現:

  1. stage3/datatype_assignment_check.cc:負責檢查賦值操作的型別相容性
  2. stage3/type_symbol_table.cc:維護變數和表達式的型別資訊
  3. stage3/fill_candidate_datatypes.cc:推導表達式的可能型別

檢查流程如下:

2.1 遞迴收集型別資訊

首先,系統會通過遞迴遍歷 AST 節點來收集和填充型別資訊:

// 偽代碼 - 實際實現在 fill_candidate_datatypes.cc
visit(assignment_statement_c *node) {
    // 遞迴訪問左側,確定其型別
    visit(node->l_exp);
    
    // 遞迴訪問右側,確定其型別
    visit(node->r_exp);
    
    // 型別資訊被存儲在每個節點的 candidate_datatypes 中
}

2.2 型別相容性檢查

然後,執行賦值語句的型別相容性檢查:

// 偽代碼 - 實際實現在 datatype_assignment_check.cc
visit(assignment_statement_c *node) {
    // 獲取左側的型別
    datatype_t lhs_type = get_type_id(node->l_exp->datatype);
    
    // 獲取右側的型別
    datatype_t rhs_type = get_type_id(node->r_exp->datatype);
    
    // 檢查型別相容性
    if (!is_type_compatible(lhs_type, rhs_type)) {
        // 報告型別錯誤
        report_type_error(node, lhs_type, rhs_type);
    }
}

2.3 實際範例(以 average_st.txt 為例)

如果我們修改 average_st.txt 中的賦值語句:

SUM := RUN;  // 錯誤:嘗試將 BOOL 型別賦值給 REAL 型別

檢測過程將是:

  1. 語法分析: iec_bison.yy 接受這行語句,因為它符合 variable ASSIGN expression; 的語法形式
  2. 型別識別: Stage 3 確定 SUM 的型別是 REALRUN 的型別是 BOOL
  3. 相容性檢查: 系統檢查 BOOL 是否可以賦值給 REAL
  4. 錯誤報告: 型別不相容,產生錯誤訊息:
    Error: Type mismatch in assignment - cannot convert 'BOOL' to 'REAL'
    

三、型別相容性規則

IEC 61131-3 標準定義了嚴格的型別相容性規則。主要規則包括:

  1. 相同型別: 總是允許(如 REAL := REAL
  2. 數值型別轉換:
    • INTREAL: 允許(擴展轉換)
    • REALINT: 不允許(截斷轉換,需顯式轉換函數)
  3. 布林型別:
    • BOOL 不能直接賦值給任何非布林型別
    • 非布林型別不能直接賦值給 BOOL
  4. 陣列和結構:
    • 必須型別完全相同,包括維度和成員
  5. 字串型別:
    • STRING 和 WSTRING 不可互換
    • 字串長度可以不同(較長的會被截斷)

四、示例:average_st.txt 中的型別檢查

分析示例檔案中的賦值語句:

SUM := SUM - FIFO.XOUT;   // 有效:REAL := REAL - REAL
FIFO (RUN := RUN, ...);   // 有效:BOOL := BOOL
SUM := SUM + FIFO.XOUT;   // 有效:REAL := REAL + REAL
XOUT := SUM/N;            // 有效:REAL := REAL/INT(結果是 REAL)
SUM := N*XIN;             // 有效:REAL := INT*REAL(結果是 REAL)
XOUT := XIN;              // 有效:REAL := REAL

所有這些賦值語句在型別檢查上都是合法的,因為:

  • SUMXOUTREAL 型別
  • RUNBOOL 型別
  • NINT 型別
  • FIFO.XOUTREAL 型別(根據 DELAY 功能塊定義)

五、總結

  1. 分工原則

    • iec_bison.yy (Stage 1-2) 只處理語法結構
    • Stage 3 負責型別檢查和語義驗證
  2. 檢測機制

    • 通過建立符號表收集型別資訊
    • 透過遞迴遍歷 AST 計算表達式型別
    • 根據 IEC 61131-3 標準的型別規則檢查相容性
  3. 實際應用

    • 這種設計使編譯器有更清晰的結構和責任分離
    • 更容易擴展和維護(型別系統可以獨立演化)
    • 支援 IEC 61131-3 標準的嚴格型別檢查要求

這種分層設計是工業級編譯器的標準做法,確保了高品質的錯誤檢測和更好的用戶體驗。