步骤2:VAR_TEMP 链接层(FB 布局排除 Temp 字段 + 顶层防御校验 + scope 符号映射 Local)+ linker_test
This commit is contained in:
+13
-5
@@ -214,6 +214,11 @@ namespace {
|
||||
b.vars.empty() ? 0 : b.vars[0].line,
|
||||
b.vars.empty() ? 0 : b.vars[0].col);
|
||||
}
|
||||
if (b.section == VarSection::Temp) {
|
||||
return fail(u.path, "VAR_TEMP only allowed in POU",
|
||||
b.vars.empty() ? 0 : b.vars[0].line,
|
||||
b.vars.empty() ? 0 : b.vars[0].col);
|
||||
}
|
||||
for (const VarDecl& d : b.vars) {
|
||||
if (out_->global_index.count(d.name)) {
|
||||
return fail(u.path, "duplicate global '" + d.name + "'",
|
||||
@@ -266,9 +271,11 @@ namespace {
|
||||
FbLayout lay;
|
||||
lay.type_name = p.name;
|
||||
for (const VarBlock& b : p.blocks) {
|
||||
// 字段 = input / output / 内部 var;external 与 global 不是字段
|
||||
// 字段 = input / output / 内部 var;external/global/temp 不是字段
|
||||
//(VAR_TEMP 不持久,每次调用重新初始化,走帧寄存器)
|
||||
if (b.section == VarSection::External ||
|
||||
b.section == VarSection::Global) {
|
||||
b.section == VarSection::Global ||
|
||||
b.section == VarSection::Temp) {
|
||||
continue;
|
||||
}
|
||||
for (const VarDecl& d : b.vars) {
|
||||
@@ -331,9 +338,10 @@ namespace {
|
||||
|
||||
/**
|
||||
* @brief 建一个 POU 的局部符号
|
||||
* @details VAR/INPUT/OUTPUT → 对应种类;VAR_EXTERNAL → 查全局表接同一槽
|
||||
* (类型必须一致);FB 类型 → FbInstance + 布局(内建取冻结表,
|
||||
* 用户类型查 fb_types);POU 内 VAR_GLOBAL → 拒绝
|
||||
* @details VAR/INPUT/OUTPUT → 对应种类;VAR_TEMP → Local(帧寄存器,
|
||||
* 不持久);VAR_EXTERNAL → 查全局表接同一槽(类型必须一致);
|
||||
* FB 类型 → FbInstance + 布局(内建取冻结表,用户类型查 fb_types);
|
||||
* POU 内 VAR_GLOBAL → 拒绝
|
||||
* @param file 出错文件
|
||||
* @param p POU AST
|
||||
* @param sc 输出作用域(syms / fb_instances 被填充)
|
||||
|
||||
@@ -70,6 +70,67 @@ static bool expect_link_err(const char* dir, const char* keyword) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- 0. VAR_TEMP:FB 布局排除 Temp、scope 符号为 Local ----
|
||||
|
||||
static bool test_var_temp() {
|
||||
using namespace compiler;
|
||||
const std::string toml = std::string(REPO_ROOT) + "/tests/cases/13_function_call/project.toml";
|
||||
Project p;
|
||||
std::string err;
|
||||
CHECK(parse_project(toml, &p, &err));
|
||||
SourceUnit u;
|
||||
u.path = "main.st";
|
||||
CHECK(parse_pous("main.st",
|
||||
"FUNCTION_BLOCK Foo\n"
|
||||
"VAR_INPUT\n in : BOOL;\nEND_VAR\n"
|
||||
"VAR_OUTPUT\n out : BOOL;\nEND_VAR\n"
|
||||
"VAR\n state : BOOL;\nEND_VAR\n"
|
||||
"VAR_TEMP\n tmp : INT;\nEND_VAR\n"
|
||||
" out := tmp > 0;\n"
|
||||
"END_FUNCTION_BLOCK\n"
|
||||
"PROGRAM MAIN\nEND_PROGRAM",
|
||||
&u.ast, &err));
|
||||
compiler::MachineConfig cfg;
|
||||
std::string cerr;
|
||||
CHECK(cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &cerr));
|
||||
LinkResult r;
|
||||
std::vector<SourceUnit> units;
|
||||
units.push_back(std::move(u));
|
||||
CHECK(link_project(p, units, cfg, &r, &err));
|
||||
|
||||
// FB 布局:字段 = input/output/内部 var,VAR_TEMP 不是字段
|
||||
const auto it = r.fb_types.find("foo");
|
||||
CHECK(it != r.fb_types.end());
|
||||
CHECK(it->second.fields.size() == 3);
|
||||
CHECK(it->second.fields[0].name == "in");
|
||||
CHECK(it->second.fields[1].name == "out");
|
||||
CHECK(it->second.fields[2].name == "state");
|
||||
for (const FbField& f : it->second.fields) {
|
||||
CHECK(f.name != "tmp");
|
||||
}
|
||||
|
||||
// scope 符号:VAR_TEMP → Local(在 Foo 作用域)
|
||||
bool saw_tmp = false;
|
||||
bool saw_main = false;
|
||||
for (const auto& sc : r.scopes) {
|
||||
if (sc.name == "foo") {
|
||||
for (const Symbol& s : sc.syms) {
|
||||
if (s.name == "tmp") {
|
||||
saw_tmp = true;
|
||||
CHECK(s.kind == SymbolKind::Local);
|
||||
CHECK(s.type_name == "int");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sc.name == "main") {
|
||||
saw_main = true;
|
||||
}
|
||||
}
|
||||
CHECK(saw_tmp);
|
||||
CHECK(saw_main);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- 1. 用例 09:GVL + VAR_EXTERNAL 同一槽 ----
|
||||
|
||||
static bool test_case09() {
|
||||
@@ -229,6 +290,7 @@ static bool test_builtin() {
|
||||
}
|
||||
|
||||
int main() {
|
||||
if (!test_var_temp()) return 1;
|
||||
if (!test_case09()) return 1;
|
||||
if (!test_neg_cases()) return 1;
|
||||
if (!test_case16()) return 1;
|
||||
|
||||
Reference in New Issue
Block a user