- 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 化)
613 lines
22 KiB
C++
613 lines
22 KiB
C++
/**
|
||
* @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 <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 类别是否合法(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<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.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<uint32_t>(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<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. 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<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 互锁: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
|