# TSL 类型与转换

这一篇吸收语言基础里“数据类型、初始值、弱类型行为、类型转换、复数类型”这一层。

## 本篇职责

<!-- section-id: syntax-16-001 -->

回答“运行时值会变成什么类型、默认值是什么、怎样显式转换、复数怎样表示”。本页拥有数值 / 日期时间 / 真假值 / nil / 复数字面量与类型转换边界；变量声明形式和编译期开关不在本页收口。

## 核心规则

<!-- section-id: syntax-16-002 -->

- 整数常量支持十进制、`0x` 十六进制、`0b` 二进制和 `0o` 八进制。
- `100L` 这类后缀写法表示 `Int64` 常量。
- 超过 32 位整数范围的十进制整数字面量 `2147483648` 按 `Int64`，不按实数处理。
- 实数写法包括 `1.5`、`1.0E2`、`1E2`；其中 `1E2` 按实数，不按整数处理。
- 日期时间字面量可写成 `20111231T`、`20101231.0931T`。
- `true` 和 `false` 可以直接使用；打印结果分别是 `1` 和 `0`。
- 条件判断里，`0` 按假值处理，非 `0` 数值按真值处理。
- `inf` 和 `nan` 是特殊实数常量；`ifReal(inf)`、`ifReal(nan)` 都返回 `1`。
- `inf > 1000` 为真，`nan = nan` 为假。
- `0 / 0` 得 `nan`，`2 / 0` 得 `inf`，`-2 / 0` 得 `-inf`。
- `nan` 参与 `=`、`>`、`<`、`>=`、`<=` 一律为假；只有 `<>` 为真，所以 `nan <> nan` 是可用的判定形态。
- `nan` 参与算术运算的结果仍是 `nan`：`nan + 1`、`nan * 0`、`nan - nan` 都是 `nan`。
- 未初始化的普通变量会得到整数 `0`。
- 新建对象的普通成员变量默认值是 `nil`。
- 对空数组读取一个尚未赋值的下标时，结果是 `nil`。
- `ifNil(nil)` 可用于显式空值判定。
- 空字符串、空数组和 `nil` 都按假值处理。
- `nil` 可以参与加法；`nil + 1` 和 `1 + nil` 都返回 `1`，`nil + nil` 仍然是 `nil`。
- `nil` 参与其它算术运算时按 `0` 处理：`nil / 2` 得 `0.0`、`nil * 2` 得 `0`、`nil - 2` 得 `-2`、`2 - nil` 得 `2`。
- `ifNumber(...)` 会把整数、`Int64`、实数都视为数值；普通字符串不算数值。
- TSL 不是“完全禁止混合类型表达式”；像 `b + a` 这种“整数 + 字符串”会通过编译，但在运行时会报 `operand type error`。
- 因此，类型转换要显式写出来，例如 `strToInt(...)`，不要把运行时失败留给解释器。
- 复数字面量可以直接写成 `a + bj`，也可以用 `complex(a, b)` 构造。
- `dataType(z)` 对复数返回 `41`，`ifComplex(z)` 对复数返回 `1`。
- `real`、`imag`、`conj`、`abs` 都可用于复数。
- 实数 `x` 与复数 `x + 0j` 的相等比较结果为真。
- 复数支持四则运算 `+` / `-` / `*` / `/`，可与实数混合运算；矩阵乘用 `:*`。
- `complex(array(...), imag)` 会返回 `Array`；`complex(fmarray..., imag)` 会返回 `FMArray`，并且其单元格类型是 `41`。

## 可直接照写示例

<!-- section-id: syntax-16-003 -->

### 数值、日期与真值

<!-- section-id: syntax-16-004 -->

<!-- tags: 整数小数, 十六进制, 日期字面量, 真假值, 判断类型 -->

基础数字字面量与类型判定：

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

```tsl
writeLn(ifInt(100));
writeLn(ifInt(0x10));
writeLn(ifInt(0b10));
writeLn(ifInt(0o10));
writeLn(ifInt64(100L));
writeLn(ifReal(1.5));
writeLn(ifReal(1.0E2));
writeLn(ifInt(1E2));
writeLn(ifReal(1E2));
```

结果说明：

- 前七行都输出 `1`
- `ifInt(1E2)` 输出 `0`
- `ifReal(1E2)` 输出 `1`
- 说明 `100`、`0x10`、`0b10`、`0o10` 都按整数处理
- 说明 `100L` 按 `Int64` 处理
- 说明 `1E2` 按实数，不按整数处理

代码块身份：输出片段

```text
1
1
1
1
1
1
1
0
1
```

超 32 位整数与日期时间字面量：

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

```tsl
writeLn(ifInt64(2147483648));
writeLn(ifReal(2147483648));
d1 := 20111231T;
d2 := 20101231.0931T;
writeLn(dateToStr(d1));
writeLn(dateToStr(d2));
writeLn(timeToStr(d2));
```

结果说明：

- 依次输出 `1`、`0`、`2011-12-31`、`2010-12-31`、`09:31:00`
- 说明 `2147483648` 按 `Int64`，不按实数处理
- 说明 `20111231T`、`20101231.0931T` 这类日期时间字面量可以直接使用

布尔常量、数值真值和特殊实数：

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

```tsl
writeLn(true);
writeLn(false);
if 2 then writeLn(1) else writeLn(0);
if 0 then writeLn(1) else writeLn(0);
if -1 then writeLn(1) else writeLn(0);
writeLn(ifReal(inf));
writeLn(ifReal(nan));
writeLn(inf > 1000);
writeLn(nan = nan);
```

结果说明：

- 前两行分别输出 `1`、`0`
- `if 2` 输出 `1`，`if 0` 输出 `0`，`if -1` 输出 `1`
- `ifReal(inf)` 和 `ifReal(nan)` 都输出 `1`
- `inf > 1000` 输出 `1`
- `nan = nan` 输出 `0`
- 说明 `true` / `false` / `inf` / `nan` 属于文档明确常量
- 说明数值条件判断里，`0` 为假，非 `0` 为真

`nan` / `inf` 的产生方式，以及 `nan` 比较与算术的完整边界：

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

```tsl
n := 0 / 0;
writeLn(n);
writeLn(2 / 0);
writeLn(-2 / 0);
writeLn(n > 1);
writeLn(n < 1);
writeLn(n >= 1);
writeLn(n <= 1);
writeLn(n <> n);
writeLn(n + 1);
writeLn(n * 0);
writeLn(n - n);
```

结果说明：

- `0 / 0` 输出 `NAN`，`2 / 0` 输出 `INF`，`-2 / 0` 输出 `-INF`
- `n > 1`、`n < 1`、`n >= 1`、`n <= 1` 全部输出 `0`
- `n <> n` 输出 `1`，是唯一对 `nan` 成立的比较，可用来判定 `nan`
- `n + 1`、`n * 0`、`n - n` 都输出 `NAN`，说明 `nan` 会沿算术传播
- 因此数据参与计算前要先滤掉 `nan`，否则整条链路结果都是 `nan`

代码块身份：输出片段

```text
NAN
INF
-INF
0
0
0
0
1
NAN
NAN
NAN
```

### 初始值与空值

<!-- section-id: syntax-16-005 -->

<!-- tags: 变量默认值, nil, 空值, 没赋值是什么 -->

变量初始值与缺失值：

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

```tsl
writeLn(a);
holder := new Holder();
writeLn(holder.Value = nil);
arr := array();
writeLn(arr[0] = nil);

type Holder = class
public
    Value;
end;
```

结果说明：

- 依次输出 `0`、`1`、`1`
- 说明未初始化普通变量默认是整数 `0`
- 说明类成员默认值是 `nil`（`holder.Value`）
- 也说明空数组读取尚未赋值的下标时，结果是 `nil`

`nil` 的显式判定与加法边界：

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

```tsl
writeLn(ifNil(nil));
writeLn(nil + 1);
writeLn(1 + nil);
writeLn(nil + nil = nil);
```

结果说明：

- 依次输出 `1`、`1`、`1`、`1`
- 说明 `ifNil(nil)` 是可用的显式空值判定
- 说明 `nil` 可以直接参与加法
- 其中 `nil + 1` 和 `1 + nil` 都得到 `1`
- `nil + nil` 仍保持为 `nil`

`nil` 在其它算术运算里按 `0` 参与：

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

```tsl
writeLn(nil / 2);
writeLn(nil * 2);
writeLn(nil - 2);
writeLn(2 - nil);
```

结果说明：

- 依次输出 `0.0`、`0`、`-2`、`2`
- 说明 `nil` 在乘除减里都按 `0` 参与，不会中断计算
- `nil / 2` 走实数除法所以显示成 `0.0`

代码块身份：输出片段

```text
0.0
0
-2
2
```

空值条件判断：

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

```tsl
if "" then writeLn(0) else writeLn(1);
if array() then writeLn(0) else writeLn(1);
if nil then writeLn(0) else writeLn(1);
```

结果说明：

- 依次输出 `1`、`1`、`1`
- 说明空字符串、空数组和 `nil` 都按假值处理

`ifNumber(...)` 的最小边界：

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

```tsl
writeLn(ifNumber(100));
writeLn(ifNumber(100L));
writeLn(ifNumber(1.5));
writeLn(ifNumber("100"));
```

结果说明：

- 依次输出 `1`、`1`、`1`、`0`
- 说明 `ifNumber(...)` 接受整数、`Int64`、实数
- 也说明普通数字字符串不会被 `ifNumber(...)` 直接当成数值

### 显式类型转换

<!-- section-id: syntax-16-006 -->

<!-- tags: 转整数, 转字符串, 类型互转, strToInt -->

显式类型转换的正向例子：

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

```tsl
a := "1234";
b := 1;
c := b + strToInt(a);
writeLn(c);
```

结果说明：

- 输出 `1235`
- 说明显式转换后，字符串数值与整数可以按预期参与加法

### 复数

<!-- section-id: syntax-16-007 -->

<!-- tags: 复数, 虚数, 实部虚部 -->

复数常量、类型与基础读取：

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

```tsl
z1 := 4 + 3j;
z2 := complex(5, -2);
writeLn(dataType(z1));
writeLn(ifComplex(z1));
writeLn(real(z1));
writeLn(imag(z1));
writeLn(z2);
```

结果说明：

- `dataType(4 + 3j)` 返回 `41`
- `ifComplex(4 + 3j)` 返回 `1`
- `real(4 + 3j)` 返回 `4`
- `imag(4 + 3j)` 返回 `3`
- `complex(5, -2)` 打印结果是 `5-2j`

代码块身份：输出片段

```text
41
1
4
3
5-2j
```

共轭、模与等值比较：

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

```tsl
z := 4 + 3j;
c := conj(z);
writeLn(abs(z));
writeLn(real(c));
writeLn(imag(c));
if 3.15 = 3.15 + 0j then
    writeLn(1)
else
    writeLn(0);
```

结果说明：

- `abs(4 + 3j)` 返回 `5`
- `conj(4 + 3j)` 的实部是 `4`、虚部是 `-3`
- `3.15 = 3.15 + 0j` 比较结果为真

复数 `Array` 与复数 `FMArray`：

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

```tsl
a := complex(array(1, 2, 3), 5.5);
f := complex(fmarray[1, 2, 3], 5.5);
writeLn(dataType(a));
writeLn(length(a));
writeLn(a[0], ',', a[1], ',', a[2]);
writeLn(dataType(f));
writeLn(dataType(f, 1));
writeLn(length(f));
writeLn(f[0], ',', f[1], ',', f[2]);
```

结果说明：

- `complex(array(1, 2, 3), 5.5)` 的 `dataType` 是 `5`
- 上述复数数组长度是 `3`，三个元素依次是 `1+5.5j`、`2+5.5j`、`3+5.5j`
- `complex(fmarray[1, 2, 3], 5.5)` 的 `dataType` 是 `27`
- 上述复数 `FMArray` 的单元格类型 `dataType(f, 1)` 是 `41`
- 该 `FMArray` 长度是 `3`，三个元素依次是 `1+5.5j`、`2+5.5j`、`3+5.5j`

复数四则运算：

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

```tsl
z1 := 4 + 3j;
z2 := 5 + 12j;
writeLn(z1 + z2);
writeLn(z2 - z1);
writeLn(z1 * z2);
writeLn(z2 / z1);
```

结果说明：

- `z1 + z2` 得 `9.0+15.0j`
- `z2 - z1` 得 `1.0+9.0j`
- `z1 * z2` 得 `-16.0+63.0j`
- `z2 / z1` 得 `2.24+1.32j`
- 复数参与运算后实部虚部按浮点显示（带 `.0`）
- 复数在四则运算上和实数写法一致，也支持与实数混合运算

复数矩阵乘 `:*`：

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

```tsl
a := array((-1 + 1j), (-3 + 1j));
b := array((2 + 2j, 1 + 3j));
writeLn(tostn(a :* b));
```

结果说明：

- 复数矩阵同样用 `:*` 做矩阵乘（区别于逐单元乘 `*`），矩阵乘除乘方算符体系见 [21_matrix_deep_dive.md](21_matrix_deep_dive.md)
- `a` 是 `2 x 1`、`b` 是 `1 x 2`，`a :* b` 得 `2 x 2` 结果矩阵
- 结果是 `array((-4.0+0.0j,-4.0-2.0j),(-8.0-4.0j,-6.0-8.0j))`

> 版本边界：复数相关功能只在新一代客户端与新一代服务器支持；本节示例在支持复数的运行环境（如新一代服务端）验证。虚数单位固定用 `j`，不是 `i`。

复数最短骨架：

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

```tsl
z := 4 + 3j;
writeLn(real(z));
writeLn(imag(z));
```

## 禁止项

<!-- section-id: syntax-16-008 -->

- 不要把“能编译”误解成“会自动按期望类型转换”。
- 不要把数字字符串直接当成数值参与算术；需要数值语义时先显式转换。
- 不要从其他语言的 truthy/falsy 或隐式转换规则反推 TSL。
- 不要用 `nan = nan` 判定 `nan`；等值和大小比较对 `nan` 恒假，只有 `<>` 为真。
- 不要以为 `nan` 会在后续运算里被“吃掉”；它沿算术一路传播，参与计算前要先滤掉。
- 不要以为 `nil` 参与乘除减会中断计算或报错；它按 `0` 参与。
- 不要把复数统计函数、分解函数与更大函数族当成本页文档能力。

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

```text
a := "1234";
b := 1;
writeLn(b + a);
```

上面这种写法不作为可写事实。它可能通过编译，但运行时会报 `Addition instruction error, operand type error`；不要把“能编译”误解成“会自动按你期望的类型转换”。
