# 符号表与链接(12.6) compiler 模块的符号收集与链接。输入:`Project`(toml)+ 各 `.st` 文件的 `Unit`(AST,见 [`语法.md`](语法.md));输出:链接结果(全局槽表、POU 符号表、函数表、FB 实例布局)。全工程定案见 [`初步计划.md`](../初步计划.md) 12.6。 ## 做 / 不做 **做** - 先扫完全部文件再解析引用(禁止边解析边执行) - 全局收集:仅 `gvl.file` 顶层 `VAR_GLOBAL`,声明顺序 = 全局槽顺序 - `VAR_EXTERNAL` 接已有槽,种类/类型一致,不是新变量 - `FUNCTION` 标无状态;`FUNCTION_BLOCK` 必须先有实例才能调用 - `CALL` 目标解析成 `fn_id`(立即数),无函数指针 - 校验:同名全局、未声明 `io.var`、重复 POU 名、未声明 FB 实例、函数循环调用 → 失败 - FB 实例布局:按类型算字段清单(输入/输出/内部;内建 FB 15 条字段来自 machine.toml `[[fb]]` 表,编译期按 1/2/4/8 对齐计算偏移) **不做(第一版)** - 寄存器分配(12.8)、类型检查(12.7) - 多 GVL 文件、I/Q/M 与全局分槽(v1 **单一数据区**,见下) ## 数据区定址(V2 定案) - **槽表两级**:全部全局(含 I/Q/M)与 FB 实例统一编槽号(0..n-1);槽表条目(8B:addr:u32 + 预留)指向**值区**字节偏移;值区按变量类型紧凑变宽(1/2/4/8 对齐) - 全局按 `globals.st` 声明顺序编槽;**FB 实例 = 1 个槽表条目**,字段走 `LOAD_OFF` / `STORE_OFF` 实例槽 + 编译期算死的字段偏移 - 映像头 `n_globals` = 全局声明总数;`n_i` / `n_q` / `n_m` = 0(第一版不拆,见 [`初步计划.md`](../初步计划.md) 5.1) - 访问统一 `LOAD` / `STORE`(+ `LOAD_OFF` / `STORE_OFF`),无 I/Q/M 专用操作码 - io 绑定(toml)只负责 channel/bit → 槽号映射(sidecar),不创造变量 ## 符号表一行 ```cpp enum class SymbolKind { Global, External, Local, Input, Output, Pou, FbInstance, Const }; struct Symbol { SymbolKind kind; std::string name; // 小写 std::string type_name; // bool / int / time / fb 类型名(小写) uint32_t address; // 全局槽号(Global/External);POU 局部暂 0(12.8 分配) std::string source_file; uint32_t line, col; }; ``` `kind` 与 12.1 冻结表一致:`global` / `external` / `local` / `input` / `output` / `pou` / `fb_instance` / `const`(`const` 本期未用,保留)。 ## 链接流程 ```text 1. 收集导出 遍历全部文件: - 顶层 globals:仅 gvl.file 允许;同名 → duplicate global;声明顺序编槽号 - POU 导出:program / function / fb 名登记;重复 → duplicate POU 2. 逐 POU 建局部符号 VAR / VAR_INPUT / VAR_OUTPUT → local / input / output VAR_EXTERNAL → 查全局表,必须存在、类型一致 → External(同一槽) POU 内 VAR_GLOBAL → error(仅 gvl 顶层允许) 3. 语句引用解析 - FB 调用 instance(...) → 局部表查实例,类型必须是 FB → 否则 undeclared FB instance - 字段读 fb.field → 实例类型有该字段(用户 FB 的 input/output;内建固定字段) - VarRef / 赋值左值 → 局部表或全局表查得到 → 否则 undeclared identifier - 函数调用 fn(...) → POU 表查得到且是 FUNCTION → 记依赖边;FUNCTION 不得调用 PROGRAM/FB 4. 循环调用检测 函数依赖图 DFS 找环 → recursive call(用例 19) 5. io 校验 io.var 必须在全局表 → 否则 io.var not in GVL(用例 12) 6. 输出 全局表 / POU 符号表 / 函数表(fn_id 按收集序)/ FB 实例布局 ``` ## FB 实例布局 - 用户 FB:字段 = 全部 `VAR_INPUT` + `VAR_OUTPUT` + `VAR`(内部),按声明段序排 - 内建 FB 15 条(`compiler/machine.toml` `[[fb]]` 表驱动,fb_id 0..14): | fb_id | FB | 字段(名 → 类型) | |---|---|---| | 0 | TON | in BOOL, pt TIME, q BOOL, et TIME | | 1 | TOF | 同 TON | | 2 | TP | 同 TON | | 3 | CTU | cu BOOL, r BOOL, pv INT, q BOOL, cv INT | | 4 | DCTU | cu BOOL, r BOOL, pv DINT, q BOOL, cv DINT | | 5 | CTD | cd BOOL, ld BOOL, pv INT, q BOOL, cv INT | | 6 | DCTD | cd BOOL, ld BOOL, pv DINT, q BOOL, cv DINT | | 7 | CTUD | cu BOOL, cd BOOL, r BOOL, lu BOOL, pv INT, qu BOOL, qd BOOL, cv INT | | 8 | DCTUD | cu BOOL, cd BOOL, r BOOL, lu BOOL, pv DINT, qu BOOL, qd BOOL, cv DINT | | 9 | R_TRIG | clk BOOL, q BOOL | | 10 | F_TRIG | clk BOOL, q BOOL | | 11 | SR | S1 BOOL, R BOOL, Q1 BOOL | | 12 | RS | SET BOOL, RESET1 BOOL, Q1 BOOL | | 13 | PWM | in BOOL, pt TIME, duty REAL, q BOOL, et TIME | | 14 | RTC | enable BOOL, date DATE, tod TOD | - 实例布局 = 类型字段表的一份拷贝(`VAR_TEMP` 不是字段,不持久,编译期映射为帧寄存器 + 每次调用初值装载);字段偏移编译期按 1/2/4/8 对齐计算(字段类型决定宽度),实例 = 槽表 1 条 ## 错误 稳定前缀 `link error`: ```text link error: duplicate global 'g1' (globals.st, line 4, col 1) link error: VAR_GLOBAL only allowed in gvl file (other.st, line 2, col 1) link error: io.var 'Nope' not in GVL (project.toml) link error: undeclared FB instance 'starter' (main.st, line 4, col 5) link error: recursive call involving 'a' (main.st, line 2, col 4) link error: undeclared identifier 'x' (main.st, line 3, col 3) ``` ## 完成标准 1. 用例 9(同一槽)、10(非 GVL 写全局)、11(同名全局)、12(io.var 未声明)、16(未声明实例)、19(循环调用)按表变绿 2. 符号表能打印 `name kind type address file` 3. 全部构建 + `ctest` 无回归 --- ## 执行计划(单步确认) 1. **`Linker.h`(新建 `compiler/include/compiler/Linker.h`)**:`Symbol`/`SymbolKind` + 链接结果结构 + `link_project` 声明 2. **`Linker.cpp` 前半**:收集导出(gvl 校验 / 全局定槽 / POU 登记)+ 逐 POU 局部符号 3. **`Linker.cpp` 后半**:引用解析(FB 实例 / 字段 / 变量 / 函数)+ 循环检测 + io 校验 4. **`linker_test`(`tests/src/linker_test.cpp` + CMake)**:用例 09/10/11/12/16/19 + 符号表打印断言 5. **验证**:`cmake --build` + `ctest`(新增 `linker_links` 后 7/7 全绿) 6. **提交**:`Linker.h/cpp` + `linker_test` + 文档