@@ -0,0 +1,183 @@
# 语法(12.5 递归下降)
compiler 模块的语法分析。输入:单个 `.st` 文件的 token 流(见 [`词法.md` ](词法.md ));输出:**AST**(不分配寄存器、不写映像)。全工程定案见 [`初步计划.md` ](../初步计划.md ) 12.1、12.5。
## 做 / 不做
**做 **
- POU 外壳:`PROGRAM` / `FUNCTION` / `FUNCTION_BLOCK` 与对应 `END_*`
- 变量段:`VAR` / `VAR_INPUT` / `VAR_OUTPUT` / `VAR_GLOBAL` / `VAR_EXTERNAL`
- 类型名只接受 `BOOL` `INT` `TIME` ;内置 FB 名 `TON` `TOF` `CTU` ;标识符(用户 FB 类型)
- 语句:赋值、`IF` / `ELSIF` / `ELSE` 、`WHILE` 、FB 调用 `fb(in := …)` 、字段 `fb.Q`
- 表达式:`NOT` 、`AND` / `OR` (AST 保留短路语义,12.8 编跳转)、比较、加减乘除、一元负号、函数调用 `Add(3, 4)`
- 明确拒绝(`syntax error` ,不静默):`VAR_IN_OUT` 、`VAR_TEMP` 、`REF` 、`CLASS` 、`ANY` 等保留名
**不做(第一版) **
- 寄存器分配、跳转回填、映像(12.8)
- 链式比较 `a < b < c` 、字符串、`REAL` 、指针、`VAR_IN_OUT` 、接口、`TYPE` 定义
- 语义检查(未声明变量等归 12.6 / 12.7)
## AST 结构
``` cpp
// 类型引用:类型位置出现的名字
enum class TypeKind { Bool , Int , Time , FbBuiltin , FbUser } ;
struct TypeRef {
TypeKind kind ;
std : : string name ; // FbUser 时为用户 FB 类型名(小写)
} ;
struct VarDecl {
std : : string name ;
TypeRef type ;
bool has_init ; // 可选初值(v1 仅 BOOL/INT/TIME 字面量)
int64_t init_value ; // BOOL 0/1、INT 值、TIME 毫秒
uint32_t line , col ;
} ;
enum class VarSection { Local , Input , Output , Global , External } ;
struct VarBlock {
VarSection section ;
std : : vector < VarDecl > vars ;
} ;
// 表达式
enum class ExprKind {
LitBool , LitInt , LitTime ,
VarRef , // 标识符(局部/全局/外部/实例名)
Field , // 对象.字段:fb.Q / fb.ET
Not , And , Or ,
Cmp , Add , Sub , Mul , Div , // 二目;比较不连锁
Neg , // 一元负号
Call , // 函数调用(仅 FUNCTION,有返回值)
} ;
enum class BinOp { Eq , Ne , Lt , Le , Gt , Ge , Add , Sub , Mul , Div } ;
struct Expr {
ExprKind kind ;
BinOp op ; // 二目运算
std : : string name ; // VarRef / Field 对象名 / Call 函数名
std : : string field ; // Field 字段名
int64_t int_value ; // 字面量值
std : : unique_ptr < Expr > lhs , rhs , operand ; // 二目 / NOT / Neg
std : : vector < std : : unique_ptr < Expr > > args ; // Call 实参
} ;
// 语句
enum class StmtKind { Assign , If , While , FbCall } ;
struct FbArg { std : : string name ; std : : unique_ptr < Expr > value ; } ;
struct IfBranch { std : : unique_ptr < Expr > cond ; std : : vector < Stmt > body ; } ;
struct Stmt {
StmtKind kind ;
// Assign
std : : string target ; // 左值标识符
bool target_is_field ; // 左值是 fb.field(v1:只允许读取,禁止赋值)
std : : string field ;
std : : unique_ptr < Expr > value ;
// If / While
std : : unique_ptr < Expr > cond ;
std : : vector < Stmt > body ; // IF/WHILE 体
std : : vector < IfBranch > elsifs ;
std : : vector < Stmt > else_body ;
// FbCall
std : : string instance ;
std : : vector < FbArg > args ;
} ;
enum class PouKind { Program , Function , FunctionBlock } ;
struct POU {
PouKind kind ;
std : : string name ;
TypeRef result_type ; // FUNCTION 才有
std : : vector < VarBlock > blocks ;
std : : vector < Stmt > body ;
std : : string source_file ;
} ;
```
## 文法(递归下降)
``` text
file := (VAR_GLOBAL vars END_VAR | POU)*
POU := PROGRAM ident var_block* body END_PROGRAM
| FUNCTION ident ':' type var_block* body END_FUNCTION
| FUNCTION_BLOCK ident var_block* body END_FUNCTION_BLOCK
var_block := VAR vars END_VAR
| VAR_INPUT vars END_VAR | VAR_OUTPUT vars END_VAR
| VAR_GLOBAL vars END_VAR | VAR_EXTERNAL vars END_VAR
vars := (ident (',' ident)* ':' type (':=' literal)? ';')*
type := BOOL | INT | TIME | TON | TOF | CTU | ident
body := stmt*
stmt := ident ('.' ident)? ':=' expr ';' (* 赋值 *)
| ident '(' arg (',' arg)* ')' ';' (* FB 调用 *)
| IF expr THEN body (ELSIF expr THEN body)* (ELSE body)? END_IF
| WHILE expr DO body END_WHILE
arg := ident ':=' expr
expr := or_expr
or_expr := and_expr (OR and_expr)*
and_expr := not_expr (AND not_expr)*
not_expr := NOT not_expr | cmp_expr
cmp_expr := add_expr (('='|'<>'|'<'|'<='|'>'|'>=') add_expr)?
add_expr := mul_expr (('+'|'-') mul_expr)*
mul_expr := unary (('*'|'/') unary)*
unary := '-' unary | primary
primary := INT_LIT | TIME_LIT | TRUE | FALSE | ident
| ident '.' ident (* fb.field *)
| ident '(' expr (',' expr)* ')' (* 函数调用 *)
| '(' expr ')'
```
要点:
- `file` 顶层允许无 POU 的 `VAR_GLOBAL` 段(**GVL 文件形态**,如 `globals.st` );
其他变量段出现在顶层 → syntax error。解析结果:
```cpp
struct Unit {
std::vector<VarBlock> globals; // 顶层 VAR_GLOBAL 段
std::vector<POU> pous;
};
` ``
- ` THEN` / ` DO` 是关键字(12.5 修订补入词法表,见 [` 词法.md`](词法.md))
- 赋值左侧 ` ident.ident` 是字段引用,v1 只能读(右侧),左侧字段赋值报错
- ` ident(...)` 是**语句**即 FB 调用(无返回值);在**表达式**里即函数调用(有返回值)——靠上下文区分,与 12.1 语义一致
- 比较不连锁(` a < b < c` → syntax error)
- ` AND` / ` OR` 短路语义保留在 AST( ` And`/` Or` 节点),12.8 编成跳转
## 明确拒绝清单
` IDENT` 文本(小写后)命中下列保留名 → ` syntax error: 'X' is not supported`:
` ``text
var_in_out var_temp ref class any pointer interface method
` ``
## 错误
稳定前缀 ` syntax error`,带文件与行列:
` ``text
syntax error: expected 'END_PROGRAM' (main.st, line 5, col 3)
syntax error: 'VAR_IN_OUT' is not supported (motor.st, line 2, col 5)
syntax error: chained comparison not supported (main.st, line 7, col 15)
` ``
## 完成标准
1. ` examples/line1/*.st` 三文件 parse 成功,AST 结构符合文法
2. ` parser_test`:AST 断言(POU 种类/变量段/语句/表达式优先级)+ 负例(缺 ` END_*`、` VAR_IN_OUT`、链式比较、字段赋值、坏语句)→ ` syntax error`
3. 全部构建 + ` ctest` 无回归
4. 用例 10 / 14 / 19 属 12.6 / 12.7 层(非语法层),不在本阶段点亮
---
## 执行计划(单步确认)
1. **` Parser.h`(新建 ` compiler/include/compiler/Parser.h`) **:上述 AST 结构 + ` bool parse_pous(source_file, content, out, err)`
2. **` Parser.cpp` 前半**:POU 外壳 + 变量段 + 类型(文法 program / POU / var_block / vars / type)
3. **` Parser.cpp` 后半**:语句 + 表达式(优先级爬升)+ 拒绝清单
4. **` parser_test`( ` tests/src/parser_test.cpp` + CMake) **: line1 三文件 AST 断言 + 负例
5. **验证**: ` cmake --build` + ` ctest`(新增 ` parser_syntax` 后 6/6 全绿)
6. **提交**: ` Parser.h/cpp` + ` parser_test` + 文档