Files
Interpreter/tests/src/cases_test.cpp
T
Admin 447a8d0119 阶段 B 步骤 4:compiler 配置驱动化,去除 isa 依赖。
- Project.h 自带 IoBinding;Stb.h/cpp:compiler 侧 .stb 规范(常量/ConstEntry/FNV/StbView/读写/sidecar)
- Codec.h/cpp:指令字 pack/字段/配置驱动 disasm(format 表驱动)
- Codegen:opcode 全查 MachineConfig(E_* 发射器)、常量 tag 查配置类型、初值按基元宽度写、
  映像拼装用 Stb 常量;codegen_project 增加 cfg 参数
- main.cpp:--machine <path>(编译/反汇编必填),--disasm 用 StbView+Codec
- CMake:compiler 不再链接 isa(仅 toml++ 私有);测试补链 isa(自身断言用)
- 验证:20 用例字节不变(hash 0xc3fe4ae74ad45900 相同)、ctest 12/12、缺 --machine 报错退出
2026-08-21 22:14:29 +08:00

179 lines
6.3 KiB
C++

/**
* @file cases_test.cpp
* @brief 12.2 二十用例点亮(12.11):每例按 EXPECTED.md 期望跑完整管线
* @author
* @date 2026-08-21
*
* @details 对 tests/cases/01..20 逐一执行:解析 → 链接 → 类型检查 → 寄存器码 → VM 周期,
* 断言期望结果(编译运行通过 / 报错类别 / cycle_limit 故障),与各用例 EXPECTED.md 一致。
*/
#include <cstdio>
#include <string>
#include <vector>
#include "compiler/Codegen.h"
#include "compiler/Linker.h"
#include "compiler/Project.h"
#include "compiler/Typecheck.h"
#include "vm/Machine.h"
#ifndef REPO_ROOT
#define REPO_ROOT "."
#endif
static int g_checks = 0;
#define CHECK(cond) \
do { \
if (!(cond)) { \
std::printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \
return false; \
} \
++g_checks; \
} while (0)
namespace {
enum class Outcome { Ok, CompileError, CycleLimit };
// 期望表(与 tests/cases/*/EXPECTED.md 一致;负例断言错误类别关键词)
struct CaseSpec {
const char* dir;
Outcome out;
const char* err_keyword; // CompileError 时须出现在错误消息里
};
const CaseSpec kCases[] = {
{"01_empty_main", Outcome::Ok, ""},
{"02_bool_assign", Outcome::Ok, ""},
{"03_short_circuit", Outcome::Ok, ""},
{"04_if_elsif_else", Outcome::Ok, ""},
{"05_while_normal", Outcome::Ok, ""},
{"06_while_cycle_limit", Outcome::CycleLimit, ""},
{"07_int_arith", Outcome::Ok, ""},
{"08_time_literal", Outcome::Ok, ""},
{"09_gvl_external", Outcome::Ok, ""},
{"10_gvl_wrong_file", Outcome::CompileError, "VAR_GLOBAL only allowed in gvl file"},
{"11_duplicate_global", Outcome::CompileError, "duplicate global"},
{"12_io_unknown_var", Outcome::CompileError, "io.var"},
{"13_function_call", Outcome::Ok, ""},
{"14_function_write_global", Outcome::CompileError, "function cannot write global"},
{"15_fb_instance", Outcome::Ok, ""},
{"16_fb_undeclared", Outcome::CompileError, "undeclared FB instance"},
{"17_ton", Outcome::Ok, ""},
{"18_tof_ctu", Outcome::Ok, ""},
{"19_recursive_call", Outcome::CompileError, "recursive call"},
{"20_line1", Outcome::Ok, ""},
};
bool run_case(const char* dir, Outcome want, const char* keyword) {
using namespace compiler;
const std::string toml = std::string(REPO_ROOT) + "/tests/cases/" + dir + "/project.toml";
std::string err;
Project p;
if (!parse_project(toml, &p, &err)) {
if (want == Outcome::CompileError) {
++g_checks;
return true;
}
std::printf("FAIL %s parse: %s\n", dir, err.c_str());
return false;
}
std::vector<SourceUnit> units;
for (const std::string& f : compile_files(p)) {
SourceUnit u;
if (!load_unit(p.base_dir + "/" + f, &u, &err)) {
if (want == Outcome::CompileError) {
++g_checks;
return true;
}
std::printf("FAIL %s load: %s\n", dir, err.c_str());
return false;
}
units.push_back(std::move(u));
}
LinkResult link;
if (!link_project(p, units, &link, &err)) {
if (want == Outcome::CompileError) {
if (err.find(keyword) == std::string::npos) {
std::printf("FAIL %s: want '%s', got '%s'\n", dir, keyword, err.c_str());
return false;
}
++g_checks;
return true;
}
std::printf("FAIL %s link: %s\n", dir, err.c_str());
return false;
}
if (!check_project(p, units, link, &err)) {
if (want == Outcome::CompileError) {
if (err.find(keyword) == std::string::npos) {
std::printf("FAIL %s: want '%s', got '%s'\n", dir, keyword, err.c_str());
return false;
}
++g_checks;
return true;
}
std::printf("FAIL %s type: %s\n", dir, err.c_str());
return false;
}
MachineConfig cfg;
if (!cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &err)) {
std::printf("FAIL %s machine load\n", dir);
return false;
}
std::vector<uint8_t> img;
if (!codegen_project(p, units, link, cfg, &img, &err)) {
if (want == Outcome::CompileError) {
++g_checks;
return true;
}
std::printf("FAIL %s codegen: %s\n", dir, err.c_str());
return false;
}
if (want == Outcome::CompileError) {
std::printf("FAIL %s: expected compile error\n", dir);
return false;
}
vm::Machine m;
if (!vm::Machine::create(img, &m, &err)) {
std::printf("FAIL %s vm create: %s\n", dir, err.c_str());
return false;
}
const vm::Fault f = m.run_cycle();
if (want == Outcome::CycleLimit) {
if (f == vm::Fault::CycleLimit) {
++g_checks;
return true;
}
std::printf("FAIL %s: want CycleLimit, got %d\n", dir, static_cast<int>(f));
return false;
}
if (f != vm::Fault::None) {
std::printf("FAIL %s: fault %d\n", dir, static_cast<int>(f));
return false;
}
// 可重复性:再跑一个周期
if (m.run_cycle() != vm::Fault::None) {
std::printf("FAIL %s: 2nd cycle fault\n", dir);
return false;
}
++g_checks;
return true;
}
} // namespace
int main() {
for (const CaseSpec& c : kCases) {
if (!run_case(c.dir, c.out, c.err_keyword)) {
return 1;
}
}
std::printf("cases_test: %d cases lit up\n", g_checks);
return 0;
}