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

435 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TSL 值与字面量
文档类型:语法主线
是否可直接用于生成代码:是
是否含可直接照写示例:是
是否含不可照写反例:是
遇到不确定时:先按本页候选页继续判断;[04_variables_and_constants.md](04_variables_and_constants.md)、[06_expressions_and_operators.md](06_expressions_and_operators.md)、[12_matrix_and_collections.md](12_matrix_and_collections.md)、[17_types_and_conversions.md](17_types_and_conversions.md);仍不命中时回到语法路由中心 [index.md](index.md);如果问题已经超出语法层,回到 TSL 总入口 [../index.md](../index.md)
这一篇整理基本类型、字面量、数组、字符串、字符串编码边界与基础值模型,避免把值规则分散在函数或金融示例里。
## 本篇职责
回答“基本类型怎么写、数组和字符串怎么索引、字符串编码边界怎么写、哪些值规则属于语言级事实”。矩阵、集合扩展和复杂容器不在本页展开;命中这些任务时跳到 [12_matrix_and_collections.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.md](12_matrix_and_collections.md)、[13_resultset_and_filters.md](13_resultset_and_filters.md) 或 [14_ts_sql.md](14_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") = 1`、`length(U"\u0041") = 1`。
- `U""` 不是宽串;需要在 UTF8、宽串、普通串之间显式转换时继续用 `utf8ToUnicode(...)`、`utf8ToAnsi(...)`、`ansiToUtf8(...)`、`unicodeToUtf8(...)`、`string(...)`、`wideString(...)`。
- `#number` 可以直接把字符码拼进字符串。
- `\0``#0` 都能把 ASCII `0` 放进字符串,并且不会把字符串截断。
## 可直接照写示例
使用这些示例时遵守:
- 只复制任务需要的值、字面量、下标或字符串片段,不要把多个示例拼成未写入文档的新语法。
- 普通示例默认按 `.tsl` 脚本语句区书写;需要函数或类型时,放在后置声明区。
- 字符串编码、宽串和原始字符串边界不清楚时,优先看本页“字符串边界规则”。
最基础的值写法:
代码块身份:可直接照写示例
```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]);
```
代码块身份:输出片段
```text
1
12.5
ABC
1
0
2
```
代码块说明:`true` / `false` 可以直接写成布尔值;运行输出里分别表现为 `1` / `0`
普通字符串字面量:
代码块身份:可直接照写示例
```tsl
writeLn("ABC");
writeLn('XYZ');
```
结果说明:
- 依次输出 `ABC`、`XYZ`
字符串内引号与基础转义:
代码块身份:可直接照写示例
```tsl
writeLn("A""B");
writeLn('A''B');
writeLn("A\\B");
writeLn("A\"B");
writeLn("A\nB");
```
结果说明:
- 依次输出 `A"B`、`A'B`、`A\B`
- `"A\nB"` 会分成两行输出 `A``B`
更多基础转义:
代码块身份:可直接照写示例
```tsl
writeLn("A\tB" = "A"#9"B");
writeLn("A\x30B" = "A0B");
```
结果说明:
- 两行都输出 `1`
- 说明 `\t` 可以表示制表符,`\x30` 这类写法可以按 ANSI 字节值插入字符
经典控制字符转义:
代码块身份:可直接照写示例
```tsl
writeLn("\r" = #13);
writeLn("\r\n" = #13#10);
writeLn(length("\r\n"));
writeLn("\a" = #7);
writeLn("\b" = #8);
writeLn("\f" = #12);
writeLn("\v" = #11);
```
结果说明:
- 依次输出 `1`、`1`、`2`、`1`、`1`、`1`、`1`
- 说明 `\r` / `\r\n` 可以直接表示回车和回车换行
- 说明 `\a`、`\b`、`\f`、`\v` 也属于可用的经典转义
代码块身份:可直接照写示例
```tsl
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]);
```
代码块身份:输出片段
```text
10
20
0001
A
B
C
```
代码块说明:顺序数组 `items``0` 开始,字符串键表 `row` 用字符串键访问,字符串 `s``1` 开始。
### 字符串边界规则
原始字符串 `%%` 的空白分隔、标识符和多行:
代码块身份:可直接照写示例
```tsl
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` 输出两行 `A`、`B`
- 说明 `%%` 开头后的第一个空白只是分隔符,不属于正文
- 说明 `%%tag ...%%tag` 可以用标识符配对
- 也说明 `%%` 原始字符串支持直接跨行
代码块身份:输出片段
```text
ABC
A"B'C
A
B
```
带前缀的原始字符串:
代码块身份:可直接照写示例
```tsl
writeLn(L%% ABC%% = L"ABC");
writeLn(U%% ABC%% = "ABC");
```
结果说明:
- 依次输出 `1`、`1`
- 说明 `L%% ...%%``U%% ...%%` 都属于文档明确写法
`\uXXXX` 在普通串、宽串和 `U` 串里的边界:
代码块身份:可直接照写示例
```tsl
writeLn(length("\u0041"));
writeLn(length(L"\u0041"));
writeLn(length(U"\u0041"));
```
结果说明:
- 依次输出 `2`、`1`、`1`
- 因此不要把普通字符串里的 `"\u0041"` 直接理解成和 `L"\u0041"` 一样的“单字符”写法
字符串显式编码转换:
代码块身份:可直接照写示例
```tsl
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"));
```
结果说明:
- 依次输出 `0`、`1`、`1`、`1`
- 说明 `U""` 不是宽串
- 说明 UTF8、宽串、普通串之间可以按示例显式转换
ASCII `0` 字符不会截断字符串:
代码块身份:可直接照写示例
```tsl
s1 := "A\0B";
s2 := "A"#0"B";
writeLn(length(s1));
writeLn(length(s2));
writeLn(s1 = s2);
writeLn(s1[2] = #0);
```
结果说明:
- 依次输出 `3`、`3`、`1`、`1`
- 说明 `\0``#0` 都可以把 ASCII `0` 放进字符串
- 也说明 TSL 字符串不是遇到 `#0` 就自动截断的 C 风格零结尾串
字符码拼接:
代码块身份:可直接照写示例
```tsl
writeLn("A"#48"B");
writeLn("A"#13#10"B");
```
结果说明:
- 第一行输出 `A0B`
- 第二组输出会分成两行 `A`、`B`
- 说明 `#48` 这类写法会按字符码直接参与字符串拼接,`#13#10` 可以直接表示换行
二进制缓冲区:
代码块身份:可直接照写示例
```tsl
b := binary("30");
writeLn(ifBinary(b));
writeLn(b[0] = "3");
writeLn(b[1] = "0");
b[1] := 0x31;
writeLn(b[1] = "1");
```
结果说明:
- 依次输出 `1`、`1`、`1`、`1`
- 说明 `binary(...)` 会得到二进制缓冲区
- 说明二进制缓冲区的 `[]` 下标从 `0` 开始
- 也说明二进制缓冲区项可以直接按单字节读写
字符串子串读取:
代码块身份:可直接照写示例
```tsl
s := "ABCDE";
writeLn(s[2:4]);
```
代码块身份:输出片段
```text
BCD
```
代码块说明:字符串区间 `2:4` 会包含结束位 `4`
字符串替换子串:
代码块身份:可直接照写示例
```tsl
s := "ABCDE";
s[2:4] := "XYZ";
writeLn(s);
```
结果说明:
- 输出 `AXYZE`
字符串删除子串:
代码块身份:可直接照写示例
```tsl
s := "ABCDE";
s[2:4] := "";
writeLn(s);
```
结果说明:
- 输出 `AE`
字符串插入子串:
代码块身份:可直接照写示例
```tsl
s := "AAABBB";
s[4:0] := "111";
writeLn(s);
```
结果说明:
- 输出 `AAA111BBB`
- 这说明 `s[index:0] := ...` 是在指定位置插入,而不是删除或替换区间
## 默认生成模板
如果你只是要抓住“基本类型 + array”的第一层用这个最短例子
代码块身份:可直接照写示例
```tsl
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 风格字符串终止符。
代码块身份:反例 / 不可照写
```text
s := "ABC";
writeLn(s[0]);
```
上面这类写法在运行时会报字符串下标越界。
代码块身份:输出片段
```text
String index out of bounds
```
代码块身份:可直接照写示例
```tsl
s := "ABCDE";
writeLn(s[1:3]);
```
不要把上面这个区间访问类比成数组切片的半开区间。按文档输出,它返回 `ABC`,不是 `AB`;字符串区间的结束位会被包含进结果。
代码块身份:输出片段
```text
ABC
```