阶段3-1:编译器语言层 V2 完成(19 类型/字面量扩展/禁隐式转换/配置驱动 FB)

Lexer V2:
- 19 类型关键字(BOOL..DT);FB 名不再设关键字(IDENT)
- FLOAT_LIT(1.5/2.5e3)、进制前缀(2#/8#/16# 换算十进制)
- 日期字面量 D#/TOD#/DT#(历法换算 1970 纪元、闰年、值域检查)

Parser V2:
- TypeRef 字符串化(TypeKind{Scalar,FbBuiltin,FbUser},名字小写)
- ExprKind 增 LitReal/LitDate/LitTod/LitDt;Expr.double_value
- IDENT 类型统一 FbUser(内建判定在 Linker 查 fb 表)

Typecheck V2:
- TType 19 + Unresolved(字面量未定型)
- 禁隐式转换(赋值/算术/比较严格同型)
- resolve_to:未定型→消费点定型+值域检查(BOOL 0/1、无符号负数、
  溢出、浮点仅 REAL/LREAL);算术按 kind 允许(TIME 仅 ±)
- Call 实参按函数参数类型;check_project 加 cfg

Linker V2:
- FbField.type_name 字符串化;内建 FB 布局查 machine.toml [[fb]] 表
  (删 8 个硬编码布局,带缓存);link_project 加 cfg

Codegen V2:
- 浮点/日期字面量(LOADK func 8/4/4/6;LitReal 按 REAL 装载,LREAL TODO)
- 算术/比较 func 按操作数类型(expr_func)
- TypeKind 连锁适配

main.cpp:link/check 传 cfg;--disasm 头 22 字段逐行中文注释(L5)
+ slots 节;StbView 补 offset_const/offset_funcs

测试适配:parser/linker/typecheck/vm/cases 适配新接口与 V2 语义。
ctest 11/13(vm_cycles/cases_all 待阶段 4)。codegen_test 仍剥离(阶段 3-2 重写)。
This commit is contained in:
2026-08-26 09:29:31 +08:00
parent 6ab2195a6b
commit 6be2873c88
16 changed files with 863 additions and 336 deletions
+8 -7
View File
@@ -94,8 +94,14 @@ namespace {
}
units.push_back(std::move(u));
}
compiler::MachineConfig cfg;
std::string cerr;
if (!cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &cerr)) {
std::printf("FAIL %s machine: %s\n", dir, cerr.c_str());
return false;
}
LinkResult link;
if (!link_project(p, units, &link, &err)) {
if (!link_project(p, units, cfg, &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());
@@ -107,7 +113,7 @@ namespace {
std::printf("FAIL %s link: %s\n", dir, err.c_str());
return false;
}
if (!check_project(p, units, link, &err)) {
if (!check_project(p, units, link, cfg, &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());
@@ -119,11 +125,6 @@ namespace {
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) {
+19 -8
View File
@@ -43,7 +43,12 @@ static bool link_case(const char* dir, compiler::LinkResult* out, std::string* e
}
units.push_back(std::move(u));
}
return link_project(p, units, out, err);
compiler::MachineConfig cfg;
std::string cerr;
if (!cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &cerr)) {
return false;
}
return link_project(p, units, cfg, out, err);
}
static bool expect_link_err(const char* dir, const char* keyword) {
@@ -146,7 +151,10 @@ static bool test_line1() {
CHECK(load_unit(p.base_dir + "/" + f, &u, &err));
units.push_back(std::move(u));
}
CHECK(link_project(p, units, &r, &err));
compiler::MachineConfig cfg;
std::string cerr;
CHECK(cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &cerr));
CHECK(link_project(p, units, cfg, &r, &err));
// 全局槽序 = 声明顺序
CHECK(r.globals.size() == 4);
@@ -162,7 +170,7 @@ static bool test_line1() {
CHECK(it->second.fields[0].name == "start");
CHECK(it->second.fields[1].name == "stop");
CHECK(it->second.fields[2].name == "q");
CHECK(it->second.fields[2].type == TypeKind::Bool);
CHECK(it->second.fields[2].type_name == "bool");
// 内建 TON 布局(用例 17 的 t)
const FbLayout* ton = nullptr;
@@ -204,16 +212,19 @@ static bool test_builtin() {
CHECK(load_unit(p.base_dir + "/" + f, &u, &err));
units.push_back(std::move(u));
}
CHECK(link_project(p, units, &r, &err));
compiler::MachineConfig cfg;
std::string cerr;
CHECK(cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &cerr));
CHECK(link_project(p, units, cfg, &r, &err));
CHECK(r.scopes.size() == 1);
const LinkResult::PouScope& main = r.scopes[0];
const auto it = main.fb_instances.find("t");
CHECK(it != main.fb_instances.end());
CHECK(it->second.fields.size() == 4);
CHECK(it->second.fields[0].name == "in" && it->second.fields[0].type == TypeKind::Bool);
CHECK(it->second.fields[1].name == "pt" && it->second.fields[1].type == TypeKind::Time);
CHECK(it->second.fields[2].name == "q" && it->second.fields[2].type == TypeKind::Bool);
CHECK(it->second.fields[3].name == "et" && it->second.fields[3].type == TypeKind::Time);
CHECK(it->second.fields[0].name == "in" && it->second.fields[0].type_name == "bool");
CHECK(it->second.fields[1].name == "pt" && it->second.fields[1].type_name == "time");
CHECK(it->second.fields[2].name == "q" && it->second.fields[2].type_name == "bool");
CHECK(it->second.fields[3].name == "et" && it->second.fields[3].type_name == "time");
return true;
}
+3 -3
View File
@@ -38,7 +38,7 @@ static bool test_gvl_file() {
CHECK(g.section == VarSection::Global);
CHECK(g.vars.size() == 4);
CHECK(g.vars[0].name == "emergencystop");
CHECK(g.vars[0].type.kind == TypeKind::Bool);
CHECK(g.vars[0].type.kind == TypeKind::Scalar && g.vars[0].type.name == "bool");
CHECK(g.vars[0].has_init && g.vars[0].init_value == 0); // := FALSE
CHECK(!g.vars[1].has_init); // I0_0
return true;
@@ -145,7 +145,7 @@ static bool test_function() {
const POU& f = u.pous[0];
CHECK(f.kind == PouKind::Function);
CHECK(f.name == "add");
CHECK(f.result_type.kind == TypeKind::Int);
CHECK(f.result_type.kind == TypeKind::Scalar && f.result_type.name == "int");
CHECK(f.body[0].kind == StmtKind::Assign);
CHECK(f.body[0].value->kind == ExprKind::Add);
@@ -192,7 +192,7 @@ static bool test_control() {
CHECK(parse_pous("t.st", src, &u, &err));
const POU& p = u.pous[0];
CHECK(p.blocks[0].vars[2].name == "t");
CHECK(p.blocks[0].vars[2].type.kind == TypeKind::FbBuiltin);
CHECK(p.blocks[0].vars[2].type.kind == TypeKind::FbUser);
CHECK(p.blocks[0].vars[2].type.name == "ton");
CHECK(p.body.size() == 4);
+17 -7
View File
@@ -45,10 +45,15 @@ static bool check_case(const char* dir, compiler::LinkResult* r, std::string* er
}
units.push_back(std::move(u));
}
if (!link_project(p, units, r, err)) {
compiler::MachineConfig cfg;
std::string cerr;
if (!cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &cerr)) {
return false;
}
return check_project(p, units, *r, err);
if (!link_project(p, units, cfg, r, err)) {
return false;
}
return check_project(p, units, *r, cfg, err);
}
// 从内联源码构造工程(写临时目录),加载、链接、类型检查
@@ -81,10 +86,15 @@ static bool check_src(const char* src_name, const char* st_content,
units.push_back(std::move(u));
}
LinkResult r;
if (!link_project(p, units, &r, err)) {
compiler::MachineConfig cfg;
std::string cerr;
if (!cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &cerr)) {
return false;
}
return check_project(p, units, r, err);
if (!link_project(p, units, cfg, &r, err)) {
return false;
}
return check_project(p, units, r, cfg, err);
}
static bool expect_type_err(const char* src_name, const char* st_content,
@@ -134,14 +144,14 @@ static bool test_negative() {
if (!expect_type_err("assign_mismatch",
"PROGRAM MAIN\nVAR\n b : BOOL;\nEND_VAR\n"
" b := 5;\nEND_PROGRAM\n",
"type mismatch in assignment to 'b'")) {
"BOOL literal must be 0 or 1")) {
return false;
}
// INT 与 TIME 混用算术
if (!expect_type_err("int_time",
"PROGRAM MAIN\nVAR\n x : INT;\n t : TIME;\n y : INT;\nEND_VAR\n"
" y := x + t;\nEND_PROGRAM\n",
"TIME has no arithmetic")) {
"arithmetic operands must have same type")) {
return false;
}
// NOT 吃 INT
@@ -163,7 +173,7 @@ static bool test_negative() {
"PROGRAM MAIN\nVAR\n t : TON;\n q : BOOL;\nEND_VAR\n"
" t(in := TRUE, pt := TRUE);\n"
" q := t.Q;\nEND_PROGRAM\n",
"FB input 'pt' expects TIME")) {
"type mismatch: BOOL vs TIME")) {
return false;
}
// IF 条件非 BOOL
+4 -4
View File
@@ -73,10 +73,10 @@ static bool make_machine(const char* dir, vm::Machine* m, std::string* err) {
units.push_back(std::move(u));
}
LinkResult link;
if (!link_project(p, units, &link, err)) {
if (!link_project(p, units, cfg, &link, err)) {
return false;
}
if (!check_project(p, units, link, err)) {
if (!check_project(p, units, link, cfg, err)) {
return false;
}
std::vector<uint8_t> img;
@@ -460,10 +460,10 @@ static bool test_verify() {
CHECK(compiler::load_unit(p.base_dir + "/" + f, &u, &err));
units.push_back(std::move(u));
}
compiler::LinkResult link;
CHECK(compiler::link_project(p, units, &link, &err));
compiler::MachineConfig cfg;
CHECK(cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &err));
compiler::LinkResult link;
CHECK(compiler::link_project(p, units, cfg, &link, &err));
std::vector<uint8_t> img;
CHECK(compiler::codegen_project(p, units, link, cfg, &img, &err));
img[100] ^= 0x01; // 篡改代码段一字节