阶段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 化)
This commit is contained in:
2026-08-25 20:04:49 +08:00
parent fc76fea47b
commit a4335693d4
4 changed files with 545 additions and 344 deletions
+255 -121
View File
@@ -1,16 +1,17 @@
/**
* @file MachineConfig.cpp
* @brief 机器定义(machine.toml)加载与强校验
* @brief 机器定义(machine.toml V2)加载与强校验
* @author
* @date 2026-08-21
*
* @details 校验项(见 Doc/isa/指令配置.md 强校验清单 1~5):
* 1. TOML 语法与字段完整性(全属性必填)
* 2. type.base 命中内建基元;range 合法(min ≤ max
* 3. tag 与 .stb 常量表契约一致(BOOL=0、INT=1、TIME=2,唯一)
* 4. op.opcode 唯一、0..255class/format 合法;类别-格式互锁(INSTANCE ↔ CAL
* params 数量与 format 一致
* 5. fb.opcode 与 op 行(instance 类)一致;字段类型命中配置类型表;字段名唯一
* @details 校验项(见 Doc/isa/指令配置.md 强校验清单 1~5V2):
* 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"
@@ -73,31 +74,113 @@ namespace {
}
/**
* @brief format → 期望参数数量(解析时校验)。
* @param format 操作数形态(RR/RRR/IMM/SLOT/JMP/JC/CALL/CAL/NONE
* @return 参数数量;未知 format 返回 -1
* @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& format) {
if (format == "RR") return 2;
if (format == "RRR") return 3;
if (format == "IMM") return 2;
if (format == "SLOT") return 2;
if (format == "JMP") return 1;
if (format == "JC") return 2;
if (format == "CALL") return 1;
if (format == "CAL") return 1;
if (format == "NONE") return 0;
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 format 是否已知(params_of 返回非负)。
/// @brief fmt 是否已知(params_of 返回非负)。
bool is_known_format(const std::string& f) {
return params_of(f) >= 0;
}
/// @brief tag 是否在 .stb 常量表契约内(0=BOOL、1=INT、2=TIME)。
bool is_known_tag(uint32_t tag) {
return tag <= 2; // .stb 常量表契约:0=BOOL 1=INT 2=TIME
/**
* @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;
}
/**
@@ -163,17 +246,17 @@ namespace {
} // namespace
/**
* @brief 加载 machine.toml 并做强校验。
* @brief 加载 machine.tomlV2并做强校验。
* @param path machine.toml 路径
* @param err 错误输出;可为 nullptr(静默)
* @return true 成功;falseerr 已写,前缀 "machine error"
* @details 校验顺序与文件头清单 1~5 一致:
* 1. TOML 语法与字段完整性(meta/type/op/fb 全属性必填)
* 2. type.base 命中内建基元;range 合法(min ≤ max
* 3. tag 与 .stb 常量表契约一致(BOOL=0、INT=1、TIME=2唯一
* 4. op.opcode 唯一、0..255class/format 合法;类别-格式互锁(INSTANCE ↔ CAL);
* params 数量与 format 一致
* 5. fb.opcode 与 op 行(instance 类)一致;字段类型命中配置类型表;字段名唯一
* @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;
@@ -191,29 +274,38 @@ bool MachineConfig::load(const std::string& path, std::string* err) {
return false;
}
// ---- meta ----
// ---- 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 < 0 || ver > 0xFFFFFFFFLL) {
fail(err, "invalid version in [meta]");
if (ver != 2) {
fail(err, "version must be 2 (V2, STATOR2)");
return false;
}
version_ = static_cast<uint32_t>(ver);
// ---- type ----
// ---- 2. type19 条全集)----
{
const toml::array* arr = root["type"].as_array();
if (!arr || arr->empty()) {
fail(err, "missing 'type' table");
return false;
}
std::map<uint32_t, std::string> tag_owner; // tag → 类型名
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) {
@@ -223,59 +315,56 @@ bool MachineConfig::load(const std::string& path, std::string* err) {
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 (find_prim(ct.base) == nullptr) {
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;
}
int64_t tag = 0;
if (!req_int(*t, "type", "tag", &tag, err)) return false;
if (tag < 0 || !is_known_tag(static_cast<uint32_t>(tag))) {
fail(err, "type '" + ct.name + "': tag out of contract (0..2)");
// 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;
}
ct.tag = static_cast<uint32_t>(tag);
if (tag_owner.count(ct.tag)) {
fail(err, "duplicate tag " + std::to_string(ct.tag));
return false;
}
tag_owner[ct.tag] = ct.name;
if (t->contains("range")) {
const toml::array* r = (*t)["range"].as_array();
if (!r || r->size() != 2) {
fail(err, "type '" + ct.name + "': range must be [min, max]");
return false;
}
ct.has_range = true;
ct.range_min = (*r)[0].value_or<int64_t>(0);
ct.range_max = (*r)[1].value_or<int64_t>(0);
if (ct.range_min > ct.range_max) {
fail(err, "type '" + ct.name + "': range min > max");
return false;
}
}
if (!func_matches_prim(ct.name, *prim, ct.func, err)) return false;
types_.push_back(ct);
}
// .stb 契约:BOOL/INT/TIME 必须存在且 tag 为 0/1/2
const std::string need[3] = {"BOOL", "INT", "TIME"};
for (uint32_t i = 0; i < 3; ++i) {
const auto it = tag_owner.find(i);
if (it == tag_owner.end() || it->second != need[i]) {
fail(err, std::string("type contract broken: tag ") + std::to_string(i) +
" must be " + need[i]);
// 19 条全集必填
for (const char* need : kAllTypes) {
if (!seen.count(need)) {
fail(err, std::string("type contract broken: missing '") + need + "'");
return false;
}
}
}
// ---- op ----
// ---- 3. op ----
{
const toml::array* arr = root["op"].as_array();
if (!arr || arr->empty()) {
fail(err, "missing 'op' table");
return false;
}
std::map<uint32_t, std::string> code_owner;
std::map<std::string, uint32_t> name_owner;
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) {
@@ -284,51 +373,56 @@ bool MachineConfig::load(const std::string& path, std::string* err) {
}
ConfigOp op;
if (!req_string(*t, "op", "name", &op.name, err)) return false;
int64_t code = 0;
if (!req_int(*t, "op", "opcode", &code, err)) return false;
if (code < 0 || code > 255) {
fail(err, "op '" + op.name + "': opcode out of range");
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.opcode = static_cast<uint32_t>(code);
if (code_owner.count(op.opcode)) {
fail(err, "duplicate opcode " + std::to_string(op.opcode));
return false;
}
code_owner[op.opcode] = op.name;
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] = op.opcode;
std::string cls;
if (!req_string(*t, "op", "class", &cls, err)) return false;
if (cls == "instance") {
op.is_instance = true;
} else if (cls == "plain") {
op.is_instance = false;
} else {
fail(err, "op '" + op.name + "': bad class '" + cls + "'");
name_owner[op.name] = true;
if (!is_known_prefix(op.prefix)) {
fail(err, "op '" + op.name + "': bad prefix '" + op.prefix + "'");
return false;
}
if (!req_string(*t, "op", "format", &op.format, err)) return false;
if (!is_known_format(op.format)) {
fail(err, "op '" + op.name + "': bad format '" + op.format + "'");
// 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;
}
// 类别-格式互锁:INSTANCE ↔ CAL
const bool is_cal = op.format == "CAL";
if (op.is_instance != is_cal) {
fail(err, "op '" + op.name + "': class-format mismatch (instance <-> CAL)");
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.format);
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");
@@ -338,7 +432,7 @@ bool MachineConfig::load(const std::string& path, std::string* err) {
}
if (static_cast<int>(op.params.size()) != want) {
fail(err, "op '" + op.name + "': params count " +
std::to_string(op.params.size()) + " != format expects " +
std::to_string(op.params.size()) + " != fmt expects " +
std::to_string(want));
return false;
}
@@ -346,13 +440,15 @@ bool MachineConfig::load(const std::string& path, std::string* err) {
}
}
// ---- fb ----
// ---- 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) {
@@ -361,16 +457,23 @@ bool MachineConfig::load(const std::string& path, std::string* err) {
}
ConfigFb fb;
if (!req_string(*t, "fb", "name", &fb.name, err)) return false;
int64_t code = 0;
if (!req_int(*t, "fb", "opcode", &code, err)) return false;
fb.opcode = static_cast<uint32_t>(code);
// opcode 必须命中 instance 类 op 行
const ConfigOp* op = find_op_by_code(fb.opcode);
if (op == nullptr || !op->is_instance) {
fail(err, "fb '" + fb.name + "': opcode " + std::to_string(code) +
" must match an instance op");
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'");
@@ -403,6 +506,20 @@ bool MachineConfig::load(const std::string& path, std::string* err) {
}
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;
@@ -438,13 +555,15 @@ const ConfigOp* MachineConfig::find_op(const std::string& name) const {
}
/**
* @brief 按 opcode 查指令行。
* @param opcode 操作码(0..255
* @brief 按 (prefix, op) 键查指令行。
* @param prefix 形态前缀(如 "11"/"010001"
* @param op 分区内编号
* @return 命中指针;未找到返回 nullptr
*/
const ConfigOp* MachineConfig::find_op_by_code(uint32_t opcode) const {
const ConfigOp* MachineConfig::find_op_by_key(const std::string& prefix,
uint32_t op) const {
for (const ConfigOp& o : ops_) {
if (o.opcode == opcode) {
if (o.prefix == prefix && o.op == op) {
return &o;
}
}
@@ -452,18 +571,19 @@ const ConfigOp* MachineConfig::find_op_by_code(uint32_t opcode) const {
}
/**
* @brief 查询某 opcode 是否启用。
* @param opcode 操作码
* @return 指令存在且 enabled 为 true;未知 opcode 返回 false
* @brief 查询某 (prefix, op) 是否启用。
* @param prefix 形态前缀
* @param op 分区内编号
* @return 指令存在且 enabled 为 true;未知返回 false
*/
bool MachineConfig::op_enabled(uint32_t opcode) const {
const ConfigOp* op = find_op_by_code(opcode);
return op != nullptr && op->enabled;
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"
* @param name FB 名(如 "TON"
* @return 命中指针;未找到返回 nullptr
*/
const ConfigFb* MachineConfig::find_fb(const std::string& name) const {
@@ -475,4 +595,18 @@ const ConfigFb* MachineConfig::find_fb(const std::string& name) const {
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