7.3 KiB
Control Flow
文档类型:语法主线 是否可直接用于生成代码:是 是否含已验证可执行示例:是 是否含已验证反例:是 遇到不确定时跳转到:07_expressions_and_operators.md、16_debug_and_profiler.md、12_pitfalls.md
手册位置:第 8 篇,共 32 篇。上一篇:07_expressions_and_operators.md。下一篇:09_objects_and_classes.md。
这一篇只收录流程控制与异常控制,不讨论金融语义。
这一篇解决什么问题
回答“if、case、for、while、repeat、break、continue、try、raise 这些流程结构在 TSL 里到底怎么写,哪些写法已经被当前解释器实测验证过”。
必须记住的规则
if ... then ... else ...可以直接跟单条语句;需要多条语句时再补begin ... end。for已验证支持to、downto、可选step,以及for i, v in array遍历。while和repeat ... until都可直接使用;repeat至少会先执行一轮再判断结束条件。break会跳出当前最近一层循环,continue会跳过当前轮剩余语句。case ... of ... else ... end;已验证支持语句形态和表达式形态。case分支标签已验证支持逗号并列和to区间。try ... except ... end;可以捕获raise产生的错误,并继续执行后续语句。ExceptObject.ErrInfo在except块中可读,能拿到当前错误信息。ExceptObject.ErrLine和ExceptObject.ErrNo在except块中当前也可读。try ... finally ... end;无论是否报错,都会先执行finally;如果没有except吞掉错误,脚本仍会在finally之后报错终止。raise "message"是当前解释器已验证可用的最小抛错写法。goto、DEBUGRETURN、DebugRunEnv、计时和 profiler 这类“控制流补充工具”统一放到 16_debug_and_profiler.md。
已验证语法
if、while、repeat ... until
代码块身份:已验证可执行示例
flag := 1;
if flag > 0 then
value := 1
else
value := 0;
counter := 0;
while counter < 3 do
counter := counter + 1;
repeat
counter := counter - 1;
until counter = 0;
for 的几种主干写法
最基础的递增循环:
代码块身份:已验证可执行示例
sum := 0;
for i := 0 to 2 do
sum := sum + i;
带 step 的递增循环:
代码块身份:已验证可执行示例
s := 0;
for i := 1 to 5 step 2 do
s := s + i;
WriteLn(s);
已验证运行结果:
- 输出
9
带 step 的 downto 递减循环:
代码块身份:已验证可执行示例
s := 0;
for i := 5 downto 1 step 2 do
s := s + i;
WriteLn(s);
已验证运行结果:
- 输出
9
数组遍历:
代码块身份:已验证可执行示例
data := array(10, 20, 30);
for i, v in data do
WriteLn(i * 100 + v);
已验证运行结果:
- 依次输出
10、120、230 - 这说明
for i, v in data里的i从0开始
break 与 continue
break:
代码块身份:已验证可执行示例
i := 0;
sum := 0;
while true do
begin
i := i + 1;
if i > 3 then
break;
sum := sum + i;
end;
WriteLn(sum);
WriteLn(i);
已验证运行结果:
sum输出6i输出4
continue:
代码块身份:已验证可执行示例
i := 0;
sum := 0;
while i < 4 do
begin
i := i + 1;
if i = 2 then
continue;
sum := sum + i;
end;
WriteLn(sum);
已验证运行结果:
- 输出
8
case 语句形态
普通分支:
代码块身份:已验证可执行示例
a := 2;
case a of
1:
WriteLn("one");
2:
WriteLn("two");
else
WriteLn("other");
end;
已验证运行结果:
- 输出
two
并列标签与区间:
代码块身份:已验证可执行示例
a := 4;
case a of
1, 2:
WriteLn("small");
3 to 5:
WriteLn("mid");
else
WriteLn("other");
end;
已验证运行结果:
- 输出
mid
case 表达式形态
代码块身份:已验证可执行示例
a := 2;
b := case a of
1:
"one";
2:
"two";
else
"other";
end;
WriteLn(b);
已验证运行结果:
- 输出
two
try ... except
代码块身份:已验证可执行示例
WriteLn("before");
try
raise "boom";
except
WriteLn("caught");
WriteLn(ExceptObject.ErrInfo);
end;
WriteLn("after");
已验证运行结果:
- 先输出
before - 再输出
caught ExceptObject.ErrInfo输出包含raise: boom的错误信息- 最后继续输出
after
ExceptObject 的扩展字段:
代码块身份:已验证可执行示例
program test;
begin
try
raise "boom";
except
WriteLn(ExceptObject.ErrLine);
WriteLn(ExceptObject.ErrNo);
end;
end.
已验证运行结果:
ExceptObject.ErrLine输出4ExceptObject.ErrNo输出2- 说明当前解释器里,异常对象除了
ErrInfo以外,也能直接提供出错行号和错误号
try ... finally
正常路径:
代码块身份:已验证可执行示例
WriteLn("before");
try
WriteLn("body");
finally
WriteLn("finally");
end;
WriteLn("after");
已验证运行结果:
- 依次输出
before、body、finally、after
报错路径:
代码块身份:已验证可执行示例
WriteLn("before");
try
WriteLn("body");
raise "boom";
finally
WriteLn("finally");
end;
WriteLn("after");
已验证运行结果:
- 先输出
before - 再输出
body - 然后仍会输出
finally - 随后脚本报错终止,
after不会执行
raise
代码块身份:已验证可执行示例
WriteLn("before");
raise "boom";
WriteLn("after");
已验证运行结果:
- 先输出
before - 随后脚本报错终止,
after不会执行
最小可编译示例
如果你只想先写一个最短条件分支,从这个开始:
代码块身份:已验证可执行示例
flag := 1;
if flag > 0 then
value := 1
else
value := 0;
常见误写
- 在
else前面误加分号。 - 以为
try ... finally会吞掉异常。 - 把
@case直接当成普通case表达式主写法。 - 在还没搞清表达式规则前,先把复杂业务函数塞进条件里。
- 把控制流问题和函数文件模型问题混在一起排查。
代码块身份:反例 / 不可照写
if flag > 0 then
value := 1;
else
value := 0;
上面这种写法会编译失败,因为 else 前面的分号会让 if 语句在上一行提前结束。
代码块身份:反例 / 不可照写
b := @case a of
1:
"one";
2:
"two";
else
"other";
end;
WriteLn(b);
我在 2026-04-13 用当前解释器实测,上面这类最小例子输出的是 <STREXP>,不能把它当成普通 case 表达式的可靠主语法写进新 session 默认生成结果。
跳转指引
- 回看条件表达式:见 07_expressions_and_operators.md
- 继续封装成函数:见 06_functions_and_calls.md
- 看
goto、DEBUGRETURN、计时和 profiler:见 16_debug_and_profiler.md