/** * @file MachineConfig.cpp * @brief 机器定义(machine.toml V2)加载与强校验 * @author * @date 2026-08-21 * * @details 校验项(见 Doc/isa/指令配置.md 强校验清单 1~5,V2): * 1. meta:name 非空、version = 2 * 2. type:19 条必填全集(BOOL..DT);base 命中内建基元;kind 合法; * func ∈ 0..9 且与 base 的 (width, signed, is_float) 互锁 * 3. op:prefix 合法;prefix ↔ fmt 互锁;op 分区内唯一;func 类别合法; * params 数量与 fmt 互锁;enabled 布尔 * 4. fb:15 条必填全集(fb_id 0..14);fb_id 唯一;字段名唯一;字段类型命中 type 表 * 5. op-fb 互锁:CAL(010101)存在 ↔ fb 表非空 */ #include "compiler/MachineConfig.h" #include #include #include namespace compiler { /** * @brief 内建基元表。 * @return 静态基元表(bit / int8..uint64 / float32 / float64,元数据在代码) */ const std::vector& MachineConfig::prims() { static const std::vector kPrims = { {"bit", 1, false, false}, {"int8", 1, true, false}, {"int16", 2, true, false}, {"int32", 4, true, false}, {"int64", 8, true, false}, {"uint8", 1, false, false}, {"uint16", 2, false, false}, {"uint32", 4, false, false}, {"uint64", 8, false, false}, {"float32", 4, true, true}, {"float64", 8, true, true}, }; return kPrims; } /** * @brief 按名称查内建基元。 * @param name 基元名 * @return 命中指针;未找到返回 nullptr */ const PrimType* MachineConfig::find_prim(const std::string& name) { for (const PrimType& p : prims()) { if (p.name == name) { return &p; } } return nullptr; } namespace { /** * @brief 写错误信息(稳定前缀 "machine error")并原样返回 msg。 * @param err 错误输出;可为 nullptr(静默) * @param msg 错误描述 * @return msg(供调用方直接 return) */ std::string fail(std::string* err, const std::string& msg) { if (err) { *err = "machine error: " + msg; } return msg; } /** * @brief fmt → 期望参数数量(解析时校验)。 * @param fmt 操作数形态(RR/NONE/RRR/IMM/SLOT/JMP/JC/CALL/CAL/SLOT+off/IMM64/预留) * @return 参数数量;未知 fmt 返回 -1 */ int params_of(const std::string& fmt) { if (fmt == "RR") return 2; if (fmt == "NONE") return 0; if (fmt == "RRR") return 3; if (fmt == "IMM") return 2; if (fmt == "SLOT") return 2; if (fmt == "JMP") return 1; if (fmt == "JC") return 2; if (fmt == "CALL") return 1; if (fmt == "CAL") return 2; if (fmt == "SLOT+off") return 3; if (fmt == "IMM64") return 2; if (fmt == "预留") return 0; return -1; } /// @brief fmt 是否已知(params_of 返回非负)。 bool is_known_format(const std::string& f) { return params_of(f) >= 0; } /** * @brief prefix 是否合法(V2 形态前缀集合)。 * @param prefix 前缀字符串 * @return 是否在 11/10/011/010000..010101/0010/0011/0001 内 */ bool is_known_prefix(const std::string& prefix) { static const char* kPrefixes[] = { "11", "10", "011", "010000", "010001", "010010", "010011", "010100", "010101", "0010", "0011", "0001", }; for (const char* p : kPrefixes) { if (prefix == p) { return true; } } return false; } /** * @brief prefix ↔ fmt 互锁表查询。 * @param prefix 形态前缀 * @return 期望 fmt;未知 prefix 返回空串 */ const char* fmt_of_prefix(const std::string& prefix) { if (prefix == "11") return "RR"; if (prefix == "10") return "NONE"; if (prefix == "011") return "RRR"; if (prefix == "010000") return "IMM"; if (prefix == "010001") return "SLOT"; if (prefix == "010010") return "JMP"; if (prefix == "010011") return "JC"; if (prefix == "010100") return "CALL"; if (prefix == "010101") return "CAL"; if (prefix == "0010") return "SLOT+off"; if (prefix == "0011") return "IMM64"; if (prefix == "0001") return "预留"; return ""; } /// @brief func 类别是否合法(V2:none/width/uint/full/float/dst)。 bool is_known_func_mode(const std::string& m) { return m == "none" || m == "width" || m == "uint" || m == "full" || m == "float" || m == "dst"; } /// @brief kind 是否合法(V2:bool/bit/int/uint/float/time/date)。 bool is_known_kind(const std::string& k) { return k == "bool" || k == "bit" || k == "int" || k == "uint" || k == "float" || k == "time" || k == "date"; } /** * @brief func 与基元的 (width, signed, is_float) 互锁校验。 * @param type_name 类型名(错误信息用) * @param prim 基元 * @param func func 值 * @param err 错误输出 * @return true 匹配;false(err 已写) */ bool func_matches_prim(const std::string& type_name, const PrimType& prim, uint32_t func, std::string* err) { if (prim.is_float) { if (func == (prim.width == 4 ? 8 : 9)) { return true; } } else { uint32_t want = 0; if (prim.width == 1 && !prim.is_signed) want = 0; else if (prim.width == 1 && prim.is_signed) want = 1; else if (prim.width == 2 && !prim.is_signed) want = 2; else if (prim.width == 2 && prim.is_signed) want = 3; else if (prim.width == 4 && !prim.is_signed) want = 4; else if (prim.width == 4 && prim.is_signed) want = 5; else if (prim.width == 8 && !prim.is_signed) want = 6; else if (prim.width == 8 && prim.is_signed) want = 7; if (func == want) { return true; } } fail(err, "type '" + type_name + "': func " + std::to_string(func) + " mismatches base '" + prim.name + "' (width/sign)"); return false; } /** * @brief 读必填字符串字段。 * @param t 配置表 * @param what 表归属描述(用于错误信息,如 "[meta]") * @param key 字段名 * @param out 输出值 * @param err 错误输出;可为 nullptr(静默) * @return true 成功;false(字段缺失或类型不符,err 已写 "machine error: ...") */ bool req_string(const toml::table& t, const char* what, const std::string& key, std::string* out, std::string* err) { const auto nv = t[key]; if (!nv || !nv.is_string()) { fail(err, std::string("missing field '") + key + "' in " + what); return false; } *out = nv.value_or(std::string()); return true; } /** * @brief 读必填整数字段。 * @param t 配置表 * @param what 表归属描述(用于错误信息,如 "[meta]") * @param key 字段名 * @param out 输出值 * @param err 错误输出;可为 nullptr(静默) * @return true 成功;false(字段缺失或类型不符,err 已写 "machine error: ...") */ bool req_int(const toml::table& t, const char* what, const std::string& key, int64_t* out, std::string* err) { const auto nv = t[key]; if (!nv || !nv.is_integer()) { fail(err, std::string("missing field '") + key + "' in " + what); return false; } *out = nv.value_or(0); return true; } /** * @brief 读必填布尔字段。 * @param t 配置表 * @param what 表归属描述(用于错误信息,如 "[meta]") * @param key 字段名 * @param out 输出值 * @param err 错误输出;可为 nullptr(静默) * @return true 成功;false(字段缺失或类型不符,err 已写 "machine error: ...") */ bool req_bool(const toml::table& t, const char* what, const std::string& key, bool* out, std::string* err) { const auto nv = t[key]; if (!nv || !nv.is_boolean()) { fail(err, std::string("missing field '") + key + "' in " + what); return false; } *out = nv.value_or(false); return true; } } // namespace /** * @brief 加载 machine.toml(V2)并做强校验。 * @param path machine.toml 路径 * @param err 错误输出;可为 nullptr(静默) * @return true 成功;false(err 已写,前缀 "machine error") * @details 校验顺序与文件头清单 1~5 一致(V2): * 1. meta:name 非空、version = 2 * 2. type:19 条必填全集;base 命中基元;kind 合法;func 与 base 互锁 * 3. op:prefix 合法;prefix↔fmt 互锁;(prefix,op) 分区内唯一; * func 类别合法;params 数量与 fmt 互锁 * 4. fb:15 条必填全集(fb_id 0..14);fb_id 唯一;字段类型命中 type 表 * 5. op-fb 互锁:CAL(010101)存在 ↔ fb 表非空 */ bool MachineConfig::load(const std::string& path, std::string* err) { ok_ = false; model_name_.clear(); version_ = 0; types_.clear(); ops_.clear(); fbs_.clear(); toml::table root; try { root = toml::parse_file(path); } catch (const toml::parse_error& e) { fail(err, std::string("parse error: ") + std::string(e.description())); return false; } // ---- 1. meta ---- const toml::table* meta = root["meta"].as_table(); if (!meta) { fail(err, "missing table 'meta'"); return false; } if (!req_string(*meta, "[meta]", "name", &model_name_, err)) return false; if (model_name_.empty()) { fail(err, "empty name in [meta]"); return false; } int64_t ver = 0; if (!req_int(*meta, "[meta]", "version", &ver, err)) return false; if (ver != 2) { fail(err, "version must be 2 (V2, STATOR2)"); return false; } version_ = static_cast(ver); // ---- 2. type(19 条全集)---- { const toml::array* arr = root["type"].as_array(); if (!arr || arr->empty()) { fail(err, "missing 'type' table"); return false; } const char* kAllTypes[19] = { "BOOL", "BYTE", "WORD", "DWORD", "LWORD", "SINT", "INT", "DINT", "LINT", "USINT", "UINT", "UDINT", "ULINT", "TIME", "REAL", "LREAL", "DATE", "TOD", "DT", }; std::map seen; for (const auto& el : *arr) { const toml::table* t = el.as_table(); if (!t) { fail(err, "bad entry in type"); return false; } ConfigType ct; if (!req_string(*t, "type", "name", &ct.name, err)) return false; if (!req_string(*t, "type", "base", &ct.base, err)) return false; if (!req_string(*t, "type", "kind", &ct.kind, err)) return false; int64_t func = 0; if (!req_int(*t, "type", "func", &func, err)) return false; if (func < 0 || func > 9) { fail(err, "type '" + ct.name + "': func out of range (0..9)"); return false; } ct.func = static_cast(func); if (seen.count(ct.name)) { fail(err, "duplicate type '" + ct.name + "'"); return false; } seen[ct.name] = true; if (!is_known_kind(ct.kind)) { fail(err, "type '" + ct.name + "': bad kind '" + ct.kind + "'"); return false; } const PrimType* prim = find_prim(ct.base); if (prim == nullptr) { fail(err, "type '" + ct.name + "': unknown base '" + ct.base + "'"); return false; } // kind 与基元语义互锁:float ↔ is_float const bool kind_float = ct.kind == "float"; if (kind_float != prim->is_float) { fail(err, "type '" + ct.name + "': kind '" + ct.kind + "' mismatches base '" + ct.base + "'"); return false; } if (!func_matches_prim(ct.name, *prim, ct.func, err)) return false; types_.push_back(ct); } // 19 条全集必填 for (const char* need : kAllTypes) { if (!seen.count(need)) { fail(err, std::string("type contract broken: missing '") + need + "'"); return false; } } } // ---- 3. op ---- { const toml::array* arr = root["op"].as_array(); if (!arr || arr->empty()) { fail(err, "missing 'op' table"); return false; } std::map key_owner; // "prefix/op" → 已存在 std::map name_owner; // 助记符 → 已存在 for (const auto& el : *arr) { const toml::table* t = el.as_table(); if (!t) { fail(err, "bad entry in op"); return false; } ConfigOp op; if (!req_string(*t, "op", "name", &op.name, err)) return false; if (!req_string(*t, "op", "prefix", &op.prefix, err)) return false; if (!req_string(*t, "op", "fmt", &op.fmt, err)) return false; if (!req_string(*t, "op", "func", &op.func_mode, err)) return false; int64_t opno = 0; if (!req_int(*t, "op", "op", &opno, err)) return false; if (opno < 0 || opno > 63) { fail(err, "op '" + op.name + "': op out of range (0..63)"); return false; } op.op = static_cast(opno); if (name_owner.count(op.name)) { fail(err, "duplicate op name '" + op.name + "'"); return false; } name_owner[op.name] = true; if (!is_known_prefix(op.prefix)) { fail(err, "op '" + op.name + "': bad prefix '" + op.prefix + "'"); return false; } // prefix ↔ fmt 互锁 const char* want_fmt = fmt_of_prefix(op.prefix); if (want_fmt == nullptr || op.fmt != want_fmt) { fail(err, "op '" + op.name + "': prefix '" + op.prefix + "' must pair with fmt '" + std::string(want_fmt ? want_fmt : "?") + "'"); return false; } if (!is_known_format(op.fmt)) { fail(err, "op '" + op.name + "': bad fmt '" + op.fmt + "'"); return false; } // (prefix, op) 分区内唯一 const std::string key = op.prefix + "/" + std::to_string(op.op); if (key_owner.count(key)) { fail(err, "duplicate (prefix, op) '" + key + "'"); return false; } key_owner[key] = true; // func 类别合法 if (!is_known_func_mode(op.func_mode)) { fail(err, "op '" + op.name + "': bad func mode '" + op.func_mode + "'"); return false; } if (!req_bool(*t, "op", "enabled", &op.enabled, err)) return false; // params 数量与 fmt 互锁 const auto pv = (*t)["params"]; if (!pv || !pv.is_array()) { fail(err, "op '" + op.name + "': missing 'params'"); return false; } const int want = params_of(op.fmt); for (const auto& p : *pv.as_array()) { if (!p.is_string()) { fail(err, "op '" + op.name + "': params must be strings"); return false; } op.params.push_back(p.value_or(std::string())); } if (static_cast(op.params.size()) != want) { fail(err, "op '" + op.name + "': params count " + std::to_string(op.params.size()) + " != fmt expects " + std::to_string(want)); return false; } ops_.push_back(op); } } // ---- 4. fb(15 条全集 fb_id 0..14)---- { const toml::array* arr = root["fb"].as_array(); if (!arr || arr->empty()) { fail(err, "missing 'fb' table"); return false; } std::map id_owner; std::map name_owner; for (const auto& el : *arr) { const toml::table* t = el.as_table(); if (!t) { fail(err, "bad entry in fb"); return false; } ConfigFb fb; if (!req_string(*t, "fb", "name", &fb.name, err)) return false; int64_t fid = 0; if (!req_int(*t, "fb", "fb_id", &fid, err)) return false; if (fid < 0 || fid > 14) { fail(err, "fb '" + fb.name + "': fb_id out of range (0..14)"); return false; } fb.fb_id = static_cast(fid); if (id_owner.count(fb.fb_id)) { fail(err, "duplicate fb_id " + std::to_string(fb.fb_id)); return false; } id_owner[fb.fb_id] = true; if (name_owner.count(fb.name)) { fail(err, "duplicate fb name '" + fb.name + "'"); return false; } name_owner[fb.name] = true; const auto fv = (*t)["fields"]; if (!fv || !fv.is_array()) { fail(err, "fb '" + fb.name + "': missing 'fields'"); return false; } std::map fnames; for (const auto& f : *fv.as_array()) { const toml::array* pair = f.as_array(); if (!pair || pair->size() != 2) { fail(err, "fb '" + fb.name + "': field must be [name, type]"); return false; } ConfigFbField field; field.name = (*pair)[0].value_or(std::string()); field.type = (*pair)[1].value_or(std::string()); if (field.name.empty() || field.type.empty()) { fail(err, "fb '" + fb.name + "': empty field name/type"); return false; } if (fnames.count(field.name)) { fail(err, "fb '" + fb.name + "': duplicate field '" + field.name + "'"); return false; } fnames[field.name] = true; if (find_type(field.type) == nullptr) { fail(err, "fb '" + fb.name + "': unknown field type '" + field.type + "'"); return false; } fb.fields.push_back(field); } fbs_.push_back(fb); } // 15 条全集必填(fb_id 0..14) for (uint32_t i = 0; i < 15; ++i) { if (!id_owner.count(i)) { fail(err, "fb contract broken: missing fb_id " + std::to_string(i)); return false; } } } // ---- 5. op-fb 互锁:CAL(010101)存在 ↔ fb 表非空 ---- const ConfigOp* cal = find_op_by_key("010101", 0); if (cal == nullptr || !cal->enabled) { fail(err, "op-fb interlock: CAL (010101) must exist and be enabled"); return false; } ok_ = true; return true; } /** * @brief 按配置类型名查类型行。 * @param name 配置类型名 * @return 命中指针;未找到返回 nullptr */ const ConfigType* MachineConfig::find_type(const std::string& name) const { for (const ConfigType& t : types_) { if (t.name == name) { return &t; } } return nullptr; } /** * @brief 按助记符查指令行。 * @param name 助记符(如 "MOVE") * @return 命中指针;未找到返回 nullptr */ const ConfigOp* MachineConfig::find_op(const std::string& name) const { for (const ConfigOp& o : ops_) { if (o.name == name) { return &o; } } return nullptr; } /** * @brief 按 (prefix, op) 键查指令行。 * @param prefix 形态前缀(如 "11"/"010001") * @param op 分区内编号 * @return 命中指针;未找到返回 nullptr */ const ConfigOp* MachineConfig::find_op_by_key(const std::string& prefix, uint32_t op) const { for (const ConfigOp& o : ops_) { if (o.prefix == prefix && o.op == op) { return &o; } } return nullptr; } /** * @brief 查询某 (prefix, op) 是否启用。 * @param prefix 形态前缀 * @param op 分区内编号 * @return 指令存在且 enabled 为 true;未知返回 false */ bool MachineConfig::op_enabled(const std::string& prefix, uint32_t op) const { const ConfigOp* o = find_op_by_key(prefix, op); return o != nullptr && o->enabled; } /** * @brief 按名称查内建 FB。 * @param name FB 名(如 "TON") * @return 命中指针;未找到返回 nullptr */ const ConfigFb* MachineConfig::find_fb(const std::string& name) const { for (const ConfigFb& f : fbs_) { if (f.name == name) { return &f; } } return nullptr; } /** * @brief 按 fb_id 查内建 FB。 * @param fb_id FB 类型 id(0..14) * @return 命中指针;未找到返回 nullptr */ const ConfigFb* MachineConfig::find_fb_by_id(uint32_t fb_id) const { for (const ConfigFb& f : fbs_) { if (f.fb_id == fb_id) { return &f; } } return nullptr; } } // namespace compiler