落地 12.2 二十个用例目录:toml + .st + 期望表。

- tests/cases/ 01~20:13 正例 / 6 负例 / 1 运行时故障
- 负例冻结报错类别:非 GVL 写全局、同名全局、io.var 未声明、FUNCTION 写全局、未声明实例、循环调用
- tests/README 补充用例清单与点亮节奏
This commit is contained in:
2026-08-19 14:43:47 +08:00
parent dd7f7bcba7
commit ef94ab1987
70 changed files with 513 additions and 0 deletions
+10
View File
@@ -4,6 +4,16 @@
可以 `add_test``STCompiler` / `BytecodeExecutor`(编译→执行管线),或直接链接 `compiler` + `vm` 做单元断言。样例工程数据按「一份 toml + 若干 `.st`」分子目录存放,不要做成 library。 可以 `add_test``STCompiler` / `BytecodeExecutor`(编译→执行管线),或直接链接 `compiler` + `vm` 做单元断言。样例工程数据按「一份 toml + 若干 `.st`」分子目录存放,不要做成 library。
## 用例目录(`cases/`
每个用例一个子目录:`project.toml` + `.st` + `EXPECTED.md`(期望:通过 / 报哪类错)。清单与编号见 [`Doc/初步计划.md`](../Doc/初步计划.md) 12.2 节,共 20 个:
- 正例:01 空 MAIN、02 BOOL、03 短路、04 IF、05 WHILE、07 算术、08 TIME、09 GVL+EXTERNAL、13 FUNCTION、15 FB、17 TON、18 TOF/CTU、20 line1
- 负例(编译错误/链接失败):10 非 GVL 写全局、11 同名全局、12 io.var 未声明、14 FUNCTION 写全局、16 未声明实例、19 循环调用
- 运行时故障:06 cycle_limit 打满
12.2 完成标准:目录和期望表齐,此时大部分还编不过,属正常。实现阶段逐个点亮。
## 第一版要冻的 ## 第一版要冻的
- 关键字表与约 20 个样例程序 - 关键字表与约 20 个样例程序
+4
View File
@@ -0,0 +1,4 @@
# 期望:编译通过
- 映像:MAIN(fn_id=0)字节码为一条 `RET``nregs=0`
- 运行任意周期数无故障
+2
View File
@@ -0,0 +1,2 @@
PROGRAM MAIN
END_PROGRAM
+8
View File
@@ -0,0 +1,8 @@
[project]
name = "case01"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["main.st"]
+4
View File
@@ -0,0 +1,4 @@
# 期望:编译通过
- 执行后 `a` = TRUE、`b` = FALSE
- `TRUE` / `FALSE` 是关键字,不是标识符
+8
View File
@@ -0,0 +1,8 @@
PROGRAM MAIN
VAR
a : BOOL;
b : BOOL;
END_VAR
a := TRUE;
b := FALSE;
END_PROGRAM
+8
View File
@@ -0,0 +1,8 @@
[project]
name = "case02"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["main.st"]
+4
View File
@@ -0,0 +1,4 @@
# 期望:编译通过
- 反汇编中 `AND` / `OR` 必须编成条件跳转(`JT` / `JF`),不得先算两侧再组合
- v1 表达式无调用、无副作用;「右侧有副作用时不执行」以编译产物含条件跳转为验收标准
+9
View File
@@ -0,0 +1,9 @@
PROGRAM MAIN
VAR
a : BOOL;
b : BOOL;
x : BOOL;
END_VAR
x := a AND b;
x := a OR b;
END_PROGRAM
@@ -0,0 +1,8 @@
[project]
name = "case03"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["main.st"]
+4
View File
@@ -0,0 +1,4 @@
# 期望:编译通过
- `sel` = 0 → `out` = 10`sel` = 1 → `out` = 20;其他 → `out` = 30
- 必须编成比较 + 条件跳转,不逐分支执行
+13
View File
@@ -0,0 +1,13 @@
PROGRAM MAIN
VAR
sel : INT;
out : INT;
END_VAR
IF sel = 0 THEN
out := 10;
ELSIF sel = 1 THEN
out := 20;
ELSE
out := 30;
END_IF
END_PROGRAM
@@ -0,0 +1,8 @@
[project]
name = "case04"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["main.st"]
+3
View File
@@ -0,0 +1,3 @@
# 期望:编译通过
- 循环结束后 `n` = 10(正常终止,不触发 cycle_limit
+9
View File
@@ -0,0 +1,9 @@
PROGRAM MAIN
VAR
n : INT;
END_VAR
n := 0;
WHILE n < 10 DO
n := n + 1;
END_WHILE
END_PROGRAM
+8
View File
@@ -0,0 +1,8 @@
[project]
name = "case05"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["main.st"]
@@ -0,0 +1,4 @@
# 期望:运行时故障
- 单周期指令数超 `cycle_limit = 10`VM 报故障并停止
- 编译本身通过;WHILE 不做终止证明
+4
View File
@@ -0,0 +1,4 @@
PROGRAM MAIN
WHILE TRUE DO
END_WHILE
END_PROGRAM
@@ -0,0 +1,8 @@
[project]
name = "case06"
entry = "program MAIN"
cycle_limit = 10
dt_ms = 10
[files]
st = ["main.st"]
+4
View File
@@ -0,0 +1,4 @@
# 期望:编译通过
- `a` = 42、`b` = 10、`c` = 32、`eq` = TRUE
- 溢出饱和:`30000 + 30000` = 32767、`-30000 - 30000` = -32768(由 isa 饱和函数保证)
+10
View File
@@ -0,0 +1,10 @@
PROGRAM MAIN
VAR
a, b, c : INT;
eq : BOOL;
END_VAR
a := 7 * 6;
b := a / 4;
c := a - b;
eq := c > 30;
END_PROGRAM
+8
View File
@@ -0,0 +1,8 @@
[project]
name = "case07"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["main.st"]
+4
View File
@@ -0,0 +1,4 @@
# 期望:编译通过
- `t1` = 10 毫秒、`t2` = 1250 毫秒(内部一律毫秒)
- `TIME``INT` 不混用;不支持 `T#` 以外的 TIME 语法
+7
View File
@@ -0,0 +1,7 @@
PROGRAM MAIN
VAR
t1, t2 : TIME;
END_VAR
t1 := T#10ms;
t2 := T#1s250ms;
END_PROGRAM
+8
View File
@@ -0,0 +1,8 @@
[project]
name = "case08"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["main.st"]
+4
View File
@@ -0,0 +1,4 @@
# 期望:编译通过
- 符号表里 `G1` 的 global 与 external 指向**同一槽**,不是新变量
- 执行后 `x` = 5(初值来自 GVL
+3
View File
@@ -0,0 +1,3 @@
VAR_GLOBAL
G1 : INT := 5;
END_VAR
+9
View File
@@ -0,0 +1,9 @@
PROGRAM MAIN
VAR_EXTERNAL
G1 : INT;
END_VAR
VAR
x : INT;
END_VAR
x := G1;
END_PROGRAM
+11
View File
@@ -0,0 +1,11 @@
[project]
name = "case09"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["globals.st", "main.st"]
[gvl]
file = "globals.st"
@@ -0,0 +1,4 @@
# 期望:编译错误
- `other.st` 出现 `VAR_GLOBAL`,但 `gvl.file = globals.st`
- 报错类别:VAR_GLOBAL 只允许出现在 gvl.file
+3
View File
@@ -0,0 +1,3 @@
VAR_GLOBAL
G1 : BOOL;
END_VAR
+5
View File
@@ -0,0 +1,5 @@
PROGRAM MAIN
VAR_GLOBAL
G2 : BOOL;
END_VAR
END_PROGRAM
@@ -0,0 +1,11 @@
[project]
name = "case10"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["globals.st", "other.st"]
[gvl]
file = "globals.st"
@@ -0,0 +1,3 @@
# 期望:链接失败
- 同名 `VAR_GLOBAL`G1)出现两次,禁止覆盖
@@ -0,0 +1,7 @@
VAR_GLOBAL
G1 : INT;
END_VAR
VAR_GLOBAL
G1 : INT;
END_VAR
+2
View File
@@ -0,0 +1,2 @@
PROGRAM MAIN
END_PROGRAM
@@ -0,0 +1,11 @@
[project]
name = "case11"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["globals.st", "main.st"]
[gvl]
file = "globals.st"
@@ -0,0 +1,4 @@
# 期望:编译错误
- `[[io.input]]``var = "Nope"` 未在 GVL 声明
- 报错类别:io.var 未在 GVL(io 不创造变量)
+3
View File
@@ -0,0 +1,3 @@
VAR_GLOBAL
G1 : BOOL;
END_VAR
+2
View File
@@ -0,0 +1,2 @@
PROGRAM MAIN
END_PROGRAM
@@ -0,0 +1,16 @@
[project]
name = "case12"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["globals.st", "main.st"]
[gvl]
file = "globals.st"
[[io.input]]
var = "Nope"
channel = 0
bit = 0
+4
View File
@@ -0,0 +1,4 @@
# 期望:编译通过
- `CALL fn_id` + 有界调用帧;函数无内部状态
- 执行后 `x` = 7;结果值 = 函数名赋值(`Add := ...`
+13
View File
@@ -0,0 +1,13 @@
FUNCTION Add : INT
VAR_INPUT
a, b : INT;
END_VAR
Add := a + b;
END_FUNCTION
PROGRAM MAIN
VAR
x : INT;
END_VAR
x := Add(3, 4);
END_PROGRAM
@@ -0,0 +1,8 @@
[project]
name = "case13"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["main.st"]
@@ -0,0 +1,4 @@
# 期望:编译错误
- `FUNCTION` 内写全局(此处经 `VAR_EXTERNAL``G1`
- 报错类别:FUNCTION 禁止写全局
@@ -0,0 +1,3 @@
VAR_GLOBAL
G1 : INT;
END_VAR
@@ -0,0 +1,9 @@
FUNCTION Bad : INT
VAR_EXTERNAL
G1 : INT;
END_VAR
G1 := 1;
END_FUNCTION
PROGRAM MAIN
END_PROGRAM
@@ -0,0 +1,11 @@
[project]
name = "case14"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["globals.st", "main.st"]
[gvl]
file = "globals.st"
+4
View File
@@ -0,0 +1,4 @@
# 期望:编译通过
- FB 实例固定布局,`starter.Q` 字段偏移编译期算死
- 命名输入绑定 `start` / `stop`;执行后 `q` = TRUE
+8
View File
@@ -0,0 +1,8 @@
PROGRAM MAIN
VAR
starter : MotorStarter;
q : BOOL;
END_VAR
starter(start := TRUE, stop := FALSE);
q := starter.Q;
END_PROGRAM
+9
View File
@@ -0,0 +1,9 @@
FUNCTION_BLOCK MotorStarter
VAR_INPUT
start, stop : BOOL;
END_VAR
VAR_OUTPUT
Q : BOOL;
END_VAR
Q := start AND NOT stop;
END_FUNCTION_BLOCK
+8
View File
@@ -0,0 +1,8 @@
[project]
name = "case15"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["motor.st", "main.st"]
+4
View File
@@ -0,0 +1,4 @@
# 期望:编译错误
- 调用 `starter(...)` 但未声明实例
- 报错类别:FB 必须先声明实例才能调用
+7
View File
@@ -0,0 +1,7 @@
PROGRAM MAIN
VAR
q : BOOL;
END_VAR
starter(start := TRUE, stop := FALSE);
q := starter.Q;
END_PROGRAM
+9
View File
@@ -0,0 +1,9 @@
FUNCTION_BLOCK MotorStarter
VAR_INPUT
start, stop : BOOL;
END_VAR
VAR_OUTPUT
Q : BOOL;
END_VAR
Q := start AND NOT stop;
END_FUNCTION_BLOCK
@@ -0,0 +1,8 @@
[project]
name = "case16"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["motor.st", "main.st"]
+4
View File
@@ -0,0 +1,4 @@
# 期望:编译通过;运行行为与 Δt 一致
- `dt_ms = 10``PT = 30ms`:前 2 周期 `Q` = FALSE,第 3 周期起 `Q` = TRUE
- 累计用映像里的 Δt,不用 wall clock
+8
View File
@@ -0,0 +1,8 @@
PROGRAM MAIN
VAR
t : TON;
q : BOOL;
END_VAR
t(in := TRUE, pt := T#30ms);
q := t.Q;
END_PROGRAM
+8
View File
@@ -0,0 +1,8 @@
[project]
name = "case17"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["main.st"]
+5
View File
@@ -0,0 +1,5 @@
# 期望:编译通过;运行行为与 Δt 一致
- TOF`in` 掉电后 `Q` 保持 `PT` 时长(20ms)再复位
- CTU`cu` 上升沿计数 +1`r` 复位;`CV``PV`3)时 `Q` = TRUE
- 内部累计用映像 Δt,不用 wall clock
+13
View File
@@ -0,0 +1,13 @@
PROGRAM MAIN
VAR
tf : TOF;
c : CTU;
q1, q2 : BOOL;
cnt : INT;
END_VAR
tf(in := FALSE, pt := T#20ms);
q1 := tf.Q;
c(cu := TRUE, r := FALSE, pv := 3);
q2 := c.Q;
cnt := c.CV;
END_PROGRAM
+8
View File
@@ -0,0 +1,8 @@
[project]
name = "case18"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["main.st"]
@@ -0,0 +1,4 @@
# 期望:编译错误
- `A``B` 函数体循环调用(无界递归)
- 报错类别:有界 CALL 检查拒绝循环调用
+14
View File
@@ -0,0 +1,14 @@
FUNCTION A : INT
A := B();
END_FUNCTION
FUNCTION B : INT
B := A();
END_FUNCTION
PROGRAM MAIN
VAR
x : INT;
END_VAR
x := A();
END_PROGRAM
@@ -0,0 +1,8 @@
[project]
name = "case19"
entry = "program MAIN"
cycle_limit = 1000
dt_ms = 10
[files]
st = ["main.st"]
+11
View File
@@ -0,0 +1,11 @@
# 期望:编译通过;I 组合 → `Q0_0` 与急停语义一致
| I0_0 | I0_1 | EmergencyStop | Q0_0 |
|---|---|---|---|
| TRUE | FALSE | FALSE | TRUE |
| TRUE | TRUE | FALSE | FALSE |
| FALSE | FALSE | FALSE | FALSE |
| TRUE | FALSE | TRUE | FALSE |
- `Q0_0 = I0_0 AND NOT I0_1 AND NOT EmergencyStop`
- 固定 I 序列跑两遍,槽位与工程哈希一致(回放确定性)
+7
View File
@@ -0,0 +1,7 @@
(* globals.st *)
VAR_GLOBAL
EmergencyStop : BOOL := FALSE;
I0_0 : BOOL;
I0_1 : BOOL;
Q0_0 : BOOL;
END_VAR
+8
View File
@@ -0,0 +1,8 @@
(* main.st *)
PROGRAM MAIN
VAR
starter : MotorStarter;
END_VAR
starter(start := I0_0, stop := I0_1);
Q0_0 := starter.Q;
END_PROGRAM
+13
View File
@@ -0,0 +1,13 @@
(* motor.st *)
FUNCTION_BLOCK MotorStarter
VAR_EXTERNAL
EmergencyStop : BOOL;
END_VAR
VAR_INPUT
start, stop : BOOL;
END_VAR
VAR_OUTPUT
Q : BOOL;
END_VAR
Q := start AND NOT stop AND NOT EmergencyStop;
END_FUNCTION_BLOCK
+28
View File
@@ -0,0 +1,28 @@
[project]
name = "line1"
entry = "program MAIN"
cycle_limit = 100000
dt_ms = 10
[files]
st = ["globals.st", "motor.st", "main.st"]
# 全工程只允许这一处出现 VAR_GLOBAL(第一版:单个文件)
[gvl]
file = "globals.st"
# 可选:I/O 绑定。不创造变量,var 必须已在 GVL 里声明
[[io.input]]
var = "EmergencyStop"
channel = 0
bit = 2
[[io.input]]
var = "I0_0"
channel = 0
bit = 0
[[io.output]]
var = "Q0_0"
channel = 0
bit = 0