import re import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[1] RULESET_TSL = ROOT / "rulesets" / "tsl" / "index.md" TSL_QUICKSTART = ROOT / "docs" / "tsl" / "syntax" / "01_quickstart.md" TSL_CORE_MODEL = ROOT / "docs" / "tsl" / "syntax" / "02_core_model.md" TSL_VALUES = ROOT / "docs" / "tsl" / "syntax" / "03_values_and_literals.md" TSL_VARIABLES = ROOT / "docs" / "tsl" / "syntax" / "04_variables_and_constants.md" TSL_FUNCTIONS = ROOT / "docs" / "tsl" / "syntax" / "05_functions_and_calls.md" TSL_EXPRESSIONS = ROOT / "docs" / "tsl" / "syntax" / "06_expressions_and_operators.md" TSL_CONTROL_FLOW = ROOT / "docs" / "tsl" / "syntax" / "07_control_flow.md" TSL_TS_SQL = ROOT / "docs" / "tsl" / "syntax" / "14_ts_sql.md" TSL_TYPES = ROOT / "docs" / "tsl" / "syntax" / "17_types_and_conversions.md" TSL_EXTERNAL = ROOT / "docs" / "tsl" / "syntax" / "18_external_calls_and_threads.md" TSL_MATRIX_DEEP_DIVE = ROOT / "docs" / "tsl" / "syntax" / "22_matrix_deep_dive.md" TSL_FMARRAY = ROOT / "docs" / "tsl" / "syntax" / "23_fmarray.md" TSL_OBJECT_RUNTIME = ROOT / "docs" / "tsl" / "syntax" / "20_object_runtime_and_introspection.md" TSL_BUILTIN_RUNTIME_OBJECTS = ROOT / "docs" / "tsl" / "syntax" / "21_builtin_runtime_objects.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_UNAVAILABLE = ROOT / "docs" / "tsl" / "reference" / "unavailable_methods.md" TSL_MODULES_INDEX = ROOT / "docs" / "tsl" / "modules" / "index.md" TSL_TOOLCHAIN = ROOT / "docs" / "tsl" / "toolchain.md" TSL_REMAINING_SYNTAX_AGENT_SECTIONS = { "07_control_flow.md": "智能体控制流判断流程", "08_objects_and_classes.md": "智能体对象/类判断流程", "09_units_and_scope.md": "智能体 unit/作用域判断流程", "10_runtime_context_and_with.md": "智能体运行时上下文判断流程", "11_pitfalls.md": "智能体常见误写判断流程", "12_matrix_and_collections.md": "智能体数组/矩阵样数据判断流程", "13_resultset_and_filters.md": "智能体结果集/过滤判断流程", "14_ts_sql.md": "智能体 TS-SQL 判断流程", "15_debug_and_profiler.md": "智能体调试/性能分析器判断流程", "16_lexical_structure_and_compile_options.md": "智能体词法/编译选项判断流程", "17_types_and_conversions.md": "智能体类型/转换判断流程", "18_external_calls_and_threads.md": "智能体外部调用/线程判断流程", "19_namespace_libpath_and_unit_runtime.md": "智能体命名空间/Libpath 判断流程", "20_object_runtime_and_introspection.md": "智能体对象运行时/反射判断流程", "21_builtin_runtime_objects.md": "智能体内置运行时对象判断流程", "22_matrix_deep_dive.md": "智能体矩阵深水判断流程", "23_fmarray.md": "智能体 FMArray 判断流程", "24_object_overloads_and_iteration.md": "智能体对象重载/迭代判断流程", } TSL_SYNTAX_MAINLINE_FILES = [ "01_quickstart.md", "02_core_model.md", "03_values_and_literals.md", "04_variables_and_constants.md", "05_functions_and_calls.md", "06_expressions_and_operators.md", "07_control_flow.md", "08_objects_and_classes.md", "09_units_and_scope.md", "10_runtime_context_and_with.md", "12_matrix_and_collections.md", "13_resultset_and_filters.md", "14_ts_sql.md", "15_debug_and_profiler.md", ] TSL_SYNTAX_DEEP_DIVE_FILES = [ "16_lexical_structure_and_compile_options.md", "17_types_and_conversions.md", "18_external_calls_and_threads.md", "19_namespace_libpath_and_unit_runtime.md", "20_object_runtime_and_introspection.md", "21_builtin_runtime_objects.md", "22_matrix_deep_dive.md", "23_fmarray.md", "24_object_overloads_and_iteration.md", ] class TslEntrypointsConsistencyTests(unittest.TestCase): def test_ruleset_lists_canonical_tsl_layers(self): text = RULESET_TSL.read_text(encoding="utf-8") self.assertIn("docs/tsl/index.md", text) self.assertIn("docs/tsl/reference/catalog/datawarehouse.md", text) self.assertIn("docs/tsl/modules/pytsl_api.md", text) self.assertIn("docs/tsl/modules/tsbacktesting.md", text) self.assertIn("docs/tsl/modules/index.md", text) self.assertIn("docs/tsl/reference/index.md", text) tsl_index = (ROOT / "docs/tsl/index.md").read_text(encoding="utf-8") self.assertIn("syntax/index.md", tsl_index) def test_readme_only_lists_two_official_deployment_routes(self): text = README.read_text(encoding="utf-8") self.assertIn("方式一:git subtree", text) self.assertIn("方式二:外部 clone 后执行部署", text) self.assertIn("- `rulesets/typescript/index.md`:TypeScript 核心约定", text) self.assertIn("`project_root`:目标项目根目录", text) self.assertIn("`playbook_root`:相对于 `project_root` 的项目内 Playbook 根目录", text) self.assertIn("不是外部 clone 出来的 Playbook 仓库路径", text) self.assertIn("外部 clone 场景下必须显式填写 `playbook_root`", text) self.assertNotIn("方式二:手动复制快照", text) self.assertNotIn("方式三:CLI 裁剪复制", text) self.assertNotIn("如果省略 `playbook_root`,默认仍部署到 `docs/standards/playbook`", text) def test_playbook_example_defines_playbook_root_as_target_path(self): text = PLAYBOOK_EXAMPLE.read_text(encoding="utf-8") self.assertIn('playbook_root = "docs/standards/playbook"', text) self.assertIn('install_mode = "subtree"', text) self.assertIn("相对于 project_root", text) self.assertIn("不是外部 clone 的 playbook 路径", text) self.assertIn("snapshot 表示从外部 clone 安装快照", text) self.assertNotIn("target_dir", text) def test_deployment_docs_do_not_reference_legacy_terms(self): self.assertNotIn("vendoring", README.read_text(encoding="utf-8")) self.assertNotIn("`.tmp`", README.read_text(encoding="utf-8")) self.assertNotIn("vendoring", SKILLS_DOC.read_text(encoding="utf-8")) self.assertNotIn("vendoring", TEMPLATES_CI_README.read_text(encoding="utf-8")) def test_repo_docs_use_platform_neutral_skills_source_dir(self): readme_text = README.read_text(encoding="utf-8") skills_text = SKILLS_DOC.read_text(encoding="utf-8") templates_text = TEMPLATES_README.read_text(encoding="utf-8") self.assertIn("`skills/`", readme_text) self.assertIn("`skills/`", skills_text) self.assertIn("├── skills/", templates_text) self.assertNotIn("`codex/skills/`", readme_text) self.assertNotIn("`codex/skills/`", skills_text) self.assertNotIn("├── codex/skills/", templates_text) def test_repo_no_longer_ships_tsl_guide_skill(self): self.assertFalse(REMOVED_TSL_GUIDE.exists()) self.assertNotIn("tsl-guide", README.read_text(encoding="utf-8")) 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_decision_router(self): text = RULESET_TSL.read_text(encoding="utf-8") self.assertIn("# TSL 智能体规则", text) self.assertEqual(len(text.splitlines()), 45) self.assertIn("作用:控制智能体在阅读、修改、生成 TSL 代码前的判断顺序", text) self.assertIn("本文件不是 TSL 语法手册", text) self.assertIn("禁止凭 Pascal、Python、JavaScript、TypeScript", text) self.assertIn("代码生成协议", text) self.assertIn("TSL 核心事实", text) self.assertIn("任务路由", text) self.assertIn("文档事实使用策略", text) self.assertRegex( text, r"\| 语法、文件模型、最短骨架、反例\s+\| `docs/tsl/index\.md`\s+\|", ) self.assertNotIn("最终 TSL 自检", text) self.assertNotIn("修改纪律", text) self.assertNotIn("文档入口", text) self.assertNotIn("## 安全规则", text) self.assertNotIn("`docs/tsl/index.md` 或 `docs/tsl/syntax/index.md`", text) self.assertNotIn("代码块身份:可直接照写示例", text) self.assertNotIn("funcext", text) def test_tsl_syntax_docs_keep_script_then_declarations_model(self): quickstart = (ROOT / "docs/tsl/syntax/01_quickstart.md").read_text( encoding="utf-8" ) core_model = (ROOT / "docs/tsl/syntax/02_core_model.md").read_text( encoding="utf-8" ) functions = (ROOT / "docs/tsl/syntax/05_functions_and_calls.md").read_text( encoding="utf-8" ) pitfalls = (ROOT / "docs/tsl/syntax/11_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_reader_entrypoints_are_decision_routes_not_agent_process_docs(self): entrypoints = [ ROOT / "docs/tsl/index.md", ROOT / "docs/tsl/syntax/index.md", ] for path in entrypoints: with self.subTest(path=path.relative_to(ROOT)): text = path.read_text(encoding="utf-8") self.assertIn("任务信号", text) self.assertIn("入口", text) self.assertIn("阻断条件", text) self.assertNotIn("智能体语法判断流程", text) self.assertNotIn("智能体提交前最低自检", text) self.assertNotIn("本套 TSL 文档面向智能体决策", text) self.assertNotIn("## 决策顺序", text) def test_tsl_docs_use_compact_uncertain_header(self): docs_root = ROOT / "docs" / "tsl" for path in docs_root.rglob("*.md"): with self.subTest(path=path.relative_to(ROOT)): text = path.read_text(encoding="utf-8") self.assertNotIn("遇到不确定时跳转到", text) def test_tsl_index_uses_documented_generation_facts(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("项目执行信息会被写成通用 TSL 事实", text) self.assertNotIn("## 路由冲突", text) self.assertNotIn("## 生成前阻断条件", text) def test_tsl_toolchain_is_template_not_execution_authority(self): text = TSL_TOOLCHAIN.read_text(encoding="utf-8") self.assertIn("# TSL 工具链与验证命令模板", text) self.assertIn("文档类型:项目工具链模板页", text) self.assertIn("不是通用执行手册", text) self.assertIn("不是 TSL 语法事实", text) self.assertIn("目标项目已真实填写", text) self.assertIn("仍有 `<...>` 占位符时", text) self.assertIn("不能作为执行依据", text) self.assertIn("不提供真实可执行命令", text) self.assertNotIn("真实 CLI 示例", text) def test_tsl_execution_routes_do_not_treat_toolchain_template_as_facts(self): tsl_index = (ROOT / "docs" / "tsl" / "index.md").read_text(encoding="utf-8") code_style = (ROOT / "docs" / "tsl" / "code_style.md").read_text( encoding="utf-8" ) readme = README.read_text(encoding="utf-8") self.assertIn( "目标项目已真实填写且没有 `<...>` 占位符的 [toolchain.md](toolchain.md)", tsl_index, ) self.assertIn("验证命令或自动化入口", code_style) self.assertIn("目标项目已真实填写且没有 `<...>` 占位符的 [toolchain.md](toolchain.md)", code_style) self.assertNotIn( "遇到不确定时:[naming.md](naming.md)、[syntax/index.md](syntax/index.md)、[toolchain.md](toolchain.md)", code_style, ) # README 中的 Layer 3 表格描述是合理的,不需要额外断言检查 def test_tsl_naming_and_code_style_delegate_file_model_facts_to_core_model(self): naming = (ROOT / "docs" / "tsl" / "naming.md").read_text(encoding="utf-8") code_style = (ROOT / "docs" / "tsl" / "code_style.md").read_text( encoding="utf-8" ) self.assertIn("本页只规定命名后的实体如何取名", naming) self.assertIn("syntax/02_core_model.md", naming) self.assertNotIn("文件只能有一个顶层声明", naming) self.assertNotIn("文件基名必须与该顶层声明名字一致", naming) self.assertNotIn("部署到解释器 `funcext`", naming) self.assertNotIn("语句区按顺序执行", naming) self.assertNotIn("语句区在前,声明区在后", naming) self.assertNotIn("语句区在前,并按顺序执行", naming) self.assertIn("文件模型和顶层声明规则以", code_style) self.assertIn("syntax/02_core_model.md", code_style) self.assertNotIn("语句区在前并按顺序执行", code_style) self.assertNotIn("部署到解释器 `funcext`", code_style) self.assertNotIn("每个 `.tsf` 文件只能有一个顶层声明", code_style) self.assertNotIn("文件基名必须与顶层声明同名", code_style) def test_tsl_syntax_docs_do_not_keep_stale_manual_sequence_metadata(self): self.assertFalse((ROOT / "docs/tsl/syntax/01_introduction.md").exists()) forbidden_phrases = [ "01_introduction.md", "手册位置:", "共 32 篇", "上一篇:", "下一篇:", "完整吸收原 8 个", "原 8 章", "编号说明", ] syntax_paths = sorted((ROOT / "docs" / "tsl" / "syntax").glob("*.md")) numbered_paths = [path for path in syntax_paths if path.name != "index.md"] numbers = [] for path in numbered_paths: match = re.match(r"^(\d{2})_", path.name) self.assertIsNotNone(match, msg=f"missing two-digit prefix: {path.name}") numbers.append(int(match.group(1))) self.assertEqual(list(range(1, len(numbered_paths) + 1)), numbers) self.assertEqual("01_quickstart.md", numbered_paths[0].name) for path in syntax_paths: 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_quickstart_is_agent_coding_gate(self): text = TSL_QUICKSTART.read_text(encoding="utf-8") self.assertIn("智能体快速落代码流程", text) self.assertIn("先看用户有没有指定 `.tsl` / `.tsf` 后缀", text) self.assertIn("再根据交付目标判断 `.tsl` 或 `.tsf`", text) self.assertIn("入口流程、脚本任务或一次性执行逻辑用 `.tsl`", text) self.assertIn("可复用交付物(函数、过程、类、模块或扩展文件)用 `.tsf`", text) self.assertIn("如果只是脚本内部封装函数或类,仍按 `.tsl` 处理", text) self.assertIn("只从 `代码块身份:可直接照写示例` 的骨架起手", text) self.assertIn("不要发明语法", text) self.assertIn("`procedure` 头后不允许写返回类型", text) self.assertIn("不要在声明区后面继续追加脚本语句", text) self.assertIn("普通对象创建默认优先 `new ClassName()`", text) self.assertIn("`createObject(...)` 也是对象创建方式", text) self.assertIn("obj := new MyClass();", text) self.assertNotIn('obj := createObject("MyClass");', 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("智能体文件模型判断流程", text) self.assertIn("后缀是第一证据", text) self.assertIn("入口流程、脚本任务或一次性执行逻辑", text) self.assertIn("可复用交付物(函数、过程、类、模块或扩展文件)", text) self.assertIn("只是脚本内部封装函数或类时,仍按 `.tsl` 处理", text) self.assertIn("`.tsf` 顶层类声明", text) self.assertIn("因为 `.tsl` 脚本内部需要函数或类,就自动改成 `.tsf`", text) self.assertIn("类声明必须使用 `type Name = class ... end;`", text) self.assertIn("普通对象创建默认优先 `new ClassName()`", text) self.assertIn("`createObject(...)` 也是对象创建方式", text) self.assertIn("obj := new MyClass();", text) self.assertNotIn('obj := createObject("MyClass");', 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_object_docs_prefer_new_for_plain_construction(self): text = (ROOT / "docs/tsl/syntax/08_objects_and_classes.md").read_text( encoding="utf-8" ) self.assertIn("创建对象有两种方式:`new ClassName()` 最常用", text) self.assertIn("[02_core_model.md](02_core_model.md)(优先)", text) self.assertNotIn("[09_units_and_scope.md](09_units_and_scope.md)(优先)", text) self.assertIn("命中对象反射 / 运行时状态、内置运行时对象、对象重载 / 迭代时", text) self.assertIn("21_builtin_runtime_objects.md", text) self.assertIn("24_object_overloads_and_iteration.md", text) self.assertIn("`createObject(...)` 作为次选", text) self.assertIn("普通本地类创建默认用 `new ClassName()`", text) self.assertIn("普通本地类实例化默认生成 `new ClassName()`", text) self.assertIn("普通类成员默认显式写 `public` 段", text) self.assertIn("生成代码默认显式写 `public`", text) self.assertIn("跨 `unit` 类路径创建和继承属于多文件边界", text) self.assertIn("跨 `unit` 类路径骨架", text) self.assertIn("跨 `unit` 父类路径骨架", text) self.assertIn("type Class1 = class\npublic\n type Class2 = class", text) self.assertNotIn("多文件文档事实", text) self.assertNotIn("已在多文件环境下验证", text) self.assertNotIn("`createObject(\"Unit1.Class1.Class2\")` 可以创建对象", text) self.assertNotIn("`type MyNewClass = class(Unit1.Class1.Class2)` 可以通过", text) self.assertIn("字段可以写类型注解", text) self.assertIn("类方法参数和返回值可以写类型注解", text) self.assertIn("类的声明和实现可以分离", text) self.assertIn("类内可以只声明方法签名", text) self.assertIn("类外实现属于声明区", text) self.assertIn("字段和类方法类型注解", text) self.assertIn("name_: string;", text) self.assertIn("value_: any;", text) self.assertIn("function create(_name: string; _value: any);", text) self.assertIn("function ReadName(): string;", text) self.assertIn("property Value: any read value_ write value_;", text) self.assertIn("```text\nabc\n7\nabc\n```", text) self.assertIn("类内声明、类外实现的带类型重载方法", text) self.assertIn("function PairBox.create(_left: string); overload;", text) self.assertIn( "function PairBox.create(_left: string; _right: any); overload;", text ) self.assertIn("function PairBox.ReadLeft(): string;", text) self.assertIn("create(_left, nil);", text) self.assertIn("```text\nleft\n\nleft\n2\nleft\n```", text) self.assertIn("成员读写为了性能不加 `self` 前缀", text) self.assertIn("普通成员访问仍直接写成员名,不加 `self` 前缀", text) self.assertIn("字段、方法和 `new`", text) self.assertIn("return new Person();", text) self.assertIn("obj := new MyClass();", text) self.assertIn("static mCount;", text) self.assertIn("单个静态字段默认写成 `static mCount;`", text) self.assertIn("上述声明顺序可以编译通过", text) self.assertLess( text.index("a := new Person(22, c:99);"), text.index('b := createObject("Person", 11);'), ) self.assertNotIn("score_value", text) self.assertNotIn("slot_type", text) self.assertNotIn("裸 `class Person`", text) self.assertNotIn("没有任何类声明证据", text) self.assertNotIn("把普通当前实例成员访问写成带 `self` 前缀", text) self.assertIn("不把裸类名成员访问当成文档事实", text) self.assertIn("MathBox.Add(1, 2);\nTHuman.mCount := 7;", text) self.assertIn("上面这种裸类名成员访问不作为可写事实", text) self.assertNotIn("上面这种裸类名静态字段访问不作为可写事实", text) self.assertNotIn("字段、方法和 `createObject`", text) self.assertNotIn('obj := createObject("MyClass");', text) self.assertNotIn("static\n mCount;", text) self.assertNotIn("self.", text) self.assertNotIn("上述 `.tsf` 例子", text) self.assertNotIn("当前解释器", text) self.assertNotIn("文档明确运行结果", text) self.assertNotIn("文档结果", text) self.assertNotIn("当前已记录验证", text) self.assertNotIn("已做双文件运行验证", text) def test_tsl_syntax_docs_do_not_use_self_member_access(self): for path in (ROOT / "docs" / "tsl" / "syntax").glob("*.md"): text = path.read_text(encoding="utf-8") self.assertNotIn("self.", text, msg=f"self. found in {path}") def test_tsl_values_page_is_agent_value_decision_page(self): text = TSL_VALUES.read_text(encoding="utf-8") self.assertIn("智能体值写法判断流程", text) self.assertIn("普通值优先从整数、实数、普通字符串、布尔和 `array(...)` 起手", text) self.assertIn("字符串默认用普通字符串", text) self.assertIn("只有编码、宽串、UTF8、原始字符串、字符码或 ASCII `0` 需求明确时", text) self.assertIn("先判断要顺序数组还是字符串键表", text) self.assertIn("顺序数组和二进制缓冲区下标从 `0` 开始", text) self.assertIn("字符串下标从 `1` 开始", text) self.assertIn("矩阵、集合扩展", text) self.assertIn("12_matrix_and_collections.md", text) self.assertIn("普通示例默认按 `.tsl` 脚本语句区书写", text) self.assertIn("没有对应代码块时不要发明值写法", text) self.assertNotIn("20_strings_and_text.md", 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\nABC\n```", text) self.assertIn("```text\nString index out of bounds\n```", text) def test_tsl_values_page_owns_string_boundaries(self): text = TSL_VALUES.read_text(encoding="utf-8") self.assertFalse((ROOT / "docs/tsl/syntax/20_strings_and_text.md").exists()) self.assertIn("字符串边界规则", text) self.assertIn("宽串 / UTF8 前缀", text) self.assertIn("原始字符串", text) self.assertIn("字符码拼接", text) self.assertIn("ASCII `0` 字符不会截断字符串", text) self.assertIn("普通字符串里的 `\\uXXXX` 不要直接按“单字符宽串”理解", text) self.assertIn("`U\"\"` 不是宽串", text) self.assertIn("`#number` 可以直接把字符码拼进字符串", text) self.assertIn("`\\0` 和 `#0` 都能把 ASCII `0` 放进字符串", text) def test_tsl_values_string_boundary_examples_have_verified_output_snippets(self): text = TSL_VALUES.read_text(encoding="utf-8") self.assertIn("代码块身份:输出片段", text) self.assertIn("```text\nABC\nA\"B'C\nA\nB\n```", text) self.assertIn("writeLn(length(\"\\u0041\"));", text) self.assertIn("writeLn(length(L\"\\u0041\"));", text) self.assertIn("writeLn(length(U\"\\u0041\"));", text) self.assertIn("依次输出 `2`、`1`、`1`", text) self.assertIn("writeLn(ifWString(utf8_s));", text) self.assertIn("依次输出 `0`、`1`、`1`、`1`", text) self.assertIn('s2 := "A"#0"B";', text) self.assertIn("依次输出 `3`、`3`、`1`、`1`", 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("智能体变量/常量判断流程", text) self.assertIn("普通变量默认直接用 `:=` 首次赋值", text) self.assertIn("只有用户要求显式声明或遇到 `{$explicit+}` 时才优先写 `var`", text) self.assertIn("常量声明必须同时初始化", text) self.assertIn("默认只生成 `const name = value;`", text) self.assertIn("单变量拆包必须写成 `[name, ] := array(...)`", text) self.assertIn("06_expressions_and_operators.md", text) self.assertIn("16_lexical_structure_and_compile_options.md", text) self.assertIn("普通示例默认按 `.tsl` 脚本语句区书写", text) self.assertIn("复制常量示例时只使用 `const name = value;`", text) self.assertIn("没有对应代码块时不要发明变量/常量写法", text) self.assertIn("运行时类型 / 转换", text) self.assertIn("17_types_and_conversions.md", text) self.assertNotIn("const name :=", text) self.assertNotIn("语法设计兼容边界", text) self.assertNotIn("兼容通过", text) self.assertNotRegex(text, r"const\s+[A-Za-z_][A-Za-z0-9_]*\s*:=") def test_tsl_types_page_does_not_own_declaration_rules(self): text = TSL_TYPES.read_text(encoding="utf-8") self.assertIn("智能体类型/转换判断流程", text) self.assertIn("运行时值会变成什么类型", text) self.assertIn("默认值是什么", text) self.assertIn("怎样显式转换", text) self.assertIn("`var` / `const` 声明选择回看", text) self.assertIn("16_lexical_structure_and_compile_options.md", text) self.assertNotIn("显式声明边界", text) self.assertNotIn("怎样受编译选项影响", 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\n1\n2\n3\n4\n```", text) self.assertIn("```text\n0\n7\n```", text) self.assertIn("```text\n6\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("智能体函数/调用判断流程", text) self.assertIn("先判断目标文件是 `.tsl` 还是 `.tsf`", text) self.assertIn("`.tsl` 中先写语句区,再把 `function` / `procedure` 声明放在后面", text) self.assertIn("用户只说“写一个函数”且没有指定 `procedure` 时,默认用 `function`", text) self.assertIn("即使任务没有返回值,也默认用 `function`", text) self.assertIn("不要因为没有返回值就自动改用 `procedure`", text) self.assertIn("用户提示词里的“函数”默认对应 `function`", text) self.assertIn("命名参数只写 `name: value`", text) self.assertIn("不要把 `name = value` 当成命名参数", text) self.assertIn("函数头后默认保留分号", text) self.assertIn("16_lexical_structure_and_compile_options.md", text) self.assertIn("普通运行示例默认按 `.tsl` 脚本语句区书写", text) self.assertIn("`.tsf` 函数 / 过程示例只按可复用顶层声明理解", text) self.assertIn("`procedure` 示例只在用户明确要求 `procedure` / 过程时复制", text) self.assertIn("默认从 `function` 骨架起步", text) self.assertIn("跨 `unit` 声明边界", text) self.assertIn("默认参数先按普通函数规则处理", text) self.assertIn("匿名函数和 TSL 函数值", text) self.assertIn("原生函数指针包装", text) self.assertIn("C 回调", text) self.assertIn("只按本页明确的 `call(f, ...)` / `##f(...)` 生成", text) self.assertIn("没有对应代码块时不要发明函数/调用写法", text) self.assertNotIn("在我于", text) self.assertNotIn("LIBPATH", text) self.assertNotIn("tsl .\\main.tsl", text) self.assertNotIn("command", text) self.assertNotIn("function MissingSemi()", text) self.assertNotIn("input_value", text) self.assertNotIn("handler): result_type", text) self.assertNotIn("命名参数可以调换顺序", 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\n9\n1\n7\n```", text) self.assertIn("```text\n12\n```", text) self.assertIn("```text\n888\n123\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") text = text.replace("|", "|") self.assertIn("智能体表达式/运算符判断流程", text) self.assertIn("先判断要写基础表达式运算符,还是集合、矩阵、对象重载、运行时调用或 TS-SQL 这类专题运算符", text) self.assertIn("本页直接生成的运算符", text) self.assertIn("专题运算符入口", text) self.assertIn("默认生成规则", text) self.assertIn("按需生成规则", text) self.assertIn("边界规则", text) self.assertIn("赋值只能用 `:=`", text) self.assertIn("比较才用 `=`", text) self.assertIn("`const name = value;` 是常量初始化规则", text) self.assertIn("常量初始化不按普通赋值判断", text) self.assertIn("函数签名里的默认参数 `name = value` 不是比较表达式", text) self.assertIn("不要把函数签名里的 `name = value` 当成比较表达式", text) self.assertIn("04_variables_and_constants.md", text) self.assertIn("05_functions_and_calls.md", text) self.assertIn("03_values_and_literals.md", text) self.assertIn("12_matrix_and_collections.md", text) self.assertIn("16_lexical_structure_and_compile_options.md", text) self.assertNotIn("20_strings_and_text.md", text) self.assertIn("`{$ifdef ...}` 只作为能力探测", text) self.assertIn("条件求值优先用 `flag ? true_value : false_value`", text) self.assertIn("`if condition then true_value else false_value` 必须带 `else`", text) self.assertIn("需要延迟求值或动态表达式对象时,才用 `@expr` 或 `&\"...\"`", text) self.assertIn("数组逐元素链式比较才用 `::>`", text) self.assertIn("本页是 TSL 表达式与运算符的生成规则页", text) self.assertIn("只使用本页或对应专题页明确记录的运算符", text) self.assertIn("`div`、`mod`、`^`", text) self.assertIn("一元倒数", text) self.assertIn("`!x`", text) self.assertIn("整型、实型输入返回实型倒数", text) self.assertIn("`+`、`$`", text) self.assertIn("`and`、`or`、`not`、`&&`、||", text) self.assertIn("`.&`、`.|`、`.^`", text) self.assertIn("`^=`、`.&=`、`.|=`、`.^=`", text) self.assertIn("`a++`、`a--`、`++a`、`--a`", text) self.assertIn("`union2`、`intersect`、`minus`、`outersect`", text) self.assertIn("`union`、|、`:|`", text) self.assertIn("矩阵并右方", text) self.assertIn("`->`、`!matrix`", text) self.assertIn("矩阵逆/广义逆", text) self.assertIn("反引号转置", text) self.assertIn("`operator +`", text) self.assertIn("`select` / `sselect` / `vselect` / `mselect`", text) self.assertIn("没有文档事实的运算符", text) self.assertIn("不要生成猜测写法", text) self.assertNotIn("`. ^`", text) self.assertNotIn("补证", text) self.assertNotIn("待确认", text) self.assertNotIn("不是完整运算符支持清单", text) self.assertNotIn("未列运算符不等于语法不支持", text) self.assertNotIn("未列出的运算符不要直接判为不支持", text) self.assertNotIn("直接写成该运算符不支持", 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\n3\n3\n12\n4\n1\n2\n1\n8\n2\n3\n```", text) self.assertIn("```text\n1\n1\n1\n1\n1\n1\n```", text) self.assertIn("```text\n0.5\n0.25\n1\n1\n```", text) self.assertIn("```text\n1\n1\n1\n1\n1\n```", text) self.assertIn("```text\n2\n5\n5\n```", text) self.assertIn("```text\n8\n2\n5\n5\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("++a;", text) self.assertIn("--a;", 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_tsl_control_flow_page_routes_function_and_debug_boundaries(self): text = TSL_CONTROL_FLOW.read_text(encoding="utf-8") self.assertIn("智能体控制流判断流程", text) self.assertIn("05_functions_and_calls.md", text) self.assertIn("函数里的控制流只按控制流语法处理", text) self.assertIn("15_debug_and_profiler.md", text) self.assertIn("默认写成块式分支", text) self.assertIn("控制流块的 `end` 默认不加分号", text) self.assertIn("只按语句形态生成", text) self.assertIn("begin\n value := 1;\nend\nelse\nbegin\n value := 0;\nend", text) self.assertNotIn("begin\n value := 1;\nend\nelse\nbegin\n value := 0;\nend;", text) self.assertIn("不要在 `else` 前提前加分号", text) self.assertNotIn("then\n value := 1\nelse", text) self.assertNotIn("b := case", text) self.assertNotIn("b := @case", text) self.assertNotIn("@case", text) self.assertNotIn("case 表达式形态", text) self.assertNotIn("当前解释器", text) self.assertNotIn("文档明确运行结果", text) self.assertNotIn("文档明确支持", text) def test_tsl_control_flow_key_examples_have_verified_output_snippets(self): text = TSL_CONTROL_FLOW.read_text(encoding="utf-8") self.assertIn("代码块身份:输出片段", text) self.assertIn("```text\n1\n0\n```", text) self.assertIn("```text\n3\n```", text) self.assertIn("```text\n10\n120\n230\n```", text) self.assertIn("```text\n6\n4\n```", text) self.assertIn("```text\ntwo\n```", text) self.assertIn("```text\nbefore\ncaught\nraise: boom\nafter\n```", text) self.assertIn("```text\nbefore\nbody\nfinally\n```", text) def test_tsl_units_page_omits_environment_and_orders_uses_before_statements(self): text = (ROOT / "docs/tsl/syntax/09_units_and_scope.md").read_text( encoding="utf-8" ) self.assertIn("多文件结构骨架", text) self.assertIn("[02_core_model.md](02_core_model.md)(优先)", text) self.assertNotIn("[19_namespace_libpath_and_unit_runtime.md](19_namespace_libpath_and_unit_runtime.md)(优先)", text) self.assertIn("命中 `unit` 生命周期、命名空间、查找路径、`tsl.conf` 或命令行查找路径参数", text) self.assertIn("没有对应代码块时不要发明 unit/作用域写法", text) self.assertIn( "interface\n\nfunction Ping();\n\nimplementation\n\nfunction Ping();", text, ) self.assertIn("`implementation` 前保留空行", text) self.assertIn("`unit` 允许接口声明与实现段分离", text) self.assertIn("类方法放在 `implementation` 段实现", text) self.assertIn("### 接口声明与实现段分离", text) self.assertIn("function UnitBox.create(_value);", text) self.assertIn("function UnitBox.ReadValue();", text) self.assertIn("只写在 `implementation` 段,对 `unit` 外部不可见", text) self.assertIn("uses PrivateDemo;\nwriteLn(PublicFunc());\n```", text) self.assertIn("代码块身份:反例 / 不可照写\n\n```text\nuses PrivateDemo;\nwriteLn(PrivateFunc());", text) self.assertNotIn("interface\n function Ping();", text) self.assertNotIn("implementation\n function Ping();", text) self.assertNotIn("## 生成代码排版规则", text) self.assertIn("type Worker = class\n uses DemoUnit;\npublic", text) self.assertIn("// main.tsl\n\nuses UnitA;", text) self.assertIn("// main.tsl\n\nuses PrivateDemo;", text) self.assertIn("type TBox = class\npublic", text) self.assertIn("type Box = class\npublic", text) self.assertIn("function Box.create();", text) self.assertIn("合并成单条 `uses UnitA, UnitB;`", text) self.assertIn("失败点是“第二条 `uses`”", text) self.assertIn("只生成单条逗号合并写法", text) self.assertIn("连续多条顶层 `uses` 不再作为文档明确生成形态", text) self.assertIn("uses UnitA, UnitB;", text) self.assertIn("多个 `unit` 默认写成 `uses UnitA, UnitB;`", text) self.assertNotIn("当前解释器", text) self.assertNotIn("文档结果", text) self.assertNotIn("已在多文件环境下验证", text) self.assertNotIn("// command", text) self.assertNotIn("LIBPATH", text) self.assertNotIn("tsl .", text) def test_tsl_runtime_context_page_omits_environment_process_details(self): text = (ROOT / "docs/tsl/syntax/10_runtime_context_and_with.md").read_text( encoding="utf-8" ) self.assertIn("系统参数优先用本页明确的", text) self.assertIn("运行时上下文、服务与全局缓存", text) self.assertIn("本地函数后缀 `with` 属于反例", text) self.assertIn("网格调用返回的不是最终值;需要最终结果时继续写 `dupvalue(...)`", text) self.assertIn("全局缓存读写要成对出现", text) self.assertIn("多文件结构骨架", text) self.assertIn("依赖函数文件查找路径", text) self.assertIn("结果说明:", text) self.assertIn('r := #TestDo() with array("a": 101, "b": 202);', text) self.assertIn("writeLn(dupvalue(#AddOne(5)));", text) self.assertIn("writeLn(dupvalue(#AddOne(5) timeout 3000));", text) self.assertIn('writeLn(setGlobalCache("PB_TEST_GC_BASIC", v1));', text) self.assertIn("writeLn(checkGlobalCacheExpired(v));", text) self.assertIn("对缓存值做 `select` 可以正常执行", text) self.assertIn("这种“本地函数后缀 `with`”写法不作为可写事实", text) self.assertFalse((ROOT / "docs/tsl/syntax/30_runtime_services_and_global_cache.md").exists()) self.assertNotIn("30_runtime_services_and_global_cache.md", text) self.assertNotIn("当前解释器", text) self.assertNotIn("文档明确运行结果", text) self.assertNotIn("当前已做双文件运行验证", text) def test_tsl_external_page_owns_native_pointer_and_callback_boundaries(self): text = TSL_EXTERNAL.read_text(encoding="utf-8") self.assertIn("智能体外部调用/线程判断流程", text) self.assertIn("原生函数指针包装", text) self.assertIn("C 回调", text) self.assertIn("包装原生函数指针", text) self.assertIn("生成 C 回调", text) self.assertIn("makeInstance(thisFunction(Func), \"cdecl\", 0)", text) self.assertIn("makeInstance(..., \"cdecl\", 1)", text) self.assertIn("函数指针包装", text) self.assertNotIn("普通函数怎么写已经清楚后,外部 DLL、函数指针、多线程", text) self.assertNotIn("// command", text) self.assertNotIn("LIBPATH", text) self.assertNotIn("tsl .", text) def test_tsl_pitfalls_page_uses_stable_boundary_language(self): text = (ROOT / "docs/tsl/syntax/11_pitfalls.md").read_text( encoding="utf-8" ) self.assertIn("## 反例索引", text) self.assertIn("必须跳回对应语法页找可直接照写示例", text) self.assertIn("只有已经有明确反例边界的误写", text) self.assertIn("细节型边界放回对应专题页", text) self.assertIn("最容易诱导智能体写错的高频误写", text) self.assertIn("只针对函数体和类定义体里的 `uses`", text) self.assertIn("默认回到顶层写成单条 `uses UnitA, UnitB;`", text) self.assertIn("类定义体里出现了第二条 `uses`", text) self.assertIn("可用写法是先拿到类类型", text) self.assertIn("不要把反例边界写成可用语法", text) self.assertNotIn("当前解释器", text) self.assertNotIn("当前命令行解释器", text) self.assertNotIn("实测", text) self.assertNotIn("当前文档明确", text) self.assertNotIn("对应代码块", text) self.assertNotIn("## 不可照写反例索引", text) self.assertNotIn("LIBPATH", text) self.assertNotIn("在同一作用域里重复写第二个 `uses`", text) self.assertNotIn('writeLn(length("\\u0041"));', text) self.assertNotIn('writeLn(ifWString(U"\\u5929\\u8F6F"));', text) self.assertNotIn('s := "A"#0"B";', text) self.assertNotIn("const value = 1;", text) self.assertNotIn("unit UnitConst;", text) self.assertNotIn("TickFromExpr", text) self.assertNotIn("文件就在当前目录", text) self.assertNotIn("DemoUnit.UnitCounter", text) def test_tsl_matrix_collections_page_uses_entry_language(self): text = ( ROOT / "docs/tsl/syntax/12_matrix_and_collections.md" ).read_text(encoding="utf-8") self.assertIn("本页明确的矩阵样比较", text) self.assertIn("本页文档明确形态", text) self.assertIn("14_ts_sql.md", text) self.assertIn("[14_ts_sql.md](14_ts_sql.md)", text) self.assertIn("没有对应代码块时不要发明数组/矩阵样数据/集合关系写法", text) self.assertIn("结果说明:", text) self.assertIn("矩阵链式比较 `::>`、`::<`、`::<>`、`::==`、`::>=`、`::<=`", text) self.assertIn("`in` / `not in` 处理的是元素存在关系", text) self.assertIn("`sqlin` / `not sqlin` 处理的是行存在关系", text) self.assertIn("`union2`、`intersect`、`minus`、`outersect` 都按“行”运算", text) self.assertIn("集合运算结果会折叠重复行", text) self.assertIn("writeLn(1 not in array(0, 2));", text) self.assertIn("writeLn(array(5, 6) not sqlin array((1, 2), (3, 4)));", text) self.assertIn("union_rows := left_rows union2 right_rows;", text) self.assertIn("outersect_rows := left_rows outersect right_rows;", text) self.assertNotIn("25_set_operations.md", text) self.assertNotIn("当前解释器", text) self.assertNotIn("文档明确运行结果", text) self.assertNotIn("文档明确运行结果对应关系", text) self.assertNotIn("稳定验证", text) self.assertNotIn("稳定通过", text) def test_tsl_resultset_filters_page_uses_complete_examples(self): text = ( ROOT / "docs/tsl/syntax/13_resultset_and_filters.md" ).read_text(encoding="utf-8") self.assertIn("字段访问优先照本页明确的", text) self.assertIn("复杂查询需求优先跳转到 [14_ts_sql.md](14_ts_sql.md)", text) self.assertIn("金融数据筛选要先确认数据来源", text) self.assertIn("[../reference/catalog/datawarehouse.md](../reference/catalog/datawarehouse.md)", text) self.assertIn("没有对应代码块时不要发明结果集/过滤写法", text) self.assertIn("结果说明:", text) self.assertIn('writeLn(keep_rows[0]["Code"]);', text) self.assertIn('writeLn(drop_rows[1]["Code"]);', text) self.assertIn('rows := array(\n ("Code": "0001"),\n ("Code": "0002")\n);', text) self.assertIn('code_arr := array("0001");\nkeep_rows := filterIn(rows, code_arr, "Code");', text) self.assertIn("本页只把显式写出 `nil` 作为可靠规则", text) self.assertNotIn("文档明确运行结果", text) self.assertNotIn("当前手册", text) self.assertNotIn("当前解释器", text) def test_tsl_ts_sql_page_uses_entry_and_route_language(self): text = (ROOT / "docs/tsl/syntax/14_ts_sql.md").read_text( encoding="utf-8" ) self.assertIn("没有对应代码块时不要发明 TS-SQL 写法", text) self.assertIn("基础查询文档骨架", text) self.assertIn("文档字段访问写法", text) self.assertIn("在一维数组上做 TS-SQL 时,优先使用", text) self.assertIn("只想按已有结果集保留/排除行时跳到", text) self.assertIn("13_resultset_and_filters.md", text) self.assertIn("12_matrix_and_collections.md", text) self.assertIn("23_fmarray.md", text) self.assertIn("这一篇是 TS-SQL 的唯一语法入口", text) self.assertIn("多表 `join` 时,字段访问应写成", text) self.assertIn("结果说明:", text) self.assertNotIn("当前的稳定骨架", text) self.assertNotIn("当前稳定字段访问", text) self.assertNotIn("当前应优先", text) self.assertNotIn("文档明确运行结果", text) def test_tsl_debug_profiler_page_uses_entry_language(self): text = (ROOT / "docs/tsl/syntax/15_debug_and_profiler.md").read_text( encoding="utf-8" ) self.assertIn("本页明确的调试、计时、性能分析器", text) self.assertIn("没有对应代码块时不要发明调试/性能分析器写法", text) self.assertIn("只照文档最小调用写", text) self.assertIn("普通控制流优先回到 [07_control_flow.md](07_control_flow.md)", text) self.assertIn("属于文档明确写法", text) self.assertIn("本页正向边界只覆盖", text) self.assertIn("跨函数跳转不作为可写事实", text) self.assertIn("目标 `label` 单独成行不作为可写事实", text) self.assertIn("不作为本页输出事实", text) self.assertIn("结果说明:", text) self.assertIn("不要把调试客户端副作用写成普通输出事实", text) self.assertNotIn("当前", text) self.assertNotIn("当前解释器", text) self.assertNotIn("文档明确运行结果", text) self.assertNotIn("文档结果", text) self.assertNotIn("未验证参数", text) self.assertNotIn("当前命令行环境", text) self.assertNotIn("命令行环境不可直接观察", text) def test_tsl_runtime_context_routes_function_query_and_runtime_boundaries(self): text = (ROOT / "docs/tsl/syntax/10_runtime_context_and_with.md").read_text( encoding="utf-8" ) self.assertIn("智能体运行时上下文判断流程", text) self.assertIn("05_functions_and_calls.md", text) self.assertIn("14_ts_sql.md", text) self.assertIn("普通函数调用回到 [05_functions_and_calls.md](05_functions_and_calls.md)", text) self.assertIn("缓存值参与 `select` 时,查询语法仍回到 [14_ts_sql.md](14_ts_sql.md)", text) self.assertIn("内置运行时对象", text) def test_tsl_complex_and_weakref_content_is_split_by_owner_page(self): self.assertFalse((ROOT / "docs/tsl/syntax/31_complex_and_weakref.md").exists()) types_text = TSL_TYPES.read_text(encoding="utf-8") object_text = TSL_OBJECT_RUNTIME.read_text(encoding="utf-8") self.assertIn("复数类型", types_text) self.assertIn("复数字面量可以直接写成 `a + bj`", types_text) self.assertIn("z1 := 4 + 3j;", types_text) self.assertIn("z2 := complex(5, -2);", types_text) self.assertIn("`dataType(z)` 对复数返回 `41`", types_text) self.assertIn("complex(array(1, 2, 3), 5.5)", types_text) self.assertIn("complex(fmarray[1, 2, 3], 5.5)", types_text) self.assertIn("弱引用与自动弱引用", object_text) self.assertIn("访问弱引用前先做 `checkWeakRef(...)` 判定", object_text) self.assertIn("弱引用能力的条件编译宏是 `weakptr`", object_text) self.assertIn("自动弱引用相关宏是 `autoWeak`", object_text) self.assertIn("w := weakRef(a);", object_text) self.assertIn("s := weakref_get(w);", object_text) self.assertIn("[weakRef] owner1;", object_text) self.assertIn("[autoRef] owner2;", object_text) self.assertIn("invalid class definition", object_text) self.assertNotIn("31_complex_and_weakref.md", types_text) self.assertNotIn("31_complex_and_weakref.md", object_text) def test_tsl_syntax_examples_do_not_use_unterminated_if_else_branches(self): pattern = re.compile(r"if[^\n]*then\s*\n\s*[^\n;]+\n\s*else") for path in (ROOT / "docs" / "tsl" / "syntax").glob("*.md"): text = path.read_text(encoding="utf-8") self.assertIsNone(pattern.search(text), msg=f"裸 if/else 分支缺分号: {path}") def test_tsl_matrix_deep_dive_documents_inverse_operator(self): text = TSL_MATRIX_DEEP_DIVE.read_text(encoding="utf-8") self.assertIn("矩阵逆/广义逆", text) self.assertIn("`!A` 是一元倒数运算符作用于矩阵的形态", text) self.assertIn("inverse_value := !matrix_value;", text) self.assertIn("不要把它改写成 `1 / matrix_value`", text) self.assertIn("`!` 不表示逻辑非", text) self.assertIn("```text\n2\n2\n-2\n1\n1.5\n-0.5\n```", text) self.assertIn( "```text\n" "3\n" "2\n" "-0.944444444444444\n" "0.444444444444444\n" "-0.111111111111111\n" "0.111111111111111\n" "0.722222222222222\n" "-0.222222222222222\n" "```", text, ) def test_tsl_fmarray_documents_right_matrix_merge_operator(self): text = TSL_FMARRAY.read_text(encoding="utf-8") self.assertIn("矩阵连接 / 矩阵并右方:`union`、`|`、`:|`", text) self.assertIn("`|` 和 `:|` 都可做矩阵并右方(按列连接)", text) self.assertIn("`|` 和 `:|` 会执行矩阵并右方,也就是按列拼接", text) self.assertIn( "```text\n" "27\n" "3\n" "4\n" "1,2,3,4\n" "3,4,7,8\n" "5,5,6,9\n" "1,2,3,4\n" "3,4,7,8\n" "5,5,6,9\n" "```", text, ) self.assertIn( "```text\n" "1,2,3,4\n" "3,4,0,0\n" "5,5,0,0\n" "3,4,1,2\n" "0,0,3,4\n" "0,0,5,5\n" "```", text, ) def test_tsl_deep_dive_pages_avoid_process_verification_language(self): syntax_root = ROOT / "docs" / "tsl" / "syntax" forbidden_phrases = [ "当前解释器", "当前命令行", "当前版本", "文档明确运行结果", "文档结果", "文档明确支持", "实测", "补证", "待确认", "没有文档明确", "未验证", "// command", ] for filename in TSL_SYNTAX_DEEP_DIVE_FILES: 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_object_runtime_prefers_new_for_normal_object_creation(self): text = TSL_OBJECT_RUNTIME.read_text(encoding="utf-8") self.assertIn("obj := new RuntimeBox();", text) self.assertIn("type RuntimeBox = class", text) self.assertIn('t := findOverLoad(2, "fun", new TestClass());', text) self.assertNotIn("new TStringList()", text) self.assertNotIn('obj := createObject("TStringList");', text) self.assertNotIn('findOverLoad(2, "fun", createObject("TestClass"))', text) def test_tsl_builtin_runtime_objects_default_to_new_syntax(self): text = TSL_BUILTIN_RUNTIME_OBJECTS.read_text(encoding="utf-8") self.assertIn("`TStringList` 可以直接用 `new TStringList()` 创建", text) self.assertIn("obj := new TStringList();", text) self.assertIn("mem := new TMemoryStream();", text) self.assertIn("cipher := new TCipher(2);", text) self.assertIn("bad := new TCipher();", text) self.assertIn("`new TStream()`", text) self.assertIn("New Class TCipher Error.", text) self.assertNotIn('createObject("TStringList")', text) self.assertNotIn('createObject("TMemoryStream")', text) self.assertNotIn('createObject("TCipher"', text) def test_tsl_ts_sql_page_documents_thisrow_field_order(self): text = TSL_TS_SQL.read_text(encoding="utf-8") self.assertIn("第一行 `Value=20, Idx=1`,第二行 `Value=30, Idx=2`", text) self.assertNotIn("`(1,20)`、`(2,30)`", text) def test_tsl_ts_sql_has_single_authoritative_syntax_page(self): syntax_root = ROOT / "docs" / "tsl" / "syntax" text = TSL_TS_SQL.read_text(encoding="utf-8") self.assertFalse((syntax_root / "28_ts_sql_core.md").exists()) self.assertFalse((syntax_root / "29_ts_sql_advanced.md").exists()) self.assertIn("这一篇是 TS-SQL 的唯一语法入口", text) self.assertIn("多表 `join` 时,字段访问应写成 `[表序号].[\"字段名\"]`", text) self.assertIn("`thisGroup` 不是普通值,而是分组后的子结果集", text) self.assertIn("`refMaxOf(...)` 和 `refMinOf(...)` 可与", 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("智能体", text) self.assertIn("不要发明", text) def test_tsl_syntax_pages_label_mainline_and_deep_dive_roles(self): syntax_root = ROOT / "docs" / "tsl" / "syntax" for filename in TSL_SYNTAX_MAINLINE_FILES: with self.subTest(filename=filename): text = (syntax_root / filename).read_text(encoding="utf-8") self.assertIn("文档类型:语法主线", text) text = (syntax_root / "11_pitfalls.md").read_text(encoding="utf-8") self.assertIn("文档类型:反例索引页", text) for filename in TSL_SYNTAX_DEEP_DIVE_FILES: with self.subTest(filename=filename): text = (syntax_root / filename).read_text(encoding="utf-8") self.assertIn("文档类型:语法深水专题", text) def test_tsl_syntax_uncertain_routes_have_center_fallbacks(self): syntax_root = ROOT / "docs" / "tsl" / "syntax" for path in syntax_root.glob("*.md"): with self.subTest(filename=path.name): text = path.read_text(encoding="utf-8") self.assertNotIn("遇到不确定时跳转到", text) self.assertNotIn("补证", text) self.assertNotIn("待确认", text) self.assertIn("遇到不确定时:", text) self.assertIn("TSL 总入口 [../index.md](../index.md)", text) if path.name == "index.md": self.assertIn("按任务跳转", text) continue self.assertIn("继续判断", text) self.assertIn("语法路由中心 [index.md](index.md)", text) def test_tsl_syntax_examples_do_not_use_program_test_shell(self): syntax_root = ROOT / "docs" / "tsl" / "syntax" for path in syntax_root.glob("*.md"): with self.subTest(filename=path.name): text = path.read_text(encoding="utf-8") self.assertNotIn("program test;", text) self.assertNotIn("`program test; begin ... end.` 只是自包含示例外壳", text) def test_tsl_syntax_code_block_identity_labels_are_fixed(self): syntax_root = ROOT / "docs" / "tsl" / "syntax" allowed_labels = { "可直接照写示例", "输出片段", "反例 / 不可照写", "配置片段 / 概念骨架", } for path in syntax_root.glob("*.md"): text = path.read_text(encoding="utf-8") for match in re.finditer(r"^代码块身份:(.+)$", text, re.MULTILINE): with self.subTest(filename=path.name, label=match.group(1)): self.assertIn(match.group(1), allowed_labels) 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_function_fact_workflow(self): text = TSL_REFERENCE_INDEX.read_text(encoding="utf-8") self.assertIn("智能体函数使用规则", text) self.assertIn("进入 catalog 分类页后按函数事实条目生成调用", text) self.assertIn("只从带完整参数表的函数事实条目读取签名", text) self.assertIn("catalog 使用推荐大小写展示函数名", text) self.assertIn("以函数事实条目的函数名拼写为准", text) self.assertIn("没在 catalog 正式条目里的函数,不要根据函数名猜调用", text) self.assertIn("函数名属于哪个模块", text) self.assertIn("模块目录", text) self.assertIn("catalog/base.md", text) self.assertIn("catalog/math.md", text) self.assertIn("catalog/resource.md", text) self.assertNotIn("函数参数事实", text) self.assertNotIn("catalog 里的裸函数名只是候选名", text) self.assertNotIn("裸函数名仍只表示候选名", text) self.assertNotIn("catalog/index.md", text) self.assertNotIn("core.md", text) self.assertNotIn("functions/", text) self.assertNotIn("verified", text) self.assertNotIn("unavailable_methods.md", text) self.assertNotIn("不可用方法", 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_index_contains_function_fact_catalog(self): catalog_index = TSL_REFERENCE_INDEX.read_text(encoding="utf-8") self.assertIn("函数模块目录", catalog_index) self.assertIn("进入分类页后,按函数事实条目的参数表生成调用", catalog_index) self.assertIn("函数事实数", catalog_index) self.assertIn("catalog/base.md", catalog_index) self.assertIn("catalog/math.md", catalog_index) self.assertIn("catalog/system.md", catalog_index) self.assertIn("catalog/third_party.md", catalog_index) self.assertIn("catalog 使用推荐大小写展示函数名", catalog_index) self.assertIn("以函数事实条目的函数名拼写为准", catalog_index) self.assertNotIn("catalog/index.md", catalog_index) self.assertNotIn("core.md", catalog_index) self.assertNotIn("functions/", catalog_index) self.assertNotIn("verified", catalog_index) self.assertNotIn("错误参数组合", catalog_index) self.assertNotIn("参数验证", catalog_index) catalog_root = ROOT / "docs" / "tsl" / "reference" / "catalog" route_pages = { "base.md", "math.md", "datawarehouse.md", "system.md", "resource.md", "graphics.md", } for path in catalog_root.rglob("*.md"): text = path.read_text(encoding="utf-8") if path.parent == catalog_root and path.name in route_pages: self.assertIn("文档类型:函数事实路由页", text) self.assertIn("分类目录", text) self.assertNotIn("### `", text) continue self.assertIn("文档类型:函数事实页", text) self.assertIn("参数位置", text, msg=f"{path.name} missing parameter facts") self.assertNotIn("候选函数索引", text) self.assertIn("../index.md", text) def test_tsl_reference_catalog_function_names_start_lowercase(self): catalog_root = ROOT / "docs" / "tsl" / "reference" / "catalog" for path in catalog_root.rglob("*.md"): if path.name == "index.md": continue text = path.read_text(encoding="utf-8") for match in re.finditer(r"^(?:- |#{3,6} )`([^`(]+)", text, re.MULTILINE): with self.subTest(path=path.relative_to(ROOT), name=match.group(1)): name = match.group(1) self.assertFalse(name[0].isupper(), msg=f"{name} starts uppercase") def test_tsl_reference_completed_catalog_pages_record_parameter_facts(self): catalog_root = ROOT / "docs" / "tsl" / "reference" / "catalog" completed_pages = { "base.md": [ "dateToStr", "dateTimeToStr", "formatDateTime", "time", "now", "date", "currentYear", "dayOfWeek", "encodeDate", "encodeTime", "strToDate", "strToTime", "strToDateTime", "strToDateDef", "strToTimeDef", "timeToStr", "dateToInt", "intToDate", "today", "yesterday", "tomorrow", "format", "setLength", "copy", "upperCase", "lowerCase", "trim", "trimLeft", "trimRight", "pos", "strToIntDef", "floatToStr", "strToInt", "intToStr", "length", ], "compression.md": [ "zipCompress", "zipExtract", "rarExtract", "unicompress", "uniuncompress", ], "digest_encoding.md": [ "getMsgDigest", "strToBase64", "base64ToStr", "encodeRadixstr", "decodeRadixstr", "encoderadixwstr", "decoderadixwstr", ], "system.md": [ "dataType", "ifInt", "ifInt64", "ifReal", "ifNumber", "ifString", "ifArray", "ifNil", "ifExp", "ifGraph", "ifGraphGroup", "ifBinary", "ifMatrix", "ifObj", "createMatrix", "matrixToArray", "getMatrixFields", "createBinary", "convertDataToBuf", "convertBufToData", "integer", "int64", "real", "string", "wideString", "binary", "setPrecision", "toSTM", "toSTN", "exportCsv", "importCsv", "dupValue", "randomize", "random", "randomFrom", "ifThen", "eval", "call", "callInArray", "invoke", "invokeinarray", "setProfiler", "getProfilerInfo", "sysgettsllibpath", "syssettsllibpath", ], } for filename, signatures in completed_pages.items(): path = catalog_root / filename text = path.read_text(encoding="utf-8") if "文档类型:函数事实路由页" in text: text = "\n".join( detail.read_text(encoding="utf-8") for detail in sorted((catalog_root / path.stem).glob("*.md")) ) with self.subTest(filename=filename): self.assertIn("文档类型:函数事实页", text) self.assertIn("参数个数:", text) self.assertIn("返回值:", text) self.assertRegex( text, r"\|\s*参数位置\s*\|\s*参数名\s*\|\s*必填\s*\|\s*接收类型\s*\|\s*说明\s*\|", ) for name in signatures: self.assertRegex(text, rf"(?m)^### `{re.escape(name)}\(", msg=name) def test_tsl_reference_index_records_parameter_facts(self): text = TSL_REFERENCE_INDEX.read_text(encoding="utf-8") self.assertIn("函数事实路由页", text) self.assertIn("查可调用函数参数类型", text) self.assertIn("带完整参数表的函数事实条目", text) self.assertIn("函数事实数", text) self.assertIn("catalog/base.md", text) self.assertIn("catalog/system.md", text) self.assertNotIn("## 函数参数事实", text) self.assertNotIn("`abs(value)`", text) self.assertNotIn("verified", 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_does_not_ship_verified_or_unavailable_layers(self): reference_root = ROOT / "docs" / "tsl" / "reference" self.assertFalse((reference_root / "verified").exists()) self.assertFalse((reference_root / "functions").exists()) self.assertFalse((reference_root / "core.md").exists()) self.assertFalse((reference_root / "catalog" / "index.md").exists()) self.assertFalse(TSL_REFERENCE_UNAVAILABLE.exists()) for path in reference_root.rglob("*.md"): text = path.read_text(encoding="utf-8") with self.subTest(path=path.relative_to(ROOT)): self.assertNotIn("verified", text) self.assertNotIn("unavailable_methods", text) self.assertNotIn("不可用方法", text) self.assertNotIn("已写入文档", 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) self.assertNotIn("abs(\"-3\")", text) self.assertNotIn("dateToStr(\"2011-12-31\")", text) self.assertNotIn("length(123)", text) def test_tsl_finance_directory_is_removed(self): finance_root = ROOT / "docs" / "tsl" / "finance" self.assertFalse(finance_root.exists()) 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("智能体", 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("智能体模块路由规则", text) self.assertIn("tsbacktesting.md", text) self.assertIn("wechat_message.md", text) self.assertIn("pytsl_api.md", text) def test_tsl_modules_omit_environment_verification_details(self): forbidden_phrases = [ "Docker", "docker exec", "LD_LIBRARY_PATH", "/data/workspace", "U22Cli", "gitea-runner", "当前环境里", "实测", ] for path in (ROOT / "docs" / "tsl" / "modules").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()