实现 12.6 符号表与链接:全局定槽、引用解析、循环检测。

- Linker.h/cpp:收集导出(仅 gvl 文件顶层 VAR_GLOBAL、声明顺序定槽、重复名拒绝)
- VAR_EXTERNAL 接同一槽、类型一致;FB 实例必须先声明;字段存在性校验;函数调用图 DFS 环检测
- io.var 必须在 GVL;内建 TON/TOF/CTU 布局冻结;用户 FB 字段 = input/output/var(external 不是字段)
- 函数名赋值 = 结果值写入(用例 13 约定);dump_symbols 打印 name kind type address file
- linker_test:51 断言(用例 09/10/11/12/16/19 + line1/17 正例),ctest 7/7;20 用例扫描 15 绿 5 按期望拒
This commit is contained in:
2026-08-21 11:14:03 +08:00
parent 93d2c96f20
commit 6f02c18bbb
7 changed files with 985 additions and 1 deletions
+82
View File
@@ -0,0 +1,82 @@
/**
* @file Linker.h
* @brief 符号表与链接
* @author
* @date 2026-08-21
*/
#pragma once
#include <cstdint>
#include <map>
#include <string>
#include <vector>
#include "compiler/Parser.h"
#include "compiler/Project.h"
namespace compiler {
// 符号种类(与 Doc/初步计划.md 12.1 一致)
enum class SymbolKind { Global, External, Local, Input, Output, Pou, FbInstance, Const };
struct Symbol {
SymbolKind kind = SymbolKind::Local;
std::string name; // 小写
std::string type_name; // bool / int / time / fb 类型名(小写)
uint32_t address = 0; // 全局槽号(Global/External);局部暂 012.8 分配)
std::string source_file;
uint32_t line = 0;
uint32_t col = 0;
};
// FB 类型字段(实例布局用;v1 字段类型仅 BOOL/INT/TIME
struct FbField {
std::string name;
TypeKind type = TypeKind::Bool;
};
struct FbLayout {
std::string type_name; // 类型名(小写)
std::vector<FbField> fields; // 段序:input → output → 内部 var
};
// 一个源文件的 AST(供链接输入)
struct SourceUnit {
std::string path;
Unit ast;
};
// 链接结果
struct LinkResult {
// 全局槽表:下标即槽号(声明顺序)
std::vector<Symbol> globals;
std::map<std::string, uint32_t> global_index; // 名 → 槽号
// 用户 FB 类型布局(按类型名)
std::map<std::string, FbLayout> fb_types;
// 每 POU 的局部符号与 FB 实例
struct PouScope {
std::string name;
PouKind kind = PouKind::Program;
std::vector<Symbol> syms; // local/input/output/external/fb_instance
std::map<std::string, FbLayout> fb_instances; // 实例名 → 布局
};
std::vector<PouScope> scopes;
// 函数表顺序(PROGRAM / FUNCTION / FUNCTION_BLOCK 名,收集序)
std::vector<std::string> fn_order;
};
// 链接一个工程:校验 GVL、定全局槽、解析 VAR_EXTERNAL、FB 实例与字段、
// 函数循环调用检测、io 校验。失败返回 falseerr 前缀 "link error"。
bool link_project(const Project& proj, const std::vector<SourceUnit>& units,
LinkResult* out, std::string* err);
// 读取并解析一个 .st 文件成 SourceUnit
bool load_unit(const std::string& path, SourceUnit* out, std::string* err);
// 符号表文本:每行 "name kind type address file"(全局表 + 各 POU 局部表)
std::string dump_symbols(const LinkResult& r);
}