compiler 模块注释完善:Doxygen 风格(Lexer/Parser/Linker/Typecheck/Project/MachineConfig/TypeInfo/Codec/Codegen/Stb/main 共 21 个文件)

This commit is contained in:
2026-08-21 23:29:15 +08:00
parent fff7aef968
commit b0aebe264e
21 changed files with 1332 additions and 341 deletions
+186 -26
View File
@@ -48,7 +48,11 @@ namespace compiler {
namespace {
/**
* @brief 代码生成器
* @brief 代码生成器(寄存器码)。
* @details 流程:布局数据区 → 布局 FB 实例 → 逐 POU 建函数 → 拼映像。
* 寄存器分配:r0 结果、r1..r7 参数(调用约定区,输入只读)、r8+ 变量/临时;
* 跳转偏移相对下一条(目标 = 当前 + 1 + off)。失败统一经 fail() 写
* err(前缀 "codegen error")。
*/
class Builder {
public:
@@ -107,6 +111,11 @@ namespace {
}
private:
/**
* @brief 记录错误并返回失败。
* @param msg 错误消息(自动加前缀 "codegen error: "
* @return 恒 false(便于 return fail(...) 连写)
*/
bool fail(const std::string& msg) {
if (err_) {
*err_ = "codegen error: " + msg;
@@ -125,32 +134,45 @@ namespace {
}
// ---- 指令发射(配置 opcode----
/// 双寄存器指令(rd, rs)。
Instr E_rr(const char* name, uint8_t rd, uint8_t rs) {
return pack(opc(name), rd, rs, 0);
}
/// 三寄存器指令(rd, ra, rb)。
Instr E_rrr(const char* name, uint8_t rd, uint8_t ra, uint8_t rb) {
return pack(opc(name), rd, ra, rb);
}
/// 立即数指令(imm 拆低/高字节入字段)。
Instr E_imm(const char* name, uint8_t rd, uint16_t imm) {
return pack(opc(name), rd, static_cast<uint8_t>(imm & 0xFFu),
static_cast<uint8_t>((imm >> 8) & 0xFFu));
}
/// 槽号指令(slot 复用 imm 字段,u16)。
Instr E_slot(const char* name, uint8_t rd, uint16_t slot) {
return E_imm(name, rd, slot);
}
/// 无条件跳转(off 相对下一条指令)。
Instr E_jmp(int16_t off) {
const uint16_t u = static_cast<uint16_t>(off);
return pack(opc("JMP"), 0, static_cast<uint8_t>(u & 0xFFu),
static_cast<uint8_t>((u >> 8) & 0xFFu));
}
/// 条件跳转(寄存器 r 为真则跳;off 相对下一条指令)。
Instr E_jc(const char* name, uint8_t r, int16_t off) {
const uint16_t u = static_cast<uint16_t>(off);
return pack(opc(name), r, static_cast<uint8_t>(u & 0xFFu),
static_cast<uint8_t>((u >> 8) & 0xFFu));
}
/// CALL 指令(fn_id 为函数表下标)。
Instr E_call(uint16_t fn_id) { return E_imm("CALL", 0, fn_id); }
/// RET 指令。
Instr E_ret() { return pack(opc("RET"), 0, 0, 0); }
/**
* @brief 按名查 POU AST。
* @param name POU 名
* @return 找到返回指针;否则 nullptr
*/
const POU* find_pou(const std::string& name) const {
for (const SourceUnit& u : units_) {
for (const POU& p : u.ast.pous) {
@@ -163,27 +185,41 @@ namespace {
}
// 每函数的编译态
/**
* @brief 单个函数的编译状态。
* @details 寄存器分配(调用约定):结果 r0、输入 r1..r7、变量与临时 r8+。
*/
struct FuncCtx {
std::string name;
std::string pou_name; // 所属 POU 名(实例查找键)
std::vector<Instr> code; // 字节码
std::map<std::string, uint8_t> regs; // 变量名 → 帧寄存器
uint8_t nlocals = 0; // 变量区终点 = 临时寄存器起始
uint8_t nregs = 0; // 峰值(变量 + 临时)
uint8_t temp_used = 0; // 本语句已用临时数(语句结束清零)
bool is_function = false; // FUNCTION(结果 r0 / 输入只读)
std::string result_name; // FUNCTION 名(结果寄存器映射)
std::string name; ///< 函数名(函数表行用)
std::string pou_name; ///< 所属 POU 名(实例查找键)
std::vector<Instr> code; ///< 字节码
std::map<std::string, uint8_t> regs; ///< 变量名 → 帧寄存器
uint8_t nlocals = 0; ///< 变量区终点 = 临时寄存器起始
uint8_t nregs = 0; ///< 寄存器峰值(变量 + 临时)
uint8_t temp_used = 0; ///< 本语句已用临时数(语句结束清零)
bool is_function = false; ///< FUNCTION(结果 r0 / 输入只读)
std::string result_name; ///< FUNCTION 名(结果寄存器映射)
};
// FB 实例:字段名 → 数据区槽号
/**
* @brief FB 实例布局。
* @details 字段槽号 = 实例基槽 + 字段序号(跨周期持久)。
*/
struct InstFields {
std::map<std::string, uint32_t> field_addr;
uint32_t base = 0;
std::string type_name; // 实例的 FB 类型名(内建 / 用户)
std::map<std::string, uint32_t> field_addr; ///< 字段名 → 数据区槽号
uint32_t base = 0; ///< 实例基槽
std::string type_name; ///< 实例的 FB 类型名(内建 / 用户)
};
/// 语句开始:清零临时寄存器计数。
void begin_stmt(FuncCtx& f) { f.temp_used = 0; }
/**
* @brief 分配一个临时寄存器。
* @param f 函数编译态
* @return 临时寄存器号(nlocals + 已用数;随用抬高 nregs 峰值)
*/
uint8_t alloc_temp(FuncCtx& f) {
const uint8_t r = f.nlocals + f.temp_used;
++f.temp_used;
@@ -248,6 +284,15 @@ namespace {
return true;
}
/**
* @brief 编译一条语句。
* @param f 函数编译态
* @param st 语句 AST
* @return true 成功;falseerr 已写)
* @details 支持 IF / WHILE / FB 调用 / 赋值;其他语句报
* "statement not supported"。内联 FB 体内左值可能是实例字段
* (走 STORE_GLOBAL);写 FUNCTION 输入报 "cannot write function input"。
*/
bool compile_stmt(FuncCtx& f, const Stmt& st) {
if (st.kind == StmtKind::If) {
return compile_if(f, st);
@@ -283,6 +328,15 @@ namespace {
return compile_expr(f, *st.value, it->second);
}
/**
* @brief 编译 FB 调用语句。
* @param f 函数编译态
* @param st FB 调用语句 AST
* @return true 成功;falseerr 已写)
* @details 实参逐项编译后 STORE_GLOBAL 写实例字段;内建 FB
* 发 CAL_<TYPE> <实例基槽>;用户 FB 调用点内联展开。
* 错误:"no instance" / "unknown input" / "no FB type"。
*/
bool compile_fb_call(FuncCtx& f, const Stmt& st) {
const InstFields* inst = instance_of(f.pou_name, st.instance);
if (inst == nullptr) {
@@ -315,6 +369,7 @@ namespace {
return compile_fb_inline(f, *fb, *inst);
}
/// 转大写(内建 FB 名 → 操作码名)。
static std::string uppercase_of(const std::string& s) {
std::string out = s;
for (char& ch : out) {
@@ -323,6 +378,15 @@ namespace {
return out;
}
/**
* @brief 用户 FB 调用点内联展开。
* @param f 函数编译态
* @param fb 用户 FB 的 POU AST
* @param inst 实例布局
* @return true 成功;falseerr 已写)
* @details 临时置 inline_fields_ 使体内变量引用改查实例字段
* (编译结束后恢复)。
*/
bool compile_fb_inline(FuncCtx& f, const POU& fb, const InstFields& inst) {
const InstFields* saved = inline_fields_;
inline_fields_ = &inst;
@@ -336,6 +400,14 @@ namespace {
return true;
}
/**
* @brief 编译 WHILE 循环。
* @param f 函数编译态
* @param st WHILE 语句 AST
* @return true 成功;falseerr 已写)
* @details 结构:条件 → JF 跳出 → 体 → JMP 回条件;偏移经
* patch_jump 回填(相对下一条)。
*/
bool compile_while(FuncCtx& f, const Stmt& st) {
const size_t loop = f.code.size();
const uint8_t t = alloc_temp(f);
@@ -357,6 +429,14 @@ namespace {
return true;
}
/**
* @brief 编译 IF / ELSIF / ELSE。
* @param f 函数编译态
* @param st IF 语句 AST
* @return true 成功;falseerr 已写)
* @details 每条分支:条件 → JF 跳下一条 → 体;分支间 JMP 跳
* 公共结束点;偏移经 patch_jump 回填。
*/
bool compile_if(FuncCtx& f, const Stmt& st) {
std::vector<std::pair<const Expr*, const std::vector<Stmt>*>> branches;
branches.push_back({st.cond.get(), &st.body});
@@ -401,6 +481,17 @@ namespace {
return true;
}
/**
* @brief 编译左值存储(全局槽)。
* @param f 函数编译态
* @param target 目标名
* @param value 表达式
* @return true 成功;falseerr 已写)
* @details 目标槽号在 link_.global_index 中查;I/O 输入禁止写
* "cannot write to input");存储操作码经 store_name 选择
* I/O 输出 STORE_Q,其余 STORE_GLOBAL)。
* 错误:"no storage for ..." / "cannot write to input ..."。
*/
bool store_target(FuncCtx& f, const std::string& target, const Expr& value) {
const auto git = link_.global_index.find(target);
if (git == link_.global_index.end()) {
@@ -417,6 +508,18 @@ namespace {
return true;
}
/**
* @brief 编译表达式到目标寄存器。
* @param f 函数编译态
* @param e 表达式 AST
* @param rd 目标寄存器号
* @return true 成功;falseerr 已写)
* @details 支持:字面量(LOADK,tag 来自配置类型表)、变量/字段引用、
* 四则、比较(cmp_name)、NOT、短路 AND/ORJF/JT)、函数调用
* (实参 MOVE 到 r1..r7 后 CALL,结果 MOVE 回 rd)。
* 错误:"no register or slot" / "no instance" / "unknown field" /
* "too many arguments (max 7)" / "no fn_id" / "expression not supported"。
*/
bool compile_expr(FuncCtx& f, const Expr& e, uint8_t rd) {
if (e.kind == ExprKind::LitBool || e.kind == ExprKind::LitInt ||
e.kind == ExprKind::LitTime) {
@@ -533,6 +636,11 @@ namespace {
return fail("expression not supported");
}
/**
* @brief 按名查 fn_id(函数表下标)。
* @param name 函数名
* @return 找到返回下标;否则 -1
*/
int find_fn_id(const std::string& name) const {
for (size_t i = 0; i < link_.scopes.size(); ++i) {
if (link_.scopes[i].name == name) {
@@ -542,6 +650,14 @@ namespace {
return -1;
}
/**
* @brief 回填跳转偏移。
* @param f 函数编译态
* @param idx 跳转指令下标
* @param target_idx 目标指令下标
* @details 偏移相对下一条:目标 = 当前 + 1 + offoff 为
* int16,重写指令的低 16 位)。
*/
void patch_jump(FuncCtx& f, size_t idx, size_t target_idx) {
const int16_t off = static_cast<int16_t>(
static_cast<int64_t>(target_idx) - (static_cast<int64_t>(idx) + 1));
@@ -552,6 +668,7 @@ namespace {
}
// ---- 操作码名(MachineConfig 已强校验存在)----
/// 四则运算操作码名(Add→"ADD" 等;未知回退 "ADD")。
static const char* arith_name(ExprKind k) {
switch (k) {
case ExprKind::Add: return "ADD";
@@ -561,6 +678,7 @@ namespace {
default: return "ADD";
}
}
/// 比较操作码名(Eq→"CMP_EQ" 等;未知回退 "CMP_EQ")。
static const char* cmp_name(BinOp op) {
switch (op) {
case BinOp::Eq: return "CMP_EQ";
@@ -572,13 +690,23 @@ namespace {
default: return "CMP_EQ";
}
}
/// 加载操作码选择:I/O 输入走 LOAD_I,其余 LOAD_GLOBAL。
const char* load_name(const std::string& name) const {
return io_input_.count(name) ? "LOAD_I" : "LOAD_GLOBAL";
}
/// 存储操作码选择:I/O 输出走 STORE_Q,其余 STORE_GLOBAL。
const char* store_name(const std::string& name) const {
return io_output_.count(name) ? "STORE_Q" : "STORE_GLOBAL";
}
/**
* @brief 布局全局数据区。
* @return true 成功;falseerr 已写)
* @details 每槽 8 字节定宽小端,槽号 × 8 定位;初值按类型元数据
* 写(宽度 2 写 u16、8 写 u64、否则写 0/1;别名先经 type_meta 解析)。
* 错误:"data area exceeds slot range" / "no type in machine.toml" /
* "float initializer not supported yet"。
*/
bool layout_data() {
for (const Symbol& s : link_.globals) {
if (s.address > 0xFFFF) {
@@ -614,6 +742,12 @@ namespace {
return true;
}
/**
* @brief 布局 FB 实例块(全局区之后顺序追加)。
* @return true 成功;falseerr 已写)
* @details 实例 key 为 "POU名/实例名";字段槽号 = 基槽 + 字段序号。
* 错误:"no layout for instance ..."。
*/
bool layout_fb_instances() {
uint32_t cur = static_cast<uint32_t>(data_.size() / 8);
bool any = false;
@@ -643,12 +777,19 @@ namespace {
return true;
}
/// 按 "POU名/实例名" 查实例布局;找不到返回 nullptr。
const InstFields* instance_of(const std::string& pou,
const std::string& name) const {
const auto it = instances_.find(pou + "/" + name);
return it == instances_.end() ? nullptr : &it->second;
}
/**
* @brief 查全局变量初值(源文件 globals 段)。
* @param name 变量名
* @param has_init 输出:是否有初值
* @param init 输出:初值
*/
void init_of(const std::string& name, bool* has_init, int64_t* init) const {
for (const SourceUnit& u : units_) {
for (const VarBlock& b : u.ast.globals) {
@@ -683,6 +824,13 @@ namespace {
return static_cast<uint16_t>(consts_.size() - 1);
}
/**
* @brief 拼装最终映像。
* @return true 成功;falseerr 已写)
* @details 布局:头(104) + 常量表 + 函数表 + 字节码 + 数据段 +
* SHA-256 文件尾;型号标识 @72;头内各段偏移(52..68)与函数表
* code_offset 小端写入;SHA-256 对文件尾之前全部内容计算。
*/
bool assemble_image() {
const uint32_t off_const = static_cast<uint32_t>(kHeaderSize);
const uint32_t off_funcs = off_const +
@@ -774,12 +922,14 @@ namespace {
return true;
}
/// 小端写 32 位。
static void put_le32(std::vector<uint8_t>& b, size_t off, uint32_t v) {
b[off + 0] = static_cast<uint8_t>(v & 0xFFu);
b[off + 1] = static_cast<uint8_t>((v >> 8) & 0xFFu);
b[off + 2] = static_cast<uint8_t>((v >> 16) & 0xFFu);
b[off + 3] = static_cast<uint8_t>((v >> 24) & 0xFFu);
}
/// 小端写 64 位。
static void put_le64(std::vector<uint8_t>& b, size_t off, uint64_t v) {
for (int i = 0; i < 8; ++i) {
b[off + i] = static_cast<uint8_t>((v >> (8 * i)) & 0xFFu);
@@ -787,23 +937,33 @@ namespace {
}
// ---- 成员 ----
const Project& proj_;
const std::vector<SourceUnit>& units_;
const LinkResult& link_;
const MachineConfig& cfg_;
std::vector<uint8_t>* image_;
std::string* err_;
std::vector<FuncCtx> funcs_;
std::vector<ConstEntry> consts_;
std::map<std::string, bool> io_input_;
std::map<std::string, bool> io_output_;
std::vector<uint8_t> data_;
std::map<std::string, InstFields> instances_;
const InstFields* inline_fields_ = nullptr;
const Project& proj_; ///< 工程定义(cycle_limit / dt_ms / 哈希)
const std::vector<SourceUnit>& units_; ///< 全部源文件 AST
const LinkResult& link_; ///< 链接结果(POU 顺序 / 全局符号 / FB 布局)
const MachineConfig& cfg_; ///< 机器定义(opcode / tag / FB 布局)
std::vector<uint8_t>* image_; ///< 输出映像字节
std::string* err_; ///< 错误输出(可为 nullptr
std::vector<FuncCtx> funcs_; ///< 已编译函数
std::vector<ConstEntry> consts_; ///< 常量表(tag + value
std::map<std::string, bool> io_input_; ///< I/O 输入变量名集合(小写;只影响操作码选择)
std::map<std::string, bool> io_output_; ///< I/O 输出变量名集合(小写;只影响操作码选择)
std::vector<uint8_t> data_; ///< 全局数据区字节(每槽 8 字节定宽)
std::map<std::string, InstFields> instances_; ///< "POU名/实例名" → 实例布局
const InstFields* inline_fields_ = nullptr; ///< 内联 FB 字段表(非空 = 正在内联 FB 体)
};
} // namespace
/**
* @brief 编译工程为 .stb 映像字节(对外入口)。
* @param proj 工程定义
* @param units 全部源文件的 AST
* @param link 链接结果
* @param cfg 机器定义(machine.toml
* @param image 输出映像字节
* @param err 错误输出;可为 nullptr
* @return true 成功;falseerr 前缀 "codegen error"
*/
bool codegen_project(const Project& proj, const std::vector<SourceUnit>& units,
const LinkResult& link, const MachineConfig& cfg,
std::vector<uint8_t>* image, std::string* err) {