- 打印模式(无 -o)不再要求 --machine(恢复 12.3 行为) - 编译路径与 --disasm 必填 --machine,加载/校验失败报错退出 - 验证:打印/编译/disasm/缺参四种模式行为正确;ctest 12/12
242 lines
8.7 KiB
C++
242 lines
8.7 KiB
C++
/**
|
||
* @file main.cpp
|
||
* @brief STCompiler 可执行入口
|
||
* @author
|
||
* @date 2026-08-21
|
||
*/
|
||
|
||
#include <cctype>
|
||
#include <cstdio>
|
||
#include <cstring>
|
||
#include <filesystem>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
#include "compiler/Codec.h"
|
||
#include "compiler/Codegen.h"
|
||
#include "compiler/Linker.h"
|
||
#include "compiler/MachineConfig.h"
|
||
#include "compiler/Project.h"
|
||
#include "compiler/Stb.h"
|
||
#include "compiler/Typecheck.h"
|
||
|
||
namespace {
|
||
void usage() {
|
||
std::printf("usage: STCompiler <project.toml> [-o <name>.stb] --machine <machine.toml>\n"
|
||
" STCompiler <name>.stb --disasm --machine <machine.toml>\n"
|
||
" 解析工程并编译(词法 → 语法 → 链接 → 类型 → 寄存器码)\n"
|
||
" -o <name>.stb 编译并写出映像文件\n"
|
||
" --machine <path> 机器定义(machine.toml,编译路径必填)\n"
|
||
" --disasm 反汇编一个已编译的 .stb 映像(需要 --machine)\n"
|
||
" --help 打印本帮助\n");
|
||
}
|
||
|
||
// 反汇编一个已编译映像(函数表逐条 + 常量表 + 数据段摘要)。
|
||
// 指令偏移为文件绝对字节偏移;反汇编按 machine.toml 的 format 输出。
|
||
int dump_image(const char* path, const compiler::MachineConfig& cfg) {
|
||
std::vector<uint8_t> bytes;
|
||
std::string err;
|
||
if (!compiler::read_stb_file(path, &bytes, &err)) {
|
||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||
return 1;
|
||
}
|
||
const compiler::StbView v = compiler::StbView::from(bytes);
|
||
if (!v.ok()) {
|
||
std::fprintf(stderr, "error: %s\n", v.error().c_str());
|
||
return 1;
|
||
}
|
||
std::printf("image: %s (%zu bytes, %u functions, %u globals, entry fn %u)\n",
|
||
path, bytes.size(), v.n_funcs(), v.n_globals(), v.entry_fn_id());
|
||
std::printf(" dt_ms=%u cycle_limit=%u hash=0x%016llx model=%s sha=%s\n",
|
||
v.dt_ms(), v.cycle_limit(),
|
||
static_cast<unsigned long long>(v.project_hash()),
|
||
v.model_id().c_str(), v.sha_ok() ? "ok" : "BAD");
|
||
|
||
if (v.n_consts()) {
|
||
std::printf("constants:\n");
|
||
for (uint32_t i = 0; i < v.n_consts(); ++i) {
|
||
const compiler::ConstEntry c = v.const_entry(i);
|
||
const char* tag = c.tag == 0 ? "BOOL" : c.tag == 1 ? "INT" : "TIME";
|
||
std::printf(" [%u] %s %llu\n", i, tag,
|
||
static_cast<unsigned long long>(c.value));
|
||
}
|
||
}
|
||
|
||
std::printf("functions:\n");
|
||
for (uint32_t i = 0; i < v.n_funcs(); ++i) {
|
||
const compiler::StbView::FuncRow r = v.func_row(i);
|
||
std::printf(" fn %u: nregs=%u, offset=%u, len=%u\n", i, r.nregs,
|
||
r.code_offset, r.code_len);
|
||
const uint8_t* base = v.code_bytes() + r.code_offset;
|
||
for (uint32_t j = 0; j < r.code_len; ++j) {
|
||
char buf[64];
|
||
compiler::disasm(cfg, reinterpret_cast<const uint32_t*>(base)[j], buf,
|
||
sizeof buf);
|
||
std::printf(" 0x%04x %s\n",
|
||
v.offset_code() + r.code_offset + j * 4, buf);
|
||
}
|
||
}
|
||
|
||
if (v.data_len()) {
|
||
std::printf("data (%zu bytes):\n", v.data_len());
|
||
const uint8_t* d = v.data_bytes();
|
||
for (size_t i = 0; i < v.data_len(); i += 16) {
|
||
std::printf(" 0x%04x:", static_cast<unsigned>(i));
|
||
for (size_t j = 0; j < 16 && i + j < v.data_len(); ++j) {
|
||
std::printf(" %02x", d[i + j]);
|
||
}
|
||
std::printf("\n");
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
int main(int argc, char** argv) {
|
||
if (argc < 2) {
|
||
std::printf("STCompiler 0.1\n");
|
||
usage();
|
||
return 0;
|
||
}
|
||
if (std::strcmp(argv[1], "--help") == 0) {
|
||
usage();
|
||
return 0;
|
||
}
|
||
|
||
// 参数收集
|
||
std::string machine_path;
|
||
std::string out_path;
|
||
bool disasm_mode = false;
|
||
for (int i = 2; i + 1 < argc; ++i) {
|
||
if (std::strcmp(argv[i], "--machine") == 0) {
|
||
machine_path = argv[i + 1];
|
||
} else if (std::strcmp(argv[i], "-o") == 0) {
|
||
out_path = argv[i + 1];
|
||
}
|
||
}
|
||
for (int i = 1; i < argc; ++i) {
|
||
if (std::strcmp(argv[i], "--disasm") == 0) {
|
||
disasm_mode = true;
|
||
}
|
||
}
|
||
|
||
// --disasm:需要机器定义(反汇编按 machine.toml 的 format 输出)
|
||
if (disasm_mode) {
|
||
if (machine_path.empty()) {
|
||
std::fprintf(stderr, "error: missing --machine <machine.toml>\n");
|
||
usage();
|
||
return 1;
|
||
}
|
||
compiler::MachineConfig cfg;
|
||
std::string err;
|
||
if (!cfg.load(machine_path, &err)) {
|
||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||
return 1;
|
||
}
|
||
return dump_image(argv[1], cfg);
|
||
}
|
||
|
||
const std::string toml_path = argv[1];
|
||
compiler::Project proj;
|
||
std::string err;
|
||
if (!compiler::parse_project(toml_path, &proj, &err)) {
|
||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||
return 1;
|
||
}
|
||
const std::vector<std::string> files = compiler::compile_files(proj);
|
||
|
||
// 打印模式(无 -o):不需要机器定义
|
||
if (out_path.empty()) {
|
||
uint64_t hash = 0;
|
||
if (!compiler::compute_project_hash(proj, &hash, &err)) {
|
||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||
return 1;
|
||
}
|
||
std::printf("project: %s\n", proj.name.c_str());
|
||
std::printf("files:\n");
|
||
for (const std::string& f : files) {
|
||
std::printf(" %s\n", f.c_str());
|
||
}
|
||
std::printf("hash: 0x%016llx\n", static_cast<unsigned long long>(hash));
|
||
return 0;
|
||
}
|
||
|
||
// 编译路径:机器定义必填(编码/类型/FB 布局来自 machine.toml)
|
||
if (machine_path.empty()) {
|
||
std::fprintf(stderr, "error: missing --machine <machine.toml>\n");
|
||
usage();
|
||
return 1;
|
||
}
|
||
compiler::MachineConfig cfg;
|
||
if (!cfg.load(machine_path, &err)) {
|
||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||
return 1;
|
||
}
|
||
|
||
// 完整管线:读 .st → 链接 → 类型检查 → 寄存器码
|
||
std::vector<compiler::SourceUnit> units;
|
||
for (const std::string& f : files) {
|
||
compiler::SourceUnit u;
|
||
if (!compiler::load_unit(proj.base_dir + "/" + f, &u, &err)) {
|
||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||
return 1;
|
||
}
|
||
units.push_back(std::move(u));
|
||
}
|
||
compiler::LinkResult link;
|
||
if (!compiler::link_project(proj, units, &link, &err)) {
|
||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||
return 1;
|
||
}
|
||
if (!compiler::check_project(proj, units, link, &err)) {
|
||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||
return 1;
|
||
}
|
||
std::vector<uint8_t> image;
|
||
if (!compiler::codegen_project(proj, units, link, cfg, &image, &err)) {
|
||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||
return 1;
|
||
}
|
||
if (!compiler::write_stb_file(out_path.c_str(), image, &err)) {
|
||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||
return 1;
|
||
}
|
||
|
||
// 写后自检:型号标识与 SHA-256
|
||
const compiler::StbView self = compiler::StbView::from(image);
|
||
if (!self.ok() || !self.sha_ok()) {
|
||
std::fprintf(stderr, "error: self-check failed: %s\n", self.error().c_str());
|
||
return 1;
|
||
}
|
||
if (!self.model_matches(cfg.model_name(), cfg.version())) {
|
||
std::fprintf(stderr, "error: self-check model mismatch\n");
|
||
return 1;
|
||
}
|
||
|
||
// sidecar:I/O 绑定 var → 槽号 → channel/bit(不进映像,执行器采样用)
|
||
std::vector<compiler::IoBinding> bindings;
|
||
for (const compiler::IoBinding& b : proj.io) {
|
||
compiler::IoBinding out_b = b;
|
||
std::string key = b.var;
|
||
for (char& ch : key) {
|
||
ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
|
||
}
|
||
const auto it = link.global_index.find(key);
|
||
if (it != link.global_index.end()) {
|
||
out_b.slot = it->second; // 12.6 已校验存在
|
||
}
|
||
bindings.push_back(out_b);
|
||
}
|
||
std::filesystem::path sidecar = std::filesystem::path(out_path);
|
||
sidecar.replace_extension(".runtime.toml");
|
||
if (!compiler::write_sidecar_file(sidecar.string().c_str(), bindings, &err)) {
|
||
std::fprintf(stderr, "error: %s\n", err.c_str());
|
||
return 1;
|
||
}
|
||
|
||
const compiler::StbView v = compiler::StbView::from(image);
|
||
std::printf("compiled: %s (%zu bytes, %u functions, %u globals)\n",
|
||
out_path.c_str(), image.size(), v.n_funcs(), v.n_globals());
|
||
return 0;
|
||
}
|