如何定義並檢出 `var_decl` (變數宣告) 的合法性(1)。 - benchen2001/matiec GitHub Wiki
好的,我們來分析 iec_bison.yy (或類似的 Bison 語法檔案,用於解析 IEC 61131-3 語言) 如何定義並檢出 var_decl (變數宣告) 的合法性。
核心概念:
Bison (或 Yacc) 的主要工作是語法分析 (Syntax Analysis)。它根據你在 .yy 檔案中定義的語法規則 (Grammar Rules) 來判斷輸入的 token 序列是否符合語言的語法結構。
對於 var_decl,"合法" 在 Bison 的層面上主要是指語法結構上的正確性。也就是說,變數宣告的各個部分 (名稱、冒號、類型、可選的地址、可選的初始值、分號) 是否按照預期的順序和格式出現。
尋找相關程式碼:
你需要在 iec_bison.yy 檔案中找到定義 var_decl 這個非終端符 (non-terminal symbol) 的語法規則。它通常看起來像這樣 (具體名稱和結構可能因不同的實作而略有差異):
%{
// C/C++ 宣告和 include 檔案
#include "iec_common.h" // 假設有共用定義
#include "symbol_table.h" // 假設有符號表處理
// ... 其他 include
%}
// Bison 宣告部分 (%token, %type, %start 等)
%token T_VAR T_END_VAR T_COLON T_SEMICOLON T_ASSIGN T_AT
%token T_IDENTIFIER // 由 Lexer 返回的標識符 token
%token T_BOOL T_INT T_REAL // ... 其他基本類型 token
// ... 其他 token 定義
%type <symbol> identifier // 假設 identifier 返回一個符號對象
%type <type_info> type_spec // 假設 type_spec 返回類型資訊
%type <expr_node> simple_spec_init // 假設返回表達式節點
// ... 其他 %type 定義
%% /* 語法規則開始 */
/* ... 其他規則 ... */
var_declarations_list:
var_declaration
| var_declarations_list var_declaration
;
var_declaration:
identifier_list T_COLON type_spec location_spec simple_spec_init T_SEMICOLON
{
// 動作代碼 (Action Code): 當此規則成功匹配時執行
// 這裡可以進行語義檢查、建立符號表條目、建構 AST 節點等
printf("Parsed a variable declaration.\n");
// 例如: process_variable_declaration($1, $3, $4, $5);
// $1: identifier_list 的值
// $3: type_spec 的值
// $4: location_spec 的值 (可能是 NULL)
// $5: simple_spec_init 的值 (可能是 NULL)
}
;
identifier_list:
identifier
{ $$ = create_identifier_list($1); } // 建立只包含一個標識符的列表
| identifier_list T_COMMA identifier
{ $$ = add_to_identifier_list($1, $3); } // 將標識符添加到現有列表
;
// 類型說明符 (可能很複雜,包含基本類型、陣列、結構體、FB 等)
type_spec:
simple_type_name
{ $$ = process_simple_type($1); }
| array_spec
{ $$ = process_array_type($1); }
/* | structured_type_spec */
/* | function_block_type_name */
/* ... 其他類型規則 ... */
;
// 可選的位置說明符
location_spec:
/* empty */
{ $$ = NULL; } // 沒有 AT 子句
| T_AT direct_variable
{ $$ = process_location($2); }
;
// 可選的初始化
simple_spec_init:
/* empty */
{ $$ = NULL; } // 沒有初始化
| T_ASSIGN constant_expression // 或更通用的 expression
{ $$ = process_initialization($2); }
;
/* ... 其他規則,例如 simple_type_name, array_spec, direct_variable, constant_expression 等 ... */
%% /* 語法規則結束 */
// C/C++ 輔助函數
// ... process_variable_declaration, create_identifier_list, 等函數的實作 ...說明:
-
var_declaration規則: 這是核心。它定義了一個合法的變數宣告必須包含:-
identifier_list: 一個或多個用逗號分隔的變數名稱 (標識符)。identifier_list本身是另一個非終端符,它遞迴地定義了如何處理逗號分隔的列表。 -
T_COLON: 一個冒號 token。 -
type_spec: 變數的類型說明。這本身可能是一個複雜的非終端符,涵蓋基本類型、陣列、結構體、功能塊等。Bison 會遞迴地去匹配type_spec的規則。 -
location_spec: 可選的AT位置說明符。這個規則可以匹配空 (epsilon,表示沒有AT子句) 或者T_AT後面跟著一個direct_variable(表示具體的地址,如%IX0.0)。 -
simple_spec_init: 可選的初始化部分。這個規則可以匹配空 (表示沒有初始化) 或者T_ASSIGN(即:=) 後面跟著一個constant_expression(或更一般的表達式)。 -
T_SEMICOLON: 一個分號 token,標示宣告結束。
-
-
Token: 大寫的
T_開頭的通常是終端符 (terminal symbols) 或 token。這些是由詞法分析器 (Lexer, 通常是 Flex) 從原始碼中識別並傳遞給 Bison 的最小單元。例如T_VAR,T_COLON,T_IDENTIFIER,T_INT等。 -
非終端符: 小寫的名稱 (如
var_declaration,identifier_list,type_spec) 是非終端符 (non-terminal symbols)。它們代表了語法結構中的一個概念,由一個或多個終端符或其他非終端符組成。 -
匹配過程: 當 Bison 讀取由 Lexer 提供的 token 流時,它會嘗試將這些 token 與語法規則進行匹配。
- 如果輸入的 token 序列能夠符合
var_declaration規則所定義的順序和結構 (例如,看到一個標識符,接著是冒號,接著是合法的類型,然後是可選的 AT,可選的初始化,最後是分號),Bison 就認為這是一個語法上合法的var_declaration。 - 如果 token 序列不符合任何定義的規則 (例如,缺少冒號、類型名稱拼錯導致 Lexer 無法識別、分號遺漏等),Bison 就會報告語法錯誤 (Syntax Error)。
- 如果輸入的 token 序列能夠符合
-
動作代碼 (Action Code
{...}): 規則後面花括號{...}中的 C/C++ 代碼是動作代碼。當 Bison 成功匹配整個規則後,會執行這些代碼。這些代碼通常用於:- 建立抽象語法樹 (AST) 節點。
- 填充符號表 (Symbol Table),記錄變數名稱、類型、地址等資訊。
- 執行初步的語義檢查 (雖然複雜的語義檢查通常在 AST 建立後進行),例如檢查類型名稱是否存在。
- 計算或傳遞值 (使用
$1,$2,$$等特殊變數)。
總結:
在 iec_bison.yy 中,var_decl 的 "合法性" 主要是通過其語法規則來定義和檢出的。Bison 檢查輸入的 token 是否嚴格按照 identifier_list T_COLON type_spec location_spec simple_spec_init T_SEMICOLON 這個模式 (以及其子規則定義的模式) 排列。如果匹配成功,則語法上合法;否則,報告語法錯誤。更深層次的語義合法性 (如類型相容性、名稱衝突等) 通常在規則匹配成功後執行的動作代碼中處理,或者在後續的語義分析階段處理。