Files
Interpreter/compiler/src/MachineConfig.cpp
T
Admin a4335693d4 阶段2-2:MachineConfig 重写为 V2(强校验 5 项)+ TypeInfo/machine_test V2 适配
- ConfigType:base/kind/func(V1 的 tag/range 删除);ConfigOp:prefix/op/fmt/
  func_mode(V1 的 opcode/class 删除);ConfigFb:fb_id(V1 的 opcode 删除)
- 强校验:meta(version=2) / type 19 条全集·base 命中·kind 合法·func 与
  (width,signed,float) 互锁 / op prefix 合法·prefix↔fmt 互锁·(prefix,op) 分区
  唯一·func 类别·params 互锁 / fb 15 条全集·fb_id 唯一·字段命中 type 表 /
  op-fb 互锁(CAL↔fb 表)
- TypeMeta:tag/range → func/kind
- machine_test:V2 断言(正例 68 op/19 type/15 fb + 14 个负例)
- 状态:compiler 库暂挂于 V1 Codec/Codegen/Stb/main(下一步按
  Codec → Stb → Codegen → main 顺序 V2 化)
2026-08-25 20:04:49 +08:00

613 lines
22 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file MachineConfig.cpp
* @brief 机器定义(machine.toml V2)加载与强校验
* @author
* @date 2026-08-21
*
* @details 校验项(见 Doc/isa/指令配置.md 强校验清单 1~5,V2):
* 1. metaname 非空、version = 2
* 2. type19 条必填全集(BOOL..DT);base 命中内建基元;kind 合法;
* func ∈ 0..9 且与 base 的 (width, signed, is_float) 互锁
* 3. opprefix 合法;prefix ↔ fmt 互锁;op 分区内唯一;func 类别合法;
* params 数量与 fmt 互锁;enabled 布尔
* 4. fb15 条必填全集(fb_id 0..14);fb_id 唯一;字段名唯一;字段类型命中 type 表
* 5. op-fb 互锁:CAL010101)存在 ↔ fb 表非空
*/
#include "compiler/MachineConfig.h"
#include <toml++/toml.hpp>
#include <map>
#include <string>
namespace compiler {
/**
* @brief 内建基元表。
* @return 静态基元表(bit / int8..uint64 / float32 / float64,元数据在代码)
*/
const std::vector<PrimType>& MachineConfig::prims() {
static const std::vector<PrimType> 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 类别是否合法(V2none/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 是否合法(V2bool/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 匹配;falseerr 已写)
*/
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<int64_t>(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<bool>(false);
return true;
}
} // namespace
/**
* @brief 加载 machine.tomlV2)并做强校验。
* @param path machine.toml 路径
* @param err 错误输出;可为 nullptr(静默)
* @return true 成功;falseerr 已写,前缀 "machine error"
* @details 校验顺序与文件头清单 1~5 一致(V2):
* 1. metaname 非空、version = 2
* 2. type19 条必填全集;base 命中基元;kind 合法;func 与 base 互锁
* 3. opprefix 合法;prefix↔fmt 互锁;(prefix,op) 分区内唯一;
* func 类别合法;params 数量与 fmt 互锁
* 4. fb15 条必填全集(fb_id 0..14);fb_id 唯一;字段类型命中 type 表
* 5. op-fb 互锁:CAL010101)存在 ↔ 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<uint32_t>(ver);
// ---- 2. type19 条全集)----
{
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<std::string, bool> 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<uint32_t>(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<std::string, bool> key_owner; // "prefix/op" → 已存在
std::map<std::string, bool> 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<uint32_t>(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<int>(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. fb15 条全集 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<uint32_t, bool> id_owner;
std::map<std::string, bool> 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<uint32_t>(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<std::string, bool> 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 互锁:CAL010101)存在 ↔ 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 类型 id0..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