# TSL 控制流 文档类型:语法主线 是否可直接用于生成代码:是 是否含可直接照写示例:是 是否含不可照写反例:是 遇到不确定时:先按本页规则和示例继续判断;[05_functions_and_calls.md](05_functions_and_calls.md)、[06_expressions_and_operators.md](06_expressions_and_operators.md)、[15_debug_and_profiler.md](15_debug_and_profiler.md)、[11_pitfalls.md](11_pitfalls.md);仍不命中时回到语法路由中心 [index.md](index.md);如果问题已经超出语法层,回到 TSL 总入口 [../index.md](../index.md) 这一篇只收录流程控制与异常控制,不讨论金融语义。 ## 本篇职责 回答“`if`、`case`、`for`、`while`、`repeat`、`break`、`continue`、`try`、`raise` 这些流程结构在 TSL 里到底怎么写,哪些写法可以直接生成”。 ## 智能体控制流判断流程 1. 先判断任务需要条件分支、循环、`case`、异常处理还是调试跳转。 2. `if` / `for` / `while` / `repeat` 优先照本页文档骨架写,不要套用其他 Pascal 方言。 3. 生成带 `else` 的条件分支时,默认用 `begin ... end` 包住 `then` 和 `else` 分支,让分支内部语句正常以分号结尾;控制流块的 `end` 默认不加分号,也不要在 `else` 前提前加分号。 4. `case` 可写成语句形态,也可写成赋值右侧的表达式形态;表达式形态的分支只能放单条表达式/单条语句,不写 `begin ... end` 语句段。 5. 没有文档事实时不要发明控制流写法。 ## 核心规则 - `if ... then ... else ...` 默认写成块式分支:`then begin ... end else begin ... end`。 - 块式分支内部的普通语句照常用分号结尾;语句形态的控制流块 `end` 默认不加分号。 - `for` 支持 `to`、`downto`、可选 `step`,以及 `for i, v in array` 遍历。 - `while` 和 `repeat ... until` 都可直接使用;`repeat` 至少会先执行一轮再判断结束条件。 - `break` 会跳出当前最近一层循环,`continue` 会跳过当前轮剩余语句。 - `case ... of ... else ... end` 可作为语句形态生成;语句形态的 `end` 后默认不加分号。 - `value := 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`、计时和性能分析器这类“控制流补充工具”统一放到 [15_debug_and_profiler.md](15_debug_and_profiler.md)。 ## 可直接照写示例 使用这些示例时遵守: - 条件表达式、比较、布尔值和普通赋值回 [06_expressions_and_operators.md](06_expressions_and_operators.md)。 - 函数里的控制流只按控制流语法处理;函数文件模型、返回值和参数规则回 [05_functions_and_calls.md](05_functions_and_calls.md)。 - `goto`、`debugReturn`、计时和性能分析器不在本页生成,统一回 [15_debug_and_profiler.md](15_debug_and_profiler.md)。 ### `if`、`while`、`repeat ... until` 代码块身份:可直接照写示例 ```tsl flag := 1; if flag > 0 then begin value := 1; end else begin value := 0; end counter := 0; while counter < 3 do counter := counter + 1; repeat counter := counter - 1; until counter = 0; writeLn(value); writeLn(counter); ``` 代码块身份:输出片段 ```text 1 0 ``` ### `for` 的几种主干写法 最基础的递增循环: 代码块身份:可直接照写示例 ```tsl sum := 0; for i := 0 to 2 do sum := sum + i; writeLn(sum); ``` 代码块身份:输出片段 ```text 3 ``` 带 `step` 的递增循环: 代码块身份:可直接照写示例 ```tsl s := 0; for i := 1 to 5 step 2 do s := s + i; writeLn(s); ``` 输出说明: - 输出 `9` 代码块身份:输出片段 ```text 9 ``` 带 `step` 的 `downto` 递减循环: 代码块身份:可直接照写示例 ```tsl s := 0; for i := 5 downto 1 step 2 do s := s + i; writeLn(s); ``` 输出说明: - 输出 `9` 代码块身份:输出片段 ```text 9 ``` 数组遍历: 代码块身份:可直接照写示例 ```tsl 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` 开始 代码块身份:输出片段 ```text 10 120 230 ``` ### `break` 与 `continue` `break`: 代码块身份:可直接照写示例 ```tsl 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` 输出 `6` - `i` 输出 `4` 代码块身份:输出片段 ```text 6 4 ``` `continue`: 代码块身份:可直接照写示例 ```tsl i := 0; sum := 0; while i < 4 do begin i := i + 1; if i = 2 then continue; sum := sum + i; end writeLn(sum); ``` 输出说明: - 输出 `8` 代码块身份:输出片段 ```text 8 ``` ### `case` 语句形态 普通分支: 代码块身份:可直接照写示例 ```tsl a := 2; case a of 1: writeLn("one"); 2: writeLn("two"); else writeLn("other"); end ``` 输出说明: - 输出 `two` 代码块身份:输出片段 ```text two ``` 并列标签与区间: 代码块身份:可直接照写示例 ```tsl a := 4; case a of 1, 2: writeLn("small"); 3 to 5: writeLn("mid"); else writeLn("other"); end ``` 输出说明: - 输出 `mid` 代码块身份:输出片段 ```text mid ``` `case` 表达式形态: 代码块身份:可直接照写示例 ```tsl a := 3; label_value := case a of 1, 2: "small"; 3, 4: "mid"; else "other"; end; writeLn(label_value); ``` 输出说明: - 输出 `mid` 代码块身份:输出片段 ```text mid ``` 说明: - 表达式形态可以放在赋值右侧。 - 表达式形态的每个分支只写单条表达式/单条语句,不写 `begin ... end` 语句段。 - 赋值语句整体以 `end;` 收尾。 ### `try ... except` 代码块身份:可直接照写示例 ```tsl writeLn("before"); try raise "boom"; except writeLn("caught"); writeLn(exceptObject.errInfo); end writeLn("after"); ``` 输出说明: - 先输出 `before` - 再输出 `caught` - `exceptObject.errInfo` 输出包含 `raise: boom` 的错误信息 - 最后继续输出 `after` 代码块身份:输出片段 ```text before caught raise: boom after ``` `exceptObject` 的扩展字段: 代码块身份:可直接照写示例 ```tsl try raise "boom"; except writeLn(exceptObject.errLine); writeLn(exceptObject.errNo); end ``` 输出说明: - `exceptObject.errLine` 输出 `4` - `exceptObject.errNo` 输出 `2` - 说明异常对象除了 `errInfo` 以外,也能直接提供出错行号和错误号 代码块身份:输出片段 ```text 4 2 ``` ### `try ... finally` 正常路径: 代码块身份:可直接照写示例 ```tsl writeLn("before"); try writeLn("body"); finally writeLn("finally"); end writeLn("after"); ``` 输出说明: - 依次输出 `before`、`body`、`finally`、`after` 代码块身份:输出片段 ```text before body finally after ``` 报错路径: 代码块身份:可直接照写示例 ```tsl writeLn("before"); try writeLn("body"); raise "boom"; finally writeLn("finally"); end writeLn("after"); ``` 输出说明: - 先输出 `before` - 再输出 `body` - 然后仍会输出 `finally` - 随后脚本报错终止,`after` 不会执行 代码块身份:输出片段 ```text before body finally ``` ### `raise` 代码块身份:可直接照写示例 ```tsl writeLn("before"); raise "boom"; writeLn("after"); ``` 输出说明: - 先输出 `before` - 随后脚本报错终止,`after` 不会执行 代码块身份:输出片段 ```text before ``` ## 默认生成模板 最短条件分支的默认骨架如下: 代码块身份:可直接照写示例 ```tsl flag := 1; if flag > 0 then begin value := 1; end else begin value := 0; end ``` ## 禁止项 - 在 `else` 前面误加分号。 - 生成没有分号的裸分支赋值,例如 `then value := 1 else ...`;带 `else` 时用块式分支。 - 以为 `try ... finally` 会吞掉异常。 - 把 `case` 写成赋值右侧表达式。 - 在还没搞清表达式规则前,先把复杂业务函数塞进条件里。 - 把控制流问题和函数文件模型问题混在一起排查。 代码块身份:反例 / 不可照写 ```text if flag > 0 then value := 1; else value := 0; ``` 上面这种写法会编译失败,因为 `else` 前面的分号会让 `if` 语句在上一行提前结束。生成带 `else` 的赋值分支时,不要改成省略分号的裸分支;使用前文的块式分支。