@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: tsl-syntax-reference
|
||||
description: 当用户需要编写、修改、审查或解释 TSL/TSF、TS-SQL、Tinysoft/天软脚本或公式,涉及 `.tsl` / `.tsf` 文件,排查 invalid statement 等语法错误,或核对语言与运行时结构规则时使用。
|
||||
description: "当用户需要编写、修改、审查或解释 TSL/TSF、TS-SQL、Tinysoft/天软脚本或公式,涉及 `.tsl` / `.tsf` 文件,排查 invalid statement 等语法错误,或核对语言与运行时结构规则时使用;包括你自认为已经确定的写法。不用于查询 API 签名或天软数据字典字段,也不用于判断解释器、平台或运行方式。"
|
||||
---
|
||||
|
||||
# TSL Syntax Reference
|
||||
@@ -12,67 +12,15 @@ description: 当用户需要编写、修改、审查或解释 TSL/TSF、TS-SQL
|
||||
下标起点这类"看起来确定"的规则同样要取回。`<this-skill-dir>` 指本 `SKILL.md`
|
||||
所在目录。
|
||||
|
||||
检索必须分两步:
|
||||
|
||||
1. `--query` 只返回紧凑候选和 Section ID,不返回事实正文。
|
||||
2. 从候选中选择支持当前结论的章节,再用 `--section` 取回唯一事实正文。
|
||||
|
||||
一次 `--query` 只覆盖一个语法要素。一段代码涉及多个要素时(如文件模型、函数
|
||||
骨架、赋值运算符),逐个要素分别执行 `--query`;不用一个要素的取回结果推断另
|
||||
一个要素。各要素选定的 Section ID 可以合并成一次 `--section` 批量取回。
|
||||
|
||||
根据任务意图选择模式:
|
||||
|
||||
```bash
|
||||
python <this-skill-dir>/scripts/lookup.py --query "命名参数" --mode write
|
||||
python <this-skill-dir>/scripts/lookup.py --query "invalid statement 声明区" --mode diagnose
|
||||
python <this-skill-dir>/scripts/lookup.py --query "数组下标" --mode explain
|
||||
python <this-skill-dir>/scripts/lookup.py --help
|
||||
```
|
||||
|
||||
- `write`:编写或修改代码。
|
||||
- `diagnose`:定位语法错误或错误写法。
|
||||
- `explain`:解释语言规则或代码含义。
|
||||
|
||||
用 `--limit N`(1..10,默认 5)控制返回的查询候选条数。
|
||||
|
||||
`--mode write` 会在查询候选之前额外附加 `Required: yes` 的前置章节(文件模型与
|
||||
语言核心事实速查)。它们不占 `--limit` 预算,也不是本次查询的命中结果:先按它们
|
||||
核对文件模型和硬规则,再从 `Required: no` 的候选里挑选支持当前结论的章节。前置
|
||||
章节在同一任务内取回一次即可;后续 `--query` 重复列出它们时,不必再次 `--section`。
|
||||
其中 `syntax-01-002` 是从专题页逐条派生的强制速查,不是另一份完整事实源;生成结论
|
||||
仍要取回拥有该规则的专题 Section。
|
||||
|
||||
`--query` 输出候选后,必须执行下面这种精确取回。多个已选定的 Section ID 可以
|
||||
一次传入:
|
||||
|
||||
```bash
|
||||
python <this-skill-dir>/scripts/lookup.py --section "syntax-05-004" "syntax-02-002"
|
||||
```
|
||||
|
||||
只有 `--section` 返回的章节正文可作为本次任务的语法事实来源;候选摘要和概念地图都不能直接支持代码结论。需要补充时改进查询词再次检索。
|
||||
|
||||
## 构造查询词
|
||||
|
||||
从用户原话中提取错误原文、TSL 标识符和单一语法要素名;保留这些词在用户原话中的
|
||||
写法,不传完整句,也不自行翻译成猜测术语。脚本会自动处理两类词,不必手工调整:
|
||||
|
||||
- 中文口语词自动扩展到 TSL 术语(如"打印"→输出/writeLn,"列表"→数组,
|
||||
"程序慢"→性能分析)。保留提取词的原写法即可。
|
||||
- `tsl`、`tsf`、`tinysoft`、`debug`、`please` 这几个词不参与逐词匹配,
|
||||
但可能整体把查询导向某个专题页。无结果时靠加这类词补救没有用,改为换更具体的
|
||||
语法要素名。`program` 是真实 TSL 关键字,会参与精确匹配。
|
||||
|
||||
## 弱命中视同无匹配
|
||||
|
||||
候选里的 `Weak: yes` 表示该节没有任何强信号命中(意图短语、标题、标识符、
|
||||
标签),只靠正文低分撞词进入候选。弱候选不作为选择对象:
|
||||
|
||||
- 候选全部为弱命中时,lookup 返回 rc=2,按无匹配处理——改进查询词重试,
|
||||
仍全弱即为事实缺口。
|
||||
- 个别候选为弱命中、其余为强命中时,只从强命中里挑选。
|
||||
|
||||
没有 `Weak` 行的候选也不等于相关:词面命中不代表该节支持当前结论,仍要按
|
||||
Summary 判断后再取回正文核对。
|
||||
构造任一命令前先运行上面的 `--help`。帮助输出独占动作、mode、参数范围、查询词、
|
||||
弱命中、概念地图、前置章节、Owner Section、结构校验和退出码的命令契约;不要在本
|
||||
文件中推断或复用旧参数。检索必须完成 `--query` 到 `--section` 两步,只有
|
||||
`--section` 返回的正文可作为语法事实。`syntax-01-002` 是派生速查;命中其中规则后,
|
||||
按紧邻规则的 `Owner Section` 取回真正拥有该事实的专题 Section。
|
||||
|
||||
## 代码块必须标注来源
|
||||
|
||||
@@ -125,14 +73,14 @@ Section ID 抄自 `--section` 输出首部的 `Section ID:` 行。多个要素
|
||||
**检索缺口** —— `--query` 改进查询词后仍无匹配;lookup 返回非零状态;`--section`
|
||||
取回的正文没有支持当前结论;`--section` 正文里目标围栏前没有 `代码块身份:` 行。
|
||||
|
||||
**环境缺口** —— 目标运行时版本不明;项目路径、数据结构或运行参数等项目事实缺失;
|
||||
`tsl-api-reference` 等依赖的 Skill 不可用。
|
||||
**范围缺口** —— 请求需要 API 签名、数据字典、解释器或平台可用性、运行结果,或其
|
||||
它不属于本 Skill 语法事实的专属知识。
|
||||
|
||||
缺口时的输出由三部分组成,按此顺序:
|
||||
|
||||
1. 已取回并可用的事实,只列 Section ID,不复述正文。
|
||||
2. 缺失的具体要素,以及为它试过的查询词。
|
||||
3. 需要用户提供什么,或需要哪个 Skill、哪份项目文档补齐。
|
||||
3. 需要补齐的具体语法事实,或明确说明该请求超出本 Skill 的范围。
|
||||
|
||||
不输出包含缺口要素的 TSL 代码,注释掉的、标 TODO 的和「仅供参考」的版本同样不
|
||||
输出。一段代码里只要有一个要素没取回,整段都不给出,不交付「其余部分已验证」的
|
||||
@@ -145,69 +93,28 @@ Section ID 抄自 `--section` 输出首部的 `Section ID:` 行。多个要素
|
||||
- 用候选摘要或概念地图代替章节正文。
|
||||
- 给出未标注来源的代码块。
|
||||
|
||||
## 查询词无从下手时先取概念地图
|
||||
|
||||
若需求只有自然语言、无法从中取出 TSL 概念词或错误文本来构造 `--query`,先运行:
|
||||
|
||||
```bash
|
||||
python <this-skill-dir>/scripts/lookup.py --map
|
||||
```
|
||||
|
||||
它列出全部专题的纯文本职责摘要,用于把需求映射到 TSL 特有概念。地图不含可照写事实;从地图取得概念词后,仍按上面的两步执行 `--query` 和 `--section`。
|
||||
|
||||
## 退出码
|
||||
|
||||
| 退出码 | 含义 | 处理方式 |
|
||||
| ------ | --------------------------------------------------------------------- | ------------------------------------------------------- |
|
||||
| 0 | 查询或取回成功 | 读取输出并继续两步流程 |
|
||||
| 1 | `--check` 发现参考页问题,或参考目录内没有可校验的参考页 | 修复参考页,或检查 `--references-dir` 路径与 skill 安装 |
|
||||
| 2 | 参数不合法、`--query` 无匹配或候选全部为弱命中、`--section` ID 不存在 | 见下 |
|
||||
|
||||
`--section` ID 不存在时,脚本会在 stderr 打印最接近的若干 Section ID;从中挑选正确 ID 重试,不要凭猜测拼写 ID。一次传入多个 ID 时只要有一个不存在就不返回任何正文,修正后整批重试。
|
||||
|
||||
`--query` 无匹配或候选全部为 `Weak: yes` 时改进查询词重试。仍无强命中即为事实缺口:按上面「缺口时停止」处理。
|
||||
|
||||
`--map`、`--query`、`--section`、`--check` 共用安装前置检查。参考目录不存在或没有
|
||||
参考页时,任何动作都以 rc=1 报告路径/安装错误;这不属于事实零命中,不能靠改查询词
|
||||
重试。
|
||||
|
||||
## 事实边界
|
||||
|
||||
本 Skill 只拥有 TSL/TSF 的语言语法、文件模型、表达式、控制流、对象、运行时语言
|
||||
结构和 TS-SQL 外形。混合请求先逐项拆分,只回答其中的语法部分。
|
||||
|
||||
API 名称、签名、参数、返回值、平台 scope、解释器可用性和金融取数事实属于
|
||||
`tsl-api-reference` skill。准备面向用户交付的 TSL/TSF 代码时,按下面顺序处理:
|
||||
|
||||
1. 先枚举代码中除语言关键字和用户定义符号外的调用标识符。
|
||||
2. 语言结构逐项用本 Skill 取回;每个 builtin/API 都逐项用 `tsl-api-reference` 取回
|
||||
名称、签名和目标 scope。
|
||||
3. 任一 API 依赖没有取回时,整段交付代码按「缺口时停止」处理;不能只给语法已查的
|
||||
半成品,也不能凭语法页示例中的 API 名称补齐。
|
||||
API 名称、签名、参数、返回值、平台 scope、解释器可用性、运行结果和金融取数事实不
|
||||
属于本 Skill 的语法事实。准备代码时,先区分语言关键字、用户定义符号和未验证调用;
|
||||
对未验证调用不声称签名、行为、可用性或输出。代码交付只要依赖这些未验证事实,就按
|
||||
「缺口时停止」处理,不能凭参考页示例中的调用名称补齐。
|
||||
|
||||
纯语法说明可以使用明确标为占位的用户定义调用来展示调用位置,而不必为该占位符查
|
||||
API;但不得声称该占位调用的 API 行为或输出,也不得把真实 builtin/API 当成无需核对
|
||||
的占位符。解释一旦包含真实 API 的签名、可用性、返回值、输出,或代码将面向用户
|
||||
交付,就必须执行上面的 API 取回步骤。
|
||||
的占位符。解释一旦包含真实调用的签名、可用性、返回值或输出,就属于本 Skill 未覆盖
|
||||
的事实,按「缺口时停止」处理。
|
||||
|
||||
## 运行方式不属于本 Skill
|
||||
## 执行边界
|
||||
|
||||
TSL 的运行方式、解释器路径、平台检测和环境选择都不是本 Skill 的事实。用户询问
|
||||
如何运行 TSL,或需要实际执行 TSL 时,读取目标文件附近最近的 `AGENTS.md`、项目
|
||||
脚本或 CI,并严格照其规定执行。
|
||||
|
||||
本 Skill 只能确认语法外形正确,不能确认代码在目标解释器上可运行。给出未经执行的
|
||||
代码时,据此区分「语法已取回」与「运行时未验证」。用户未要求实际执行时,解释器
|
||||
不可用是受支持状态,不构成事实缺口,也不要求提供可重放运行证据。
|
||||
本 Skill 只检索和解释语法事实,不执行 TSL 代码,也不判断解释器、平台、运行方式或
|
||||
项目配置。未经执行的代码只能描述为“语法已取回、运行结果未验证”;不得把参考页中的
|
||||
输出片段当成当前代码的实跑结果。
|
||||
|
||||
## 维护校验
|
||||
|
||||
Section ID 来自标题下的显式 `<!-- section-id: ... -->` 元数据,不从标题派生;改标题
|
||||
时保留原 ID。新增章节时分配新 ID,不重排或复用旧 ID。
|
||||
|
||||
改动本 Skill、参考页、`data/` 词表或 lookup 实现后,先运行
|
||||
`scripts/lookup.py --check` 校验显式 ID、标题层级、quickstart 派生规则、代码块身份、
|
||||
链接与词表页覆盖。在本 playbook 仓库维护时,再从仓库根目录运行
|
||||
`python -m unittest test.test_tsl_syntax_reference -v` 校验检索排序、退出码和 CLI
|
||||
行为。`--check` 只是结构检查,不证明检索排序、事实语义或示例运行结果。口语同义词
|
||||
和页级意图短语维护在 `data/lexicon.json`,策展纪律见 `data/README.md`。
|
||||
维护本 Skill 的参考页、词表或检索器时,只使用随附 `scripts/lookup.py --check` 做结构
|
||||
校验;命令参数和校验范围以随附脚本的 `--help` 为准。
|
||||
|
||||
@@ -21,16 +21,28 @@
|
||||
|
||||
- 键必须与 `references/` 下的文件名完全一致;每个参考页至少一条短语。
|
||||
键写错或参考页改名后 `_intent_score` 会静默返回 0 分,该页失去自然语言
|
||||
入口。`lookup.py --check` 负责拦截页键漂移,行为回归测试负责拦截排序漂移。
|
||||
入口。`lookup.py --check` 负责拦截页键漂移;查询排序以 lookup 实现和实际
|
||||
查询结果为准。
|
||||
|
||||
## 改动后的校验
|
||||
## Skill 维护与校验
|
||||
|
||||
Section ID 来自标题下的显式 `<!-- section-id: ... -->` 元数据,不从标题派生。改标题
|
||||
时保留原 ID;新增章节时分配新 ID,不重排或复用旧 ID。quickstart 派生规则必须与
|
||||
专题事实逐字一致,并显式给出可由 `--section` 直接取回的 Owner Section。
|
||||
|
||||
大型参考页必须把可检索事实切成受控大小的叶子 Section,避免一次精确取回返回数百行。
|
||||
`--check` 会校验大页的叶子 Section 粒度;拆分时保留已有 ID,把新主题分配给新 ID。
|
||||
|
||||
改动本 Skill、参考页、`data/` 词表或 lookup 实现后运行:
|
||||
|
||||
```bash
|
||||
python skills/tsl-syntax-reference/scripts/lookup.py --check
|
||||
python -m unittest test.test_tsl_syntax_reference -v
|
||||
```
|
||||
|
||||
`--check` 会校验 `page_intent_aliases` 的键与 `references/` 参考页一一对应:
|
||||
键指向不存在的页、或某页没有自然语言入口都会报错。新增参考页时必须同时
|
||||
在这里补一条页级意图短语。它只做结构检查,不验证自然语言排序;原始 alias、
|
||||
助词变体、弱命中和已知真实问法由专属测试覆盖。
|
||||
在这里补一条页级意图短语。它只做结构检查,不验证自然语言排序;自然语言
|
||||
排序和真实问法应通过实际查询验证。
|
||||
|
||||
`--check` 还覆盖显式 ID、标题层级、quickstart 派生与 Owner Section、代码块身份、
|
||||
本地链接和大页粒度;它不证明事实语义、检索排序或示例运行结果。
|
||||
|
||||
@@ -111,7 +111,12 @@
|
||||
"注释怎么写",
|
||||
"嵌套注释"
|
||||
],
|
||||
"16_types_and_conversions.md": ["字符串转整数", "类型转换"],
|
||||
"16_types_and_conversions.md": [
|
||||
"字符串转整数",
|
||||
"类型转换",
|
||||
"nan 参与运算",
|
||||
"nil 参与计算"
|
||||
],
|
||||
"17_external_calls_and_threads.md": [
|
||||
"调用 dll",
|
||||
"dll",
|
||||
|
||||
@@ -19,66 +19,82 @@
|
||||
<!-- quickstart-rule: assignment -->
|
||||
|
||||
- 普通变量赋值使用 `:=`;`=` 在普通表达式里用于比较,不用于赋值。
|
||||
Owner Section:`syntax-06-004`
|
||||
|
||||
<!-- quickstart-rule: file-choice -->
|
||||
|
||||
- 未给后缀时,入口流程、脚本任务或一次性执行逻辑对应 `.tsl`;可复用交付物(函数、过程、类、模块或扩展文件)对应 `.tsf`;只是脚本内部封装函数或类时,仍按 `.tsl` 处理;仍不明确时向用户确认,不要把脚本入口和可复用模块合并成一个猜测文件。
|
||||
Owner Section:`syntax-02-002`
|
||||
|
||||
<!-- quickstart-rule: tsl-layout -->
|
||||
|
||||
- `.tsl` 脚本按两段理解:语句区在前并按顺序执行;声明区在后,可放 `function / procedure` 或 `type Name = class`。写 `.tsl` 时先写语句区,需要函数、过程或类时把声明区放在语句区之后。
|
||||
Owner Section:`syntax-02-002`
|
||||
|
||||
<!-- quickstart-rule: tsf-layout -->
|
||||
|
||||
- 写 `.tsf` 时只写顶层函数 / 过程 / 类声明,或 `unit`;不要写成会直接顺序执行的脚本入口。
|
||||
Owner Section:`syntax-02-002`
|
||||
|
||||
<!-- quickstart-rule: tsf-filename -->
|
||||
|
||||
- `.tsf` 文件名(不含扩展名)必须与第一个顶层声明同名;第一个声明可以是同名 `function`、`type Name = class` 或 `unit`。
|
||||
Owner Section:`syntax-02-002`
|
||||
|
||||
<!-- quickstart-rule: function-default -->
|
||||
|
||||
- 用户提示词里的“函数”默认对应 `function`,不要自动改写成 `procedure`。
|
||||
Owner Section:`syntax-05-002`
|
||||
|
||||
<!-- quickstart-rule: procedure-explicit -->
|
||||
|
||||
- `procedure Name(...); begin ... end;` 只在用户明确要求 `procedure` / 过程时生成;不要因为没有返回值就自动改用 `procedure`。
|
||||
Owner Section:`syntax-05-002`
|
||||
|
||||
<!-- quickstart-rule: class-shape -->
|
||||
|
||||
- 类定义统一按 `type Name = class ... end;` 写。
|
||||
Owner Section:`syntax-08-002`
|
||||
|
||||
<!-- quickstart-rule: object-creation -->
|
||||
|
||||
- 普通本地类实例化默认生成 `new ClassName()`;`createObject("ClassName")`、`createObject(ClassType)` 只在字符串类名、类类型变量或跨 `unit` 路径场景生成。
|
||||
Owner Section:`syntax-08-002`
|
||||
|
||||
<!-- quickstart-rule: unit-shape -->
|
||||
|
||||
- `unit` 是完整的顶层主体;常见完整形态是 `unit Name; interface ... implementation ... end.`。
|
||||
Owner Section:`syntax-09-002`
|
||||
|
||||
<!-- quickstart-rule: unit-default -->
|
||||
|
||||
- 如果没有特殊需求,默认优先用完整形态;简写形态只在不需要显式区分 `interface` / `implementation` 时再用。
|
||||
Owner Section:`syntax-09-002`
|
||||
|
||||
<!-- quickstart-rule: named-arguments -->
|
||||
|
||||
- 调用时支持命名参数,写法是 `name: value`。
|
||||
Owner Section:`syntax-05-002`
|
||||
|
||||
<!-- quickstart-rule: named-argument-order -->
|
||||
|
||||
- 一旦某次调用里开始使用命名参数,后面的参数就不能再退回位置参数。
|
||||
Owner Section:`syntax-05-002`
|
||||
|
||||
<!-- quickstart-rule: string-literal-default -->
|
||||
|
||||
- 普通单行文本默认使用 `"..."` 或 `'...'`;根据内容选择不冲突的引号,必要时再使用转义或连续同类引号。`%% ...%%` 仅用于多行文本、引号非常密集等原始字符串场景;不得因为内容是中文、非 ASCII 或较长就自动改用 `%%`。
|
||||
Owner Section:`syntax-03-004`
|
||||
|
||||
<!-- quickstart-rule: string-prefix-by-type -->
|
||||
|
||||
- `U`、`L` 前缀只由目标字符串类型或已确认的 API 编码要求决定,不能因为内容是中文就自动添加;普通中文内容优先直接写成 `"中文内容"`。
|
||||
Owner Section:`syntax-03-004`
|
||||
|
||||
<!-- quickstart-rule: index-origins -->
|
||||
|
||||
- `array(...)` 既可以写顺序数组,也可以写字符串键表;顺序数组和 `binary(...)` 二进制缓冲区下标从 `0` 开始,字符串下标从 `1` 开始。
|
||||
Owner Section:`syntax-03-002`
|
||||
|
||||
## 术语对照
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
- 默认变量模型是“直接赋值即得到变量”,不要求先写 `var`。
|
||||
- 例如:`a := 1; b := array(1, 2, 3);` 这种写法可直接编译。
|
||||
- 这条只覆盖整体赋值;对还没建立的变量直接写单元格赋值(`q[0][0] := 1;`)会在运行时报
|
||||
`变量类型不是Array`,要先用 `q := array();` 建立容器再写下标。
|
||||
- `var name;` 只作为显式声明写法,不能当成默认必需步骤。
|
||||
- `const name = value;` 是常量初始化的默认生成写法;`const` 不能只声明名字而不初始化。
|
||||
- 文档规则:
|
||||
@@ -73,6 +75,40 @@ var a;
|
||||
a := 1;
|
||||
```
|
||||
|
||||
整体赋值免声明,但单元格赋值不免——容器要先建立:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
whole := rand(3, 3);
|
||||
writeLn(mRows(whole));
|
||||
cell := array();
|
||||
cell[0][0] := 1;
|
||||
writeLn(cell[0][0]);
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 依次输出 `3`、`1`
|
||||
- `whole := rand(3, 3)` 说明整体接一个数组不需要先声明
|
||||
- `cell := array();` 这一行不能省;省掉后 `cell[0][0] := 1;` 会在运行时报
|
||||
`变量类型不是Array`,因为下标赋值需要一个已经是数组的容器
|
||||
|
||||
代码块身份:输出片段
|
||||
|
||||
```text
|
||||
3
|
||||
1
|
||||
```
|
||||
|
||||
代码块身份:反例 / 不可照写
|
||||
|
||||
```text
|
||||
q[0][0] := 1;
|
||||
```
|
||||
|
||||
对还没建立的变量直接写单元格赋值会运行时报 `变量类型不是Array`;先写 `q := array();`。
|
||||
|
||||
`{$explicit+}` 下的显式声明:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -393,6 +429,7 @@ items := array(1, 2, 3);
|
||||
<!-- section-id: syntax-04-008 -->
|
||||
|
||||
- 以为普通变量必须先写 `var` 才能使用。
|
||||
- 把“整体赋值免声明”推广到单元格赋值,直接对未建立的变量写 `q[0][0] := 1;`。
|
||||
- 把常量初始化写成普通变量赋值风格。
|
||||
- 以为 `const =` 顶层单独写一行就一定成立。
|
||||
- 以为 `const` 可以只声明名字,不写初始化表达式。
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
- 字符串字面量、拼接与文本边界见 [03_values_and_literals.md](03_values_and_literals.md);数组扩展和矩阵样数据见 [11_matrix_and_collections.md](11_matrix_and_collections.md)。
|
||||
- `{$ifdef ...}` 能力探测见 [15_lexical_structure_and_compile_options.md](15_lexical_structure_and_compile_options.md),不要写成普通业务逻辑。
|
||||
|
||||
### 基础赋值和条件求值
|
||||
## 基础赋值和条件求值
|
||||
|
||||
<!-- section-id: syntax-06-006 -->
|
||||
|
||||
@@ -195,6 +195,12 @@ writeLn(a);
|
||||
3
|
||||
```
|
||||
|
||||
## 基础算术与比较
|
||||
|
||||
<!-- section-id: syntax-06-017 -->
|
||||
|
||||
<!-- tags: 基础算术, 基础比较, 点前缀比较, 一元倒数, 加减乘除 -->
|
||||
|
||||
基础算术:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -308,6 +314,12 @@ writeLn(dataType(rb));
|
||||
- 整型 `a` 和实型 `b` 都可以用 `!` 求倒数。
|
||||
- 上面两个 `dataType(...)` 都输出 `1`,表示结果是实型。
|
||||
|
||||
## 逻辑与位运算
|
||||
|
||||
<!-- section-id: syntax-06-018 -->
|
||||
|
||||
<!-- tags: and or not, 逻辑门, 按位运算, 移位运算 -->
|
||||
|
||||
逻辑运算:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -364,6 +376,12 @@ writeLn(2 ror 1);
|
||||
1
|
||||
```
|
||||
|
||||
## 复合赋值与字符串运算
|
||||
|
||||
<!-- section-id: syntax-06-019 -->
|
||||
|
||||
<!-- tags: 复合赋值, 字符串运算, 字符串连接, like 正则, 原地更新 -->
|
||||
|
||||
基础算术复合赋值:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -513,6 +531,12 @@ writeLn("abc" like "a%");
|
||||
|
||||
因此 `like` 更接近“正则匹配”,不是 SQL 那套 `%` / `_` 通配语义。
|
||||
|
||||
## 自增、自减与 if 表达式
|
||||
|
||||
<!-- section-id: syntax-06-020 -->
|
||||
|
||||
<!-- tags: 自增, 自减, 前置自增, 前置自减, if 表达式 -->
|
||||
|
||||
自增与自减:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -567,7 +591,7 @@ writeLn(if 2 > 1 then 2 else 1);
|
||||
|
||||
`if condition then true_value else false_value` 必须带 `else`,否则不是本页可照写的表达式形态。
|
||||
|
||||
### 运算符优先级
|
||||
## 运算符优先级
|
||||
|
||||
<!-- section-id: syntax-06-015 -->
|
||||
|
||||
@@ -614,7 +638,7 @@ writeLn(flag);
|
||||
|
||||
稀有矩阵运算符的详细优先级以其专题页为准;不要用本表外推尚未写入正式文档的符号。
|
||||
|
||||
### 静态计算表达式 `static`
|
||||
## 静态计算表达式 `static`
|
||||
|
||||
<!-- section-id: syntax-06-016 -->
|
||||
|
||||
@@ -674,7 +698,7 @@ end;
|
||||
|
||||
这不是类成员的 `static` 字段;类静态成员见 [08_objects_and_classes.md](08_objects_and_classes.md)。缓存结果具有运行时状态,不要用它保存每次调用都必须重新计算的值。
|
||||
|
||||
### 表达式对象
|
||||
## 表达式对象
|
||||
|
||||
<!-- section-id: syntax-06-007 -->
|
||||
|
||||
@@ -756,7 +780,7 @@ writeLn(result_value);
|
||||
|
||||
逗号表达式本身可以作为一个普通子表达式继续参与后续运算。
|
||||
|
||||
### 空安全访问
|
||||
## 空安全访问
|
||||
|
||||
<!-- section-id: syntax-06-008 -->
|
||||
|
||||
@@ -803,7 +827,7 @@ writeLn(c?.a?.[1] = nil);
|
||||
|
||||
不要从这一段外推成所有深度、所有成员/下标组合都可写。
|
||||
|
||||
### 否定形式运算
|
||||
## 否定形式运算
|
||||
|
||||
<!-- section-id: syntax-06-009 -->
|
||||
|
||||
@@ -833,7 +857,7 @@ end;
|
||||
1
|
||||
```
|
||||
|
||||
### 标量链式比较
|
||||
## 标量链式比较
|
||||
|
||||
<!-- section-id: syntax-06-010 -->
|
||||
|
||||
@@ -861,7 +885,7 @@ writeLn(1 :<> 2 :<> 3);
|
||||
1
|
||||
```
|
||||
|
||||
### 矩阵链式比较
|
||||
## 矩阵链式比较
|
||||
|
||||
<!-- section-id: syntax-06-011 -->
|
||||
|
||||
@@ -893,7 +917,7 @@ writeLn(s[2]);
|
||||
|
||||
矩阵链式比较会按元素位置分别得到结果数组,并且可以和标量混用。
|
||||
|
||||
### 条件编译探测
|
||||
## 条件编译探测
|
||||
|
||||
<!-- section-id: syntax-06-012 -->
|
||||
|
||||
|
||||
@@ -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` 摊平成单元格序列;它按行推进,行本身是数组。
|
||||
|
||||
代码块身份:反例 / 不可照写
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
- `createObject(...)` 示例只在字符串类名、类类型变量或跨 `unit` 路径场景复制。
|
||||
- `property` 类型注解只有在已有类型名证据时生成;不要为了完整性发明说明性类型名。
|
||||
|
||||
### 最小类与声明位置
|
||||
## 最小类与声明位置
|
||||
|
||||
<!-- section-id: syntax-08-004 -->
|
||||
|
||||
@@ -180,7 +180,7 @@ writeLn(a);
|
||||
- 函数体内部声明类会报 `invalid statement`
|
||||
- 在松散语句脚本里,`type MyClass = class ... end;` 之后继续写 `writeLn(a);` 也会报 `invalid statement`
|
||||
|
||||
### 字段、静态成员、常量与可见性
|
||||
## 字段、静态成员、常量与成员访问
|
||||
|
||||
<!-- section-id: syntax-08-005 -->
|
||||
|
||||
@@ -275,6 +275,12 @@ end;
|
||||
|
||||
- `c.Inc()` 输出 `11`
|
||||
|
||||
## 成员可见性与继承访问
|
||||
|
||||
<!-- section-id: syntax-08-013 -->
|
||||
|
||||
<!-- tags: 成员访问可见性, private protected public, 可见性, 访问权限, 子类访问 -->
|
||||
|
||||
可见性 `private` / `protected` / `public`:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -396,7 +402,7 @@ end;
|
||||
- 子类里直接调用父类 `private` 方法也会在执行时报错。
|
||||
- `private` / `protected` 方法访问也遵循同样边界:外部不能调 `private` / `protected` 方法,子类只能调 `protected` 方法,不能调 `private` 方法。
|
||||
|
||||
### 构造函数边界
|
||||
## 构造函数边界
|
||||
|
||||
<!-- section-id: syntax-08-006 -->
|
||||
|
||||
@@ -427,11 +433,11 @@ end;
|
||||
- 上述例子里的 `a.value` 输出 `<NIL>`,说明 `private create` 没有执行。
|
||||
- `protected create`、`createObject("A", ...)` 和 `createObject(class(A), ...)` 也按同一规则处理;构造函数应保持 `public`。
|
||||
|
||||
### 属性、类型注解与类外实现
|
||||
## 基础 property 与类型注解
|
||||
|
||||
<!-- section-id: syntax-08-007 -->
|
||||
|
||||
<!-- tags: property, 读写属性, getter setter, 方法写在类外 -->
|
||||
<!-- tags: property, 读写属性, getter setter -->
|
||||
|
||||
基础 `property`:
|
||||
|
||||
@@ -530,6 +536,12 @@ abc
|
||||
abc
|
||||
```
|
||||
|
||||
## 类内声明与类外实现
|
||||
|
||||
<!-- section-id: syntax-08-014 -->
|
||||
|
||||
<!-- tags: 类外实现, 方法写在类外, 类内声明, 外部实现, 类方法实现 -->
|
||||
|
||||
类内声明、类外实现的带类型重载方法:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -590,6 +602,12 @@ left
|
||||
left
|
||||
```
|
||||
|
||||
## 索引与固定 index property
|
||||
|
||||
<!-- section-id: syntax-08-015 -->
|
||||
|
||||
<!-- tags: 索引 property, 固定索引 property, index property, 固定整数索引, 固定字符串索引 -->
|
||||
|
||||
索引型 `property`:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -695,6 +713,12 @@ end;
|
||||
- `obj.school` 输出 `math`
|
||||
- `obj.idx("High school")` 也输出 `math`
|
||||
|
||||
## 参数化 property 与 accessor
|
||||
|
||||
<!-- section-id: syntax-08-016 -->
|
||||
|
||||
<!-- tags: 参数化 property, property 参数, accessor, getter setter 参数 -->
|
||||
|
||||
参数化 `property`:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -762,7 +786,7 @@ end;
|
||||
- `write setItem` 这种“写方法接参数个数 + 赋值值”的写法可以通过
|
||||
- 上述例子中的 `obj.Item(2)` 输出 `x`
|
||||
|
||||
### 对象创建与类类型
|
||||
## 对象创建与类类型
|
||||
|
||||
<!-- section-id: syntax-08-008 -->
|
||||
|
||||
@@ -916,11 +940,11 @@ end;
|
||||
- `findClass("MathBox").Add(...)` 可以调用类方法
|
||||
- 上述例子依次输出 `7`、`11`
|
||||
|
||||
### 重载、继承与析构
|
||||
## 重载与继承
|
||||
|
||||
<!-- section-id: syntax-08-009 -->
|
||||
|
||||
<!-- tags: 继承, 父类子类, 方法重载, 同名不同参, 析构, 对象销毁时, 调用父类方法, 父类同名方法, inherited -->
|
||||
<!-- tags: 继承, 父类子类, 方法重载, 同名不同参, 析构, 对象销毁时, 调用父类方法, 父类同名方法 -->
|
||||
|
||||
`overload` 方法:
|
||||
|
||||
@@ -1036,6 +1060,12 @@ end;
|
||||
- 当多个父类存在同名方法时,本例优先命中第一个父类 `A`
|
||||
- 上述例子中的 `obj.Speak()` 输出 `1`
|
||||
|
||||
## 虚方法、覆盖与隐藏
|
||||
|
||||
<!-- section-id: syntax-08-017 -->
|
||||
|
||||
<!-- tags: 方法隐藏 hide, hide, override, virtual, 覆盖与隐藏 -->
|
||||
|
||||
基础 `virtual` / `override`:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -1133,6 +1163,12 @@ end;
|
||||
- `c.Ask()` 输出 `child`:`virtual` + `override` 后,父类方法内部的 `Who()` 定向到子类实现
|
||||
- 这就是 hide 与 override 的关键区别:hide 只影响直接调用,override 改变了所有经由基类的间接调用
|
||||
|
||||
## 祖先类调用与类型视图
|
||||
|
||||
<!-- section-id: syntax-08-018 -->
|
||||
|
||||
<!-- tags: 调用父类 inherited, inherited, Inherited, 祖先类调用, 父类方法, 类型视图 -->
|
||||
|
||||
`class(BaseClass, ObjectName).MethodName()`:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -1257,6 +1293,12 @@ end;
|
||||
- `Inherited BaseValue(5)` 可以显式调用父类指定方法
|
||||
- 上述例子依次输出 `6`、`9`
|
||||
|
||||
## 析构与 self 工厂
|
||||
|
||||
<!-- section-id: syntax-08-019 -->
|
||||
|
||||
<!-- tags: 析构 destroy, destroy, 对象销毁, self 工厂, self(0), self(1) -->
|
||||
|
||||
析构函数 `destroy`:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -1323,7 +1365,7 @@ end;
|
||||
- `self(1)` 返回的对象在这个例子里是 `ChildBox`,因此输出 `1`
|
||||
- `self(0)` 返回的对象在这个例子里不是 `ChildBox`,因此输出 `0`
|
||||
|
||||
### 跨 unit 类路径
|
||||
## 跨 unit 类路径
|
||||
|
||||
<!-- section-id: syntax-08-010 -->
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
- 多表联接中,`thisRow(表序号)` 和 `thisRowIndex(表序号)` 分别取得指定来源表的当前整行与原始下标。
|
||||
- 联接类型:`left join` 保留左表、`right join` 保留右表、`full join` 保留双方、`cross join` 笛卡尔积、逗号联接等价于 `cross join`;不匹配处用 `nil` 填充。
|
||||
- `select` 列表支持 `distinct` 去重、`as 别名`、`as nil`(参与计算但不返回)、`起始列 to 结束列` 字段区间、`selectopt(位选项)`、`drange(区间/M of N)`。
|
||||
- `select` 输出列表里出现同名列时,靠后的列会覆盖靠前的,结果集里只留一列;多表 `join` 两侧有同名字段时尤其容易踩。要两列都保留,用 `as` 给其中一个改名。
|
||||
- 聚集函数统一形态 `Func(Expr[, Cond[, N[, MovingFirst[, CacheId]]]])`:条件聚集、移动聚集、多字段聚集、`refof(Expr, N)` 引用相对行;`aggof('名', Expr)` 调用自定义聚集回调。
|
||||
- `thisGroup` 不是普通值,而是分组后的子结果集;要通过子 `select` / `vselect` 的 `from thisGroup` 来访问。
|
||||
- `thisRowIndex` 在 `order by` 之后仍可返回原始行位置;`thisOrder` 返回排序后的自然排名。
|
||||
@@ -699,6 +700,48 @@ writeLn(mcols(range_rows));
|
||||
- `表达式 as nil` 让该列只参与临时计算,不出现在结果集里(上例 `nil_rows` 只剩 `id` 一列)
|
||||
- `StartIndex to EndIndex` 在选择列表里取列区间;`0 to 1` 返回第 0、1 两列
|
||||
|
||||
### 输出列表里的同名列会被覆盖
|
||||
|
||||
<!-- section-id: syntax-13-032 -->
|
||||
|
||||
<!-- tags: 同名列丢失, join 后值不对, 列被覆盖, 两个表同字段, 必须起别名 -->
|
||||
|
||||
`join` 两侧有同名字段又不改名时,靠后的列覆盖靠前的:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
t1 := array(("A": 1, "B": 2, "C": 7),
|
||||
("A": 10, "B": 3, "C": 12),
|
||||
("A": 4, "B": 20, "C": 34));
|
||||
t2 := array(("A": 10, "B": 10, "D": 3),
|
||||
("A": 4, "B": 10, "D": 3));
|
||||
|
||||
dup_rows := select [1].*, [2].["B"], [2].["D"]
|
||||
from t1 join t2 on [1].["A"] = [2].["A"] end;
|
||||
writeLn("dup B:", dup_rows[0]["B"]);
|
||||
|
||||
alias_rows := select [1].*, [2].["B"] as "B2", [2].["D"]
|
||||
from t1 join t2 on [1].["A"] = [2].["A"] end;
|
||||
writeLn("kept B:", alias_rows[0]["B"]);
|
||||
writeLn("added B2:", alias_rows[0]["B2"]);
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- `dup B` 输出 `10`:`[1].*` 已带来左表的 `B`(值 `3`),后写的 `[2].["B"]` 把它覆盖成 `10`
|
||||
- 结果集里只有一列 `B`,左表原值无法再取回,且不会有任何报错
|
||||
- `kept B` 输出 `3`、`added B2` 输出 `10`:给右侧列写 `as "B2"` 后两列并存
|
||||
- 所以 `[表序号].*` 与显式列混写时,要先确认展开后有没有撞名
|
||||
|
||||
代码块身份:输出片段
|
||||
|
||||
```text
|
||||
dup B:10
|
||||
kept B:3
|
||||
added B2:10
|
||||
```
|
||||
|
||||
### `drange` 取行区间
|
||||
|
||||
<!-- section-id: syntax-13-021 -->
|
||||
@@ -1005,6 +1048,7 @@ query_result := select * from source_rows end;
|
||||
- 处理一维数组时直接把 `[0]` 当成稳定列访问。
|
||||
- 把 `thisGroup` 当成普通字段或普通变量。
|
||||
- 把排序后的 `thisRowIndex` 误当成排序序号。
|
||||
- 让 `[表序号].*` 和两侧同名的显式列同时出现在选择列表里,却不给其中一个写 `as` 别名。
|
||||
- 在 `left join` 时省略 `on` 子句或不用 `[表序号].["字段"]` 形式。
|
||||
- 在 `insert` 时漏掉 `insertfields` 或字段数与值数不匹配。
|
||||
- 期望 `update`/`delete` 返回新数组;它们直接修改原数组。
|
||||
|
||||
@@ -21,12 +21,16 @@
|
||||
- 条件判断里,`0` 按假值处理,非 `0` 数值按真值处理。
|
||||
- `inf` 和 `nan` 是特殊实数常量;`ifReal(inf)`、`ifReal(nan)` 都返回 `1`。
|
||||
- `inf > 1000` 为真,`nan = nan` 为假。
|
||||
- `0 / 0` 得 `nan`,`2 / 0` 得 `inf`,`-2 / 0` 得 `-inf`。
|
||||
- `nan` 参与 `=`、`>`、`<`、`>=`、`<=` 一律为假;只有 `<>` 为真,所以 `nan <> nan` 是可用的判定形态。
|
||||
- `nan` 参与算术运算的结果仍是 `nan`:`nan + 1`、`nan * 0`、`nan - nan` 都是 `nan`。
|
||||
- 未初始化的普通变量会得到整数 `0`。
|
||||
- 新建对象的普通成员变量默认值是 `nil`。
|
||||
- 对空数组读取一个尚未赋值的下标时,结果是 `nil`。
|
||||
- `ifNil(nil)` 可用于显式空值判定。
|
||||
- 空字符串、空数组和 `nil` 都按假值处理。
|
||||
- `nil` 可以参与加法;`nil + 1` 和 `1 + nil` 都返回 `1`,`nil + nil` 仍然是 `nil`。
|
||||
- `nil` 参与其它算术运算时按 `0` 处理:`nil / 2` 得 `0.0`、`nil * 2` 得 `0`、`nil - 2` 得 `-2`、`2 - nil` 得 `2`。
|
||||
- `ifNumber(...)` 会把整数、`Int64`、实数都视为数值;普通字符串不算数值。
|
||||
- TSL 不是“完全禁止混合类型表达式”;像 `b + a` 这种“整数 + 字符串”会通过编译,但在运行时会报 `operand type error`。
|
||||
- 因此,类型转换要显式写出来,例如 `strToInt(...)`,不要把运行时失败留给解释器。
|
||||
@@ -132,6 +136,49 @@ writeLn(nan = nan);
|
||||
- 说明 `true` / `false` / `inf` / `nan` 属于文档明确常量
|
||||
- 说明数值条件判断里,`0` 为假,非 `0` 为真
|
||||
|
||||
`nan` / `inf` 的产生方式,以及 `nan` 比较与算术的完整边界:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
n := 0 / 0;
|
||||
writeLn(n);
|
||||
writeLn(2 / 0);
|
||||
writeLn(-2 / 0);
|
||||
writeLn(n > 1);
|
||||
writeLn(n < 1);
|
||||
writeLn(n >= 1);
|
||||
writeLn(n <= 1);
|
||||
writeLn(n <> n);
|
||||
writeLn(n + 1);
|
||||
writeLn(n * 0);
|
||||
writeLn(n - n);
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- `0 / 0` 输出 `NAN`,`2 / 0` 输出 `INF`,`-2 / 0` 输出 `-INF`
|
||||
- `n > 1`、`n < 1`、`n >= 1`、`n <= 1` 全部输出 `0`
|
||||
- `n <> n` 输出 `1`,是唯一对 `nan` 成立的比较,可用来判定 `nan`
|
||||
- `n + 1`、`n * 0`、`n - n` 都输出 `NAN`,说明 `nan` 会沿算术传播
|
||||
- 因此数据参与计算前要先滤掉 `nan`,否则整条链路结果都是 `nan`
|
||||
|
||||
代码块身份:输出片段
|
||||
|
||||
```text
|
||||
NAN
|
||||
INF
|
||||
-INF
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
1
|
||||
NAN
|
||||
NAN
|
||||
NAN
|
||||
```
|
||||
|
||||
### 初始值与空值
|
||||
|
||||
<!-- section-id: syntax-16-005 -->
|
||||
@@ -181,6 +228,32 @@ writeLn(nil + nil = nil);
|
||||
- 其中 `nil + 1` 和 `1 + nil` 都得到 `1`
|
||||
- `nil + nil` 仍保持为 `nil`
|
||||
|
||||
`nil` 在其它算术运算里按 `0` 参与:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
writeLn(nil / 2);
|
||||
writeLn(nil * 2);
|
||||
writeLn(nil - 2);
|
||||
writeLn(2 - nil);
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 依次输出 `0.0`、`0`、`-2`、`2`
|
||||
- 说明 `nil` 在乘除减里都按 `0` 参与,不会中断计算
|
||||
- `nil / 2` 走实数除法所以显示成 `0.0`
|
||||
|
||||
代码块身份:输出片段
|
||||
|
||||
```text
|
||||
0.0
|
||||
0
|
||||
-2
|
||||
2
|
||||
```
|
||||
|
||||
空值条件判断:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
@@ -376,6 +449,9 @@ writeLn(imag(z));
|
||||
- 不要把“能编译”误解成“会自动按期望类型转换”。
|
||||
- 不要把数字字符串直接当成数值参与算术;需要数值语义时先显式转换。
|
||||
- 不要从其他语言的 truthy/falsy 或隐式转换规则反推 TSL。
|
||||
- 不要用 `nan = nan` 判定 `nan`;等值和大小比较对 `nan` 恒假,只有 `<>` 为真。
|
||||
- 不要以为 `nan` 会在后续运算里被“吃掉”;它沿算术一路传播,参与计算前要先滤掉。
|
||||
- 不要以为 `nil` 参与乘除减会中断计算或报错;它按 `0` 参与。
|
||||
- 不要把复数统计函数、分解函数与更大函数族当成本页文档能力。
|
||||
|
||||
代码块身份:反例 / 不可照写
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
- 遍历时需要维度信息可用 `mIndexCount`;需要第 `n` 维下标可用 `mIndex(n)`。
|
||||
- 子矩阵提取可以写 `matrix[row_start:row_end, col_start:col_end]`、`matrix[:, col_range]`、`matrix[row_index_array, col_index_array]`。
|
||||
- 子矩阵赋值可以写成单个标量,也可以写成同结构矩阵;赋值矩阵应和目标子矩阵形状匹配。
|
||||
- 子矩阵赋值语句本身**没有返回值**:`(m[:, "a"] := v)` 求值为 `0`,而普通赋值 `(a := v)` 求值为被赋的值。不要把子矩阵赋值写在需要取值的位置。
|
||||
- `t[:, :] := source` 按**位置**逐单元复制,不按列名对齐。`source` 里若有某行的列存储顺序与其它行不同,复制结果会静默错位;先用 `source[:, array("列1", "列2")]` 统一列序再赋值。
|
||||
- 子矩阵也可以接 `::=` 做逐单元赋值。
|
||||
- `mfind(matrix)` 返回真值单元格下标;一维数组的无条件 `mfind` 返回一维下标数组。
|
||||
- `mfind(matrix, condition)` 返回符合条件的下标行;二维矩阵下每行形如 `array(row_index, col_index)`。
|
||||
@@ -424,6 +426,79 @@ writeLn(matrix_value[1][1]);
|
||||
11
|
||||
```
|
||||
|
||||
### 子矩阵赋值的返回值与对齐方式
|
||||
|
||||
<!-- section-id: syntax-21-019 -->
|
||||
|
||||
<!-- tags: 赋值返回0, 赋值没返回值, 重构数组错位, 按位置复制, 列顺序不一致 -->
|
||||
|
||||
子矩阵赋值不产生返回值,普通赋值才产生:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
source := array(1, 2, 3);
|
||||
plain := (target := source);
|
||||
writeLn("plain:", plain[0]);
|
||||
|
||||
matrix_value := zeros(3, "a");
|
||||
sub := (matrix_value[:, "a"] := source);
|
||||
writeLn("sub:", sub);
|
||||
writeLn("written:", matrix_value[0]["a"]);
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- `plain` 拿到被赋的数组,所以 `plain[0]` 输出 `1`
|
||||
- `sub` 输出 `0`:子矩阵赋值语句本身求值为 `0`,不是被赋的值
|
||||
- `written` 输出 `1`:赋值本身生效了,只是取不到返回值
|
||||
- 所以 `return m[:, "a"] := v;` 这种写法会返回 `0`,要分成两句写
|
||||
|
||||
代码块身份:输出片段
|
||||
|
||||
```text
|
||||
plain:1
|
||||
sub:0
|
||||
written:1
|
||||
```
|
||||
|
||||
`t[:, :] := source` 按位置复制,不按列名对齐:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
source := array("k1": ("p": 651, "v": 637),
|
||||
"k2": ("p": 599, "v": 170),
|
||||
"k3": ("v": 200, "p": 607));
|
||||
target := zeros(mRows(source), mCols(source, 1));
|
||||
target[:, :] := source;
|
||||
writeLn("row3 p:", target[2]["p"]);
|
||||
writeLn("row3 v:", target[2]["v"]);
|
||||
|
||||
ordered := source[:, array("p", "v")];
|
||||
fixed := zeros(mRows(ordered), mCols(ordered, 1));
|
||||
fixed[:, :] := ordered;
|
||||
writeLn("fixed p:", fixed[2]["p"]);
|
||||
writeLn("fixed v:", fixed[2]["v"]);
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- 第三行源数据的列存储顺序是 `v`、`p`,与前两行相反
|
||||
- `row3 p` 输出 `200`、`row3 v` 输出 `607`:两个值互换了,因为复制按位置而不按列名
|
||||
- 前两行看不出问题,所以这种错位不会报错,只会静默出现在个别行
|
||||
- `ordered := source[:, array("p", "v")]` 先统一列序后,`fixed p` 输出 `607`、`fixed v` 输出 `200`,恢复正确
|
||||
- 源数据由多个过程分别写入时,各行列序可能不一致,用 `[:, :]` 重构前先统一列序
|
||||
|
||||
代码块身份:输出片段
|
||||
|
||||
```text
|
||||
row3 p:200
|
||||
row3 v:607
|
||||
fixed p:607
|
||||
fixed v:200
|
||||
```
|
||||
|
||||
### `mfind` 与 `mfindSparse`
|
||||
|
||||
<!-- section-id: syntax-21-011 -->
|
||||
@@ -963,6 +1038,44 @@ bar 结果 (1,2):2
|
||||
- 上述差异只适用于普通 `array`;`FMArray` 上 `|` 与 `:|` 行为一致且行数不齐时补 `0`,见 [22_fmarray.md](22_fmarray.md)
|
||||
- 对应的复合赋值形式:`&=`(行并)、`|=`(列并)、`:|=`(列并补 nil)
|
||||
|
||||
### 列并遇到同名字符串列键
|
||||
|
||||
<!-- section-id: syntax-21-018 -->
|
||||
|
||||
<!-- tags: 同名列合并, 列被覆盖, 合并后值变了, 相同列名, 列并覆盖 -->
|
||||
|
||||
两侧共有的字符串列键不会并成两列,而是被右操作数覆盖:
|
||||
|
||||
代码块身份:可直接照写示例
|
||||
|
||||
```tsl
|
||||
left := ones(3, array("A", "B"));
|
||||
right := zeros(3, array("A", "C"));
|
||||
merged := left | right;
|
||||
writeLn("A:", merged[0]["A"]);
|
||||
writeLn("B:", merged[0]["B"]);
|
||||
writeLn("C:", merged[0]["C"]);
|
||||
|
||||
reversed := right | left;
|
||||
writeLn("reversed A:", reversed[0]["A"]);
|
||||
```
|
||||
|
||||
结果说明:
|
||||
|
||||
- `A` 输出 `0`:`A` 两侧都有,取右操作数 `right` 的值,`left` 的 `1` 被覆盖
|
||||
- `B` 输出 `1`、`C` 输出 `0`:只在一侧出现的列都原样保留
|
||||
- `reversed A` 输出 `1`:换序后仍是右操作数胜出,说明规则是「右侧覆盖左侧」而非「零值覆盖」
|
||||
- 默认数字列下标不会触发覆盖,只有字符串列键相同才会
|
||||
|
||||
代码块身份:输出片段
|
||||
|
||||
```text
|
||||
A:0
|
||||
B:1
|
||||
C:0
|
||||
reversed A:1
|
||||
```
|
||||
|
||||
## 默认生成模板
|
||||
|
||||
<!-- section-id: syntax-21-016 -->
|
||||
@@ -990,6 +1103,9 @@ matrix_value := zeros(2, 3);
|
||||
- 把 `::=` 写成带 `begin ... end` 的语句块。
|
||||
- 用 `::` 期待遍历到任意深度;深度遍历使用 `:.`。
|
||||
- 子矩阵赋值时用形状不匹配的矩阵硬塞。
|
||||
- 把子矩阵赋值写在需要取值的位置,例如 `return m[:, "a"] := v;`;它求值为 `0`。
|
||||
- 用 `t[:, :] := source` 重构下标时,假定它会按列名对齐;它按位置复制。
|
||||
- 期待 `|` 把两侧同名字符串列键并成两列;同名键会被右操作数覆盖。
|
||||
- 把 `union` 的复合赋值写成 `union=`;正确写法是 `&=`。
|
||||
- 一维数组直接 `union` 期待得到二维结果;需要先对它连用两次转置前缀(双转置)变成行向量。
|
||||
- 在基础函数异常参数时用分号分隔;正确写法用逗号:`sqrt(data, 1, -999)`。
|
||||
|
||||
@@ -37,6 +37,9 @@ QUICKSTART_RULE_RE = re.compile(
|
||||
QUICKSTART_RULE_PREFIX_RE = re.compile(
|
||||
r"^<!--\s*quickstart-rule\s*:", re.IGNORECASE
|
||||
)
|
||||
OWNER_SECTION_RE = re.compile(
|
||||
r"^\s+Owner Section:`([a-z0-9][a-z0-9._-]*)`\s*$", re.IGNORECASE
|
||||
)
|
||||
STRUCTURAL_METADATA_RE = re.compile(
|
||||
r"<!--\s*(?:section-id|quickstart-rule)\s*:.*?-->",
|
||||
re.DOTALL | re.IGNORECASE,
|
||||
@@ -83,23 +86,24 @@ WRITE_PRELUDE_SECTIONS = (
|
||||
)
|
||||
QUICKSTART_PAGE = "01_quickstart.md"
|
||||
QUICKSTART_SUMMARY_HEADING = "语言核心事实速查"
|
||||
QUICKSTART_RULE_OWNER_PAGES = {
|
||||
"assignment": "06_expressions_and_operators.md",
|
||||
"file-choice": "02_core_model.md",
|
||||
"tsl-layout": "02_core_model.md",
|
||||
"tsf-layout": "02_core_model.md",
|
||||
"tsf-filename": "02_core_model.md",
|
||||
"function-default": "05_functions_and_calls.md",
|
||||
"procedure-explicit": "05_functions_and_calls.md",
|
||||
"class-shape": "08_objects_and_classes.md",
|
||||
"object-creation": "08_objects_and_classes.md",
|
||||
"unit-shape": "09_units_and_scope.md",
|
||||
"unit-default": "09_units_and_scope.md",
|
||||
"named-arguments": "05_functions_and_calls.md",
|
||||
"named-argument-order": "05_functions_and_calls.md",
|
||||
"string-literal-default": "03_values_and_literals.md",
|
||||
"string-prefix-by-type": "03_values_and_literals.md",
|
||||
"index-origins": "03_values_and_literals.md",
|
||||
MAX_LEAF_SECTION_LINES = 180
|
||||
QUICKSTART_RULE_OWNERS = {
|
||||
"assignment": ("06_expressions_and_operators.md", "syntax-06-004"),
|
||||
"file-choice": ("02_core_model.md", "syntax-02-002"),
|
||||
"tsl-layout": ("02_core_model.md", "syntax-02-002"),
|
||||
"tsf-layout": ("02_core_model.md", "syntax-02-002"),
|
||||
"tsf-filename": ("02_core_model.md", "syntax-02-002"),
|
||||
"function-default": ("05_functions_and_calls.md", "syntax-05-002"),
|
||||
"procedure-explicit": ("05_functions_and_calls.md", "syntax-05-002"),
|
||||
"class-shape": ("08_objects_and_classes.md", "syntax-08-002"),
|
||||
"object-creation": ("08_objects_and_classes.md", "syntax-08-002"),
|
||||
"unit-shape": ("09_units_and_scope.md", "syntax-09-002"),
|
||||
"unit-default": ("09_units_and_scope.md", "syntax-09-002"),
|
||||
"named-arguments": ("05_functions_and_calls.md", "syntax-05-002"),
|
||||
"named-argument-order": ("05_functions_and_calls.md", "syntax-05-002"),
|
||||
"string-literal-default": ("03_values_and_literals.md", "syntax-03-004"),
|
||||
"string-prefix-by-type": ("03_values_and_literals.md", "syntax-03-004"),
|
||||
"index-origins": ("03_values_and_literals.md", "syntax-03-002"),
|
||||
}
|
||||
|
||||
HEADING_TOKEN_SCORE = 12
|
||||
@@ -232,6 +236,9 @@ class QuickstartRule:
|
||||
bullet_line: int
|
||||
key: str
|
||||
text: str
|
||||
section_id: str | None
|
||||
owner_section: str | None
|
||||
owner_line: int | None
|
||||
|
||||
|
||||
class ReferenceInstallationError(RuntimeError):
|
||||
@@ -724,6 +731,7 @@ def _quickstart_rule_records(
|
||||
records: list[QuickstartRule] = []
|
||||
problems: list[ValidationProblem] = []
|
||||
in_fence = False
|
||||
current_section_id: str | None = None
|
||||
for index, line in enumerate(lines):
|
||||
if FENCE_RE.match(line):
|
||||
in_fence = not in_fence
|
||||
@@ -731,6 +739,14 @@ def _quickstart_rule_records(
|
||||
if in_fence:
|
||||
continue
|
||||
stripped = line.strip()
|
||||
heading = HEADING_RE.match(line)
|
||||
if heading is not None and len(heading.group(1)) in (2, 3, 4):
|
||||
metadata_line = _next_nonblank_line(lines, index + 1)
|
||||
metadata = lines[metadata_line].strip() if metadata_line is not None else ""
|
||||
section_id_match = SECTION_ID_RE.fullmatch(metadata)
|
||||
current_section_id = (
|
||||
section_id_match.group(1) if section_id_match is not None else None
|
||||
)
|
||||
if not QUICKSTART_RULE_PREFIX_RE.match(stripped):
|
||||
continue
|
||||
marker = QUICKSTART_RULE_RE.fullmatch(stripped)
|
||||
@@ -750,6 +766,8 @@ def _quickstart_rule_records(
|
||||
)
|
||||
continue
|
||||
text_parts = [lines[bullet_line].strip()]
|
||||
owner_section: str | None = None
|
||||
owner_line: int | None = None
|
||||
continuation = bullet_line + 1
|
||||
while continuation < len(lines):
|
||||
line = lines[continuation]
|
||||
@@ -757,7 +775,21 @@ def _quickstart_rule_records(
|
||||
break
|
||||
if not line.startswith((" ", "\t")):
|
||||
break
|
||||
text_parts.append(line.strip())
|
||||
owner_match = OWNER_SECTION_RE.fullmatch(line)
|
||||
if owner_match is not None:
|
||||
if owner_section is not None:
|
||||
problems.append(
|
||||
ValidationProblem(
|
||||
page,
|
||||
continuation + 1,
|
||||
f"quickstart-rule {marker.group(1)!r} 重复 Owner Section",
|
||||
)
|
||||
)
|
||||
else:
|
||||
owner_section = owner_match.group(1)
|
||||
owner_line = continuation + 1
|
||||
else:
|
||||
text_parts.append(line.strip())
|
||||
continuation += 1
|
||||
records.append(
|
||||
QuickstartRule(
|
||||
@@ -766,6 +798,9 @@ def _quickstart_rule_records(
|
||||
bullet_line=bullet_line + 1,
|
||||
key=marker.group(1),
|
||||
text=" ".join(text_parts),
|
||||
section_id=current_section_id,
|
||||
owner_section=owner_section,
|
||||
owner_line=owner_line,
|
||||
)
|
||||
)
|
||||
return records, problems
|
||||
@@ -829,7 +864,13 @@ def _quickstart_rule_problems(
|
||||
by_key: dict[str, list[QuickstartRule]] = {}
|
||||
for record in records:
|
||||
by_key.setdefault(record.key, []).append(record)
|
||||
expected_keys = set(QUICKSTART_RULE_OWNER_PAGES) if require_complete else set()
|
||||
expected_keys = set(QUICKSTART_RULE_OWNERS) if require_complete else set()
|
||||
declared_section_ids = {
|
||||
match.group(1)
|
||||
for lines in pages.values()
|
||||
for line in lines
|
||||
if (match := SECTION_ID_RE.fullmatch(line.strip())) is not None
|
||||
}
|
||||
for key in sorted(set(by_key) | expected_keys):
|
||||
key_records = by_key.get(key, [])
|
||||
summaries = [record for record in key_records if record.page == quickstart]
|
||||
@@ -843,20 +884,57 @@ def _quickstart_rule_problems(
|
||||
)
|
||||
)
|
||||
continue
|
||||
expected_page = QUICKSTART_RULE_OWNER_PAGES.get(key)
|
||||
if expected_page is not None and canonicals[0].page.name != expected_page:
|
||||
summary = summaries[0]
|
||||
canonical = canonicals[0]
|
||||
expected_owner = QUICKSTART_RULE_OWNERS.get(key)
|
||||
if expected_owner is not None and canonical.page.name != expected_owner[0]:
|
||||
problems.append(
|
||||
ValidationProblem(
|
||||
canonicals[0].page,
|
||||
canonicals[0].line,
|
||||
f"quickstart-rule {key!r} 的专题 owner 应为 {expected_page}",
|
||||
canonical.page,
|
||||
canonical.line,
|
||||
f"quickstart-rule {key!r} 的专题 owner 应为 {expected_owner[0]}",
|
||||
)
|
||||
)
|
||||
if summaries[0].text != canonicals[0].text:
|
||||
if expected_owner is not None and canonical.section_id != expected_owner[1]:
|
||||
problems.append(
|
||||
ValidationProblem(
|
||||
canonical.page,
|
||||
canonical.line,
|
||||
f"quickstart-rule {key!r} 的专题 owner Section 应为 "
|
||||
f"{expected_owner[1]}",
|
||||
)
|
||||
)
|
||||
if summary.owner_section is None:
|
||||
problems.append(
|
||||
ValidationProblem(
|
||||
quickstart,
|
||||
summaries[0].bullet_line,
|
||||
summary.bullet_line,
|
||||
f"quickstart-rule {key!r} 缺少 Owner Section",
|
||||
)
|
||||
)
|
||||
elif summary.owner_section not in declared_section_ids:
|
||||
problems.append(
|
||||
ValidationProblem(
|
||||
quickstart,
|
||||
summary.owner_line or summary.bullet_line,
|
||||
f"quickstart-rule {key!r} 的 Owner Section 不存在:"
|
||||
f"{summary.owner_section}",
|
||||
)
|
||||
)
|
||||
elif canonical.section_id is not None and summary.owner_section != canonical.section_id:
|
||||
problems.append(
|
||||
ValidationProblem(
|
||||
quickstart,
|
||||
summary.owner_line or summary.bullet_line,
|
||||
f"quickstart-rule {key!r} 的 Owner Section 与专题事实所在 Section "
|
||||
f"不一致:{summary.owner_section} != {canonical.section_id}",
|
||||
)
|
||||
)
|
||||
if summary.text != canonical.text:
|
||||
problems.append(
|
||||
ValidationProblem(
|
||||
quickstart,
|
||||
summary.bullet_line,
|
||||
f"派生摘要规则与专题事实不一致:{key}",
|
||||
)
|
||||
)
|
||||
@@ -867,6 +945,35 @@ def _quickstart_rule_problems(
|
||||
return problems
|
||||
|
||||
|
||||
def _leaf_section_granularity_problems(
|
||||
sections: list[Section],
|
||||
) -> list[ValidationProblem]:
|
||||
"""Reject exact-retrieval units whose body has grown beyond the safe budget."""
|
||||
problems: list[ValidationProblem] = []
|
||||
for section in sections:
|
||||
has_child = any(
|
||||
other.page == section.page
|
||||
and len(other.heading_path) > len(section.heading_path)
|
||||
and other.heading_path[: len(section.heading_path)]
|
||||
== section.heading_path
|
||||
for other in sections
|
||||
)
|
||||
if has_child:
|
||||
continue
|
||||
line_count = len(section.body.splitlines())
|
||||
if line_count <= MAX_LEAF_SECTION_LINES:
|
||||
continue
|
||||
problems.append(
|
||||
ValidationProblem(
|
||||
section.page,
|
||||
1,
|
||||
f"叶子 Section 正文超过 {MAX_LEAF_SECTION_LINES} 行:"
|
||||
f"{section.id}({line_count} 行)",
|
||||
)
|
||||
)
|
||||
return problems
|
||||
|
||||
|
||||
def validate_references(
|
||||
references_dir: Path = DEFAULT_REFERENCES_DIR,
|
||||
) -> list[ValidationProblem]:
|
||||
@@ -971,6 +1078,7 @@ def validate_references(
|
||||
return problems
|
||||
sections = load_sections(references_dir)
|
||||
problems.extend(_tag_problems(sections))
|
||||
problems.extend(_leaf_section_granularity_problems(sections))
|
||||
ids: dict[str, Section] = {}
|
||||
for section in sections:
|
||||
if section.id in ids:
|
||||
@@ -1534,9 +1642,24 @@ HELP_EPILOG = """\
|
||||
选定的 Section ID 可以合并成一次取回
|
||||
lookup.py --section "syntax-05-004" "syntax-02-002"
|
||||
|
||||
查询词无从下手时先 --map 把需求映射到 TSL 概念;改动参考页或 data/ 词表后用 --check 校验。
|
||||
一次 --query 只覆盖一个语法要素;从用户原话提取错误原文、标识符或要素名,
|
||||
保留原写法但不传完整用户句。多个要素分别查询,再批量 --section。
|
||||
|
||||
write 模式额外列出 Required: yes 前置章节,不占 --limit,同一任务只需取回一次。
|
||||
syntax-01-002 是派生速查;每条规则后的 Owner Section 才是该事实的专题来源。
|
||||
|
||||
查询词无从下手时先 --map 把需求映射到 TSL 概念;地图不含可照写事实。
|
||||
弱命中(候选标 Weak: yes)没有意图/标题/标识符/标签命中,只靠正文低分撞词;
|
||||
全部候选皆弱时视同无匹配并返回 rc=2,应改进查询词重试而不是从弱候选里挑。
|
||||
混合候选只从强命中里选择;全部候选皆弱时视同无匹配并返回 rc=2。
|
||||
没有 Weak 行也不保证相关,仍按 Summary 选定后取回正文核对。
|
||||
|
||||
--section 中任一 ID 不存在时整批不返回正文,并在 stderr 给出最近的 Section ID。
|
||||
改动参考页或 data/ 词表后用 --check;它不校验事实语义、排序或示例运行结果。
|
||||
|
||||
退出码:
|
||||
0 动作成功
|
||||
1 参考目录/结构/安装错误,或 --check 发现问题
|
||||
2 参数错误、无匹配、全弱命中或 Section ID 不存在
|
||||
"""
|
||||
|
||||
|
||||
@@ -1581,7 +1704,8 @@ def _parser() -> argparse.ArgumentParser:
|
||||
"--check",
|
||||
action="store_true",
|
||||
help="只校验参考页结构、显式 Section ID、代码块身份、本地链接、quickstart "
|
||||
"派生规则和词表页覆盖;检索排序由测试套件校验。发现问题时退出码为 1",
|
||||
"派生规则、词表页覆盖和叶子 Section 180 行粒度上限;检索排序由测试套件校验。"
|
||||
"发现问题时退出码为 1",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--mode",
|
||||
|
||||
Reference in New Issue
Block a user