`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_getter和print_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_c和deref_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_VAR、SET_VAR、GET_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_vg和complextype_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. 技術亮點
-
靈活的變數產生機制:
- 通過狀態變數 (
wanted_variablegeneration) 控制變數產生模式 - 可處理複雜的嵌套結構和陣列存取
- 通過狀態變數 (
-
統一的存取介面:
- 使用
GET_VAR/SET_VAR等宏隔離底層實現細節 - 為不同型別變數提供一致的存取模式
- 使用
-
智能的指標處理:
- 區分函式與功能塊的指標解參考語法
- 處理 IEC 61131-3 的
^運算符到 C 的*或->的映射
-
細緻的型別轉換:
- 為 IEC 61131-3 的特有型別實現轉換機制
- 處理自動型別提升和相容性檢查
5. 小結
generate_c 目錄是 matiec 將 IEC 61131-3 程式最終轉換為可執行 C 程式碼的關鍵模組。它通過精心設計的訪問者模式、變數存取抽象、型別檢查與功能塊處理等機制,確保生成的 C 程式碼既正確又高效。
這個模組處理了 IEC 61131-3 到 C 的複雜映射問題,隱藏了底層實現細節,同時保持了程式語義的準確性,是一個具有高度工程價值的編譯器後端實現。