📝 docs(tsl-syntax-reference): document verified facts from FAQ corpus

从 FAQ 语料(2018 篇镜像)与参考页做完 169 条逐条差集,补入 6 组此前未收录的语言
事实。每条先用 TSL.exe 实跑观察行为,再收敛成 39 条断言的回归门,在 TSLGen2、
AnalyseNG.NET、TSL 三个解释器上均通过后才写入。

- 16 页:0/0 得 nan、2/0 得 inf;nan 的 = > < >= <= 恒假而 <> 为真;nan 沿算术
  传播;nil 在乘除减里按 0 参与
- 04 页:整体赋值免声明,但对未建立的变量写单元格赋值会报「变量类型不是Array」
- 07 页:相邻两个 if 是两条独立语句都会执行;for..in 单控制变量拿到的是键不是值;
  二维数组按行推进,行本身是数组(新增 syntax-07-013)
- 21 页:子矩阵赋值语句求值为 0;t[:,:] 按位置复制不按列名对齐(新增
  syntax-21-019);| 列并时同名字符串列键由右操作数覆盖(新增 syntax-21-018)
- 13 页:select 输出列表同名列后者覆盖前者,须用 as 改名(新增 syntax-13-032)

三条 FAQ 说法实跑不复现,故未写入:12574 的括号消歧、19857 的 -2147483648
字面量限制、31474 的 :. bugfix 记录。15305/11045 的用户/公用/共享函数同名解析
优先级需要服务端多账号环境,本地无法验证,仍为已知缺口。

lexicon.json 为 16 页补两个页级 alias,使 nan/nil 语义能被自然语言查询命中。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
csh
2026-08-20 20:58:10 +08:00
co-authored by Claude Fable 5
parent cd0405d64b
commit 2e8c46e152
6 changed files with 393 additions and 1 deletions
@@ -13,9 +13,12 @@
<!-- section-id: syntax-07-002 -->
- `if ... then ... else ...` 默认写成块式分支:`then begin ... end else begin ... end`
- 两个相邻的 `if ... then begin ... end` 是两条**独立**语句,两个条件都会求值、都可能执行;只有写了 `else` 才是互斥分支。
- 块式分支内部的普通语句必须用分号结尾。
- 控制流块的 `begin ... end` 后可以加分号也可以不加(语法都允许)。
- `for` 支持 `to``downto`、可选 `step`,以及 `for i, v in array` 遍历。
- `for ... in` 也可以只写一个控制变量:`for i in array do` 里的 `i` 是行下标/键,不是行值;要拿到行值就写两个控制变量。
- 二维数组按 `for i, v in array` 遍历时按行推进,`v` 本身是整行数组;要落到单元格需要再嵌套一层 `for ... in`
- 计数 `for` 的初值、终值和步长确定后,循环次数随之固定;循环体内不要给控制变量赋值。
- `for i, v in array` 遍历期间,不要修改被遍历数组或其中元素。
- `while``repeat ... until` 都可直接使用;`repeat` 至少会先执行一轮再判断结束条件。
@@ -77,6 +80,47 @@ writeLn(counter);
0
```
相邻两个 `if``if ... else` 的区别:
代码块身份:可直接照写示例
```tsl
trace := "";
if 1 then
begin
trace := trace + "A";
end
if 1 then
begin
trace := trace + "B";
end
writeLn(trace);
trace2 := "";
if 1 then
begin
trace2 := trace2 + "A";
end
else
begin
trace2 := trace2 + "B";
end
writeLn(trace2);
```
结果说明:
- 第一段输出 `AB`:两个 `if` 是两条独立语句,条件都为真所以都执行
- 第二段输出 `A``if ... else` 是一条语句,两支互斥
- 所以“多个条件里只能命中一个”必须写 `else`,靠相邻 `if` 排列不成立
代码块身份:输出片段
```text
AB
A
```
### `for` 的几种主干写法
<!-- section-id: syntax-07-005 -->
@@ -166,6 +210,73 @@ for i, value in numbers do
230
```
### `for ... in` 的控制变量个数
<!-- section-id: syntax-07-013 -->
<!-- tags: 遍历只写一个变量, 键还是值, 遍历二维数组, 每行是数组, 嵌套遍历 -->
一个控制变量拿到的是下标/键,两个才拿到行值:
代码块身份:可直接照写示例
```tsl
data := array("a": 1, "b": 5, "c": 3, "d": -2);
total := 0;
for i, value in data do
total := total + value;
writeLn(total);
keys := "";
for i in data do
keys := keys + i;
writeLn(keys);
```
结果说明:
- 先输出 `7`:两个控制变量时,`value` 是行值
- 再输出 `abcd`:只写一个控制变量时,它是行下标/键,不是行值
- 所以想累加数值却只写一个控制变量,会拿到键而不是值
代码块身份:输出片段
```text
7
abcd
```
二维数组按行推进,每个 `value` 本身是一行数组:
代码块身份:可直接照写示例
```tsl
rows := array(("A": 1, "B": 2), ("A": 3, "B": 4));
colA := 0;
for i, value in rows do
colA := colA + value["A"];
writeLn(colA);
all := 0;
for i, value in rows do
for j, cell in value do
all := all + cell;
writeLn(all);
```
结果说明:
- 先输出 `4``value` 是整行数组,可以直接按列键取 `value["A"]`
- 再输出 `10`:要落到每个单元格,需要再嵌套一层 `for ... in`
- 所以二维数组不会被自动摊平成单元格序列
代码块身份:输出片段
```text
4
10
```
### `break` 与 `continue`
<!-- section-id: syntax-07-006 -->
@@ -500,6 +611,9 @@ end
- 把控制流问题和函数文件模型问题混在一起排查。
- 在计数 `for` 循环体里给控制变量赋值。
-`for ... in` 遍历期间修改被遍历数组或其中元素。
- 用相邻两个 `if ... then begin ... end` 表达互斥分支;它们是两条独立语句,需要互斥就写 `else`
-`for ... in` 只写一个控制变量却当成行值使用;单变量拿到的是下标/键。
- 以为二维数组会被 `for ... in` 摊平成单元格序列;它按行推进,行本身是数组。
代码块身份:反例 / 不可照写