`generate_c` 目錄 code - benchen2001/matiec GitHub Wiki

generate_c 目錄是 matiec 編譯器的第四階段 (Stage 4) 核心部分,主要負責將前面階段處理過的抽象語法樹 (AST) 轉換成可編譯的 C 語言程式碼。這是 IEC 61131-3 程式碼轉換為具體可執行程式的最後階段。讓我深入探討這個目錄的結構、檔案和功能。

1. 目錄功能與設計理念

generate_c 的主要任務是:

  • 將 IEC 61131-3 的五種語言 (ST, IL, LD, FBD, SFC) 轉換為標準 C 程式碼
  • 處理 IEC 特有的資料型別 (如 TIME, DATE, BOOL, STRING 等) 的 C 實作
  • 處理 IEC 特有的變數宣告方式 (如 VAR_INPUT, VAR_OUTPUT, RETAIN 等)
  • 生成可攜性高、結構清晰的 C 程式碼

2. 核心檔案與功能

2.1 generate_c_base.cc/hh

這是所有程式碼產生器的基礎類別:

  • 提供共用的 AST 遍歷與處理功能
  • 處理基本型別和常數表示
  • 管理輸出格式 (如縮排、換行等)
  • 提供變數前綴處理

2.2 generate_c_st.cc/hh (結構化文本)

根據提供的程式碼片段,這是處理結構化文本 (ST) 語言的專用產生器:

class generate_c_st_c: public generate_c_base_and_typeid_c {

關鍵功能包括:

  • 變數產生模式:通過 variablegeneration_t 枚舉控制變數生成的方式

    typedef enum {
      expression_vg,        // 一般表達式中的變數
      complextype_base_vg,  // 複合型別的基底部分
      complextype_suffix_vg, // 複合型別的後綴部分
      fparam_output_vg      // 輸出參數
    } variablegeneration_t;
    
  • 變數存取抽象:透過 print_getterprint_setter 方法實現

    void *print_getter(symbol_c *symbol) {
      // 根據變數類型選擇不同的存取宏
      unsigned int vartype = analyse_variable_c::first_nonfb_vardecltype(symbol, scope_);
      if (vartype == search_var_instance_decl_c::external_vt) {
        s4o.print(GET_EXTERNAL);
      } else if (vartype == search_var_instance_decl_c::located_vt) {
        s4o.print(GET_LOCATED);
      } else {
        s4o.print(GET_VAR);
      }
      // ...
    }
    
  • 複雜結構處理:處理結構體、陣列等複合型別

    void *visit(structured_variable_c *symbol) {
      // 針對不同的變數生成模式,產生不同的 C 程式碼
      switch (wanted_variablegeneration) {
        case complextype_base_vg:
          // ...
          break;
        case complextype_suffix_vg:
          // ...
          break;
        default:
          // ...
          break;
      }
    }
    
  • 指標處理:透過 deref_operator_cderef_expression_c 處理指標操作

    void *visit(deref_operator_c *symbol) {
      // 針對 FUNCTION 或 FUNCTION_BLOCK 產生不同的指標解參考 C 程式碼
      if (this->is_variable_prefix_null()) {
        // 在函式中
        s4o.print("(__DE_REF(");
        symbol->exp->accept(*this);
        s4o.print("))");
      } else {
        // 在功能塊中
        // ...
      }
    }
    

2.3 generate_c_il.cc/hh (指令列表)

處理指令列表 (IL) 語言的程式碼產生,主要功能:

  • 處理 IL 的累加器式語法
  • 將 IL 指令 (LD, ST, ADD 等) 轉換為 C 程式碼
  • 處理 IL 特有的標籤與跳轉

2.4 generate_c_ld.cc/hh (梯形圖)

處理梯形圖 (LD) 語言的程式碼產生:

  • 將接點、線圈等圖形元素轉換為 C 邏輯判斷
  • 處理梯形圖的並聯、串聯結構
  • 實現梯形圖特有的計時器、計數器等功能

2.5 generate_c_fbd.cc/hh (功能塊圖)

處理功能塊圖 (FBD) 語言的程式碼產生:

  • 處理功能塊的實例化與互連
  • 產生功能塊輸入/輸出連接的程式碼
  • 處理功能塊執行順序

2.6 generate_c_sfc.cc/hh (順序功能圖)

處理順序功能圖 (SFC) 語言的程式碼產生:

  • 將步驟 (Step)、轉移 (Transition) 轉換為 C 狀態機
  • 處理動作 (Action) 與條件邏輯
  • 實現 SFC 的控制流程管理

2.7 accessor.h (變數存取宏)

從程式碼中可見,大量使用了如 GET_VARSET_VARGET_EXTERNAL 等宏:

s4o.print(GET_VAR);
s4o.print(SET_VAR);

這些宏定義了統一的變數存取介面,隱藏了不同類型變數的存取細節,例如:

  • GET_VAR - 存取一般變數
  • GET_EXTERNAL - 存取外部變數
  • GET_LOCATED - 存取位址變數 (直接連結到硬體位址)
  • GET_VAR_BY_REF - 取得變數的參考

3. 關鍵設計模式與挑戰

3.1 Visitor 模式

從程式碼中可見,generate_c_st_c 繼承自基本訪問者類別,實現了大量 visit 方法:

void *visit(symbolic_variable_c *symbol) { ... }
void *visit(structured_variable_c *symbol) { ... }
void *visit(array_variable_c *symbol) { ... }

這使用了訪問者模式,根據不同的 AST 節點型別動態調用不同的處理方法。

3.2 變數存取抽象

程式碼採用了複雜的變數存取抽象機制:

  • 透過 wanted_variablegeneration 控制產生模式
  • 使用 print_getter/print_setter 統一處理變數存取
  • 區分 complextype_base_vgcomplextype_suffix_vg 處理複合型別

這使得不同種類的變數 (本地、全域、外部、位址等) 能夠以統一的方式處理。

3.3 型別安全與相容性處理

程式碼中大量使用 get_datatype_info_c 檢查型別:

if (get_datatype_info_c::is_BOOL_compatible(symbol->datatype)) {
  // ...
}

這確保生成的 C 程式碼維持 IEC 61131-3 的型別系統規則。

3.4 功能塊與程式產生

在處理結構變數時對功能塊有特殊處理:

if (get_datatype_info_c::is_function_block(symbol->record_variable->datatype)
    || get_datatype_info_c::is_sfc_step(symbol->record_variable->datatype)) {
  // ...
}

這確保功能塊的成員存取使用適當的語法 (如 .->)。

4. 技術亮點

  1. 靈活的變數產生機制

    • 通過狀態變數 (wanted_variablegeneration) 控制變數產生模式
    • 可處理複雜的嵌套結構和陣列存取
  2. 統一的存取介面

    • 使用 GET_VAR/SET_VAR 等宏隔離底層實現細節
    • 為不同型別變數提供一致的存取模式
  3. 智能的指標處理

    • 區分函式與功能塊的指標解參考語法
    • 處理 IEC 61131-3 的 ^ 運算符到 C 的 *-> 的映射
  4. 細緻的型別轉換

    • 為 IEC 61131-3 的特有型別實現轉換機制
    • 處理自動型別提升和相容性檢查

5. 小結

generate_c 目錄是 matiec 將 IEC 61131-3 程式最終轉換為可執行 C 程式碼的關鍵模組。它通過精心設計的訪問者模式、變數存取抽象、型別檢查與功能塊處理等機制,確保生成的 C 程式碼既正確又高效。

這個模組處理了 IEC 61131-3 到 C 的複雜映射問題,隱藏了底層實現細節,同時保持了程式語義的準確性,是一個具有高度工程價值的編譯器後端實現。