实现 12.5 递归下降语法:AST + 拒绝清单。
- Parser.h/cpp:POU 外壳(PROGRAM/FUNCTION/FUNCTION_BLOCK)、变量段、语句(赋值/IF/WHILE/FB 调用)、表达式优先级爬升(NOT/AND/OR 短路/比较/算术/函数调用/负号) - Unit 结构:顶层 VAR_GLOBAL 段(GVL 文件形态)+ POU 列表 - 拒绝:VAR_IN_OUT/REF/CLASS/ANY 等保留名、链式比较、FB 字段赋值、顶层非 GVL 段 - THEN/DO 补入关键字表(12.5 修订,同步 12.1 与词法文档) - parser_test:98 断言(line1 三文件 AST、FUNCTION 调用、IF/WHILE/TON、9 类负例),ctest 6/6
This commit is contained in:
@@ -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` + 文档
|
||||
Reference in New Issue
Block a user