playbook/docs/tsl/syntax/03_values_and_literals.md

11 KiB
Raw Blame History

TSL 值与字面量

文档类型:语法主线 是否可直接用于生成代码:是 是否含可直接照写示例:是 是否含不可照写反例:是 遇到不确定时:先按本页候选页继续判断;04_variables_and_constants.md06_expressions_and_operators.md12_matrix_and_collections.md17_types_and_conversions.md;仍不命中时回到语法路由中心 index.md;如果问题已经超出语法层,回到 TSL 总入口 ../index.md

这一篇整理基本类型、字面量、数组、字符串、字符串编码边界与基础值模型,避免把值规则分散在函数或金融示例里。

本篇职责

回答“基本类型怎么写、数组和字符串怎么索引、字符串编码边界怎么写、哪些值规则属于语言级事实”。矩阵、集合扩展和复杂容器不在本页展开;命中这些任务时跳到 12_matrix_and_collections.md 或对应深水专题。

智能体值写法判断流程

  1. 普通值优先从整数、实数、普通字符串、布尔和 array(...) 起手。
  2. 字符串默认用普通字符串只有编码、宽串、UTF8、原始字符串、字符码或 ASCII 0 需求明确时,才使用本页字符串边界规则里的文档明确形态。
  3. 看到 array(...) 时,先判断要顺序数组还是字符串键表:顺序数组用位置元素,字符串键表用 "Key": value
  4. 写下标前先判断对象类别:顺序数组和二进制缓冲区下标从 0 开始,字符串下标从 1 开始。
  5. 写字符串区间前先确认 s[start:end]end 会被包含;不要按半开区间推断。
  6. 如果任务已经变成矩阵、集合扩展、结果集过滤或 TS-SQL 数据形态,不要留在值页硬拼写法;先跳到 12_matrix_and_collections.md13_resultset_and_filters.md14_ts_sql.md
  7. 没有对应代码块时不要发明值写法;尤其不要从 JSON、JavaScript、Pascal 或 C 字符串规则直接迁移。

核心规则

  • 最先掌握的几类值是:整数、实数、普通字符串、布尔和 array(...)
  • 普通字符串既可以用双引号,也可以用单引号。
  • 同类引号本身可以通过连续写两个同类引号放进字符串里。
  • \\\"\n 这类基础转义可用。
  • \t\xNN 这类转义也可用。
  • \r\r\n\a\b\f\v 这些经典转义也可用。
  • array(...) 既可以写顺序数组,也可以写字符串键表。
  • 顺序数组下标从 0 开始。
  • binary(...) 创建的二进制缓冲区也用 [] 访问,并且下标从 0 开始。
  • 字符串下标从 1 开始。
  • s[0] 在运行时会越界,不要把字符串当成 0 基下标。
  • 字符串取子串用 s[start:end],并且 end 是包含在结果里的。
  • 字符串替换子串用 s[start:end] := "..."
  • 字符串删除子串,本质上就是 s[start:end] := ""
  • 字符串插入子串用 s[index:0] := "..."
  • 普通字符串、L"" 宽串、U"" UTF8 前缀串,以及 %% 原始字符串都属于文档明确写法。
  • %% 原始字符串开头后必须先跟一个空白分隔符;可以带标识符,也支持多行。
  • L%% ...%%U%% ...%% 这两种带前缀的原始字符串也属于文档明确写法。
  • 普通字符串里的 \uXXXX 不要直接按“单字符宽串”理解;length("\u0041") = 2,而 length(L"\u0041") = 1length(U"\u0041") = 1
  • U"" 不是宽串;需要在 UTF8、宽串、普通串之间显式转换时继续用 utf8ToUnicode(...)utf8ToAnsi(...)ansiToUtf8(...)unicodeToUtf8(...)string(...)wideString(...)
  • #number 可以直接把字符码拼进字符串。
  • \0#0 都能把 ASCII 0 放进字符串,并且不会把字符串截断。

可直接照写示例

使用这些示例时遵守:

  • 只复制任务需要的值、字面量、下标或字符串片段,不要把多个示例拼成未写入文档的新语法。
  • 普通示例默认按 .tsl 脚本语句区书写;需要函数或类型时,放在后置声明区。
  • 字符串编码、宽串和原始字符串边界不清楚时,优先看本页“字符串边界规则”。

最基础的值写法:

代码块身份:可直接照写示例

count := 1;
price := 12.5;
name := "ABC";
flag1 := true;
flag2 := false;
items := array(1, 2, 3);
writeLn(count);
writeLn(price);
writeLn(name);
writeLn(flag1);
writeLn(flag2);
writeLn(items[1]);

代码块身份:输出片段

1
12.5
ABC
1
0
2

代码块说明:true / false 可以直接写成布尔值;运行输出里分别表现为 1 / 0

普通字符串字面量:

代码块身份:可直接照写示例

writeLn("ABC");
writeLn('XYZ');

结果说明:

  • 依次输出 ABCXYZ

字符串内引号与基础转义:

代码块身份:可直接照写示例

writeLn("A""B");
writeLn('A''B');
writeLn("A\\B");
writeLn("A\"B");
writeLn("A\nB");

结果说明:

  • 依次输出 A"BA'BA\B
  • "A\nB" 会分成两行输出 AB

更多基础转义:

代码块身份:可直接照写示例

writeLn("A\tB" = "A"#9"B");
writeLn("A\x30B" = "A0B");

结果说明:

  • 两行都输出 1
  • 说明 \t 可以表示制表符,\x30 这类写法可以按 ANSI 字节值插入字符

经典控制字符转义:

代码块身份:可直接照写示例

writeLn("\r" = #13);
writeLn("\r\n" = #13#10);
writeLn(length("\r\n"));
writeLn("\a" = #7);
writeLn("\b" = #8);
writeLn("\f" = #12);
writeLn("\v" = #11);

结果说明:

  • 依次输出 1121111
  • 说明 \r / \r\n 可以直接表示回车和回车换行
  • 说明 \a\b\f\v 也属于可用的经典转义

代码块身份:可直接照写示例

items := array(10, 20, 30);
row := array("Code": "0001", "Price": 12.3);
s := "ABC";
writeLn(items[0]);
writeLn(items[1]);
writeLn(row["Code"]);
writeLn(s[1]);
writeLn(s[2]);
writeLn(s[3]);

代码块身份:输出片段

10
20
0001
A
B
C

代码块说明:顺序数组 items0 开始,字符串键表 row 用字符串键访问,字符串 s1 开始。

字符串边界规则

原始字符串 %% 的空白分隔、标识符和多行:

代码块身份:可直接照写示例

s1 := %% ABC%%;
s2 := %%tag A"B'C%%tag;
s3 := %%m
A
B%%m;
writeLn(s1);
writeLn(s2);
writeLn(s3);

结果说明:

  • s1 输出 ABC
  • s2 输出 A"B'C
  • s3 输出两行 AB
  • 说明 %% 开头后的第一个空白只是分隔符,不属于正文
  • 说明 %%tag ...%%tag 可以用标识符配对
  • 也说明 %% 原始字符串支持直接跨行

代码块身份:输出片段

ABC
A"B'C
A
B

带前缀的原始字符串:

代码块身份:可直接照写示例

writeLn(L%% ABC%% = L"ABC");
writeLn(U%% ABC%% = "ABC");

结果说明:

  • 依次输出 11
  • 说明 L%% ...%%U%% ...%% 都属于文档明确写法

\uXXXX 在普通串、宽串和 U 串里的边界:

代码块身份:可直接照写示例

writeLn(length("\u0041"));
writeLn(length(L"\u0041"));
writeLn(length(U"\u0041"));

结果说明:

  • 依次输出 211
  • 因此不要把普通字符串里的 "\u0041" 直接理解成和 L"\u0041" 一样的“单字符”写法

字符串显式编码转换:

代码块身份:可直接照写示例

utf8_s := U"\u5929\u8F6F";
utf8_s2 := unicodeToUtf8(L"\u5929\u8F6F");
wide_s := utf8ToUnicode(utf8_s);
ansi_s := utf8ToAnsi(utf8_s);
writeLn(ifWString(utf8_s));
writeLn(utf8_s = utf8_s2);
writeLn(wide_s = L"\u5929\u8F6F");
writeLn(ansi_s = string(L"\u5929\u8F6F"));

结果说明:

  • 依次输出 0111
  • 说明 U"" 不是宽串
  • 说明 UTF8、宽串、普通串之间可以按示例显式转换

ASCII 0 字符不会截断字符串:

代码块身份:可直接照写示例

s1 := "A\0B";
s2 := "A"#0"B";
writeLn(length(s1));
writeLn(length(s2));
writeLn(s1 = s2);
writeLn(s1[2] = #0);

结果说明:

  • 依次输出 3311
  • 说明 \0#0 都可以把 ASCII 0 放进字符串
  • 也说明 TSL 字符串不是遇到 #0 就自动截断的 C 风格零结尾串

字符码拼接:

代码块身份:可直接照写示例

writeLn("A"#48"B");
writeLn("A"#13#10"B");

结果说明:

  • 第一行输出 A0B
  • 第二组输出会分成两行 AB
  • 说明 #48 这类写法会按字符码直接参与字符串拼接,#13#10 可以直接表示换行

二进制缓冲区:

代码块身份:可直接照写示例

b := binary("30");
writeLn(ifBinary(b));
writeLn(b[0] = "3");
writeLn(b[1] = "0");
b[1] := 0x31;
writeLn(b[1] = "1");

结果说明:

  • 依次输出 1111
  • 说明 binary(...) 会得到二进制缓冲区
  • 说明二进制缓冲区的 [] 下标从 0 开始
  • 也说明二进制缓冲区项可以直接按单字节读写

字符串子串读取:

代码块身份:可直接照写示例

s := "ABCDE";
writeLn(s[2:4]);

代码块身份:输出片段

BCD

代码块说明:字符串区间 2:4 会包含结束位 4

字符串替换子串:

代码块身份:可直接照写示例

s := "ABCDE";
s[2:4] := "XYZ";
writeLn(s);

结果说明:

  • 输出 AXYZE

字符串删除子串:

代码块身份:可直接照写示例

s := "ABCDE";
s[2:4] := "";
writeLn(s);

结果说明:

  • 输出 AE

字符串插入子串:

代码块身份:可直接照写示例

s := "AAABBB";
s[4:0] := "111";
writeLn(s);

结果说明:

  • 输出 AAA111BBB
  • 这说明 s[index:0] := ... 是在指定位置插入,而不是删除或替换区间

默认生成模板

如果你只是要抓住“基本类型 + array”的第一层用这个最短例子

代码块身份:可直接照写示例

count := 1;
price := 12.5;
name := "ABC";
flag := true;
items := array(1, 2, 3);

禁止项

  • s[0] 访问字符串首字符。
  • 误以为 array(...) 只能写顺序数组,不能写字符串键表。
  • 误以为字符串区间和数组一样从 0 开始。
  • s[index:0] := ... 误解成“替换到第 0 位”。
  • 把二进制缓冲区也按字符串的 1 基下标去理解。
  • 在普通字符串示例里默认切到原始字符串、宽串 / UTF8 前缀、字符码或编码转换,却没有按本页字符串边界规则确认。
  • 把普通字符串里的 \uXXXX 直接当成宽串单字符。
  • U"" 当成宽串;需要宽串时按本页转换链处理。
  • #0 / \0 当成 C 风格字符串终止符。

代码块身份:反例 / 不可照写

s := "ABC";
writeLn(s[0]);

上面这类写法在运行时会报字符串下标越界。

代码块身份:输出片段

String index out of bounds

代码块身份:可直接照写示例

s := "ABCDE";
writeLn(s[1:3]);

不要把上面这个区间访问类比成数组切片的半开区间。按文档输出,它返回 ABC,不是 AB;字符串区间的结束位会被包含进结果。

代码块身份:输出片段

ABC