From 5d73964db8ffa7fe011a591effea27e820a00754 Mon Sep 17 00:00:00 2001 From: csh Date: Fri, 14 Aug 2026 17:08:52 +0800 Subject: [PATCH] :bug: fix(tsl-syntax-reference): prefer ordinary quotes for plain text --- .../references/01_quickstart.md | 8 +++++++ .../references/03_values_and_literals.md | 24 +++++++++++++++---- skills/tsl-syntax-reference/scripts/lookup.py | 2 ++ test/test_tsl_syntax_reference.py | 18 ++++++++++++++ 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/skills/tsl-syntax-reference/references/01_quickstart.md b/skills/tsl-syntax-reference/references/01_quickstart.md index b51c9aba..5b922dc0 100644 --- a/skills/tsl-syntax-reference/references/01_quickstart.md +++ b/skills/tsl-syntax-reference/references/01_quickstart.md @@ -68,6 +68,14 @@ - 一旦某次调用里开始使用命名参数,后面的参数就不能再退回位置参数。 + + +- 普通单行文本默认使用 `"..."` 或 `'...'`;根据内容选择不冲突的引号,必要时再使用转义或连续同类引号。`%% ...%%` 仅用于多行文本、引号非常密集等原始字符串场景;不得因为内容是中文、非 ASCII 或较长就自动改用 `%%`。 + + + +- `U`、`L` 前缀只由目标字符串类型或已确认的 API 编码要求决定,不能因为内容是中文就自动添加;普通中文内容优先直接写成 `"中文内容"`。 + - `array(...)` 既可以写顺序数组,也可以写字符串键表;顺序数组和 `binary(...)` 二进制缓冲区下标从 `0` 开始,字符串下标从 `1` 开始。 diff --git a/skills/tsl-syntax-reference/references/03_values_and_literals.md b/skills/tsl-syntax-reference/references/03_values_and_literals.md index 740fe863..232fbddb 100644 --- a/skills/tsl-syntax-reference/references/03_values_and_literals.md +++ b/skills/tsl-syntax-reference/references/03_values_and_literals.md @@ -30,7 +30,7 @@ - 字符串替换子串用 `s[start:end] := "..."`。 - 字符串删除子串,本质上就是 `s[start:end] := ""`。 - 字符串插入子串用 `s[index:0] := "..."`。 -- 普通字符串、`L""` 宽串、`U""` UTF8 前缀串,以及 `%%` 原始字符串都属于文档明确写法。 +- 普通字符串、`L""` 宽串、`U""` UTF8 前缀串,以及 `%%` 原始字符串都属于文档明确写法;合法形式不等于默认选择,生成优先级见本页“字符串字面量选择与边界规则”。 - `%%` 原始字符串开头后必须先跟一个空白分隔符;可以带标识符,也支持多行。 - `L%% ...%%` 与 `U%% ...%%` 这两种带前缀的原始字符串也属于文档明确写法。 - 普通字符串里的 `\uXXXX` 不要直接按“单字符宽串”理解;`length("\u0041") = 2`,而 `length(L"\u0041") = 1`、`length(U"\u0041") = 1`。 @@ -48,7 +48,7 @@ - 只复制任务需要的值、字面量、下标或字符串片段,不要把多个示例拼成未写入文档的新语法。 - 普通示例默认按 `.tsl` 脚本语句区书写;需要函数或类型时,放在后置声明区。 -- 字符串编码、宽串和原始字符串边界不清楚时,优先看本页“字符串边界规则”。 +- 字符串字面量选择、编码、宽串和原始字符串边界不清楚时,优先看本页“字符串字面量选择与边界规则”。 最基础的值写法: @@ -200,11 +200,23 @@ writeLn(keyed[1]); 说明:`length(items)` 统计已有项数,不等于“最大整数键 + 1”;下标 `1` 没有写入,因此读取为 `nil`。 -### 字符串边界规则 +### 字符串字面量选择与边界规则 - + + + + +- 普通单行文本默认使用 `"..."` 或 `'...'`;根据内容选择不冲突的引号,必要时再使用转义或连续同类引号。`%% ...%%` 仅用于多行文本、引号非常密集等原始字符串场景;不得因为内容是中文、非 ASCII 或较长就自动改用 `%%`。 + + + +- `U`、`L` 前缀只由目标字符串类型或已确认的 API 编码要求决定,不能因为内容是中文就自动添加;普通中文内容优先直接写成 `"中文内容"`。 + +需要为真实 API 选择字符串类型或编码前缀时,先用 `tsl-api-reference` 取回该 API 的精确要求;语法页不根据文本内容猜测编码。 + +以下 `%%` 示例只说明原始字符串的合法外形和边界,不表示普通文本应默认采用原始字符串。 原始字符串 `%%` 的空白分隔、标识符和多行: @@ -600,7 +612,9 @@ items := array(1, 2, 3); - 把字符串区间当成数组切片式的半开区间;`s[1:3]` 返回 `ABC` 而不是 `AB`,结束位会被包含进结果,正向示例见本页“字符串子串读写”。 - 把 `s[index:0] := ...` 误解成“替换到第 0 位”。 - 把二进制缓冲区也按字符串的 `1` 基下标去理解。 -- 在普通字符串示例里默认切到原始字符串、宽串 / UTF8 前缀、字符码或编码转换,却没有按本页字符串边界规则确认。 +- 因为内容是中文、非 ASCII 或较长,就把普通单行文本自动改写成 `%%` 原始字符串。 +- 因为内容是中文,就自动添加 `U` / `L` 前缀;前缀只由目标字符串类型或已确认的 API 编码要求决定。 +- 在普通字符串示例里默认切到原始字符串、宽串 / UTF8 前缀、字符码或编码转换,却没有按本页字符串字面量选择与边界规则确认。 - 把普通字符串里的 `\uXXXX` 直接当成宽串单字符。 - 把 `U""` 当成宽串;需要宽串时按本页转换链处理。 - 把 `#0` / `\0` 当成 C 风格字符串终止符。 diff --git a/skills/tsl-syntax-reference/scripts/lookup.py b/skills/tsl-syntax-reference/scripts/lookup.py index 25da24af..bd6d4543 100644 --- a/skills/tsl-syntax-reference/scripts/lookup.py +++ b/skills/tsl-syntax-reference/scripts/lookup.py @@ -97,6 +97,8 @@ QUICKSTART_RULE_OWNER_PAGES = { "unit-default": "09_units_and_scope.md", "named-arguments": "05_functions_and_calls.md", "named-argument-order": "05_functions_and_calls.md", + "string-literal-default": "03_values_and_literals.md", + "string-prefix-by-type": "03_values_and_literals.md", "index-origins": "03_values_and_literals.md", } diff --git a/test/test_tsl_syntax_reference.py b/test/test_tsl_syntax_reference.py index 4fb71d3e..7a8de5bd 100644 --- a/test/test_tsl_syntax_reference.py +++ b/test/test_tsl_syntax_reference.py @@ -336,6 +336,24 @@ class TslSyntaxReferenceTests(unittest.TestCase): self.assertEqual("03_values_and_literals.md", result.matches[0].section.page.name) self.assertEqual("核心规则", result.matches[0].section.heading_path[-1]) + def test_string_literal_queries_rank_generation_policy_first(self): + for query in ("普通中文字符串", "原始字符串 %%", "字符串 U L 前缀"): + with self.subTest(query=query): + result = lookup.query_sections(query, "explain", limit=1) + self.assertEqual("syntax-03-004", result.matches[0].section.id) + + def test_string_literal_generation_policy_is_in_quickstart_and_detail(self): + quickstart = run_lookup("--section", "syntax-01-002") + detail = run_lookup("--section", "syntax-03-004") + + self.assertEqual(0, quickstart.returncode, msg=quickstart.stderr) + self.assertEqual(0, detail.returncode, msg=detail.stderr) + for output in (quickstart.stdout, detail.stdout): + self.assertIn("普通单行文本默认使用", output) + self.assertIn("不得因为内容是中文、非 ASCII 或较长", output) + self.assertIn("不能因为内容是中文就自动添加", output) + self.assertIn('普通中文内容优先直接写成 `"中文内容"`', output) + def test_every_curated_page_alias_still_ranks_its_page_first(self): for page_name, aliases in lookup.PAGE_INTENT_ALIASES.items(): for alias in aliases: