diff --git a/docs/tsl/code_style.md b/docs/tsl/code_style.md index a0cc9d9e..ca333faf 100644 --- a/docs/tsl/code_style.md +++ b/docs/tsl/code_style.md @@ -22,8 +22,8 @@ ### 1.1 单一职责 - 一个文件只做一件事;职责明确。 -- `.tsl` 建议作为“入口/编排层”:聚合参数/配置、串起流程;可复用逻辑下沉到 - `.tsf`(`unit`/`class`/`function`)中。 +- `.tsl` 作为可执行脚本:语句区在前并按顺序执行;需要本文件函数/类时,声明区放在语句区之后。 +- `.tsf` 作为模块/函数扩展文件:可复用逻辑下沉到 `.tsf`,部署到解释器 `funcext` 后供脚本调用。 - 当一个顶层声明同时承担“协议适配 + 业务计算 + I/O/环境依赖 + 临时代码”时,优先拆分边界:核心纯逻辑 → 工具函数 → 边界适配(I/O)。 @@ -32,6 +32,7 @@ - TSL 语法要求(仅 `.tsf`):每个 `.tsf` 文件只能有一个顶层声明,且文件基名必须与顶层声明同名。 - `.tsl` 允许直接写语句,不要求顶层声明;文件名不强制,但建议清晰可检索。 +- `.tsl` 中如果同时需要脚本语句和函数/类声明,先写语句区,再写声明区;不要在声明区后继续追加脚本语句。 - 推荐文件名使用 `PascalCase` 以提升检索与协作一致性;扩展名按类型使用 `.tsl`/`.tsf`(两者都属于 TSL 源文件,风格规则一致)。 - 详细约束与命名细则见 `docs/tsl/naming.md`。 diff --git a/docs/tsl/finance/backtest_and_trade_flow.md b/docs/tsl/finance/backtest_and_trade_flow.md index b7cc0bae..8845c803 100644 --- a/docs/tsl/finance/backtest_and_trade_flow.md +++ b/docs/tsl/finance/backtest_and_trade_flow.md @@ -2,12 +2,17 @@ 文档类型:业务骨架 是否可直接用于生成代码:否 -是否含已验证可执行示例:否 -是否含已验证反例:否 遇到不确定时跳转到:项目实际接口定义、[../modules/tsbacktesting.md](../modules/tsbacktesting.md)、[selection_and_signal_patterns.md](selection_and_signal_patterns.md)、[../syntax/index.md](../syntax/index.md) 这一篇收拢回测、组合、交易与结果读取流程。 +## Agent 回测/交易判断规则 + +- 先判断比例类组合还是数量类组合,再看交易输入和结果读取。 +- 本页只给业务流程骨架,不给对象 API 真值。 +- 对象创建方式、交易数据入口、结果接口和项目封装必须回项目实际接口定义。 +- 不要发明项目实际接口、回测对象创建方式、交易入口或结果读取调用链。 + ## 这一篇解决什么问题 回答“回测对象如何组织、交易流程如何设置、结果怎样取出和解释”。 diff --git a/docs/tsl/finance/entry_decision.md b/docs/tsl/finance/entry_decision.md index 3e8e43a1..9446d061 100644 --- a/docs/tsl/finance/entry_decision.md +++ b/docs/tsl/finance/entry_decision.md @@ -2,12 +2,16 @@ 文档类型:检索页 是否可直接用于生成代码:否 -是否含已验证可执行示例:否 -是否含已验证反例:否 遇到不确定时跳转到:[market_data_context.md](market_data_context.md)、[../syntax/index.md](../syntax/index.md)、[../reference/index.md](../reference/index.md) 这里是金融层的入口决策页,不是代码页。它只解决“业务任务该往哪一层跳”,不重新定义语言基础语法。 +## Agent Finance Entry 判断规则 + +- 先判断任务属于市场数据、序列指标、选股信号还是回测交易。 +- finance 只给业务组织方式;代码细节必须回 syntax、reference 或项目实际接口。 +- 不要发明项目实际接口、数据字段、函数签名或业务上下文。 + ## 这一篇解决什么问题 回答“什么时候应该进入 finance 层、进入后先去哪个业务主题页,以及什么时候该回到 syntax / reference 层”。 diff --git a/docs/tsl/finance/index.md b/docs/tsl/finance/index.md index f5ce0b0e..2ed2f3b9 100644 --- a/docs/tsl/finance/index.md +++ b/docs/tsl/finance/index.md @@ -2,12 +2,16 @@ 文档类型:检索页 是否可直接用于生成代码:否 -是否含已验证可执行示例:否 -是否含已验证反例:否 遇到不确定时跳转到:[../syntax/index.md](../syntax/index.md)(优先)、[market_data_context.md](market_data_context.md)、[../reference/index.md](../reference/index.md) 这里是业务层入口。只讨论金融任务如何使用 TSL,不重讲基础语法。 +## Agent Finance 路由规则 + +- 只用 finance 判断金融业务任务该怎么分层,不用 finance 重写语法规则。 +- 金融任务需要真实数据字段、回测接口或项目封装时,先回项目实际接口定义。 +- 不要发明项目实际接口、字段名、对象创建方式或函数签名。 + ## 先看这 5 条 - 如果你的问题是“语言怎么写”,不要留在 finance,回到 [../syntax/index.md](../syntax/index.md)。 diff --git a/docs/tsl/finance/market_data_context.md b/docs/tsl/finance/market_data_context.md index 440e348c..0a8608f5 100644 --- a/docs/tsl/finance/market_data_context.md +++ b/docs/tsl/finance/market_data_context.md @@ -2,14 +2,19 @@ 文档类型:业务骨架 是否可直接用于生成代码:否 -是否含已验证可执行示例:否 -是否含已验证反例:否 遇到不确定时跳转到:[series_and_indicator_model.md](series_and_indicator_model.md)、[../syntax/index.md](../syntax/index.md)、[../modules/tsbacktesting.md](../modules/tsbacktesting.md) 本页用于判断金融脚本运行时的数据语境,不提供独立语法模板。 这一篇整理金融场景中的市场数据语境与执行环境。 +## Agent 市场数据判断规则 + +- 先确认脚本运行时是否已经有市场数据上下文。 +- 只把本页用于判断业务语境,不把市场字段当成 TSL 通用语法。 +- 如果字段来源、周期、复权口径或数据入口不明确,回项目实际接口定义。 +- 不要发明项目实际接口、市场字段或默认数据上下文。 + ## 这一篇解决什么问题 回答“金融脚本运行时的数据上下文是什么、哪些概念属于业务层而不是语言层”。 diff --git a/docs/tsl/finance/selection_and_signal_patterns.md b/docs/tsl/finance/selection_and_signal_patterns.md index c509170a..834a3339 100644 --- a/docs/tsl/finance/selection_and_signal_patterns.md +++ b/docs/tsl/finance/selection_and_signal_patterns.md @@ -2,12 +2,17 @@ 文档类型:业务骨架 是否可直接用于生成代码:否 -是否含已验证可执行示例:否 -是否含已验证反例:否 遇到不确定时跳转到:[series_and_indicator_model.md](series_and_indicator_model.md)、[backtest_and_trade_flow.md](backtest_and_trade_flow.md)、[../syntax/index.md](../syntax/index.md) 这一篇收拢选股、筛选和信号生成的业务模式。 +## Agent 选股/信号判断规则 + +- 先确认任务输出是筛选结果、交易信号、评分排序还是回测输入。 +- 条件表达的语法回 syntax;金融任务组织留在 finance。 +- 需要真实字段、信号函数或输出接口时,回项目实际接口定义。 +- 不要发明项目实际接口、字段名、信号函数签名或输出结构。 + ## 这一篇解决什么问题 回答“如何把条件表达和金融筛选任务组织成稳定的选股/信号脚本”。 diff --git a/docs/tsl/finance/series_and_indicator_model.md b/docs/tsl/finance/series_and_indicator_model.md index 492aa142..21ff43cb 100644 --- a/docs/tsl/finance/series_and_indicator_model.md +++ b/docs/tsl/finance/series_and_indicator_model.md @@ -2,12 +2,17 @@ 文档类型:业务骨架 是否可直接用于生成代码:否 -是否含已验证可执行示例:否 -是否含已验证反例:否 遇到不确定时跳转到:[market_data_context.md](market_data_context.md)、[selection_and_signal_patterns.md](selection_and_signal_patterns.md)、[../syntax/index.md](../syntax/index.md) 这一篇处理金融序列与指标计算模式。 +## Agent 序列/指标判断规则 + +- 先确认输入是单值还是逐 bar 序列。 +- 指标公式只在业务上下文里成立,不替代 syntax 的表达式规则。 +- 需要真实字段、指标函数或窗口口径时,回项目实际接口定义或 reference 参数事实页。 +- 不要发明项目实际接口、字段名、指标函数签名或历史窗口语义。 + ## 这一篇解决什么问题 回答“指标、序列、逐 bar 计算和相关金融表达方式如何组织,以及 AI 应该先建立什么样的业务心智模型”。 diff --git a/docs/tsl/index.md b/docs/tsl/index.md index c5a13d67..ba43c5ab 100644 --- a/docs/tsl/index.md +++ b/docs/tsl/index.md @@ -6,22 +6,26 @@ 是否含已验证反例:否 遇到不确定时跳转到:[syntax/index.md](syntax/index.md)、[finance/index.md](finance/index.md)、[reference/index.md](reference/index.md)、[modules/index.md](modules/index.md)、项目自身文档、`scripts/*` 入口脚本、CI 配置 -这个入口文件只负责一件事:让新 session 先判断主问题属于哪一层,再进入最相关的单个入口页。 +这个入口文件只负责一件事:让 agent 先判断主问题属于哪一层,再进入最相关的单个入口页。这里的语法文档面向 agent 决策,不按人类教程组织;回答和生成代码时必须按流程读,不要凭语言相似性补全。 ## 先记住这些规则 - 先读本文件,不要默认通读全部 TSL 文档。 +- 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据;未给后缀时,再根据用户交付目标判断。 +- `.tsl` 是可执行脚本;`.tsf` 是部署到解释器 `funcext` 的模块/函数扩展文件。 +- 写 `.tsl` 时按两段理解:语句区在前并按顺序执行;函数/类声明区在后,供前面的语句调用或运行时解析。 +- 写 `.tsf` 时按模块/扩展理解;顶层函数部署到 `funcext` 后,`.tsl` 脚本可以直接调用。 - 语言怎么写的问题,先从 `docs/tsl/syntax/` 开始。 - 指标、选股、回测和策略流程的问题,先从 `docs/tsl/finance/` 开始;不要先把业务问题拆成纯语法问题。 - 某个函数怎么用、属于哪个函数库分类或目录,先从 `docs/tsl/reference/` 开始。 - 现成模块、外部集成和互操作问题,先从 [modules/index.md](modules/index.md) 开始。 - 账户体系、真实接口名、部署方式、脚本入口、权限模型、环境变量、CI、验证命令这类问题,先按“项目依赖 / 项目执行”处理;优先回项目自身文档、`scripts/*` 入口脚本、CI 配置。 - [toolchain.md](toolchain.md) 不是 TSL 语法子类,而是项目执行类辅证页;只有当前项目已经补齐工具链与验证信息时才使用;如果这页仍是模板,不把它当主入口。 -- 顶层主体优先按四类理解:松散语句、`function / procedure`、`type Name = class`、`unit`。 -- 不要把顶层 `function / procedure` 定义和松散语句混在同一个文件模型里。 +- 生成 `.tsl` 代码时,优先从脚本语句区开始;如果关键词要求函数或类,把声明放在语句区之后。 +- 不要在 `.tsl` 的函数/类声明区之后再追加新的脚本语句。 - 任何语法判断都先看正式语法页结论。 -- 如果涉及较新写法、资料冲突或解释器差异,先回到 `docs/tsl/syntax/index.md`,再按主题跳到对应语法页;只有对应主题页仍然没有结论时,才本地用 `tsl` 验证。 -- 如果涉及高频误写、反例或负向边界,优先回到 `docs/tsl/syntax/12_pitfalls.md`;只有结论缺失时,才本地用 `tsl` 验证。 +- 如果涉及较新写法、资料冲突或解释器差异,先回到 `docs/tsl/syntax/index.md`,再按主题跳到对应语法页;对应主题页仍然没有结论时,不要自行补语法。 +- 如果涉及高频误写、反例或负向边界,优先回到 `docs/tsl/syntax/12_pitfalls.md`;结论缺失时不要把猜测写成语法事实。 - 模板、错误示例和输出片段不算可独立编译代码。 ## 元数据与证据标签 @@ -34,8 +38,27 @@ - 如果还需要补充用途、限制或复用建议,单独写 `代码块说明`,不要把说明文字继续拼进 `代码块身份`。 - 如果页头里的 `遇到不确定时跳转到` 列出多个目标,默认第一项是优先入口,后面的目标只用于分流或补证。 +## 维护者入文档前验证 + +- TSL 语法页面向 agent 生成代码;所有写成语法事实的代码库样例,必须先由维护者在项目验证环境中确认。 +- 正向代码库样例只有在环境验证通过后,才能标成 `已验证可执行示例`;对应输出只能标成 `已验证输出片段`。 +- 负向代码库样例只有在环境中确认失败形态后,才能标成 `反例 / 不可照写`。 +- 未经过环境验证的写法不能写成语法事实;只能作为文档缺口、待验证项,或标成 `配置片段 / 概念骨架`。 +- 验证过程不写进语法页;解释器路径、Docker、Windows、Ubuntu、环境变量和执行命令属于项目执行层,不能混入通用语法事实。 +- agent 不需要自行执行验证;agent 只根据维护者已经写入文档的证据标签生成代码,遇到未覆盖写法时不要发明语法。 + ## 新 session 起手规则 +### Agent 读取流程 + +1. 识别目标层:语法、金融业务、函数库、模块/集成、项目执行。 +2. 识别用户指定的文件后缀;用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据。 +3. 用户未给后缀时,按交付目标判断:可执行代码对应 `.tsl`,通用模块对应 `.tsf`;仍不明确时向用户确认。 +4. 对 `.tsl`,先生成语句区;需要本文件函数/类时,再生成声明区。 +5. 对 `.tsf`,生成模块/扩展声明;部署后的顶层函数可由脚本直接调用。 +6. 只读取当前任务命中的单个入口页;不要同时展开语法、业务、函数库和工具链。 +7. 生成代码前找 `代码块身份:已验证可执行示例`;遇到 `反例 / 不可照写` 必须避开。 + ### If / Then 路由 - If 问题在问“语言怎么写”,then 先从 [syntax/index.md](syntax/index.md) 开始。 @@ -57,17 +80,17 @@ ### 语言事实 - 可以先把 TSL 当成 Pascal 风格语言去理解:`function`、`begin`、`end`、`unit`、`uses` 都很接近;但这里只借外形,不默认继承 Pascal 的全部语义、库习惯和文件模型。 -- 涉及赋值、`function / procedure` 外形、`unit` 骨架、命名参数、`type Name = class`、数组 / 字符串下标这类高频硬规则,统一以 [syntax/02_quickstart.md](syntax/02_quickstart.md) 的“语言核心事实速查”为准;当前页只保留跨层路由所需的最小提醒。 +- 涉及赋值、`.tsl` 语句区 / 声明区、`.tsf` 模块、`function / procedure` 外形、`unit` 骨架、命名参数、`type Name = class`、数组 / 字符串下标这类高频硬规则,统一以 [syntax/02_quickstart.md](syntax/02_quickstart.md) 的“语言核心事实速查”为准;当前页只保留跨层路由所需的最小提醒。 #### 写代码前先记住 - 写代码前先把高频硬规则收口到 [syntax/02_quickstart.md](syntax/02_quickstart.md),不要分别从入口页、介绍页和文件模型页拼接结论。 -- 顶层主体仍优先按四类理解:松散语句、`function / procedure`、`type Name = class`、`unit`。 +- `.tsl` 仍优先按“语句区在前、声明区在后”的脚本模型理解;`.tsf` 仍优先按部署到 `funcext` 的模块/扩展理解。 - 模板、错误示例和输出片段不算可独立编译代码;真正落代码时优先看块级 `代码块身份`。 ### 手册建模规则 -- 更可靠的识别方式是看顶层内容,而不是只看文件扩展名。 +- 更可靠的识别方式是同时看任务目标和顶层内容,而不是只看文件扩展名。 - 顶层允许出现 `uses`,但这里只把它当辅助语句,不把它当主体声明。 - 下游大量 `program test; begin ... end.` 形式,只作为自包含验证样例外壳,不作为这里归纳的正式顶层模型。 @@ -108,4 +131,4 @@ 4. 当前页如果已经给出结论,先采用;准备编写时优先找已验证正例,再落代码;只有需要补充时再跳到相邻页。 5. 遇到“资料写法不一致”且偏较新写法、资料冲突或解释器差异时,先回 [syntax/index.md](syntax/index.md) 按主题跳到对应语法页。 6. 遇到“资料写法不一致”且偏高频误写、反例或负向边界时,先看 [syntax/12_pitfalls.md](syntax/12_pitfalls.md)。 -7. 只有当前手册没有给出结论时,才写最小 `.tsl` / `.tsf` 例子并用 `tsl` 实测。 +7. 如果当前手册没有给出结论,不要发明语法;改为向用户确认、记录文档缺口,或等待维护者用项目环境补充已验证结论。 diff --git a/docs/tsl/modules/index.md b/docs/tsl/modules/index.md index 8151f8c0..e6b57d21 100644 --- a/docs/tsl/modules/index.md +++ b/docs/tsl/modules/index.md @@ -2,14 +2,18 @@ 文档类型:检索页 是否可直接用于生成代码:否 -是否含已验证可执行示例:否 -是否含已验证反例:否 遇到不确定时跳转到:[tsbacktesting.md](tsbacktesting.md)、[tsl_python_interop.md](tsl_python_interop.md)、[../finance/index.md](../finance/index.md)、项目自身文档、`scripts/*` 入口脚本、CI 配置 这里处理“现成模块、外部集成和互操作”,不处理基础语法教学,也不替代金融业务主线。 模块摘要页只负责确认能力边界和选路,不负责给出项目账户体系、真实接口名、部署方式、权限模型或脚本入口的真值。 +## Agent Modules 路由规则 + +- 先判断任务是在问模块能力、业务流程、语法规则还是项目真实接入参数。 +- modules 只给集成边界和选路;真实账号、部署入口、权限模型和接口细节必须回项目文档。 +- 不要发明模块专属 API 参数、项目账号来源、部署路径或脚本入口。 + ## If / Then 路由 - If 问题在问“策略回测框架怎么组织、怎么取结果”,then 先读 [tsbacktesting.md](tsbacktesting.md)。 diff --git a/docs/tsl/modules/pytsl_api.md b/docs/tsl/modules/pytsl_api.md index d43fb788..a04cda69 100644 --- a/docs/tsl/modules/pytsl_api.md +++ b/docs/tsl/modules/pytsl_api.md @@ -2,14 +2,18 @@ 文档类型:模块摘要 是否可直接用于生成代码:仅部分 -是否含已验证可执行示例:否 -是否含已验证反例:否 遇到不确定时跳转到:项目级部署文档、官方 pyTSL 详细接口文档、[tsl_python_interop.md](tsl_python_interop.md)、[index.md](index.md) 本页用于确认 pyTSL 的接入方向和最小链路,不替代项目级部署文档或完整接口手册。 - 如果登录方式、凭证来源、环境变量、部署入口、连接上下文或返回结构没有确认,不继续生成接入代码,直接回项目级部署文档或官方 pyTSL 详细接口文档。 +## Agent pyTSL 边界规则 + +- 先确认项目是否已经给出 pyTSL 安装方式、登录方式、凭证来源和返回结构。 +- 本页只给 SDK 能力分类和最小链路,不替代官方接口手册。 +- 不要发明登录参数、连接上下文、查询语句来源或返回数据结构。 + ## 定位 - 官方 Python SDK,面向取数/执行/批量/异步与数据转换。 @@ -73,8 +77,7 @@ 示例里的 `"user"` / `"password"` 只表示调用外形,不代表项目里的真实登录方式或凭证来源。 -代码块身份:配置片段 / 概念骨架 -代码块说明:可参考最小链路,不是已验证可执行示例。 +下面代码只表示调用外形;真实登录方式、凭证和查询来源以项目文档为准。 ```python import pyTSL diff --git a/docs/tsl/modules/tsbacktesting.md b/docs/tsl/modules/tsbacktesting.md index 05d9d123..6ea6cbe5 100644 --- a/docs/tsl/modules/tsbacktesting.md +++ b/docs/tsl/modules/tsbacktesting.md @@ -2,14 +2,18 @@ 文档类型:模块摘要 是否可直接用于生成代码:否 -是否含已验证可执行示例:否 -是否含已验证反例:否 遇到不确定时跳转到:项目实际接口定义、[../finance/backtest_and_trade_flow.md](../finance/backtest_and_trade_flow.md)、[index.md](index.md) 本页不足以直接生成回测代码,只用于确认回测任务的组织顺序,以及哪些地方必须回到项目实际接口定义继续核对。 - 只要任务已经进入对象创建、交易输入入口、结果读取方法或项目封装差异,就先停止生成,直接回项目实际接口定义,不要先拼调用链。 +## Agent TSBackTesting 边界规则 + +- 只把本页用于识别回测框架任务顺序和常见字段类别。 +- 对象创建方式、最小必填字段、交易入口和结果接口都以项目实际接口定义为准。 +- 不要发明 `TSBackTesting` 构造方式、交易输入函数或结果读取调用链。 + ## 适用场景 - 任务已经进入“回测框架怎么配置、怎么执行、怎么读结果”。 diff --git a/docs/tsl/modules/tsl_python_interop.md b/docs/tsl/modules/tsl_python_interop.md index cad147aa..1e877727 100644 --- a/docs/tsl/modules/tsl_python_interop.md +++ b/docs/tsl/modules/tsl_python_interop.md @@ -2,14 +2,18 @@ 文档类型:模块摘要 是否可直接用于生成代码:否 -是否含已验证可执行示例:否 -是否含已验证反例:否 遇到不确定时跳转到:项目级部署文档、对应官方文档、[pytsl_api.md](pytsl_api.md)、[index.md](index.md) 本页用于接入决策和最小链路确认,不替代项目级部署文档。 - 如果登录方式、凭证来源、位数、环境变量、连接通道或部署入口没有确认,不继续生成接入代码,直接回项目级部署文档或对应官方文档。 +## Agent Python 互操作边界规则 + +- 先判断是 Python 调 TSL、TSL 调 Python,还是服务器侧 Python 服务。 +- 本页只给接入路径和接口类别;真实登录方式、位数、环境变量和部署入口必须回项目级部署文档。 +- 不要发明凭证来源、连接通道、Python 路径或服务部署方式。 + ## 摘要 - 覆盖三类交互:Python 调用 TSL、TSL 调用 Python、落地服务器开启 Python 服务。 diff --git a/docs/tsl/modules/wechat_message.md b/docs/tsl/modules/wechat_message.md index 84c68f02..b40c0bf3 100644 --- a/docs/tsl/modules/wechat_message.md +++ b/docs/tsl/modules/wechat_message.md @@ -2,12 +2,16 @@ 文档类型:模块摘要 是否可直接用于生成代码:仅部分 -是否含已验证可执行示例:否 -是否含已验证反例:否 遇到不确定时跳转到:调用侧账户体系文档、项目实际接口说明、[index.md](index.md) 本页用于确认接口名称、参数含义和风险边界,不替代项目级接入文档。 +## Agent 微信消息边界规则 + +- 先确认调用侧已经提供 `userid`、模板类型、关键字含义和必要的账户字段。 +- 本页只给接口外形和参数含义;账户体系、授权关系和模板映射以项目实际接口说明为准。 +- 不要发明 `username` 来源、模板关键字含义、项目封装函数或账户映射。 + ## 适用场景 - 通过“天软科技服务号”向微信客户端发送模板消息。 @@ -59,8 +63,7 @@ ## 发送示例(配置片段,可参考参数组织) -代码块身份:配置片段 / 概念骨架 -代码块说明:可参考参数组织,不是已验证可执行示例。 +下面代码只表示参数组织方式;真实账户、模板关键字和调用侧封装以项目实际接口说明为准。 ```tsl // 定义参数 diff --git a/docs/tsl/naming.md b/docs/tsl/naming.md index 4134fa27..8662df6c 100644 --- a/docs/tsl/naming.md +++ b/docs/tsl/naming.md @@ -50,8 +50,7 @@ Guide 对齐:通过名字的“形状”快速判断实体类型(类型/函 ## 3. 类型命名(Type Names) -AI 先按四类顶层外形判断文件模型:顶层松散语句、顶层 `function / procedure`、 -顶层 `type Name = class`、顶层 `unit`。本页只覆盖“需要命名的顶层实体”,不覆盖松散语句本身;文件模型判断本身以 +AI 先按 `.tsl` 可执行脚本与 `.tsf` 模块/函数扩展判断文件模型。`.tsl` 里的语句区按顺序执行,函数/类声明区放在语句区之后;`.tsf` 用于可复用顶层声明并部署到解释器 `funcext`。本页只覆盖“需要命名的顶层实体”,不覆盖脚本语句本身;文件模型判断本身以 `docs/tsl/syntax/03_core_model.md` 为准。 - **类与单元**使用 @@ -76,7 +75,7 @@ TSL 的语法要求(仅 `.tsf`):每个 `.tsf` `function / procedure`、`type Name = class`、`unit`,文件基名需与之同名。 - `.tsl` 脚本文件:用于入口/编排层;允许直接写语句(如 `a := 1; echo a;`),也可能出现顶层 `function / procedure` 骨架或 `program test;` - 这类验证样例外壳;但风格上不把 `.tsl` 当成可复用顶层声明的默认落点,也不要求文件基名与函数名一致;可复用逻辑优先下沉到 + 这类验证样例外壳;如果同时出现脚本语句和函数/类声明,语句区在前,声明区在后;但风格上不把 `.tsl` 当成可复用顶层声明的默认落点,也不要求文件基名与函数名一致;可复用逻辑优先下沉到 `.tsf`(见 `docs/tsl/code_style.md`)。 - 注:`.tsf` 也是 TSL 源文件,命名/风格与 `.tsl` 遵循同一套规则。 - **硬规则(仅 diff --git a/docs/tsl/reference/catalog/base.md b/docs/tsl/reference/catalog/base.md index 8f82b7fc..e9de4685 100644 --- a/docs/tsl/reference/catalog/base.md +++ b/docs/tsl/reference/catalog/base.md @@ -2,6 +2,12 @@ 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 +## 候选函数索引说明 + +- 本页是候选函数索引,只说明函数名被归入当前模块。 +- 候选名没有进入 verified 函数页前不能当成可调用事实。 +- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。 + ## 使用方式 - 返回总目录:[catalog/index.md](index.md) diff --git a/docs/tsl/reference/catalog/client.md b/docs/tsl/reference/catalog/client.md index 5cbfab16..a066fe92 100644 --- a/docs/tsl/reference/catalog/client.md +++ b/docs/tsl/reference/catalog/client.md @@ -2,6 +2,12 @@ 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 +## 候选函数索引说明 + +- 本页是候选函数索引,只说明函数名被归入当前模块。 +- 候选名没有进入 verified 函数页前不能当成可调用事实。 +- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。 + ## 使用方式 - 返回总目录:[catalog/index.md](index.md) diff --git a/docs/tsl/reference/catalog/compression.md b/docs/tsl/reference/catalog/compression.md index 87370bdf..0e5516f2 100644 --- a/docs/tsl/reference/catalog/compression.md +++ b/docs/tsl/reference/catalog/compression.md @@ -2,6 +2,12 @@ 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 +## 候选函数索引说明 + +- 本页是候选函数索引,只说明函数名被归入当前模块。 +- 候选名没有进入 verified 函数页前不能当成可调用事实。 +- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。 + ## 使用方式 - 返回总目录:[catalog/index.md](index.md) diff --git a/docs/tsl/reference/catalog/digest_encoding.md b/docs/tsl/reference/catalog/digest_encoding.md index f8337dc0..3697241d 100644 --- a/docs/tsl/reference/catalog/digest_encoding.md +++ b/docs/tsl/reference/catalog/digest_encoding.md @@ -2,6 +2,12 @@ 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 +## 候选函数索引说明 + +- 本页是候选函数索引,只说明函数名被归入当前模块。 +- 候选名没有进入 verified 函数页前不能当成可调用事实。 +- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。 + ## 使用方式 - 返回总目录:[catalog/index.md](index.md) diff --git a/docs/tsl/reference/catalog/graphics.md b/docs/tsl/reference/catalog/graphics.md index 9526f7b2..b710aa55 100644 --- a/docs/tsl/reference/catalog/graphics.md +++ b/docs/tsl/reference/catalog/graphics.md @@ -2,6 +2,12 @@ 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 +## 候选函数索引说明 + +- 本页是候选函数索引,只说明函数名被归入当前模块。 +- 候选名没有进入 verified 函数页前不能当成可调用事实。 +- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。 + ## 使用方式 - 返回总目录:[catalog/index.md](index.md) diff --git a/docs/tsl/reference/catalog/index.md b/docs/tsl/reference/catalog/index.md index 66f455f0..f24a0923 100644 --- a/docs/tsl/reference/catalog/index.md +++ b/docs/tsl/reference/catalog/index.md @@ -1,13 +1,17 @@ # Function Catalog -这里是 canonical 函数目录。它只回答“函数在哪个模块里”,不承担基础语法教学。 +这里是候选函数索引。它只回答“函数名可能在哪个模块里”,不承担基础语法教学,也不证明函数在当前环境可用。 + +候选名没有进入 verified 函数页前不能当成可调用事实。agent 真正生成代码前,只从 verified 函数页读取参数类型。 ## 使用顺序 1. 不知道函数在哪个模块,先看下面的模块目录。 -2. 进入模块页后,在页内搜索具体函数名。 -3. 如果问题是语法怎么写,回到 [../../syntax/index.md](../../syntax/index.md)。 -4. 如果问题是金融场景如何组织,回到 [../../finance/index.md](../../finance/index.md)。 +2. 进入模块页后,在页内搜索具体候选函数名。 +3. 如果要生成代码,先看 [../verified/index.md](../verified/index.md),再进入具体函数页;当前核心函数页是 [../verified/core.md](../verified/core.md)。 +4. 如果候选函数没有进入 verified 函数页,不要生成调用代码。 +5. 如果问题是语法怎么写,回到 [../../syntax/index.md](../../syntax/index.md)。 +6. 如果问题是金融场景如何组织,回到 [../../finance/index.md](../../finance/index.md)。 ## 模块目录 @@ -27,4 +31,11 @@ ## 说明 - 这套目录页由仓库内的函数语料自动整理生成。 -- 当前目标是先提供稳定检索层,再逐步补全更细的 canonical 说明。 +- catalog 只保留候选函数索引,不写参数矩阵,不写可调用结论。 +- 当前目标是先提供稳定检索层,再逐步补全 agent 可直接读取的参数事实。 + +## 候选函数索引说明 + +- 本页是候选函数索引,只说明函数名被归入当前模块。 +- 候选名没有进入 verified 函数页前不能当成可调用事实。 +- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。 diff --git a/docs/tsl/reference/catalog/math.md b/docs/tsl/reference/catalog/math.md index 468738c7..253d84e4 100644 --- a/docs/tsl/reference/catalog/math.md +++ b/docs/tsl/reference/catalog/math.md @@ -2,6 +2,12 @@ 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 +## 候选函数索引说明 + +- 本页是候选函数索引,只说明函数名被归入当前模块。 +- 候选名没有进入 verified 函数页前不能当成可调用事实。 +- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。 + ## 使用方式 - 返回总目录:[catalog/index.md](index.md) diff --git a/docs/tsl/reference/catalog/platform.md b/docs/tsl/reference/catalog/platform.md index be3e8566..57331050 100644 --- a/docs/tsl/reference/catalog/platform.md +++ b/docs/tsl/reference/catalog/platform.md @@ -2,6 +2,12 @@ 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 +## 候选函数索引说明 + +- 本页是候选函数索引,只说明函数名被归入当前模块。 +- 候选名没有进入 verified 函数页前不能当成可调用事实。 +- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。 + ## 使用方式 - 返回总目录:[catalog/index.md](index.md) diff --git a/docs/tsl/reference/catalog/resource.md b/docs/tsl/reference/catalog/resource.md index 3f5d5338..8e00eafd 100644 --- a/docs/tsl/reference/catalog/resource.md +++ b/docs/tsl/reference/catalog/resource.md @@ -2,6 +2,12 @@ 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 +## 候选函数索引说明 + +- 本页是候选函数索引,只说明函数名被归入当前模块。 +- 候选名没有进入 verified 函数页前不能当成可调用事实。 +- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。 + ## 使用方式 - 返回总目录:[catalog/index.md](index.md) diff --git a/docs/tsl/reference/catalog/system.md b/docs/tsl/reference/catalog/system.md index a56b0f79..cba84210 100644 --- a/docs/tsl/reference/catalog/system.md +++ b/docs/tsl/reference/catalog/system.md @@ -2,6 +2,12 @@ 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 +## 候选函数索引说明 + +- 本页是候选函数索引,只说明函数名被归入当前模块。 +- 候选名没有进入 verified 函数页前不能当成可调用事实。 +- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。 + ## 使用方式 - 返回总目录:[catalog/index.md](index.md) diff --git a/docs/tsl/reference/catalog/third_party.md b/docs/tsl/reference/catalog/third_party.md index 90db5b35..ece7b2c9 100644 --- a/docs/tsl/reference/catalog/third_party.md +++ b/docs/tsl/reference/catalog/third_party.md @@ -2,6 +2,12 @@ 这一页只负责函数定位:先按主题找到模块,再在页内搜索函数名。 +## 候选函数索引说明 + +- 本页是候选函数索引,只说明函数名被归入当前模块。 +- 候选名没有进入 verified 函数页前不能当成可调用事实。 +- 生成代码前必须先查 [../verified/index.md](../verified/index.md);只从 verified 函数页读取参数类型。 + ## 使用方式 - 返回总目录:[catalog/index.md](index.md) diff --git a/docs/tsl/reference/index.md b/docs/tsl/reference/index.md index 04487139..5b26c763 100644 --- a/docs/tsl/reference/index.md +++ b/docs/tsl/reference/index.md @@ -2,33 +2,37 @@ 文档类型:检索页 是否可直接用于生成代码:否 -是否含已验证可执行示例:否 -是否含已验证反例:否 遇到不确定时跳转到:[catalog/index.md](catalog/index.md)、[../syntax/index.md](../syntax/index.md)、[../finance/index.md](../finance/index.md) 这里是函数查阅层,不是默认通读入口。现在这一页本身就是函数总入口,不再要求先跳到额外的中转页。 -## 先看这 5 条 +reference 的目标是确认每个方法的参数类型,让 agent 写代码时知道该传什么。文档只保留 agent 生成代码需要的函数事实。 -- 这里用于函数查找,不用于建立基础语法模型。 -- 不要把 reference 当成默认通读入口;如果任务就是查函数,可以直接从这里开始。 -- 函数库规模很大,应先走索引,再做定向检索。 -- 如果目录和定向检索都找不到函数,不要默认它是 TSL 内建函数;先回语法层、业务层、模块页或项目文档确认来源。 -- 如果你还在问“语言怎么写”,先回 [../syntax/index.md](../syntax/index.md)。 +## Agent 函数使用规则 + +- 只从 verified 函数页读取参数类型、返回值和调用约束。 +- catalog 只是候选函数索引,不能把 catalog 里的函数名直接当成可调用事实。 +- 函数没有进入 verified 函数页时,不要根据函数名猜参数,也不要生成调用代码。 +- 函数参数必须按正确类型传入;TSL 本身弱类型,不等于函数参数无类型。 +- 如果函数被记录为当前测试环境不支持,不要使用该函数生成代码。 ## 检索策略 -1. 先看 [catalog/index.md](catalog/index.md) 的模块目录,确定大类。 -2. 进入对应模块页,在页内搜索具体函数名。 -3. 仍然不确定时,再用 `rg` 对 `docs/tsl/reference/catalog/` 做定向搜索。 -4. 如果目录和定向检索都找不到函数,不要发明函数名,也不要默认它是 TSL 内建函数;回 [../syntax/index.md](../syntax/index.md)、[../finance/index.md](../finance/index.md)、[../modules/index.md](../modules/index.md) 或项目文档确认来源。 +1. 先看 [catalog/index.md](catalog/index.md) 的模块目录,确定候选大类。 +2. 进入对应模块页,在页内搜索具体候选函数名。 +3. 先看 [verified/index.md](verified/index.md),再进入具体 verified 函数页读取参数类型、返回值和调用约束;当前核心函数页是 [verified/core.md](verified/core.md)。 +4. 如果候选函数没有进入 verified 函数页,不要生成调用代码。 +5. 当前测试环境不支持的方法见 [unavailable_methods.md](unavailable_methods.md)。 +6. 如果目录和定向检索都找不到函数,不要发明函数名,也不要默认它是 TSL 内建函数;回 [../syntax/index.md](../syntax/index.md)、[../finance/index.md](../finance/index.md)、[../modules/index.md](../modules/index.md) 或项目文档确认来源。 ## 按任务跳转 -| 当前任务 | 先读哪里 | -| ---------------- | ------------------------------------------ | -| 查函数库入口 | [catalog/index.md](catalog/index.md) | -| 直接按模块查函数 | [catalog/index.md](catalog/index.md) | -| 回到语法层 | [../syntax/index.md](../syntax/index.md) | -| 回到金融层 | [../finance/index.md](../finance/index.md) | -| 回到模块层 | [../modules/index.md](../modules/index.md) | +| 当前任务 | 先读哪里 | +| ------------------------ | ------------------------------------------------ | +| 查函数库入口 | [catalog/index.md](catalog/index.md) | +| 直接按模块查函数 | [catalog/index.md](catalog/index.md) | +| 查可调用函数参数类型 | [verified/index.md](verified/index.md) | +| 查当前测试环境不支持方法 | [unavailable_methods.md](unavailable_methods.md) | +| 回到语法层 | [../syntax/index.md](../syntax/index.md) | +| 回到金融层 | [../finance/index.md](../finance/index.md) | +| 回到模块层 | [../modules/index.md](../modules/index.md) | diff --git a/docs/tsl/reference/unavailable_methods.md b/docs/tsl/reference/unavailable_methods.md new file mode 100644 index 00000000..de2305e8 --- /dev/null +++ b/docs/tsl/reference/unavailable_methods.md @@ -0,0 +1,11 @@ +# Reference Unavailable Methods + +文档类型:当前测试环境不支持的方法清单 +是否可直接用于生成代码:否 +遇到不确定时跳转到:[verified/core.md](verified/core.md)、[catalog/index.md](catalog/index.md) + +这里只记录方法不存在,或当前测试环境暂不支持的方法。参数类型事实写在 verified 函数页。 + +## 当前测试环境不支持的方法 + +暂无已入档记录。 diff --git a/docs/tsl/reference/verified/core.md b/docs/tsl/reference/verified/core.md new file mode 100644 index 00000000..69641f20 --- /dev/null +++ b/docs/tsl/reference/verified/core.md @@ -0,0 +1,49 @@ +# Verified Core Reference Functions + +文档类型:agent 参数事实表 +是否可直接用于生成代码:是 +遇到不确定时跳转到:[../catalog/index.md](../catalog/index.md)、[../../syntax/index.md](../../syntax/index.md) + +本页只保留 agent 生成代码需要的函数事实:签名、接收类型、返回值和调用约束。 + +## 使用规则 + +- 只按“接收类型”传参。 +- TSL 是弱类型语言,但函数参数不是任意类型。 +- catalog 同名函数没有出现在 verified 页时,不要当作可调用事实。 + +## 函数参数 + +### `Abs(value)` + +用途:返回数值绝对值。 + +| 参数 | 接收类型 | 返回 | 说明 | +| ------- | -------- | ---- | ---------- | +| `value` | 整数 | 数值 | 返回绝对值 | +| `value` | 实数 | 数值 | 返回绝对值 | + +### `ifInt(value)` + +用途:判断值当前是否按整数处理,返回 `1` 或 `0`。 + +| 参数 | 接收类型 | 返回 | 说明 | +| ------- | --------------------------------------- | ---- | ---------------------- | +| `value` | 任意值;已记录整数、实数、字符串、`nil` | 整数 | 整数返回 `1`,否则 `0` | + +### `DateToStr(value)` + +用途:把日期时间值转成日期字符串。 + +| 参数 | 接收类型 | 返回 | 说明 | +| ------- | -------- | ------ | ------------------------------ | +| `value` | 日期时间 | 字符串 | 日期时间字面量可写 `20111231T` | + +### `Length(value)` + +用途:返回字符串或数组长度。 + +| 参数 | 接收类型 | 返回 | 说明 | +| ------- | -------- | ---- | ------------ | +| `value` | 字符串 | 整数 | 返回字符长度 | +| `value` | 数组 | 整数 | 返回元素个数 | diff --git a/docs/tsl/reference/verified/index.md b/docs/tsl/reference/verified/index.md new file mode 100644 index 00000000..35a655f8 --- /dev/null +++ b/docs/tsl/reference/verified/index.md @@ -0,0 +1,19 @@ +# Verified Reference Functions + +文档类型:agent 参数事实索引 +是否可直接用于生成代码:否 +遇到不确定时跳转到:[../catalog/index.md](../catalog/index.md)、[../../syntax/index.md](../../syntax/index.md) + +本页只负责定位已经整理成参数事实表的函数页。生成调用代码时,进入具体函数页读取签名、接收类型、返回值和调用约束。 + +## 使用规则 + +- 只从具体函数页读取接收类型和返回值。 +- catalog 里的候选名没有出现在具体函数页时,不要当作可调用事实。 +- 不根据函数名猜参数。 + +## 参数事实页 + +| 范围 | 页面 | 当前函数 | +| -------- | ------------------ | ------------------------------------- | +| 核心函数 | [core.md](core.md) | `Abs`、`ifInt`、`DateToStr`、`Length` | diff --git a/docs/tsl/syntax/01_introduction.md b/docs/tsl/syntax/01_introduction.md index a9fcd529..30645c58 100644 --- a/docs/tsl/syntax/01_introduction.md +++ b/docs/tsl/syntax/01_introduction.md @@ -8,47 +8,61 @@ 手册位置:第 1 篇,共 32 篇。上一篇:手册入口 [index.md](index.md)。下一篇:[02_quickstart.md](02_quickstart.md)。 -这一篇只做一件事:让新 session 在看到 TSL 时,先用正确的读法建立心智模型。 +这一篇只做一件事:给 agent 建立 TSL 的第一判断模型。它不是人类教程,不负责展开所有语法细节;它只告诉 agent 第一次看到 TSL 任务时先判断什么、按什么证据生成代码、哪些直觉不能继承。 ## 这一篇解决什么问题 -回答“第一次读 TSL 时,应该先把它当成什么来理解,以及最容易和别的语言混淆的差异是什么”。 +回答“agent 第一次读到 TSL 需求时,怎样先识别交付形态,再选择最小可靠代码结构”。 -涉及赋值、命名参数、类外形、`unit` 骨架、数组 / 字符串下标这类高频硬规则时,统一以 [02_quickstart.md](02_quickstart.md) 的“语言核心事实速查”为准;这一页只负责建立第一印象。 +涉及赋值、命名参数、类外形、`unit` 骨架、数组 / 字符串下标这类高频硬规则时,统一以 [02_quickstart.md](02_quickstart.md) 的“语言核心事实速查”为准;这一页只负责建立第一判断模型。 -## 先用什么方式读 TSL +## Agent 第一判断流程 -- 可以先按 Pascal 风格语言去读它:常见外形是 `function ... begin ... end;`、`unit ... interface ... implementation ... end.`、`type Name = class ... end;`;但这里只借外形,不默认继承 Pascal 的全部语义、库习惯和文件模型。 -- 先看顶层主体,再看细节;不要先被文件扩展名带偏。 -- 当前手册把顶层主体优先收敛成四类:松散语句、顶层 `function / procedure`、顶层 `type Name = class`、顶层 `unit`。 -- 顶层 `uses` 可以出现,但这里只把它当成辅助组织语句,不把它当成主体声明。 +1. 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据。 +2. 用户未给后缀时,先看交付目标:可执行代码对应 `.tsl`,通用模块对应 `.tsf`。 +3. 交付目标仍不明确时,向用户确认;不要替用户发明文件形态。 +4. 写 `.tsl` 时,先生成会顺序执行的语句区;如果需要函数或类,把声明区放在语句区之后。 +5. 写 `.tsf` 时,只生成模块 / 函数扩展代码;部署后的顶层函数可被 `.tsl` 脚本直接调用。 +6. 没有文档证据时不要发明语法;只能回到对应专题页、反例页、项目规则,或记录文档缺口。 + +## 先用什么方式理解 TSL + +- TSL 可以借 Pascal 风格外形理解:`function ... begin ... end;`、`unit ... interface ... implementation ... end.`、`type Name = class ... end;` 都很接近。 +- 这里只借外形,不默认继承 Pascal 的全部语义、库习惯和文件模型。 +- `.tsl` / `.tsf` 首先是用户交付形态:`.tsl` 是可执行脚本,`.tsf` 是通用模块 / 函数扩展。 - 日常赋值先记 `:=`,不要把 `=` 当成普通赋值。 -## 第一次写时最容易混淆的差异 - -- `array(...)` 既可以当顺序数组,也可以当字符串键表。 -- 数组下标从 `0` 开始,字符串下标从 `1` 开始。 -- 顶层类定义按 `type Name = class ... end;` 去写,不使用裸 `class Name`。 -- 调用函数时,命名参数写成 `name: value`,例如 `Demo(a: 1, b: 2)`。 - ## 已验证的第一印象 -最常见的 `function / procedure` 外形: +`.tsl` 可执行脚本第一印象: 代码块身份:已验证可执行示例 ```tsl -function Demo(); -begin - return 1; -end; +a := 1; +test(); -procedure LogDemo(msg); +function test(); begin - WriteLn(msg); + echo "test"; end; ``` +代码块说明:`.tsl` 的语句区在前,按顺序执行;函数声明区在后,供前面的脚本语句调用。 + +`.tsf` 通用模块第一印象: + +代码块身份:已验证可执行示例 + +```tsl +function Test1(); +begin + echo "test1"; +end; +``` + +代码块说明:这个 `.tsf` 部署到解释器 `funcext` 后,`.tsl` 脚本可以直接调用 `Test1();`。文件部署方式属于项目执行层,不写进通用语法页。 + 赋值、数组、字符串下标的第一印象: 代码块身份:已验证可执行示例 @@ -56,43 +70,25 @@ end; ```tsl items := array(10, 20, 30); table_data := array("Code": "000001", "Name": "Demo"); -first_item := items[0]; -first_char := "ABC"[1]; +echo items[0]; +echo table_data["Code"]; +echo "ABC"[1]; ``` -类的第一印象: +代码块说明:数组下标从 `0` 开始,字符串下标从 `1` 开始;更完整规则见 [04_values_and_literals.md](04_values_and_literals.md)。 -代码块身份:已验证可执行示例 +## 第一次写时最容易混淆的边界 -```tsl -type DemoType = class -end; -``` - -`unit` 的第一印象: - -代码块身份:已验证可执行示例 - -```tsl -unit DemoUnit; -interface - -function Demo(); - -implementation - -function Demo(); -begin - return 1; -end; - -end. -``` +- 不要把 `.tsl` / `.tsf` 当成 agent 自行选择的纯语法分支;后缀和用户交付目标优先。 +- 不要把 Pascal 外形直接泛化成 Pascal 文件模型。 +- 不要在 `.tsl` 的函数/类声明区之后继续追加脚本语句。 +- 不要把未验证写法写成语法事实;agent 生成代码时只模仿带证据标签的代码块。 ## 下一步怎么读 - 需要立刻开始写代码:看 [02_quickstart.md](02_quickstart.md) -- 需要判断当前文件属于哪一种顶层模型:看 [03_core_model.md](03_core_model.md) +- 需要判断当前文件组织方式:看 [03_core_model.md](03_core_model.md) - 需要先写值、数组、字符串:看 [04_values_and_literals.md](04_values_and_literals.md) - 需要先写变量、常量:看 [05_variables_and_constants.md](05_variables_and_constants.md) - 需要先写函数和调用:看 [06_functions_and_calls.md](06_functions_and_calls.md) +- 需要先避开高频误写:看 [12_pitfalls.md](12_pitfalls.md) diff --git a/docs/tsl/syntax/02_quickstart.md b/docs/tsl/syntax/02_quickstart.md index dfe7fad5..1cf72858 100644 --- a/docs/tsl/syntax/02_quickstart.md +++ b/docs/tsl/syntax/02_quickstart.md @@ -8,19 +8,31 @@ 手册位置:第 2 篇,共 32 篇。上一篇:[01_introduction.md](01_introduction.md)。下一篇:[03_core_model.md](03_core_model.md)。 -这一篇集中回答两个紧邻问题:现在要写 TSL 时,应该从哪一种最短骨架起手;以及落代码前必须先核对哪些语言硬规则。 +这一篇集中回答两个紧邻问题:用户已给出 `.tsl` / `.tsf` 后缀时,agent 应该如何按后缀组织代码;以及落代码前必须先核对哪些语言硬规则。 ## 这一篇解决什么问题 -快速回答“当前任务应该从松散语句、函数、类还是 unit 开始”,并提供一份单点的语言核心事实速查。 +快速回答“当前任务已经给出后缀或交付目标时,应该使用哪一种骨架”,并提供一份单点的语言核心事实速查。 + +## Agent 快速落代码流程 + +1. 先看用户有没有指定 `.tsl` / `.tsf` 后缀;指定后缀时,后缀就是文件形态判断依据。 +2. 用户未指定后缀时,再根据交付目标判断 `.tsl` 或 `.tsf`:可执行脚本用 `.tsl`,通用模块 / 函数扩展用 `.tsf`。 +3. 只从 `代码块身份:已验证可执行示例` 的骨架起手;遇到 `反例 / 不可照写` 必须避开。 +4. 写 `.tsl` 时,先写会执行的语句区;需要函数或类时,把声明区放在语句区之后。 +5. 写 `.tsf` 时,只写模块 / 函数扩展内容;部署、查找路径和解释器环境属于项目执行层。 +6. 当前页和对应专题页没有覆盖的写法,不要发明语法;改为跳转补证、向用户确认,或记录文档缺口。 ## 语言核心事实速查 -这一节是当前语法手册默认的语言硬规则收口点。涉及赋值、顶层外形、命名参数、类写法、`unit` 骨架和下标规则时,统一先看这里。 +这一节是当前语法手册默认的语言硬规则收口点。涉及赋值、`.tsl` 语句区 / 声明区、`.tsf` 模块、命名参数、类写法、`unit` 骨架和下标规则时,统一先看这里。 - 普通赋值用 `:=`,不要把 `=` 当成普通赋值。 -- 顶层主体先按四类理解:松散语句、`function / procedure`、`type Name = class`、`unit`。 -- 如果当前文件采用顶层 `function / procedure` 模型,就不要再混入松散语句。 +- 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据;用户未给后缀时,再按交付目标判断。 +- 未给后缀时,可执行代码对应 `.tsl`,通用模块对应 `.tsf`;仍不明确时向用户确认。 +- `.tsl` 是可执行脚本:语句区在前并按顺序执行;函数/类声明区在后,供前面的语句调用或运行时解析。 +- `.tsf` 是模块/函数扩展文件:部署到解释器 `funcext` 后,脚本可以直接调用其中暴露的顶层函数。 +- 在 `.tsl` 中,如果需要函数或类,先写会执行的语句区,再写函数/类声明区;不要在声明区后面继续追加脚本语句。 - 无返回值时用 `procedure Name(...); begin ... end;`,不要勉强用 `function`。 - 顶层类定义统一写成 `type Name = class ... end;`,不要写裸 `class Name`。 - 多文件组织默认先按 `unit Name; interface ... implementation ... end.` 理解。 @@ -29,26 +41,30 @@ ## 术语对照 -- 文档里出现的“顶层 `function / procedure`”“顶层函数骨架”“顶层函数定义体”,指的是同一类顶层模型:文件以顶层 `function` / `procedure` 为主体。 +- 文档里出现的“脚本语句区”,指 `.tsl` 文件开头会按顺序执行的语句。 +- 文档里出现的“声明区”,指 `.tsl` 语句区之后的 `function / procedure` 或 `type Name = class` 声明。 +- 文档里出现的“顶层 `function / procedure`”“顶层函数骨架”“顶层函数定义体”,在 `.tsf` 中指模块暴露的顶层函数,在 `.tsl` 中指脚本声明区里的函数。 - 文档里出现的 `class function` 和“类方法”,指的是同一件事:前者是代码关键字写法,后者是中文描述。 ## 先选哪一种骨架 -| 当前任务 | 起手骨架 | -| ------------------------------ | ------------------- | -| 只写一段一次性脚本逻辑 | 顶层松散语句 | -| 先沉淀一个可复用逻辑块 | 顶层 `function` | -| 需要对象状态、字段、方法 | `type Name = class` | -| 需要把接口和实现组织进一个模块 | `unit` | +| 当前任务 | 起手骨架 | +| -------------------------------- | --------------------------------- | +| 写一次性可执行逻辑 | `.tsl` 脚本语句区 | +| 脚本逻辑需要调用本文件内函数 | `.tsl` 语句区 + 后置函数声明区 | +| 脚本逻辑需要对象状态、字段、方法 | `.tsl` 语句区 + 后置类声明区 | +| 沉淀可复用函数并给脚本直接调用 | `.tsf` 顶层函数,部署到 `funcext` | +| 需要把接口和实现组织进一个模块 | `.tsf` `unit` | 默认建议: -- 如果你只是要让新 session 先写出一段最稳、最容易续写的基础语法,优先从顶层 `function` 开始。 +- 如果你只是要让 agent 先写出一段最稳、最容易续写的可执行代码,优先从 `.tsl` 脚本语句区开始。 +- 不要把 `unit` 当成最小起手骨架;只有用户明确要模块接口 / 实现组织,或项目已有 `unit` 边界时,才进入 `unit` 写法。 - `uses` 往往天然进入多文件查找路径问题,所以不放进这篇的最小起手骨架里。 ## 已验证最小骨架 -顶层松散语句骨架: +`.tsl` 脚本语句区骨架: 代码块身份:已验证可执行示例 @@ -56,27 +72,66 @@ a := 1; ``` -顶层函数骨架: +`.tsl` 语句区调用后置函数声明: 代码块身份:已验证可执行示例 ```tsl -function Demo(); +a := 1; +test(); + +function test(); begin - return 1; + echo "test"; end; ``` -顶层类骨架: +代码块身份:已验证输出片段 + +```text +test +``` + +`.tsl` 语句区调用后置类声明: 代码块身份:已验证可执行示例 ```tsl -type DemoType = class +obj := CreateObject("MyClass"); +obj.value := 5; +echo obj.value; + +type MyClass = class + value; end; ``` -顶层 `unit` 骨架: +代码块身份:已验证输出片段 + +```text +5 +``` + +`.tsf` 顶层函数骨架: + +代码块身份:已验证可执行示例 + +```tsl +function Test1(); +begin + echo "test1"; +end; +``` + +代码块说明:这个 `.tsf` 部署到解释器 `funcext` 后,`.tsl` 脚本可以直接调用 `Test1();`。部署方式属于项目执行层,不写进通用语法页。 + +代码块身份:已验证输出片段 + +```text +test1 +``` + +`.tsf` `unit` 骨架: 代码块身份:已验证可执行示例 @@ -93,35 +148,50 @@ implementation end. ``` +代码块说明:这个 `.tsf` 骨架用于模块接口 / 实现组织;已通过 `.tsl` 脚本 `uses DemoUnit` 调用 `Ping()` 验证返回值为 `1`。调用脚本和查找路径边界见 [10_units_and_scope.md](10_units_and_scope.md)。 + ## 最常用起手版本 -如果你现在没有明确的多文件或对象建模需求,直接从函数版本开始: +如果你现在没有明确的模块复用或对象建模需求,直接从 `.tsl` 脚本版本开始: 代码块身份:已验证可执行示例 ```tsl -function Hello(); -begin - return 1; -end; +echo "hello"; +``` + +代码块身份:已验证输出片段 + +```text +hello ``` ## 最容易写错的一件事 -- 不要把“顶层函数定义”和“顶层松散语句”混写在同一个最小文件里。 +- `.tsl` 可以同时有语句区和声明区;真正要避免的是在声明区后面继续追加脚本语句。 代码块身份:反例 / 不可照写 ```text -function Add(a, b); +a := 1; +test(); + +function test(); begin - return a + b; + echo "test"; end; -value := Add(1, 2); +echo "after declaration"; ``` -上面这种“先定义顶层函数,再接松散语句”的混合写法会编译失败。 +上面最后一行属于“声明区之后继续写脚本语句”。写 `.tsl` 时先把会执行的语句放在前面,再把函数/类声明放在后面。 + +代码块身份:已验证输出片段 + +```text +Execute script error at Line:9 +function:__main__:line 9: invalid statement +``` ## 跳转指引 diff --git a/docs/tsl/syntax/03_core_model.md b/docs/tsl/syntax/03_core_model.md index 5b3db036..fecefce1 100644 --- a/docs/tsl/syntax/03_core_model.md +++ b/docs/tsl/syntax/03_core_model.md @@ -8,26 +8,87 @@ 手册位置:第 3 篇,共 32 篇。上一篇:[02_quickstart.md](02_quickstart.md)。下一篇:[04_values_and_literals.md](04_values_and_literals.md)。 -这一篇说明 TSL 的文件模型判断规则:顶层主体是什么、辅助语句是什么、为什么很多错误其实是“文件模型选错了”。 +这一篇说明 TSL 的文件模型判断规则:agent 如何区分 `.tsl` 可执行脚本与 `.tsf` 模块,如何识别脚本语句区和声明区,以及为什么很多错误其实是“文件模型选错了”。 ## 这一篇解决什么问题 -回答“当前文件到底属于哪一种顶层写法,以及哪些语句只是辅助组织,不应该被误当成主体声明”。 +回答“当前文件到底是 `.tsl` 脚本还是 `.tsf` 模块,以及 `.tsl` 里的哪些内容会顺序执行、哪些内容只是后置声明”。 如果问题已经变成赋值、命名参数、类外形、`unit` 骨架或下标规则这类通用硬规则,统一回 [02_quickstart.md](02_quickstart.md) 的“语言核心事实速查”;这一页只处理文件模型判断。 +## Agent 文件模型判断流程 + +1. 后缀是第一证据:用户明确要求 `.tsl` 时,按可执行脚本写;用户明确要求 `.tsf` 时,按模块 / 函数扩展写。 +2. 没有后缀时看交付目标:可执行交付对应 `.tsl`,可复用扩展交付对应 `.tsf`。 +3. 目标仍不明确时先问用户;不要把脚本入口和通用模块替用户合并成一个猜测文件。 +4. 写 `.tsl` 时,先生成脚本语句区;需要函数、过程或类时,把声明区放在语句区之后。 +5. 写 `.tsf` 时,生成顶层函数 / 过程或 `unit`;不要写成会直接顺序执行的脚本入口。 +6. 没有文档证据时不要发明文件模型;只能回到对应专题页、项目规则,或记录文档缺口。 + ## 必须记住的规则 -- 比起扩展名,更可靠的判断方式是看顶层内容。 -- 顶层主体在当前手册里优先按四种外形理解:顶层松散语句、`function / procedure`、`type Name = class`、`unit`。 +- 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据;未给后缀时,再按交付目标判断。 +- 未给后缀时,可执行代码对应 `.tsl`,通用模块对应 `.tsf`;仍不明确时向用户确认。 +- `.tsl` 脚本按两段理解:语句区在前并按顺序执行;声明区在后,可放 `function / procedure` 或 `type Name = class`。 +- `.tsf` 模块按可部署扩展理解:顶层函数部署到解释器 `funcext` 后,`.tsl` 可以直接调用;`unit` 仍按模块组织理解。 - `uses` 可以出现在顶层,但这里只把它当成辅助语句,不把它当成主体声明;函数体和类定义体里的位置限制见 [10_units_and_scope.md](10_units_and_scope.md)。 - 裸 `class Name` 不作为类定义写法使用。 -- 如果一个文件已经采用“顶层 `function / procedure`”模型,就不要再混入松散语句。 +- 在 `.tsl` 中,不要在声明区之后继续追加脚本语句。 - `unit` 默认先按完整形态理解;它也可以省略 `interface` / `implementation` 写成简写形态,见 [10_units_and_scope.md](10_units_and_scope.md)。 +- 不要把 `.tsl` 写成只有顶层函数的模块;如果用户要通用可复用函数,优先写 `.tsf`。 +- 不要把 `.tsf` 写成会直接执行脚本语句的入口;如果用户要顺序执行入口,优先写 `.tsl`。 ## 已验证语法 -顶层主体的最小形态: +`.tsl` 脚本语句区的最小形态: + +代码块身份:已验证可执行示例 + +```tsl +a := 1; +``` + +`.tsl` 语句区后接函数声明区: + +代码块身份:已验证可执行示例 + +```tsl +a := 1; +test(); + +function test(); +begin + echo "test"; +end; +``` + +代码块身份:已验证输出片段 + +```text +test +``` + +`.tsl` 语句区后接类声明区: + +代码块身份:已验证可执行示例 + +```tsl +obj := CreateObject("MyClass"); +obj.value := 5; +echo obj.value; + +type MyClass = class + value; +end; +``` + +代码块身份:已验证输出片段 + +```text +5 +``` + +`.tsf` 顶层函数的最小形态: 代码块身份:已验证可执行示例 @@ -38,35 +99,39 @@ begin end; ``` -代码块身份:已验证可执行示例 +代码块说明:这个 `.tsf` 部署为函数扩展后,可由 `.tsl` 脚本调用 `Demo()` 并取得返回值。 -```tsl -procedure DemoProc(); -begin -end; +代码块身份:已验证输出片段 + +```text +1 ``` -代码块身份:已验证可执行示例 - -```tsl -type DemoType = class -end; -``` +`.tsf` `unit` 的最小形态: 代码块身份:已验证可执行示例 ```tsl unit DemoUnit; interface -function Ping(); + function Ping(); + implementation -function Ping(); -begin - return 1; -end; + function Ping(); + begin + return 1; + end; end. ``` +代码块说明:这个 `.tsf` `unit` 已通过 `.tsl` 脚本 `uses DemoUnit` 调用 `Ping()` 验证返回值为 `1`;调用脚本和查找路径边界见 [10_units_and_scope.md](10_units_and_scope.md)。 + +代码块身份:已验证输出片段 + +```text +1 +``` + 已验证失败的形态: 代码块身份:反例 / 不可照写 @@ -78,14 +143,20 @@ end; 上面这种裸 `class` 顶层写法会编译失败。 +代码块身份:已验证输出片段 + +```text +invalid statement +``` + ## 最小可编译示例 -如果当前任务只是“先让 session 判断当前文件属于哪一种模型”,先记住下面这组归类: +如果当前任务只是“先让 agent 判断当前文件属于哪一种模型”,先记住下面这组归类: -- 顶层语句骨架:直接写松散语句。 -- 顶层函数 / 过程骨架:写 `function ... begin ... end;` 或 `procedure ... begin ... end;`。 -- 顶层类骨架:写 `type Name = class ... end;`。 -- 顶层单元骨架:默认先写 `unit ... interface ... implementation ... end.`;简写形态见 [10_units_and_scope.md](10_units_and_scope.md)。 +- `.tsl` 脚本语句区:直接写会顺序执行的语句。 +- `.tsl` 声明区:在语句区之后写 `function ... begin ... end;`、`procedure ... begin ... end;` 或 `type Name = class ... end;`。 +- `.tsf` 顶层函数 / 过程:写可部署到 `funcext` 的 `function` / `procedure` 文件。 +- `.tsf` 顶层单元:默认先写 `unit ... interface ... implementation ... end.`;简写形态见 [10_units_and_scope.md](10_units_and_scope.md)。 代码块身份:已验证可执行示例 @@ -95,9 +166,10 @@ a := 1; ## 常见误写 -- 误以为扩展名本身就完全决定能否写函数、类或 `unit`。 +- 把 `.tsl` 当成 `.tsf` 来写,只给一个顶层函数,不写任何会执行的脚本语句。 +- 把 `.tsf` 当成 `.tsl` 来写,在模块文件里直接堆顺序执行的脚本语句。 - 把 `uses` 当成主体声明,而不是辅助组织语句。 -- 把 `function / procedure` 定义体和松散语句混写。 +- 在 `.tsl` 声明区之后继续追加脚本语句。 ## 跳转指引 diff --git a/docs/tsl/syntax/04_values_and_literals.md b/docs/tsl/syntax/04_values_and_literals.md index 4a9fb7c7..67424549 100644 --- a/docs/tsl/syntax/04_values_and_literals.md +++ b/docs/tsl/syntax/04_values_and_literals.md @@ -14,9 +14,18 @@ 回答“基本类型怎么写、数组和字符串怎么索引、哪些值规则属于语言级事实”。 +## Agent 值写法判断流程 + +1. 普通值优先从整数、实数、普通字符串、布尔和 `array(...)` 起手。 +2. 字符串默认用普通字符串;`L""` / `U""` 只在编码或宽串场景,并且必须按本页已验证边界写。 +3. 看到 `array(...)` 时,先判断要顺序数组还是字符串键表:顺序数组用位置元素,字符串键表用 `"Key": value`。 +4. 写下标前先判断对象类别:顺序数组和二进制缓冲区下标从 `0` 开始,字符串下标从 `1` 开始。 +5. 写字符串区间前先记住 `s[start:end]` 的 `end` 会被包含;不要按半开区间推断。 +6. 没有已验证代码块时不要发明值写法;尤其不要从 JSON、JavaScript、Pascal 或 C 字符串规则直接迁移。 + ## 必须记住的规则 -- 最先掌握的几类值是:整数、实数、字符串、布尔和 `array`。 +- 最先掌握的几类值是:整数、实数、普通字符串、布尔和 `array(...)`。 - 普通字符串既可以用双引号,也可以用单引号。 - 同类引号本身可以通过连续写两个同类引号放进字符串里。 - `\\`、`\"`、`\n` 这类基础转义已验证可用。 @@ -55,13 +64,26 @@ name := "ABC"; flag1 := true; flag2 := false; items := array(1, 2, 3); +WriteLn(count); +WriteLn(price); +WriteLn(name); +WriteLn(flag1); +WriteLn(flag2); +WriteLn(items[1]); ``` -其中已经验证: +代码块身份:已验证输出片段 -- `true` 可以直接写成布尔值。 -- `false` 可以直接写成布尔值。 -- 运行输出里,`true` / `false` 分别表现为 `1` / `0`。 +```text +1 +12.5 +ABC +1 +0 +2 +``` + +代码块说明:`true` / `false` 可以直接写成布尔值;运行输出里分别表现为 `1` / `0`。 普通字符串字面量: @@ -404,25 +426,29 @@ WriteLn("A"#13#10"B"); 代码块身份:已验证可执行示例 ```tsl -arr := array(10, 20, 30); -hash := array("Code": "0001", "Price": 12.3); +items := array(10, 20, 30); +row := array("Code": "0001", "Price": 12.3); s := "ABC"; -WriteLn("ARR0=", arr[0]); -WriteLn("ARR1=", arr[1]); -WriteLn("HASH=", hash["Code"]); -WriteLn("STR1=", s[1]); -WriteLn("STR2=", s[2]); -WriteLn("STR3=", s[3]); +WriteLn(items[0]); +WriteLn(items[1]); +WriteLn(row["Code"]); +WriteLn(s[1]); +WriteLn(s[2]); +WriteLn(s[3]); ``` -已验证运行结果对应关系: +代码块身份:已验证输出片段 -- `arr[0] = 10` -- `arr[1] = 20` -- `hash["Code"] = "0001"` -- `s[1] = "A"` -- `s[2] = "B"` -- `s[3] = "C"` +```text +10 +20 +0001 +A +B +C +``` + +代码块说明:顺序数组 `items` 从 `0` 开始,字符串键表 `row` 用字符串键访问,字符串 `s` 从 `1` 开始。 二进制缓冲区: @@ -456,10 +482,13 @@ s := "ABCDE"; WriteLn(s[2:4]); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- 输出 `BCD` -- 这说明字符串区间 `2:4` 会包含结束位 `4` +```text +BCD +``` + +代码块说明:字符串区间 `2:4` 会包含结束位 `4`。 字符串替换子串: @@ -540,6 +569,12 @@ WriteLn(s[0]); 上面这类写法在运行时会报字符串下标越界。 +代码块身份:已验证输出片段 + +```text +String index out of bounds +``` + 代码块身份:反例 / 不可照写 ```text diff --git a/docs/tsl/syntax/05_variables_and_constants.md b/docs/tsl/syntax/05_variables_and_constants.md index 74866f1a..0f0b1507 100644 --- a/docs/tsl/syntax/05_variables_and_constants.md +++ b/docs/tsl/syntax/05_variables_and_constants.md @@ -14,6 +14,15 @@ 回答“普通变量怎样直接使用、`var` 在什么位置出现、常量有哪些基本写法、哪些名字一旦绑定就不能再赋值”。 +## Agent 变量/常量判断流程 + +1. 普通变量默认直接用 `:=` 首次赋值,不要先补一个没有需求证据的 `var` 段。 +2. 只有用户要求显式声明或遇到 `{$Explicit+}` 时才优先写 `var`。 +3. 顶层脚本常量优先用 `const name := value;`;需要后续脚本语句时,仍按 `.tsl` 语句区规则组织。 +4. `const Name = value;` 优先放在函数 `const` 段、`unit` 接口或类成员里;不要和顶层 `const :=` 任意互换。 +5. 多参数赋值按 `[a, b] := array(...)` 写;单变量拆包必须写成 `[name, ] := array(...)`。 +6. 没有已验证代码块时不要发明变量/常量写法;尤其不要从 Pascal 的声明习惯反推 TSL 必须先声明变量。 + ## 必须记住的规则 - 默认变量模型是“直接赋值即得到变量”,不要求先写 `var`。 @@ -41,6 +50,15 @@ ```tsl a := 1; b := array(1, 2, 3); +WriteLn(a); +WriteLn(b[1]); +``` + +代码块身份:已验证输出片段 + +```text +1 +2 ``` 显式 `var` 写法: @@ -66,6 +84,12 @@ begin end. ``` +代码块身份:已验证输出片段 + +```text +1 +``` + 顶层最稳的常量写法: 代码块身份:已验证可执行示例 @@ -83,6 +107,12 @@ const value := 1 + 2 * 3; WriteLn(value); ``` +代码块身份:已验证输出片段 + +```text +7 +``` + 函数内部 `const` 段: 代码块身份:已验证可执行示例 @@ -148,9 +178,12 @@ WriteLn(r1); WriteLn(r2); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- 依次输出 `1`、`3` +```text +1 +3 +``` 单变量拆包时,末尾逗号不能省略: @@ -161,9 +194,11 @@ WriteLn(r2); WriteLn(re); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- 输出 `1` +```text +1 +``` 左侧变量比右侧数组更长时,多出的变量为 `nil`: @@ -175,10 +210,12 @@ WriteLn(r1); WriteLn(r2 = nil); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- `r1` 输出 `1` -- `r2 = nil` 输出 `1` +```text +1 +1 +``` 右侧元素也可以是数组: @@ -293,6 +330,12 @@ end. 上面这类写法在当前解释器里会编译失败,主因是 `variable not defined`。 +代码块身份:已验证输出片段 + +```text +variable not defined +``` + ## 跳转指引 - 回看基本类型:见 [04_values_and_literals.md](04_values_and_literals.md) diff --git a/docs/tsl/syntax/06_functions_and_calls.md b/docs/tsl/syntax/06_functions_and_calls.md index 4748f2cb..47413fda 100644 --- a/docs/tsl/syntax/06_functions_and_calls.md +++ b/docs/tsl/syntax/06_functions_and_calls.md @@ -12,13 +12,24 @@ ## 这一篇解决什么问题 -回答“如何正确声明 `function` 和 `procedure`、如何组织主函数和子函数、怎样使用参数修饰、默认参数与可变参数,以及哪些混写方式会直接编译失败”。 +回答“如何正确声明 `function` 和 `procedure`、`.tsl` 脚本语句区如何调用后置函数声明、怎样使用参数修饰、默认参数与可变参数,以及哪些函数写法会直接编译失败”。 + +## Agent 函数/调用判断流程 + +1. 先判断当前文件是 `.tsl` 还是 `.tsf`;文件形态不明确时回 [03_core_model.md](03_core_model.md)。 +2. `.tsl` 中先写语句区,再把 `function` / `procedure` 声明放在后面;不要在声明区后面追加脚本语句。 +3. `.tsf` 中把顶层 `function` / `procedure` 当成模块 / 函数扩展声明;不要写成顺序执行入口。 +4. 需要返回值时用 `function`,不需要返回值时用 `procedure`。 +5. 调用普通 TSL 函数时,命名参数只写 `name: value`;不要把 `name = value` 当成命名参数。 +6. 参数是否写回调用方要看 `const` / `var` / `{$VarByRef-}` / `in` / `out`,不要默认按其他语言习惯推断。 +7. 没有已验证代码块时不要发明函数/调用写法;尤其不要把二进制函数、系统函数、函数指针和匿名函数都套成同一种调用语法。 ## 必须记住的规则 - 最稳妥的函数骨架仍然是 `function Name(...); begin ... end;`。 - 不需要返回值时,可以改用 `procedure Name(...); begin ... end;`。 -- 在文件模型层,`function` 和 `procedure` 归同一类顶层外形;见 [03_core_model.md](03_core_model.md)。 +- 在 `.tsl` 文件模型层,脚本语句后可以接函数声明;语句区在前顺序执行,声明区在后提供函数/过程定义。见 [03_core_model.md](03_core_model.md)。 +- 在 `.tsf` 文件模型层,顶层 `function` / `procedure` 是模块/函数扩展声明;部署到解释器 `funcext` 后可被脚本直接调用。 - 当前解释器接受省略函数头后的分号,但文档默认仍保留这个分号。 - 一个函数定义体里可以同时出现主函数和子函数。 - 函数支持参数类型注解和返回值类型注解。 @@ -48,12 +59,32 @@ - 当前解释器没有通过 `f(...)` 这种“函数变量直接调用”写法;无论 `f` 是匿名函数、`FindFunction(...)` 还是 `ThisFunction(...)` 返回的函数指针,都不要默认写成直调。 - 当前解释器接受 `::FuncName(...)` 指向全局/系统函数,用来绕过当前作用域里的同名局部函数。 - `external`、`MakeInstance` 和线程调用统一移到 [21_external_calls_and_threads.md](21_external_calls_and_threads.md)。 -- 不要把顶层函数定义和松散语句混在同一个文件模型里。 +- 不要在 `.tsl` 的函数声明区之后继续追加脚本语句。 ## 已验证语法 ### 基础函数 / 过程骨架 +`.tsl` 语句区调用后置函数声明: + +代码块身份:已验证可执行示例 + +```tsl +a := 1; +test(); + +function test(); +begin + echo "test"; +end; +``` + +代码块身份:已验证输出片段 + +```text +test +``` + 最短函数骨架: 代码块身份:已验证可执行示例 @@ -121,9 +152,11 @@ begin end. ``` -已验证运行结果: +代码块身份:已验证输出片段 -- `Bump(a)` 后输出 `2` +```text +2 +``` ### 签名增强 @@ -316,10 +349,7 @@ end. 代码块身份:已验证可执行示例 ```tsl -function NamedArgsDemo(); -begin - return Pack(a: 1, b: 2); -end; +WriteLn(Pack(a: 1, b: 2)); function Pack(a, b); begin @@ -327,7 +357,13 @@ begin end; ``` -已验证运行结果: +代码块身份:已验证输出片段 + +```text +12 +``` + +已验证补充: - `Pack(a: 1, b: 2)` 返回 `12` - `Pack(b: 2, a: 1)` 返回 `12` @@ -820,12 +856,25 @@ end. ## 最小可编译示例 -如果你只是要写一个能被 session 稳定续写的函数 / 过程,从下面任一骨架起步: +如果你只是要写一个能被 agent 稳定续写的 `.tsl` 脚本,从语句区起步,需要函数时把声明区放在后面: 代码块身份:已验证可执行示例 ```tsl +Hello(); + function Hello(); +begin + echo "hello"; +end; +``` + +如果你要写 `.tsf` 模块/函数扩展,从下面任一骨架起步: + +代码块身份:已验证可执行示例 + +```tsl +function HelloValue(); begin return 1; end; @@ -841,7 +890,7 @@ end; ## 常见误写 -- 把顶层函数定义和松散语句混写。 +- 在 `.tsl` 的声明区后面继续写脚本语句。 - 以为函数头后的分号是当前解释器的硬性要求。 - 以为 `procedure` 只是 `function` 的别名,不涉及参数传递语义。 - 带类型注解时仍然用逗号分隔参数。 @@ -857,15 +906,24 @@ end; 代码块身份:反例 / 不可照写 ```text +a := 1; +Add(1, 2); + function Add(a, b); begin return a + b; end; -value := Add(1, 2); +echo "after function"; ``` -上面这种混写方式会编译失败,问题不在 `Add` 本身,而在于文件模型混了“函数定义体”和“松散语句”两种写法。 +上面的问题不在 `Add` 本身,而在于 `.tsl` 的函数声明区后面又继续出现脚本语句。正确做法是把会执行的语句全部放在声明区之前。 + +代码块身份:已验证输出片段 + +```text +invalid statement +``` 代码块身份:反例 / 不可照写 @@ -884,7 +942,7 @@ end; Pack(a = 1, b = 2) ``` -这类写法不要当成命名参数。它虽然可能编译通过,但在我于 `2026-04-09` 的实测里返回结果不对,不能当成可靠的命名参数语法。 +这类写法不要当成命名参数。它虽然可能编译通过,但当前已验证返回结果不对,不能当成可靠的命名参数语法。 代码块身份:反例 / 不可照写 diff --git a/docs/tsl/syntax/07_expressions_and_operators.md b/docs/tsl/syntax/07_expressions_and_operators.md index bcc2c7a6..dad778dd 100644 --- a/docs/tsl/syntax/07_expressions_and_operators.md +++ b/docs/tsl/syntax/07_expressions_and_operators.md @@ -8,31 +8,42 @@ 手册位置:第 7 篇,共 32 篇。上一篇:[06_functions_and_calls.md](06_functions_and_calls.md)。下一篇:[08_control_flow.md](08_control_flow.md)。 -这一篇集中放语言级表达式与运算符,避免与业务计算示例混用。 +这一篇集中放语言级表达式与运算符。agent 写代码时,只能从本页已验证代码块归纳表达式写法,不要把其他语言或外部资料里的运算符习惯直接搬进 TSL。 ## 这一篇解决什么问题 -回答“赋值、比较、条件求值、可空访问和一部分新表达式在 TSL 里怎样写”。 +回答“赋值、比较、条件求值、表达式对象、空安全访问和链式比较在 TSL 里怎样写”。 + +## Agent 表达式/运算符判断流程 + +1. 先判断要写赋值、比较、条件求值、表达式对象、空安全访问还是链式比较。 +2. 普通赋值只能用 `:=`,不要把 `=` 当赋值写法。 +3. 比较才用 `=`,并且把比较表达式放在 `WriteLn(...)`、条件或其他需要布尔值的位置。 +4. 已有变量做复合运算时,才使用 `+=`、`-=`、`*=`、`/=`、`%=`、`a++;`、`a--;`。 +5. 条件求值优先用 `flag ? true_value : false_value`;需要 Pascal 风格时可用 `if condition then true_value else false_value` 的形态,但必须带 `else`。 +6. 需要延迟求值或动态表达式对象时,才使用 `@expr` 或 `&"..."`,并用已验证的 `eval(...)` 形态求值。 +7. 空安全访问只照本页已验证形态写:`a?.member`、`a?.[index]`、以及已验证的 `c?.a?.[1]`。不要外推成任意深度、任意组合都可写。 +8. 需要连续比较时,标量用 `:<` / `:>` 这组链式比较;数组逐元素比较用 `::<` / `::>` 这组矩阵链式比较。 +9. 没有已验证代码块时不要发明表达式/运算符写法。 ## 必须记住的规则 - 当前页只收已经单独验证过的基础表达式,不把未经逐条验证的扩展运算体系一次并进正文。 -- 赋值使用 `:=`。 -- 当前解释器接受 `+=`、`-=`、`*=`、`/=`、`%=` 这几种基础运算赋值。 -- 当前解释器接受语句级 `a++;` 与 `a--;`。 +- 普通赋值使用 `:=`。 +- 已验证支持 `+=`、`-=`、`*=`、`/=`、`%=` 这几种基础运算赋值。 +- 已验证支持语句级 `a++;` 与 `a--;`。 - `=` 用于比较,不用于赋值。 -- 当前解释器接受字符串 `+` 拼接、字符串比较和 `like` 正则匹配。 -- 当前解释器同时接受 `flag ? true_value : false_value` 和 `if condition then true_value else false_value` 这两种条件求值写法。 -- 当前解释器接受 `@expr` 把后面的内容声明成表达式对象。 -- 当前解释器接受 `&"..."` 把字符串编译成表达式对象。 -- 当前解释器接受逗号表达式 `(exp1, exp2, ..., expN)`,并按从左到右顺序求值。 -- 当前解释器接受空安全访问 `a?.member` 与 `a?.[index]`。 -- 当前解释器接受 `not in`、`not like`、`not sqlin`、`not is` 这几种否定形式运算。 -- 当前解释器接受标量链式比较 `:>`、`:<`、`:<>`、`:==`、`:>=`、`:<=`。 -- 当前解释器接受矩阵链式比较 `::>`、`::<`、`::<>`、`::==`、`::>=`、`::<=`。 -- 当前环境里 `{$IFDEF ifexp}` 为真,可用于探测 `if ... then ... else ...` 表达式能力。 -- 当前环境里 `{$IFDEF nilinvoke}` 为真,可用于探测 nil 调用相关能力。 -- 不要把外部资料里的更深链式可空访问修正,直接当成当前解释器必然支持。 +- 已验证支持字符串 `+` 拼接、字符串比较和 `like` 正则匹配。 +- 已验证支持 `flag ? true_value : false_value` 和 `if condition then true_value else false_value` 这两种条件求值写法。 +- 已验证支持 `@expr` 把后面的内容声明成表达式对象。 +- 已验证支持 `&"..."` 把字符串编译成表达式对象。 +- 已验证支持逗号表达式 `(exp1, exp2, ..., expN)`,并按从左到右顺序求值。 +- 已验证支持空安全访问 `a?.member`、`a?.[index]` 和本页示例里的 `c?.a?.[1]`。 +- 已验证支持 `not in`、`not like`、`not sqlin`、`not is` 这几种否定形式运算。 +- 已验证支持标量链式比较 `:>`、`:<`、`:<>`、`:==`、`:>=`、`:<=`。 +- 已验证支持矩阵链式比较 `::>`、`::<`、`::<>`、`::==`、`::>=`、`::<=`。 +- `{$IFDEF ifexp}` 可用于探测 `if ... then ... else ...` 表达式能力。 +- `{$IFDEF nilinvoke}` 可用于探测 nil 调用相关能力。 ## 已验证语法 @@ -45,6 +56,13 @@ a := 1; b := 2; flag := a < b; value := flag ? 10 : 20; +WriteLn(value); +``` + +代码块身份:已验证输出片段 + +```text +10 ``` 运算赋值: @@ -57,35 +75,36 @@ a += 2; WriteLn(a); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- 输出 `3` +```text +3 +``` 基础算术复合赋值: 代码块身份:已验证可执行示例 ```tsl -program test; -begin - a := 10; - a -= 3; - WriteLn(a); - a *= 4; - WriteLn(a); - a /= 7; - WriteLn(a); - a %= 5; - WriteLn(a); -end. +a := 10; +a -= 3; +WriteLn(a); +a *= 4; +WriteLn(a); +a /= 7; +WriteLn(a); +a %= 5; +WriteLn(a); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- `a -= 3` 后输出 `7` -- `a *= 4` 后输出 `28` -- `a /= 7` 后输出 `4` -- `a %= 5` 后输出 `4` +```text +7 +28 +4 +4 +``` 字符串同样支持 `+=`: @@ -97,49 +116,57 @@ s += "B"; WriteLn(s); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- 输出 `AB` +```text +AB +``` 字符串拼接、比较和 `like`: 代码块身份:已验证可执行示例 ```tsl -program test; -begin - WriteLn("222" + "888"); - WriteLn("A" < "a"); - WriteLn("AB" < "ABC"); - WriteLn("ABC" = "ABC"); - WriteLn("2009-01-01" like "\\d{4}-\\d{2}-\\d{2}"); -end. +WriteLn("222" + "888"); +WriteLn("A" < "a"); +WriteLn("AB" < "ABC"); +WriteLn("ABC" = "ABC"); +WriteLn("2009-01-01" like "\\d{4}-\\d{2}-\\d{2}"); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- 依次输出 `222888`、`1`、`1`、`1`、`1` -- 说明字符串可以直接用 `+` 拼接 -- 说明字符串比较区分字符序和大小写;当前例子里 `"A" < "a"` 为真 -- 说明当前 `like` 的右侧可以直接写正则模式 +```text +222888 +1 +1 +1 +1 +``` + +说明: + +- 字符串可以直接用 `+` 拼接。 +- 字符串比较区分字符序和大小写;当前例子里 `"A" < "a"` 为真。 +- `like` 的右侧可以直接写正则模式。 `like` 不要按 SQL `%` 通配去理解: 代码块身份:已验证可执行示例 ```tsl -program test; -begin - WriteLn("abc" like "a.*"); - WriteLn("abc" like "a%"); -end. +WriteLn("abc" like "a.*"); +WriteLn("abc" like "a%"); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- 第一行输出 `1` -- 第二行输出 `0` -- 因此当前解释器里的 `like` 更接近“正则匹配”,不是 SQL 那套 `%` / `_` 通配语义 +```text +1 +0 +``` + +因此 `like` 更接近“正则匹配”,不是 SQL 那套 `%` / `_` 通配语义。 自增与自减: @@ -153,25 +180,30 @@ a--; WriteLn(a); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- 先输出 `2` -- 再输出 `1` +```text +2 +1 +``` `if` 表达式: 代码块身份:已验证可执行示例 ```tsl -program test; -begin - WriteLn(if 2 > 1 then 2 else 1); -end. +WriteLn(if 2 > 1 then 2 else 1); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- `if 2 > 1 then 2 else 1` 返回 `2` +```text +2 +``` + +`if condition then true_value else false_value` 必须带 `else`,否则不是本页可照写的表达式形态。 + +### 表达式对象 `@` 表达式前导: @@ -184,10 +216,13 @@ C := eval(B); WriteLn(C); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- 输出 `2` -- 说明 `@A + 1` 会得到一个可交给 `eval(...)` 求值的表达式对象 +```text +2 +``` + +`@A + 1` 会得到一个可交给 `eval(...)` 求值的表达式对象。 `&"..."` 表达式常量: @@ -200,10 +235,13 @@ C := eval(B); WriteLn(C); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- 输出 `2` -- 说明 `&"A + 1"` 会把字符串编译成表达式对象,再由 `eval(...)` 求值 +```text +2 +``` + +`&"A + 1"` 会把字符串编译成表达式对象,再由 `eval(...)` 求值。 逗号表达式: @@ -220,10 +258,13 @@ begin end. ``` -已验证运行结果: +代码块身份:已验证输出片段 -- 输出 `6` -- 说明逗号表达式会按从左到右顺序执行前面的赋值,再返回最后一个表达式结果 +```text +6 +``` + +逗号表达式会按从左到右顺序执行前面的赋值,再返回最后一个表达式结果。 逗号表达式也可以继续参与外层计算: @@ -234,10 +275,13 @@ A := (b := 2, c := 3, b * c) * c; WriteLn(A); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- 输出 `18` -- 说明逗号表达式本身可以作为一个普通子表达式继续参与后续运算 +```text +18 +``` + +逗号表达式本身可以作为一个普通子表达式继续参与后续运算。 ### 空安全访问 @@ -259,11 +303,30 @@ begin end. ``` -已验证运行结果: +代码块身份:已验证输出片段 -- `a?.value = nil` 输出 `1` -- `h?.value` 输出 `7` -- `arr?.[0] = nil` 输出 `1` +```text +1 +7 +1 +``` + +更深一层的混合空安全访问,本页只确认下面这个形态: + +代码块身份:已验证可执行示例 + +```tsl +c := nil; +WriteLn(c?.a?.[1] = nil); +``` + +代码块身份:已验证输出片段 + +```text +1 +``` + +不要从这一段外推任意深度、任意成员/下标混合都可写。 ### 否定形式运算 @@ -284,86 +347,92 @@ begin end. ``` -已验证运行结果: +代码块身份:已验证输出片段 -- `1 not in array(2, 3)` 输出 `1` -- `"2009-1-1" not like "\\d{4}-\\d{2}-\\d{2}"` 输出 `1` -- `1 not sqlin array(2, 3)` 输出 `1` -- `obj not is class(B)` 输出 `1` +```text +1 +1 +1 +1 +``` ### 标量链式比较 代码块身份:已验证可执行示例 ```tsl -program test; -begin - WriteLn(1 :< 2 :< 3); - WriteLn(3 :> 2 :> 1); - WriteLn(1 :== 1 :== 1); - WriteLn(3 :>= 2 :>= 2); - WriteLn(1 :<= 2 :<= 3); - WriteLn(1 :<> 2 :<> 3); -end. +WriteLn(1 :< 2 :< 3); +WriteLn(3 :> 2 :> 1); +WriteLn(1 :== 1 :== 1); +WriteLn(3 :>= 2 :>= 2); +WriteLn(1 :<= 2 :<= 3); +WriteLn(1 :<> 2 :<> 3); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- `1 :< 2 :< 3` 输出 `1` -- `3 :> 2 :> 1` 输出 `1` -- `1 :== 1 :== 1` 输出 `1` -- `3 :>= 2 :>= 2` 输出 `1` -- `1 :<= 2 :<= 3` 输出 `1` -- `1 :<> 2 :<> 3` 输出 `1` +```text +1 +1 +1 +1 +1 +1 +``` ### 矩阵链式比较 代码块身份:已验证可执行示例 ```tsl -program test; -begin - r := array(1, 2, -1) ::< array(2, 1, 0) ::< array(3, 2, 1); - WriteLn(r[0]); - WriteLn(r[1]); - WriteLn(r[2]); - s := array(1, 2, -1) ::< 2 ::< array(3, 2, 1); - WriteLn(s[0]); - WriteLn(s[1]); - WriteLn(s[2]); -end. +r := array(1, 2, -1) ::< array(2, 1, 0) ::< array(3, 2, 1); +WriteLn(r[0]); +WriteLn(r[1]); +WriteLn(r[2]); +s := array(1, 2, -1) ::< 2 ::< array(3, 2, 1); +WriteLn(s[0]); +WriteLn(s[1]); +WriteLn(s[2]); ``` -已验证运行结果: +代码块身份:已验证输出片段 -- `array(1, 2, -1) ::< array(2, 1, 0) ::< array(3, 2, 1)` 的三个元素依次输出 `1`、`0`、`1` -- `array(1, 2, -1) ::< 2 ::< array(3, 2, 1)` 的三个元素依次输出 `1`、`0`、`0` -- 说明矩阵链式比较会按元素位置分别得到结果数组,并且可以和标量混用 +```text +1 +0 +1 +1 +0 +0 +``` + +矩阵链式比较会按元素位置分别得到结果数组,并且可以和标量混用。 ### 条件编译探测 代码块身份:已验证可执行示例 ```tsl -program test; -begin {$IFDEF ifexp} - WriteLn(1); +WriteLn(1); {$ELSE} - WriteLn(0); +WriteLn(0); {$ENDIF} {$IFDEF nilinvoke} - WriteLn(1); +WriteLn(1); {$ELSE} - WriteLn(0); +WriteLn(0); {$ENDIF} -end. ``` -已验证运行结果: +代码块身份:已验证输出片段 -- `{$IFDEF ifexp}` 输出 `1` -- `{$IFDEF nilinvoke}` 输出 `1` +```text +1 +1 +``` + +这只能作为能力探测示例使用;agent 不要把条件编译探测写成普通业务逻辑。 ## 最小可编译示例 @@ -374,13 +443,20 @@ end. ```tsl flag := 1 < 2; value := flag ? 10 : 20; +WriteLn(value); +``` + +代码块身份:已验证输出片段 + +```text +10 ``` ## 常见误写 - 用 `=` 当赋值运算符。 - 把 `if` 表达式写成没有 `else` 的半句。 -- 把更深链式可空访问 `c?.a?.[1]` 直接当成当前解释器已支持事实。 +- 把已验证的 `c?.a?.[1]` 外推成所有深链式空安全访问都可靠。 代码块身份:反例 / 不可照写 @@ -388,6 +464,12 @@ value := flag ? 10 : 20; a = 1; ``` +代码块身份:已验证输出片段 + +```text +invalid statement +``` + 上面这种写法会编译失败,因为单独的 `=` 在这里会被当成不成立的表达式。 代码块身份:反例 / 不可照写 @@ -396,16 +478,13 @@ a = 1; v := if 2 > 1 then 2; ``` -上面这种写法也会编译失败;`if` 表达式当前必须带 `else`。 - -代码块身份:反例 / 不可照写 +代码块身份:已验证输出片段 ```text -c := nil; -WriteLn(c?.a?.[1] = nil); +invalid statement ``` -上面这种更深的混合可空访问,在当前已记录验证里没有通过,不要提前把后续版本的修正结果写进结论。 +上面这种写法也会编译失败;`if` 表达式必须带 `else`。 ## 跳转指引 diff --git a/docs/tsl/syntax/08_control_flow.md b/docs/tsl/syntax/08_control_flow.md index 72c7e4bb..fe69ee98 100644 --- a/docs/tsl/syntax/08_control_flow.md +++ b/docs/tsl/syntax/08_control_flow.md @@ -14,6 +14,14 @@ 回答“`if`、`case`、`for`、`while`、`repeat`、`break`、`continue`、`try`、`raise` 这些流程结构在 TSL 里到底怎么写,哪些写法已经被当前解释器实测验证过”。 +## Agent 控制流判断流程 + +1. 先判断任务需要条件分支、循环、`case`、异常处理还是调试跳转。 +2. `if` / `for` / `while` / `repeat` 优先照本页已验证骨架写,不要套用其他 Pascal 方言。 +3. 需要多条语句时再补 `begin ... end`,不要在 `else` 前提前加分号。 +4. `case` 表达式和 `case` 语句分开判断;`@case` 不作为默认主写法。 +5. 没有已验证代码块时不要发明控制流写法。 + ## 必须记住的规则 - `if ... then ... else ...` 可以直接跟单条语句;需要多条语句时再补 `begin ... end`。 @@ -78,6 +86,12 @@ WriteLn(s); - 输出 `9` +代码块身份:已验证输出片段 + +```text +9 +``` + 带 `step` 的 `downto` 递减循环: 代码块身份:已验证可执行示例 @@ -365,7 +379,7 @@ end; WriteLn(b); ``` -我在 `2026-04-13` 用当前解释器实测,上面这类最小例子输出的是 ``,不能把它当成普通 `case` 表达式的可靠主语法写进新 session 默认生成结果。 +当前已验证结果显示,上面这类最小例子输出的是 ``,不能把它当成普通 `case` 表达式的可靠主语法写进新 session 默认生成结果。 ## 跳转指引 diff --git a/docs/tsl/syntax/09_objects_and_classes.md b/docs/tsl/syntax/09_objects_and_classes.md index 8adbc1d8..8c762be7 100644 --- a/docs/tsl/syntax/09_objects_and_classes.md +++ b/docs/tsl/syntax/09_objects_and_classes.md @@ -16,6 +16,14 @@ 本页后半段有少量依赖 `unit` 的双文件示例;如果你还没建立 `unit` / `uses` 的多文件心智模型,先看 [10_units_and_scope.md](10_units_and_scope.md)。 +## Agent 对象/类判断流程 + +1. 先判断要写普通对象、继承、构造函数、类方法、静态字段还是运行时创建。 +2. 对象声明优先使用本页已验证的 `type Name = class ... end;` 骨架。 +3. 类方法和静态字段优先用 `class(Name).Member` 或已验证反射入口,不要裸写类名调用。 +4. 构造函数默认保持 `public create`,不要把 `private` / `protected create` 当成会自动执行的构造函数。 +5. 没有已验证代码块时不要发明对象/类写法。 + ## 必须记住的规则 - 类定义统一按 `type Name = class ... end;` 写。 @@ -49,7 +57,7 @@ - 工厂式 `self(0)` / `self(1)` 已验证可用;当前解释器没有通过 `self()` 这种无参工厂式写法。 - 已做双文件运行验证:如果类定义在 `unit` 的嵌套路径里,可以用 `CreateObject("Unit1.Class1.Class2")` 这种字符串路径创建对象。 - 已做双文件运行验证:可以写 `type MyNewClass = class(Unit1.Class1.Class2)` 直接继承单元里的嵌套类路径。 -- 当前环境里 `{$IFDEF ParentClassInUnit}` 为真,可用于探测“继承和构造单元中的类”能力是否可用。 +- 已验证 `{$IFDEF ParentClassInUnit}` 为真,可用于探测“继承和构造单元中的类”能力是否可用。 - `private` / `protected` / `public` 已验证可用;类开头未写可见性时,成员默认按 `public` 处理。 - 同一个可见性段里后续没有切换关键字的成员,会沿用前一个可见性。 - 外部访问 `private` / `protected` 字段或方法,会在执行时报对象成员访问错误。 @@ -109,6 +117,12 @@ end; - 说明在当前解释器里,顶层类声明可以放在松散语句之后 - 同时也说明 `CreateObject("MyClass")` 可以解析到后面才出现的类声明 +代码块身份:已验证输出片段 + +```text +5 +``` + 顶层函数后面也可以继续声明类: 代码块身份:已验证可执行示例 @@ -1234,7 +1248,7 @@ end. 已验证运行结果: -- 当前环境里 `{$IFDEF ParentClassInUnit}` 输出 `1` +- `{$IFDEF ParentClassInUnit}` 输出 `1` ## 最小可编译示例 diff --git a/docs/tsl/syntax/10_units_and_scope.md b/docs/tsl/syntax/10_units_and_scope.md index 2cbe559f..7c20bc66 100644 --- a/docs/tsl/syntax/10_units_and_scope.md +++ b/docs/tsl/syntax/10_units_and_scope.md @@ -20,6 +20,14 @@ - 多个 `unit` 里有同名接口时,未限定调用到底命中谁 - 怎样显式指定要调用哪个 `unit` 的接口 +## Agent unit/作用域判断流程 + +1. 先判断当前交付是 `.tsl` 可执行脚本、`.tsf` 扩展模块,还是 `unit` 文件。 +2. `unit` 文件只描述可复用单元;脚本入口仍放在 `.tsl`。 +3. `uses` 必须放在普通语句之前;普通语句后不要再追加顶层 `uses`。 +4. 默认参数、接口段、实现段和作用域边界只照本页已验证形态写。 +5. 没有已验证代码块时不要发明 unit/作用域写法。 + ## 必须记住的规则 - `unit` 是完整的顶层主体;常见完整形态是 `unit Name; interface ... implementation ... end.`。 @@ -58,6 +66,14 @@ end. - 上面这就是当前解释器可通过的最小完整 `unit` 骨架。 - `unit` 文件结尾要用 `end.`,不是普通函数或类的 `end;`。 +如果该 `unit` 被脚本 `uses DemoUnit;` 后调用 `WriteLn(Ping());`,可观察输出为: + +代码块身份:已验证输出片段 + +```text +1 +``` + ### 简写 `unit` 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/11_runtime_context_and_with.md b/docs/tsl/syntax/11_runtime_context_and_with.md index 29c95bd4..94b79eec 100644 --- a/docs/tsl/syntax/11_runtime_context_and_with.md +++ b/docs/tsl/syntax/11_runtime_context_and_with.md @@ -14,6 +14,14 @@ 回答“`SetSysParam` / `GetSysParam` 怎样用、`SysParams[...]` 是什么、`#Func() with array(...)` 这种后缀环境调用在当前解释器里怎样写”。 +## Agent 运行时上下文判断流程 + +1. 先判断要操作系统参数、运行时上下文对象,还是 `with` 后缀调用。 +2. 系统参数优先用本页已验证的 `SetSysParam` / `GetSysParam` / `SysParams[...]` 形态。 +3. `#Func() with array(...)` 只作为运行时环境调用写法,不要套到普通本地函数。 +4. 本地函数后缀 `with` 属于反例时不要照写。 +5. 没有已验证代码块时不要发明运行时上下文写法。 + ## 必须记住的规则 - TSL 有一组运行时系统参数;当前页只写已经实际验证过的通用写法。 @@ -45,6 +53,13 @@ end. - 依次输出 `123`、`XYZ` +代码块身份:已验证输出片段 + +```text +123 +XYZ +``` + `SysParams[...]` 直接读写: 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/12_pitfalls.md b/docs/tsl/syntax/12_pitfalls.md index 54af8021..d5338291 100644 --- a/docs/tsl/syntax/12_pitfalls.md +++ b/docs/tsl/syntax/12_pitfalls.md @@ -14,6 +14,14 @@ 回答“哪些写法最容易凭直觉写出来,但在 TSL 里会编译失败、运行出错,或语义并不可靠”。 +## Agent 常见误写判断流程 + +1. 先识别用户写法属于值、变量、函数、表达式、对象、unit、TS-SQL 还是外部调用误区。 +2. 遇到本页反例时,不要修成相邻语言习惯,必须跳回对应语法页找已验证正例。 +3. 反例只用于排错和避免误写,不作为可照写模板。 +4. 修复时保留 `.tsl` / `.tsf` 文件模型判断,不要只改局部语句。 +5. 没有已验证代码块时不要发明替代语法。 + ## 这页怎么用 - 先按主题扫一遍,再回到对应正文看正确写法。 @@ -43,6 +51,12 @@ end; a = 1; ``` +代码块身份:已验证输出片段 + +```text +invalid statement +``` + 这会编译失败。正确页:见 [07_expressions_and_operators.md](07_expressions_and_operators.md) #### 3. 把字符串当成 0 基下标 @@ -89,20 +103,23 @@ WriteLn(Length(s)); 这不要按 C 风格字符串去理解。当前解释器里这里输出 `3`,说明 `#0` 仍是字符串内容的一部分。正确页:见 [04_values_and_literals.md](04_values_and_literals.md) -#### 7. 把顶层函数定义和松散语句混写 +#### 7. 在 `.tsl` 声明区后面继续写脚本语句 代码块身份:反例 / 不可照写 ```text -function Add(a, b); +a := 1; +test(); + +function test(); begin - return a + b; + echo "test"; end; -value := Add(1, 2); +echo "after declaration"; ``` -这会编译失败。正确页:见 [03_core_model.md](03_core_model.md) +`.tsl` 可以有语句区和声明区,但顺序必须清楚:语句区在前并按顺序执行,函数/类声明区在后。不要在声明区后面继续写脚本语句。正确页:见 [03_core_model.md](03_core_model.md) #### 8. 顶层单独写 `const Name = value;` diff --git a/docs/tsl/syntax/13_matrix_and_collections.md b/docs/tsl/syntax/13_matrix_and_collections.md index 90151530..207c1ba1 100644 --- a/docs/tsl/syntax/13_matrix_and_collections.md +++ b/docs/tsl/syntax/13_matrix_and_collections.md @@ -14,6 +14,14 @@ 回答“`array(...)` 在 TSL 里除了最普通的一维数组,还能怎样组织数据;哪些矩阵样写法已经稳定验证过”。 +## Agent 矩阵/集合判断流程 + +1. 先判断需要顺序数组、字符串键表、嵌套数组,还是矩阵样比较。 +2. 普通集合优先从 `array(...)` 起手;下标和键访问回看值语法页。 +3. 矩阵链式比较只照 `::` 系列已验证示例写,不要混用标量链式比较。 +4. 集合过滤或 TS-SQL 查询需求跳转到结果集/TS-SQL 页面。 +5. 没有已验证代码块时不要发明矩阵/集合写法。 + ## 必须记住的规则 - `array(...)` 既可以写顺序数组,也可以写字符串键表。 @@ -42,6 +50,15 @@ WriteLn("HASH=", hash["Code"]); - `arr[1] = 20` - `hash["Code"] = "0001"` +代码块身份:已验证输出片段 + +```text +1 +2 +3 +4 +``` + 嵌套数组: 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/14_resultset_and_filters.md b/docs/tsl/syntax/14_resultset_and_filters.md index ff2019c4..80f59fa2 100644 --- a/docs/tsl/syntax/14_resultset_and_filters.md +++ b/docs/tsl/syntax/14_resultset_and_filters.md @@ -12,7 +12,15 @@ ## 这一篇解决什么问题 -回答“我已经有一个数组或二维结果集,想按某个过滤集保留命中的行、排除命中的行,或者只拿到符合条件的行下标时,应该怎么写”。 +回答“已有数组或二维结果集时,想按某个过滤集保留命中的行、排除命中的行,或者只拿到符合条件的行下标时,应该怎么写”。 + +## Agent 结果集/过滤判断流程 + +1. 先判断任务是普通数组过滤、结果集字段访问,还是 TS-SQL 查询。 +2. 字段访问优先照本页已验证的字符串键或结果集字段形态写。 +3. 复杂查询需求优先跳转到 TS-SQL 页面,不要把业务过滤硬塞进基础表达式。 +4. 金融业务筛选要回到 finance 层确认上下文。 +5. 没有已验证代码块时不要发明结果集/过滤写法。 ## 必须记住的规则 @@ -53,6 +61,15 @@ end. - `FilterIn(R, CodeArr, "Code")` 返回两行:`0001`、`0003` - `FilterNotIn(R, CodeArr, "Code")` 返回两行:`0002`、`0004` +代码块身份:已验证输出片段 + +```text +0001 +0003 +0002 +0004 +``` + ### 返回行下标 第四个参数写成 `false` 时,返回行下标而不是子结果集: diff --git a/docs/tsl/syntax/15_ts_sql.md b/docs/tsl/syntax/15_ts_sql.md index 058d1791..de495124 100644 --- a/docs/tsl/syntax/15_ts_sql.md +++ b/docs/tsl/syntax/15_ts_sql.md @@ -14,6 +14,14 @@ 回答“第一次进入 TS-SQL 时,应该先记住哪些固定骨架,怎样区分基础查询和高级查询,以及哪些内容属于语法、哪些已经属于业务层数据访问”。 +## Agent TS-SQL 判断流程 + +1. 先判断是 TS-SQL 总览、核心查询,还是进阶查询。 +2. 第一次落代码优先跳转到 `28_ts_sql_core.md` 的最小 `select ... from ... end` 骨架。 +3. 需要 join、分组上下文或极值引用时再进入 `29_ts_sql_advanced.md`。 +4. 不要把 SQL 数据库语法直接当作 TS-SQL 语法。 +5. 没有已验证代码块时不要发明 TS-SQL 写法。 + ## 必须记住的规则 - TS-SQL 是 TSL 自带的类 SQL 查询语法,不是金融业务函数库。 @@ -38,6 +46,7 @@ begin ("A": 2, "B": 1) ); R := select * from T end; + WriteLn(Length(R)); end. ``` @@ -47,6 +56,12 @@ end. - 两行依次是 `(1,3)`、`(2,1)` - 说明 TS-SQL 的最短可靠入口就是“准备结果集,然后 `select ... from T end`” +代码块身份:已验证输出片段 + +```text +2 +``` + ### 四个查询入口怎样分工 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/16_debug_and_profiler.md b/docs/tsl/syntax/16_debug_and_profiler.md index fe50ae88..13b6dcf5 100644 --- a/docs/tsl/syntax/16_debug_and_profiler.md +++ b/docs/tsl/syntax/16_debug_and_profiler.md @@ -14,6 +14,14 @@ 回答“`goto`、`DebugReturn`、`DebugRunEnv`、`MTIC` / `MTOC`、`SetProfiler`、`__line__` 和 `__stack_frame` 在当前解释器里怎样写、会怎样表现”。 +## Agent 调试/Profiler 判断流程 + +1. 先判断任务需要跳转、提前返回、运行环境调试、计时还是 profiler。 +2. 普通控制流优先回到 `08_control_flow.md`,本页只处理调试补充工具。 +3. `DebugReturn` 会结束整段脚本,不能当成普通函数返回。 +4. 计时和 profiler 只照已验证最小调用写,不要补未验证参数。 +5. 没有已验证代码块时不要发明调试/Profiler 写法。 + ## 必须记住的规则 - `goto label_name;` 当前已验证可用,但目标位置当前以 `label label_name; statement` 这种内联形式最稳。 diff --git a/docs/tsl/syntax/18_lexical_structure_and_compile_options.md b/docs/tsl/syntax/18_lexical_structure_and_compile_options.md index 1477ce27..ffd4f78a 100644 --- a/docs/tsl/syntax/18_lexical_structure_and_compile_options.md +++ b/docs/tsl/syntax/18_lexical_structure_and_compile_options.md @@ -14,6 +14,14 @@ 回答“TSL 的词法层规则和编译期开关应该去哪里查,而不是把这些边界混进值、函数、类的正文里”。 +## Agent 词法/编译选项判断流程 + +1. 先判断要写注释、标识符、条件编译,还是编译选项。 +2. 注释、大小写、条件编译指令只照本页已验证形态写。 +3. `{$Explicit+}` 会改变变量声明要求,生成代码前先判断是否需要 `var`。 +4. `{$VarByRef+}` / `{$VarByRef-}` 会影响未修饰形参传递语义,细节回看函数页。 +5. 没有已验证代码块时不要发明词法/编译选项写法。 + ## 必须记住的规则 - 标识符大小写无关;当前最小运行验证里,下划线也可以出现在标识符中。 @@ -47,6 +55,13 @@ end. - 说明当前解释器下标识符大小写无关 - 也说明下划线可以出现在标识符中 +代码块身份:已验证输出片段 + +```text +7 +7 +``` + 注释与条件编译: 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/19_types_and_conversions.md b/docs/tsl/syntax/19_types_and_conversions.md index eb48c262..409d4db2 100644 --- a/docs/tsl/syntax/19_types_and_conversions.md +++ b/docs/tsl/syntax/19_types_and_conversions.md @@ -14,6 +14,14 @@ 回答“当问题不再是怎么写字面量,而是变量在运行时会变成什么类型、怎样转换、怎样受编译选项影响时,应该看哪里”。 +## Agent 类型/转换判断流程 + +1. 先判断要写数值字面量、日期时间、真假值、nil,还是类型转换边界。 +2. 整数、实数、日期时间和特殊实数只照本页已验证字面量写。 +3. 不要把能编译的混合类型表达式误判为能按预期自动转换。 +4. 遇到字符串细节跳转到 `20_strings_and_text.md`,遇到值基础跳转到 `04_values_and_literals.md`。 +5. 没有已验证代码块时不要发明类型/转换写法。 + ## 必须记住的规则 - 当前解释器接受十进制、`0x` 十六进制、`0b` 二进制、`0o` 八进制整数常量。 @@ -65,6 +73,14 @@ end. - 说明 `100L` 在当前解释器里按 `Int64` 处理 - 也说明 `1E2` 当前按实数,不按整数处理 +代码块身份:已验证输出片段 + +```text +1 +0 +1 +``` + 超 32 位整数与日期时间字面量: 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/20_strings_and_text.md b/docs/tsl/syntax/20_strings_and_text.md index b559f87a..11524e91 100644 --- a/docs/tsl/syntax/20_strings_and_text.md +++ b/docs/tsl/syntax/20_strings_and_text.md @@ -14,6 +14,14 @@ 回答“当任务进入字符串和编码细节,而不是只写普通字面量时,应该进入哪一篇”。 +## Agent 字符串/文本判断流程 + +1. 先判断要写普通字符串、宽串/UTF8 前缀、原始字符串,还是字符串边界行为。 +2. 默认优先普通字符串;只有编码或宽串需求明确时才使用 `L""` / `U""`。 +3. 字符串索引、切片、转义和 `#0` 行为只照已验证示例写。 +4. 字符串表达式行为回看表达式页,不要在文本页发明运算规则。 +5. 没有已验证代码块时不要发明字符串/文本写法。 + ## 必须记住的规则 - 普通字符串、`L""` 宽串、`U""` UTF8 前缀串,以及 `%%` 原始字符串,当前解释器都接受。 @@ -56,6 +64,15 @@ end. - 说明 `%%tag ...%%tag` 可以用标识符配对 - 也说明 `%%` 原始字符串支持直接跨行 +代码块身份:已验证输出片段 + +```text +ABC +A"B'C +A +B +``` + 带前缀的原始字符串: 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/21_external_calls_and_threads.md b/docs/tsl/syntax/21_external_calls_and_threads.md index 7548d5a8..7a803e67 100644 --- a/docs/tsl/syntax/21_external_calls_and_threads.md +++ b/docs/tsl/syntax/21_external_calls_and_threads.md @@ -14,6 +14,14 @@ 回答“普通函数怎么写已经清楚后,外部 DLL、函数指针、多线程这些系统交互能力应该去哪里查”。 +## Agent 外部调用/线程判断流程 + +1. 先判断要声明外部函数、绑定函数指针,还是处理线程相关能力。 +2. 外部函数声明只照已验证的调用约定、`external` 和可选 `name` 形态写。 +3. DLL 名优先写字面量或已验证类常量,不要拼接表达式。 +4. 函数指针包装和线程相关能力只照本页已验证边界写。 +5. 没有已验证代码块时不要发明外部调用/线程写法。 + ## 必须记住的规则 - 当前解释器接受 `function Name(...): Type; stdcall|cdecl; external "dll" [name "symbol"];` 这类外部函数声明。 @@ -24,6 +32,7 @@ - 当前已验证的 DLL 名写法包括字面量字符串和类常量字符串;不要把字符串拼接表达式直接当成稳定写法。 - `MakeInstance(ThisFunction(Func), "cdecl", 0)` 当前可以把 TSL 函数包装成 C 调用约定函数指针。 - `MakeInstance(..., "cdecl", 1)` 当前在 `Win64` 下已经拿到最小线程正例,可以直接交给 `CreateThread`。 +- 外部库名和调用约定要按目标平台选择;不要把 Windows 的 `kernel32.dll` 示例直接复制到 Linux,或把 Linux 的 `.so` 示例直接复制到 Windows。 - 当前页的线程示例是 Windows 专题,用到了 `kernel32.dll`。 ## 已验证语法 @@ -46,6 +55,32 @@ end. - 说明当前解释器接受 `stdcall` + `external "dll" name "symbol"` 这类外部函数声明骨架 - 也说明 TSL 里的函数名可以和 DLL 导出名不同,再通过 `name "ExportName"` 绑定 +代码块身份:已验证输出片段 + +```text +1 +``` + +Linux / POSIX 环境的同类最小骨架: + +代码块身份:已验证可执行示例 + +```tsl +program test; +function getpid(): integer; cdecl; external "libc.so.6"; +begin + WriteLn(getpid() > 0); +end. +``` + +代码块身份:已验证输出片段 + +```text +1 +``` + +这段只说明 Linux / POSIX 目标下可以用 `.so` 库名声明外部函数;生成代码时仍要先判断用户的目标平台。 + 当本地函数名和 DLL 导出名一致时,`name` 可以省略: 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/22_namespace_libpath_and_unit_runtime.md b/docs/tsl/syntax/22_namespace_libpath_and_unit_runtime.md index c579a45b..37d7f576 100644 --- a/docs/tsl/syntax/22_namespace_libpath_and_unit_runtime.md +++ b/docs/tsl/syntax/22_namespace_libpath_and_unit_runtime.md @@ -21,6 +21,14 @@ - `NameSpace "..."`、`tsl.conf` 和 `-LIBPATH` 怎样影响 `.tsf` 查找 - `syssettsllibpath()` / `sysgettsllibpath()` 怎样在运行时改查找路径 +## Agent 命名空间/Libpath 判断流程 + +1. 先判断任务是单元命名空间访问、`-LIBPATH` 查找,还是配置文件查找。 +2. 路径和查找顺序只写平台中立规则,不写某个开发机或容器路径。 +3. `-LIBPATH` 只作为运行时部署/查找规则,不写进普通语法示例。 +4. 未验证的路径替换和父目录继承不要当成稳定规则。 +5. 没有已验证代码块时不要发明命名空间/Libpath 写法。 + ## 必须记住的规则 - 完整 `unit` 形态可以包含 `interface`、`implementation`、`initialization`、`finalization`,并以 `end.` 结束。 @@ -86,6 +94,18 @@ end. - 这说明顶层写了 `uses DemoUnit;` 之后,`initialization` 不是立刻执行,而是在第一次真正读 `DemoUnit` 成员时触发。 - `finalization` 在脚本主体输出结束后触发。 +代码块身份:已验证输出片段 + +```text +BEFORE +INIT +7 +1 +101 +11 +FINAL +``` + ### `unit` 成员的读取边界 直接限定读取: diff --git a/docs/tsl/syntax/23_object_runtime_and_introspection.md b/docs/tsl/syntax/23_object_runtime_and_introspection.md index 76519f9e..baef551c 100644 --- a/docs/tsl/syntax/23_object_runtime_and_introspection.md +++ b/docs/tsl/syntax/23_object_runtime_and_introspection.md @@ -14,6 +14,14 @@ 回答“类已经会声明、继承、构造之后,怎样检查类信息、函数信息和对象运行时状态”。 +## Agent 对象运行时/反射判断流程 + +1. 先判断要做类查找、函数查找、对象检查,还是运行时成员访问。 +2. 反射入口优先照 `FindClass`、`FindFunction` 等已验证示例写。 +3. 对象模型本身回看 `09_objects_and_classes.md`,不要在运行时页发明类声明语法。 +4. 函数值调用边界回看函数页,避免把函数指针直接当普通函数调用。 +5. 没有已验证代码块时不要发明对象运行时/反射写法。 + ## 必须记住的规则 - 对象值当前可以用 `ifObj(...)` 做显式判定。 @@ -52,6 +60,12 @@ end. - 输出 `1` - 说明 `CreateObject(...)` 得到的对象值当前可以用 `ifObj(...)` 判定 +代码块身份:已验证输出片段 + +```text +1 +``` + `FindClass(...)` 与类类型变量: 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/24_builtin_runtime_objects.md b/docs/tsl/syntax/24_builtin_runtime_objects.md index 46d33223..5dc7d7b1 100644 --- a/docs/tsl/syntax/24_builtin_runtime_objects.md +++ b/docs/tsl/syntax/24_builtin_runtime_objects.md @@ -14,6 +14,14 @@ 回答“写普通 TSL/TSF 脚本时,哪些内置对象可以直接创建,最小可用接口是什么,哪些对象其实依赖特定运行上下文”。 +## Agent 内置运行时对象判断流程 + +1. 先判断要访问哪个内置运行时对象,以及它是不是语言对象而非业务 API。 +2. 只照本页已验证的最小读写路径使用内置对象。 +3. 不要把金融业务上下文对象和语言运行时对象混在一起。 +4. 需要系统参数或 `with` 后缀时跳转到运行时上下文页。 +5. 没有已验证代码块时不要发明内置运行时对象写法。 + ## 必须记住的规则 - 当前 CLI 里,`TStringList` 已验证可以直接 `CreateObject("TStringList")` 创建。 @@ -50,6 +58,15 @@ end. - 依次输出 `B=bbb`、`bbb`、`3`、`4` - 说明当前既可以按数字下标取整行,也可以按键名取 `Name=Value` 里的值 +代码块身份:已验证输出片段 + +```text +B=bbb +bbb +3 +4 +``` + ### `TStream` 家族的最小可靠入口:`TMemoryStream` 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/25_set_operations.md b/docs/tsl/syntax/25_set_operations.md index c09a9618..8ca95afa 100644 --- a/docs/tsl/syntax/25_set_operations.md +++ b/docs/tsl/syntax/25_set_operations.md @@ -14,6 +14,14 @@ 回答“某个元素是否在数组里、某组元素是否构成子集、某一行是否存在于结果集中,以及两个结果集如何做并集、交集、差集和对称差集”。 +## Agent 集合运算判断流程 + +1. 先判断要写 `in`、`sqlin`、否定集合判断,还是结果集集合操作。 +2. 基础成员判断优先照表达式页和本页已验证示例写。 +3. `not in` 与 `not sqlin` 是已验证否定写法,不要改成未验证组合。 +4. 复杂查询或矩阵数据跳转到 TS-SQL / 矩阵页面。 +5. 没有已验证代码块时不要发明集合运算写法。 + ## 必须记住的规则 - `in` / `not in` 处理的是元素存在关系,以及左侧为数组时的子集关系。 @@ -54,6 +62,15 @@ end. - `array(1, 2) in array(1)` 输出 `0` - `1 not in array(0, 2)` 输出 `1` +代码块身份:已验证输出片段 + +```text +1 +0 +1 +1 +``` + ### `sqlin` 与 `not sqlin` `sqlin` 改成按整行判断左侧是否存在于右侧结果集中: diff --git a/docs/tsl/syntax/26_matrix_deep_dive.md b/docs/tsl/syntax/26_matrix_deep_dive.md index 08d20a3c..33a449be 100644 --- a/docs/tsl/syntax/26_matrix_deep_dive.md +++ b/docs/tsl/syntax/26_matrix_deep_dive.md @@ -14,6 +14,14 @@ 回答“怎样直接构造全零矩阵、全一矩阵、随机矩阵、单位矩阵、空矩阵和数列数组,以及怎样拿到矩阵的行数、列数、行索引和列索引”。 +## Agent 矩阵深水判断流程 + +1. 先判断要读矩阵尺寸、列索引、矩阵选择,还是矩阵与数组转换。 +2. 基础数组和矩阵样比较先回看 `13_matrix_and_collections.md`。 +3. `MRows` / `MCols` / `MSize` 等函数只照已验证返回形态写。 +4. 不要把列索引数组误当成单个数字。 +5. 没有已验证代码块时不要发明矩阵深水写法。 + ## 必须记住的规则 - `Zeros(...)`、`Ones(...)`、`Rand(...)`、`Nils(...)`、`Eye(...)` 都可以直接用于矩阵初始化。 @@ -42,6 +50,9 @@ begin E1 := Eye(3); R1 := Rand(2, 3); T1 := Zeros(2, array("A", "B")); + WriteLn(Length(Z1)); + WriteLn(MRows(Z2)); + WriteLn(MCols(Z2)); end. ``` @@ -55,6 +66,14 @@ end. - `Rand(2, 3)` 的行数是 `2`、列数是 `3` - `Zeros(2, array("A", "B"))` 的行数是 `2`、列数是 `2`,并且 `T1[0]["A"]`、`T1[0]["B"]`、`T1[1]["A"]`、`T1[1]["B"]` 都是 `0` +代码块身份:已验证输出片段 + +```text +3 +2 +3 +``` + ### `->` 数列数组初始化 默认步长为 `1`: diff --git a/docs/tsl/syntax/27_fmarray.md b/docs/tsl/syntax/27_fmarray.md index 232a7f5d..a7051162 100644 --- a/docs/tsl/syntax/27_fmarray.md +++ b/docs/tsl/syntax/27_fmarray.md @@ -14,6 +14,14 @@ 回答“什么时候该用 `FMArray` 而不是普通 `array`,以及当前解释器里最可靠的 `FMArray` 写法到底有哪些”。 +## Agent FMArray 判断流程 + +1. 先判断是否确实需要 `FMArray`,普通数组能解决时先用普通数组。 +2. 构造、类型判断、尺寸读取、转置、连接和 TS-SQL 参与只照本页已验证形态写。 +3. `insert`、`delete`、`update` 的收尾形式分别判断,不要互相套用。 +4. FMArray 错误边界按本页反例处理,不要凭普通数组经验修写法。 +5. 没有已验证代码块时不要发明 FMArray 写法。 + ## 必须记住的规则 - `fmarray[...]` 可以直接构造 `FMArray` 常量。 @@ -66,6 +74,15 @@ end. - `f2` 的行数是 `2`、列数是 `2` - `f2` 四个单元依次是 `1`、`2`、`3`、`4` +代码块身份:已验证输出片段 + +```text +27 +0 +1 +1 +``` + ### `MInit`、`MInitDiag`、`MRand` 代码块身份:已验证可执行示例 @@ -516,7 +533,7 @@ WriteLn(f["A"]); delete from d where [1] = 4 end; ``` -这类写法不要直接当成当前默认模板。当前解释器下,我实测 `delete ... end;` 会报 `Statement missing terminator`;已验证可用的是 `delete ...;`。 +这类写法不要直接当成当前默认模板。当前已验证结果显示,`delete ... end;` 会报 `Statement missing terminator`;已验证可用的是 `delete ...;`。 代码块身份:反例 / 不可照写 diff --git a/docs/tsl/syntax/28_ts_sql_core.md b/docs/tsl/syntax/28_ts_sql_core.md index 076ee31e..1316e6b7 100644 --- a/docs/tsl/syntax/28_ts_sql_core.md +++ b/docs/tsl/syntax/28_ts_sql_core.md @@ -14,6 +14,14 @@ 回答“第一次写 TS-SQL 时,怎样在不碰 join、数据库表和对象化写回接口的前提下,稳定写出最小查询”。 +## Agent TS-SQL Core 判断流程 + +1. 先判断要写 `select`、`sselect`、`vselect` 还是 `mselect`。 +2. 内存数组查询优先从 `select ... from T end` 最小骨架起手。 +3. 二维结果集字段访问用 `["字段名"]`,一维数组优先用 `ThisRow` 和 `ThisRowIndex`。 +4. join 和进阶聚合跳转到 `29_ts_sql_advanced.md`,不要在核心页发明写法。 +5. 没有已验证代码块时不要发明 TS-SQL Core 写法。 + ## 必须记住的规则 - TS-SQL 查询以 `select`、`sselect`、`vselect` 或 `mselect` 开始,以 `end` 结束。 @@ -43,6 +51,7 @@ begin ("A": 1, "B": 2) ); R := select ["A"], ["B"] from T end; + WriteLn(Length(R)); end. ``` @@ -52,6 +61,12 @@ end. - 三行依次是 `(1,3)`、`(2,1)`、`(1,2)` - 说明 `select ["A"], ["B"] from T end` 会按原顺序返回二维结果集 +代码块身份:已验证输出片段 + +```text +3 +``` + `select *` 也已验证可用: 代码块身份:已验证可执行示例 @@ -219,7 +234,7 @@ R := select A from T end; R := select [0] from X end; ``` -我在 `2026-04-15` 的实测里,这种对一维数组直接用 `[0]` 的写法虽然返回了长度为 `3` 的结果,但取到的值是 `nil`,不能当成可靠入口。对一维数组应改用 `ThisRow` 和 `ThisRowIndex`。 +当前已验证结果显示,这种对一维数组直接用 `[0]` 的写法虽然返回了长度为 `3` 的结果,但取到的值是 `nil`,不能当成可靠入口。对一维数组应改用 `ThisRow` 和 `ThisRowIndex`。 ## 跳转指引 diff --git a/docs/tsl/syntax/29_ts_sql_advanced.md b/docs/tsl/syntax/29_ts_sql_advanced.md index 90a68995..2114cc82 100644 --- a/docs/tsl/syntax/29_ts_sql_advanced.md +++ b/docs/tsl/syntax/29_ts_sql_advanced.md @@ -14,6 +14,14 @@ 回答“基础 `select` 已经会写以后,怎样继续处理多表联接、分组后组内再查、以及极值对应行的引用值”。 +## Agent TS-SQL Advanced 判断流程 + +1. 先确认核心 `select ... from ... end` 已能表达基础查询,再进入进阶语法。 +2. 多表 join、`ThisGroup`、`ThisRowIndex` 和极值引用只照本页已验证示例写。 +3. 字段访问和返回形态仍遵守核心 TS-SQL 页规则。 +4. 不要把数据库 SQL 方言直接迁移到 TS-SQL。 +5. 没有已验证代码块时不要发明 TS-SQL Advanced 写法。 + ## 必须记住的规则 - 多表 `join` 时,字段访问应写成 `[表序号].["字段名"]`。 @@ -44,6 +52,10 @@ begin R := select [1].["ID"], [1].["V1"], [2].["V2"] from A join B on [1].["ID"] = [2].["ID"] end; + WriteLn(Length(R)); + WriteLn(R[0]["ID"]); + WriteLn(R[0]["V1"]); + WriteLn(R[0]["V2"]); end. ``` @@ -53,6 +65,15 @@ end. - 唯一一行是 `(1,10,100)` - 说明当前解释器接受 `from A join B on ...`,并接受 `[1].["字段"]`、`[2].["字段"]` 这种多表字段访问 +代码块身份:已验证输出片段 + +```text +1 +1 +10 +100 +``` + ### `ThisGroup` 分组后可在组内继续做子查询: diff --git a/docs/tsl/syntax/30_runtime_services_and_global_cache.md b/docs/tsl/syntax/30_runtime_services_and_global_cache.md index 4a51f526..2369d850 100644 --- a/docs/tsl/syntax/30_runtime_services_and_global_cache.md +++ b/docs/tsl/syntax/30_runtime_services_and_global_cache.md @@ -14,6 +14,14 @@ 回答“网格调用现在怎样写,全局缓存最稳的读写方式是什么,以及什么时候缓存值会失效或脱离缓存身份”。 +## Agent 运行时服务/全局缓存判断流程 + +1. 先判断要用 `#` 网格调用、`timeout` 后缀,还是全局缓存函数。 +2. 运行时服务只照本页已验证最小路径写,不要猜测隐藏参数。 +3. 全局缓存读写要成对出现,并明确 key/value 生命周期。 +4. 普通函数调用回到函数页,不要把运行时服务写成普通语法糖。 +5. 没有已验证代码块时不要发明运行时服务/全局缓存写法。 + ## 必须记住的规则 - 网格调用的最小写法是 `r := #Func(args);`。 @@ -48,6 +56,12 @@ end. - `#AddOne(5)` 可以执行 - `dupvalue(r)` 返回最终结果 `6` +代码块身份:已验证输出片段 + +```text +6 +``` + ### 网格调用的 `timeout` 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/31_complex_and_weakref.md b/docs/tsl/syntax/31_complex_and_weakref.md index a797fa1b..a4f97bc0 100644 --- a/docs/tsl/syntax/31_complex_and_weakref.md +++ b/docs/tsl/syntax/31_complex_and_weakref.md @@ -14,6 +14,14 @@ 回答“复数现在到底怎样写,弱引用到底有哪些语法真的能用,哪些历史资料里的写法今天不能直接照抄”。 +## Agent 复数/弱引用判断流程 + +1. 先判断任务需要复数,还是弱引用/自动弱引用。 +2. 复数字面量和比较只照本页已验证示例写。 +3. 访问弱引用前先做 `checkweakref(...)` 判定,不要假设失效弱引用安全返回 nil。 +4. 类内 `WeakRef;` / `AutoRef;` 段落式写法属于反例,不要照写。 +5. 没有已验证代码块时不要发明复数/弱引用写法。 + ## 必须记住的规则 - 复数字面量可以直接写成 `a + bj`,也可以用 `complex(a, b)` 构造。 @@ -56,6 +64,15 @@ end. - `imag(4 + 3j)` 返回 `3` - `complex(5, -2)` 打印结果是 `5-2j` +代码块身份:已验证输出片段 + +```text +41 +1 +4 +3 +``` + ### 共轭、模与等值比较 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/32_object_overloads_and_iteration.md b/docs/tsl/syntax/32_object_overloads_and_iteration.md index 2bd1b58c..4c751db0 100644 --- a/docs/tsl/syntax/32_object_overloads_and_iteration.md +++ b/docs/tsl/syntax/32_object_overloads_and_iteration.md @@ -14,6 +14,14 @@ 回答“当类不只是普通对象,而要直接参与 `obj + x`、`obj[index]`、`for v in obj`、`mrows(obj)` 这类语言级操作时,当前解释器到底支持哪些真实写法”。 +## Agent 对象重载/迭代判断流程 + +1. 先判断要重载二元算符、下标、`for in`,还是矩阵尺寸函数。 +2. 对象重载只照本页已验证的 `operator` 签名写,不要从历史资料扩展未知重载。 +3. 普通对象模型先回看 `09_objects_and_classes.md`,不要在重载页发明类基础语法。 +4. 未列入本页主干的重载族先视为未验证。 +5. 没有已验证代码块时不要发明对象重载/迭代写法。 + ## 必须记住的规则 - 对象二元算符重载当前最小可靠形态是成员方法 `function operator + (data);` 这一类写法。 @@ -78,6 +86,15 @@ end. - 说明 `obj + value` 当前可以通过成员 `operator +` 接管 - 说明带 `isLeft` 的比较算符当前可以同时处理 `obj < value` 和 `value < obj` +代码块身份:已验证输出片段 + +```text +20 +0 +1 +1 +``` + ### `[]` 重载:`operator[]` / `operator[1]` 代码块身份:已验证可执行示例 diff --git a/docs/tsl/syntax/index.md b/docs/tsl/syntax/index.md index c28bde23..447c5763 100644 --- a/docs/tsl/syntax/index.md +++ b/docs/tsl/syntax/index.md @@ -6,7 +6,7 @@ 是否含已验证反例:否 遇到不确定时跳转到:[02_quickstart.md](02_quickstart.md)(优先)、[03_core_model.md](03_core_model.md)、[07_expressions_and_operators.md](07_expressions_and_operators.md)、[09_objects_and_classes.md](09_objects_and_classes.md)、[12_pitfalls.md](12_pitfalls.md) -这里是 TSL 语法手册入口。只处理“语言怎么写”,不处理金融业务语义。 +这里是 TSL 语法手册入口。只处理“agent 应该怎样识别并生成语言写法”,不处理金融业务语义。本文档不是人类教程;它给 agent 提供判断顺序、关键词分流和可靠示例入口。 ## 元数据读法 @@ -20,10 +20,20 @@ ## 这本手册怎么读 -- 默认先走“按任务跳转”,不要先顺序读完整套。 +- 默认先走“Agent 判断流程”和“按任务跳转”,不要先顺序读完整套。 - 只有刻意系统学习时,才按下面的“推荐读法”顺序进入。 - 当前这套正式语法手册不再只覆盖主线入门,而是负责完整吸收旧 8 个语言章节。 +## Agent 判断流程 + +1. 先识别用户是否已经给出 `.tsl` / `.tsf` 后缀;给出后缀时,后缀就是判断依据。 +2. 未给后缀时,再按交付目标判断:可执行代码对应 `.tsl`,通用模块对应 `.tsf`;仍不明确时向用户确认。 +3. 如果是 `.tsl`,按可执行脚本处理:先写语句区,语句按顺序执行;需要函数/类时,把声明区放在语句区之后。 +4. 如果是 `.tsf`,按模块/函数扩展处理:生成可部署到解释器 `funcext` 的声明;部署后脚本可以直接调用暴露的顶层函数。 +5. 再识别关键词属于值/变量、函数、类、unit、表达式、控制流、运行时、TS-SQL 或其他专题。 +6. 只进入关键词命中的最小页面;写代码前优先复制已验证可执行示例的结构,不要从多个页面拼接未经验证的新骨架。 +7. 遇到反例、解释器差异或资料冲突时,先按 [12_pitfalls.md](12_pitfalls.md) 和对应专题页收口;仍无结论时不要发明语法。 + ## 如果你马上要写 TSL 1. [02_quickstart.md](02_quickstart.md)(先看语言核心事实速查和最短骨架) diff --git a/rulesets/tsl/index.md b/rulesets/tsl/index.md index 640c73a7..378de56a 100644 --- a/rulesets/tsl/index.md +++ b/rulesets/tsl/index.md @@ -16,10 +16,22 @@ ## TSL 核心约定(不可违反) -- 文件结构:一文件一顶层声明;文件名 = 声明名;`.tsl` 仅 `function`,`.tsf` 可 `function/class/unit` +- 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据;未给后缀时,再根据用户交付目标判断。 +- `.tsl` 是可执行脚本:语句区按顺序执行;如果需要函数/类,放在脚本语句后的函数/类声明区。 +- `.tsf` 是模块/函数扩展文件:部署到解释器 `funcext` 后,脚本可直接调用其中暴露的顶层函数。 - 格式:4 空格缩进;关键字小写;多语句用 `begin/end` - 命名:类型/函数/property `PascalCase`;变量/参数 `snake_case`;私有 `snake_case_`;常量 `kPascalCase` +## agent 判断流程 + +1. 用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据。 +2. 用户未给后缀时,先判断交付目标是可执行代码还是通用模块;可执行代码对应 `.tsl`,通用模块对应 `.tsf`。 +3. 交付目标仍不明确时向用户确认,不要替用户发明文件形态。 +4. 写 `.tsl` 时,先生成顺序执行的语句区;调用本脚本内函数时,函数声明写在语句区之后。 +5. 写 `.tsf` 时,只生成模块/扩展声明;顶层函数部署到解释器 `funcext` 后才可被脚本直接调用。 +6. 如果关键词命中业务、函数库、模块集成或项目执行信息,按 `docs/tsl/index.md` 分流,不把所有问题都当语法问题。 +7. 不确定时先找已验证可执行示例、反例页和对应专题页;没有文档结论时不要发明语法,改为向用户确认或记录文档缺口。 + ## 安全红线(不可触碰) - 不得在代码/日志/注释中写入明文密钥、密码、Token、API Key diff --git a/tests/test_tsl_entrypoints_consistency.py b/tests/test_tsl_entrypoints_consistency.py index f17a7b02..abcdc3cf 100644 --- a/tests/test_tsl_entrypoints_consistency.py +++ b/tests/test_tsl_entrypoints_consistency.py @@ -3,12 +3,54 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[1] RULESET_TSL = ROOT / "rulesets" / "tsl" / "index.md" +TSL_INTRODUCTION = ROOT / "docs" / "tsl" / "syntax" / "01_introduction.md" +TSL_QUICKSTART = ROOT / "docs" / "tsl" / "syntax" / "02_quickstart.md" +TSL_CORE_MODEL = ROOT / "docs" / "tsl" / "syntax" / "03_core_model.md" +TSL_VALUES = ROOT / "docs" / "tsl" / "syntax" / "04_values_and_literals.md" +TSL_VARIABLES = ROOT / "docs" / "tsl" / "syntax" / "05_variables_and_constants.md" +TSL_FUNCTIONS = ROOT / "docs" / "tsl" / "syntax" / "06_functions_and_calls.md" +TSL_EXPRESSIONS = ROOT / "docs" / "tsl" / "syntax" / "07_expressions_and_operators.md" README = ROOT / "README.md" PLAYBOOK_EXAMPLE = ROOT / "playbook.toml.example" SKILLS_DOC = ROOT / "SKILLS.md" TEMPLATES_CI_README = ROOT / "templates" / "ci" / "README.md" TEMPLATES_README = ROOT / "templates" / "README.md" REMOVED_TSL_GUIDE = ROOT / "skills" / "tsl-guide" +TSL_REFERENCE_INDEX = ROOT / "docs" / "tsl" / "reference" / "index.md" +TSL_REFERENCE_CATALOG_INDEX = ROOT / "docs" / "tsl" / "reference" / "catalog" / "index.md" +TSL_REFERENCE_VERIFIED_INDEX = ROOT / "docs" / "tsl" / "reference" / "verified" / "index.md" +TSL_REFERENCE_VERIFIED_CORE = ROOT / "docs" / "tsl" / "reference" / "verified" / "core.md" +TSL_REFERENCE_UNAVAILABLE = ROOT / "docs" / "tsl" / "reference" / "unavailable_methods.md" +TSL_FINANCE_INDEX = ROOT / "docs" / "tsl" / "finance" / "index.md" +TSL_MODULES_INDEX = ROOT / "docs" / "tsl" / "modules" / "index.md" + +TSL_REMAINING_SYNTAX_AGENT_SECTIONS = { + "08_control_flow.md": "Agent 控制流判断流程", + "09_objects_and_classes.md": "Agent 对象/类判断流程", + "10_units_and_scope.md": "Agent unit/作用域判断流程", + "11_runtime_context_and_with.md": "Agent 运行时上下文判断流程", + "12_pitfalls.md": "Agent 常见误写判断流程", + "13_matrix_and_collections.md": "Agent 矩阵/集合判断流程", + "14_resultset_and_filters.md": "Agent 结果集/过滤判断流程", + "15_ts_sql.md": "Agent TS-SQL 判断流程", + "16_debug_and_profiler.md": "Agent 调试/Profiler 判断流程", + "18_lexical_structure_and_compile_options.md": "Agent 词法/编译选项判断流程", + "19_types_and_conversions.md": "Agent 类型/转换判断流程", + "20_strings_and_text.md": "Agent 字符串/文本判断流程", + "21_external_calls_and_threads.md": "Agent 外部调用/线程判断流程", + "22_namespace_libpath_and_unit_runtime.md": "Agent 命名空间/Libpath 判断流程", + "23_object_runtime_and_introspection.md": "Agent 对象运行时/反射判断流程", + "24_builtin_runtime_objects.md": "Agent 内置运行时对象判断流程", + "25_set_operations.md": "Agent 集合运算判断流程", + "26_matrix_deep_dive.md": "Agent 矩阵深水判断流程", + "27_fmarray.md": "Agent FMArray 判断流程", + "28_ts_sql_core.md": "Agent TS-SQL Core 判断流程", + "29_ts_sql_advanced.md": "Agent TS-SQL Advanced 判断流程", + "30_runtime_services_and_global_cache.md": "Agent 运行时服务/全局缓存判断流程", + "31_complex_and_weakref.md": "Agent 复数/弱引用判断流程", + "32_object_overloads_and_iteration.md": "Agent 对象重载/迭代判断流程", +} + class TslEntrypointsConsistencyTests(unittest.TestCase): def test_ruleset_lists_canonical_tsl_layers(self): @@ -64,6 +106,512 @@ class TslEntrypointsConsistencyTests(unittest.TestCase): self.assertNotIn("tsl-guide", SKILLS_DOC.read_text(encoding="utf-8")) self.assertNotIn("$tsl-guide", RULESET_TSL.read_text(encoding="utf-8")) + def test_tsl_ruleset_describes_agent_first_file_model(self): + text = RULESET_TSL.read_text(encoding="utf-8") + + self.assertIn("agent 判断流程", text) + self.assertIn("用户已给出 `.tsl` / `.tsf` 后缀时,后缀就是判断依据", text) + self.assertIn("`.tsl` 是可执行脚本", text) + self.assertIn("语句区按顺序执行", text) + self.assertIn("函数/类声明区", text) + self.assertIn("`.tsf` 是模块/函数扩展文件", text) + self.assertIn("funcext", text) + self.assertIn("仍不明确时向用户确认", text) + self.assertNotIn("`.tsl` 仅 `function`", text) + self.assertNotIn("脚本优先 `.tsl`,可复用扩展优先 `.tsf`", text) + + def test_tsl_syntax_docs_keep_script_then_declarations_model(self): + quickstart = (ROOT / "docs/tsl/syntax/02_quickstart.md").read_text( + encoding="utf-8" + ) + core_model = (ROOT / "docs/tsl/syntax/03_core_model.md").read_text( + encoding="utf-8" + ) + functions = (ROOT / "docs/tsl/syntax/06_functions_and_calls.md").read_text( + encoding="utf-8" + ) + pitfalls = (ROOT / "docs/tsl/syntax/12_pitfalls.md").read_text( + encoding="utf-8" + ) + + for text in (quickstart, core_model, functions, pitfalls): + self.assertIn("语句区", text) + self.assertIn("声明区", text) + + self.assertIn("test();", quickstart) + self.assertIn("function test();", quickstart) + self.assertIn("脚本语句后可以接函数声明", functions) + self.assertIn("不要在声明区后面继续写脚本语句", pitfalls) + self.assertNotIn("不要把顶层函数定义和松散语句混写", quickstart) + self.assertNotIn("不要把顶层函数定义和松散语句混写", core_model) + self.assertNotIn("不要把顶层函数定义和松散语句混在同一个文件模型里", functions) + self.assertIn("后缀就是判断依据", quickstart) + self.assertIn("未给后缀", quickstart) + + def test_tsl_agent_entrypoints_do_not_require_runtime_verification(self): + entrypoints = [ + RULESET_TSL, + ROOT / "docs/tsl/index.md", + ROOT / "docs/tsl/syntax/index.md", + ] + + forbidden_phrases = [ + "用当前解释器验证", + "本地用 `tsl` 验证", + "用 `tsl` 实测", + "最小实测", + "才写最小 `.tsl` / `.tsf` 例子", + ] + + for path in entrypoints: + text = path.read_text(encoding="utf-8") + for phrase in forbidden_phrases: + self.assertNotIn(phrase, text, msg=f"{phrase!r} found in {path}") + + def test_tsl_index_requires_maintainer_verified_examples_before_publication(self): + text = (ROOT / "docs/tsl/index.md").read_text(encoding="utf-8") + + self.assertIn("维护者入文档前验证", text) + self.assertIn("代码库样例", text) + self.assertIn("环境验证通过", text) + self.assertIn("不能写成语法事实", text) + self.assertIn("验证过程不写进语法页", text) + self.assertIn("agent 不需要自行执行验证", text) + + def test_tsl_introduction_is_agent_first_decision_model(self): + text = TSL_INTRODUCTION.read_text(encoding="utf-8") + + self.assertIn("agent", text) + self.assertIn("后缀就是判断依据", text) + self.assertIn("用户未给后缀", text) + self.assertIn("可执行代码对应 `.tsl`", text) + self.assertIn("通用模块对应 `.tsf`", text) + self.assertIn("`.tsl` 可执行脚本第一印象", text) + self.assertIn("`.tsf` 通用模块第一印象", text) + self.assertIn("不要发明语法", text) + self.assertIn("test();", text) + self.assertIn("function test();", text) + self.assertIn("function Test1();", text) + + self.assertNotIn("先看顶层主体", text) + self.assertNotIn("顶层主体优先收敛成四类", text) + + def test_tsl_quickstart_is_agent_coding_gate(self): + text = TSL_QUICKSTART.read_text(encoding="utf-8") + + self.assertIn("Agent 快速落代码流程", text) + self.assertIn("先看用户有没有指定 `.tsl` / `.tsf` 后缀", text) + self.assertIn("再根据交付目标判断 `.tsl` 或 `.tsf`", text) + self.assertIn("只从 `代码块身份:已验证可执行示例` 的骨架起手", text) + self.assertIn("不要发明语法", text) + self.assertIn("不要在声明区后面继续追加脚本语句", text) + + def test_tsl_quickstart_labels_observable_outputs(self): + text = TSL_QUICKSTART.read_text(encoding="utf-8") + + self.assertIn("代码块身份:已验证输出片段", text) + self.assertIn("```text\ntest\n```", text) + self.assertIn("```text\n5\n```", text) + self.assertIn("```text\ntest1\n```", text) + self.assertIn("```text\nhello\n```", text) + + def test_tsl_quickstart_omits_environment_verification_details(self): + text = TSL_QUICKSTART.read_text(encoding="utf-8") + + for phrase in [ + "Docker", + "docker exec", + "LD_LIBRARY_PATH", + "/data/workspace", + "U22Cli", + "gitea-runner", + ]: + self.assertNotIn(phrase, text) + + def test_tsl_core_model_is_agent_file_model_decision_page(self): + text = TSL_CORE_MODEL.read_text(encoding="utf-8") + + self.assertIn("Agent 文件模型判断流程", text) + self.assertIn("后缀是第一证据", text) + self.assertIn("可执行交付", text) + self.assertIn("可复用扩展交付", text) + self.assertIn("不要把 `.tsl` 写成只有顶层函数的模块", text) + self.assertIn("不要把 `.tsf` 写成会直接执行脚本语句的入口", text) + self.assertIn("没有文档证据时不要发明文件模型", text) + self.assertNotIn("误以为扩展名本身就完全决定能否写函数、类或 `unit`", text) + + def test_tsl_core_model_examples_have_verified_outputs(self): + text = TSL_CORE_MODEL.read_text(encoding="utf-8") + + self.assertIn("代码块身份:已验证输出片段", text) + self.assertIn("```text\ntest\n```", text) + self.assertIn("```text\n5\n```", text) + self.assertIn("```text\n1\n```", text) + self.assertIn("```text\ninvalid statement\n```", text) + + def test_tsl_core_model_omits_environment_verification_details(self): + text = TSL_CORE_MODEL.read_text(encoding="utf-8") + + for phrase in [ + "Docker", + "docker exec", + "LD_LIBRARY_PATH", + "/data/workspace", + "U22Cli", + "gitea-runner", + ]: + self.assertNotIn(phrase, text) + + def test_tsl_values_page_is_agent_value_decision_page(self): + text = TSL_VALUES.read_text(encoding="utf-8") + + self.assertIn("Agent 值写法判断流程", text) + self.assertIn("普通值优先从整数、实数、普通字符串、布尔和 `array(...)` 起手", text) + self.assertIn("字符串默认用普通字符串", text) + self.assertIn("`L\"\"` / `U\"\"` 只在编码或宽串场景", text) + self.assertIn("先判断要顺序数组还是字符串键表", text) + self.assertIn("顺序数组和二进制缓冲区下标从 `0` 开始", text) + self.assertIn("字符串下标从 `1` 开始", text) + self.assertIn("没有已验证代码块时不要发明值写法", text) + + def test_tsl_values_key_examples_have_verified_output_snippets(self): + text = TSL_VALUES.read_text(encoding="utf-8") + + self.assertIn("代码块身份:已验证输出片段", text) + self.assertIn("```text\n1\n12.5\nABC\n1\n0\n2\n```", text) + self.assertIn("```text\n10\n20\n0001\nA\nB\nC\n```", text) + self.assertIn("```text\nBCD\n```", text) + self.assertIn("```text\nString index out of bounds\n```", text) + + def test_tsl_values_omits_environment_verification_details(self): + text = TSL_VALUES.read_text(encoding="utf-8") + + for phrase in [ + "Docker", + "docker exec", + "LD_LIBRARY_PATH", + "/data/workspace", + "U22Cli", + "gitea-runner", + ]: + self.assertNotIn(phrase, text) + + def test_tsl_variables_page_is_agent_binding_decision_page(self): + text = TSL_VARIABLES.read_text(encoding="utf-8") + + self.assertIn("Agent 变量/常量判断流程", text) + self.assertIn("普通变量默认直接用 `:=` 首次赋值", text) + self.assertIn("只有用户要求显式声明或遇到 `{$Explicit+}` 时才优先写 `var`", text) + self.assertIn("顶层脚本常量优先用 `const name := value;`", text) + self.assertIn("`const Name = value;` 优先放在函数 `const` 段、`unit` 接口或类成员里", text) + self.assertIn("单变量拆包必须写成 `[name, ] := array(...)`", text) + self.assertIn("没有已验证代码块时不要发明变量/常量写法", text) + + def test_tsl_variables_key_examples_have_verified_output_snippets(self): + text = TSL_VARIABLES.read_text(encoding="utf-8") + + self.assertIn("代码块身份:已验证输出片段", text) + self.assertIn("```text\n1\n2\n```", text) + self.assertIn("```text\n7\n```", text) + self.assertIn("```text\n1\n3\n```", text) + self.assertIn("```text\n1\n1\n```", text) + self.assertIn("```text\nvariable not defined\n```", text) + + def test_tsl_variables_omits_environment_verification_details(self): + text = TSL_VARIABLES.read_text(encoding="utf-8") + + for phrase in [ + "Docker", + "docker exec", + "LD_LIBRARY_PATH", + "/data/workspace", + "U22Cli", + "gitea-runner", + ]: + self.assertNotIn(phrase, text) + + def test_tsl_functions_page_is_agent_function_decision_page(self): + text = TSL_FUNCTIONS.read_text(encoding="utf-8") + + self.assertIn("Agent 函数/调用判断流程", text) + self.assertIn("先判断当前文件是 `.tsl` 还是 `.tsf`", text) + self.assertIn("`.tsl` 中先写语句区,再把 `function` / `procedure` 声明放在后面", text) + self.assertIn("需要返回值时用 `function`,不需要返回值时用 `procedure`", text) + self.assertIn("命名参数只写 `name: value`", text) + self.assertIn("不要把 `name = value` 当成命名参数", text) + self.assertIn("没有已验证代码块时不要发明函数/调用写法", text) + self.assertNotIn("在我于", text) + + def test_tsl_functions_key_examples_have_verified_output_snippets(self): + text = TSL_FUNCTIONS.read_text(encoding="utf-8") + + self.assertIn("代码块身份:已验证输出片段", text) + self.assertIn("```text\ntest\n```", text) + self.assertIn("```text\n2\n```", text) + self.assertIn("```text\n12\n```", text) + self.assertIn("```text\ninvalid statement\n```", text) + + def test_tsl_functions_omits_environment_verification_details(self): + text = TSL_FUNCTIONS.read_text(encoding="utf-8") + + for phrase in [ + "Docker", + "docker exec", + "LD_LIBRARY_PATH", + "/data/workspace", + "U22Cli", + "gitea-runner", + ]: + self.assertNotIn(phrase, text) + + def test_tsl_expressions_page_is_agent_expression_decision_page(self): + text = TSL_EXPRESSIONS.read_text(encoding="utf-8") + + self.assertIn("Agent 表达式/运算符判断流程", text) + self.assertIn("先判断要写赋值、比较、条件求值、表达式对象、空安全访问还是链式比较", text) + self.assertIn("赋值只能用 `:=`", text) + self.assertIn("比较才用 `=`", text) + self.assertIn("条件求值优先用 `flag ? true_value : false_value`", text) + self.assertIn("`if condition then true_value else false_value` 必须带 `else`", text) + self.assertIn("没有已验证代码块时不要发明表达式/运算符写法", text) + self.assertNotIn("当前环境里", text) + + def test_tsl_expressions_key_examples_have_verified_output_snippets(self): + text = TSL_EXPRESSIONS.read_text(encoding="utf-8") + + self.assertIn("代码块身份:已验证输出片段", text) + self.assertIn("```text\n3\n```", text) + self.assertIn("```text\nAB\n```", text) + self.assertIn("```text\n222888\n1\n1\n1\n1\n```", text) + self.assertIn("```text\n1\n0\n```", text) + self.assertIn("```text\n2\n```", text) + self.assertIn("```text\n6\n```", text) + self.assertIn("```text\n1\n7\n1\n```", text) + self.assertIn("```text\ninvalid statement\n```", text) + + def test_tsl_expressions_omits_environment_verification_details(self): + text = TSL_EXPRESSIONS.read_text(encoding="utf-8") + + for phrase in [ + "Docker", + "docker exec", + "LD_LIBRARY_PATH", + "/data/workspace", + "U22Cli", + "gitea-runner", + ]: + self.assertNotIn(phrase, text) + + def test_remaining_tsl_syntax_pages_are_agent_decision_pages(self): + syntax_root = ROOT / "docs" / "tsl" / "syntax" + + for filename, section in TSL_REMAINING_SYNTAX_AGENT_SECTIONS.items(): + with self.subTest(filename=filename): + text = (syntax_root / filename).read_text(encoding="utf-8") + self.assertIn(section, text) + self.assertIn("agent", text.lower()) + self.assertIn("不要发明", text) + + def test_remaining_tsl_syntax_pages_have_verified_output_snippets(self): + syntax_root = ROOT / "docs" / "tsl" / "syntax" + + for filename in TSL_REMAINING_SYNTAX_AGENT_SECTIONS: + with self.subTest(filename=filename): + text = (syntax_root / filename).read_text(encoding="utf-8") + if ( + "代码块身份:已验证可执行示例" in text + or "代码块身份:反例 / 不可照写" in text + ): + self.assertIn("代码块身份:已验证输出片段", text) + + def test_remaining_tsl_syntax_pages_omit_personal_and_environment_details(self): + syntax_root = ROOT / "docs" / "tsl" / "syntax" + forbidden_phrases = [ + "我在", + "当前环境里", + "Docker", + "docker exec", + "LD_LIBRARY_PATH", + "/data/workspace", + "U22Cli", + "gitea-runner", + ] + + for filename in TSL_REMAINING_SYNTAX_AGENT_SECTIONS: + with self.subTest(filename=filename): + text = (syntax_root / filename).read_text(encoding="utf-8") + for phrase in forbidden_phrases: + self.assertNotIn(phrase, text) + + def test_tsl_reference_index_requires_verified_function_workflow(self): + text = TSL_REFERENCE_INDEX.read_text(encoding="utf-8") + + self.assertIn("Agent 函数使用规则", text) + self.assertIn("只从 verified 函数页读取参数类型", text) + self.assertIn("catalog 只是候选函数索引", text) + self.assertIn("确认每个方法的参数类型", text) + self.assertIn("verified/index.md", text) + self.assertIn("verified/core.md", text) + self.assertIn("unavailable_methods.md", text) + self.assertIn("不能把 catalog 里的函数名直接当成可调用事实", text) + self.assertNotIn("verification_failures", text) + self.assertNotIn("函数入库验证流程", text) + self.assertNotIn("验证过程", text) + self.assertNotIn("代码块身份", text) + self.assertNotIn("已验证输出片段", text) + self.assertNotIn("验证失败", text) + + def test_tsl_reference_catalog_is_candidate_index_not_callable_fact(self): + catalog_index = TSL_REFERENCE_CATALOG_INDEX.read_text(encoding="utf-8") + + self.assertIn("候选函数索引", catalog_index) + self.assertIn("候选名没有进入 verified 函数页前不能当成可调用事实", catalog_index) + self.assertIn("../verified/index.md", catalog_index) + self.assertIn("../verified/core.md", catalog_index) + self.assertIn("只从 verified 函数页读取参数类型", catalog_index) + self.assertNotIn("错误参数组合", catalog_index) + self.assertNotIn("参数验证", catalog_index) + + for path in (ROOT / "docs" / "tsl" / "reference" / "catalog").glob("*.md"): + text = path.read_text(encoding="utf-8") + self.assertIn("候选函数索引", text, msg=f"{path.name} missing candidate warning") + self.assertIn("只从 verified 函数页读取参数类型", text) + + def test_tsl_reference_verified_index_routes_to_parameter_fact_pages(self): + text = TSL_REFERENCE_VERIFIED_INDEX.read_text(encoding="utf-8") + + self.assertIn("文档类型:agent 参数事实索引", text) + self.assertIn("core.md", text) + self.assertIn("Abs", text) + self.assertIn("ifInt", text) + self.assertIn("DateToStr", text) + self.assertIn("Length", text) + self.assertIn("只从具体函数页读取接收类型", text) + self.assertNotIn("验证过程", text) + self.assertNotIn("代码块身份", text) + + def test_tsl_reference_verified_core_records_agent_parameter_facts(self): + text = TSL_REFERENCE_VERIFIED_CORE.read_text(encoding="utf-8") + + self.assertIn("文档类型:agent 参数事实表", text) + self.assertIn("接收类型", text) + self.assertIn("返回", text) + self.assertIn("`Abs(value)`", text) + self.assertIn("`ifInt(value)`", text) + self.assertIn("`DateToStr(value)`", text) + self.assertIn("`Length(value)`", text) + self.assertIn("整数", text) + self.assertIn("实数", text) + self.assertIn("字符串", text) + self.assertIn("数组", text) + self.assertIn("日期时间", text) + self.assertNotIn("代码块身份", text) + self.assertNotIn("已验证输出片段", text) + self.assertNotIn("Function Abs execution error", text) + self.assertNotIn("错误参数", text) + + def test_tsl_reference_all_markdown_avoid_verification_logs(self): + for path in (ROOT / "docs" / "tsl" / "reference").rglob("*.md"): + text = path.read_text(encoding="utf-8") + with self.subTest(path=path.relative_to(ROOT)): + self.assertNotIn("验证过程", text) + self.assertNotIn("函数入库验证流程", text) + self.assertNotIn("代码块身份", text) + self.assertNotIn("已验证输出片段", text) + self.assertNotIn("Function Abs execution error", text) + + def test_tsl_reference_unavailable_records_only_unavailable_methods(self): + text = TSL_REFERENCE_UNAVAILABLE.read_text(encoding="utf-8") + + self.assertIn("文档类型:当前测试环境不支持的方法清单", text) + self.assertIn("当前测试环境不支持的方法", text) + self.assertIn("暂无已入档记录", text) + self.assertNotIn("failure", text.lower()) + self.assertNotIn("失败", text) + self.assertNotIn("验证过程", text) + self.assertNotIn("参数类型错误", text) + self.assertNotIn("`Abs(\"-3\")`", text) + self.assertNotIn("`Abs()`", text) + self.assertNotIn("`DateToStr(\"2011-12-31\")`", text) + self.assertNotIn("`DateToStr()`", text) + self.assertNotIn("`Length(123)`", text) + self.assertNotIn("Function Abs execution error", text) + + def test_tsl_reference_docs_do_not_name_valid_parameter_mismatches_as_failures(self): + reference_root = ROOT / "docs" / "tsl" / "reference" + + for path in reference_root.rglob("*.md"): + text = path.read_text(encoding="utf-8") + with self.subTest(path=path.relative_to(ROOT)): + self.assertNotIn("verification_failures", text) + self.assertNotIn("错误参数", text) + self.assertNotIn("验证失败", text) + self.assertNotIn("Abs(\"-3\")", text) + self.assertNotIn("DateToStr(\"2011-12-31\")", text) + self.assertNotIn("Length(123)", text) + + def test_tsl_finance_docs_are_agent_business_decision_pages(self): + finance_root = ROOT / "docs" / "tsl" / "finance" + + for path in finance_root.glob("*.md"): + text = path.read_text(encoding="utf-8") + with self.subTest(path=path.name): + self.assertIn("Agent", text) + self.assertIn("不要发明", text) + self.assertIn("项目实际接口", text) + self.assertNotIn("是否含已验证", text) + self.assertNotIn("代码块身份", text) + self.assertNotIn("验证过程", text) + + def test_tsl_finance_index_routes_all_finance_pages(self): + text = TSL_FINANCE_INDEX.read_text(encoding="utf-8") + + self.assertIn("Agent Finance 路由规则", text) + self.assertIn("entry_decision.md", text) + self.assertIn("market_data_context.md", text) + self.assertIn("series_and_indicator_model.md", text) + self.assertIn("selection_and_signal_patterns.md", text) + self.assertIn("backtest_and_trade_flow.md", text) + + def test_tsl_modules_docs_are_agent_integration_boundary_pages(self): + modules_root = ROOT / "docs" / "tsl" / "modules" + + for path in modules_root.glob("*.md"): + text = path.read_text(encoding="utf-8") + with self.subTest(path=path.name): + self.assertIn("Agent", text) + self.assertIn("不要发明", text) + self.assertNotIn("是否含已验证", text) + self.assertNotIn("代码块身份", text) + self.assertNotIn("验证过程", text) + + def test_tsl_modules_index_routes_all_module_pages(self): + text = TSL_MODULES_INDEX.read_text(encoding="utf-8") + + self.assertIn("Agent Modules 路由规则", text) + self.assertIn("tsbacktesting.md", text) + self.assertIn("tsl_python_interop.md", text) + self.assertIn("wechat_message.md", text) + self.assertIn("pytsl_api.md", text) + + def test_tsl_finance_and_modules_omit_environment_verification_details(self): + forbidden_phrases = [ + "Docker", + "docker exec", + "LD_LIBRARY_PATH", + "/data/workspace", + "U22Cli", + "gitea-runner", + "当前环境里", + "实测", + ] + + for root_name in ("finance", "modules"): + for path in (ROOT / "docs" / "tsl" / root_name).glob("*.md"): + text = path.read_text(encoding="utf-8") + for phrase in forbidden_phrases: + self.assertNotIn(phrase, text, msg=f"{phrase!r} found in {path}") + if __name__ == "__main__": unittest.main()