12.11 闭环:二十用例点亮 + 单步观察。
- cases_test:tests/cases/01..20 逐一跑完整管线(解析→链接→类型→码→VM 周期), 按 EXPECTED.md 期望断言:13 正例运行通过(含双周期可重复)、06 CycleLimit、6 负例报错类别 - BytecodeExecutor --step:采样 I → 逐指令打印 pc/fn/反汇编 + 执行后非零寄存器 → I/Q - Machine 补 header()/ended()/nregs()/cur_instr() 观察接口 - ctest 11/11(cases_all 20 用例点亮);line1 回放 0/1 组合 Q 正确、单步 I=[0 1]→Q=[1]
This commit is contained in:
@@ -89,3 +89,14 @@ target_compile_definitions(vm_test PRIVATE
|
||||
|
||||
add_test(NAME vm_cycles
|
||||
COMMAND vm_test)
|
||||
|
||||
# 12.2 二十用例点亮(12.11):按 EXPECTED.md 期望跑完整管线(REPO_ROOT 注入源目录绝对路径)
|
||||
add_executable(cases_test
|
||||
./src/cases_test.cpp)
|
||||
|
||||
target_link_libraries(cases_test PRIVATE compiler vm)
|
||||
target_compile_definitions(cases_test PRIVATE
|
||||
REPO_ROOT="${CMAKE_SOURCE_DIR}")
|
||||
|
||||
add_test(NAME cases_all
|
||||
COMMAND cases_test)
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
std::vector<uint8_t> img;
|
||||
if (!codegen_project(p, units, link, &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;
|
||||
}
|
||||
Reference in New Issue
Block a user