playbook/docs/tsl/syntax/04_variables_and_constants.md

377 lines
8.1 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 变量与常量
文档类型:语法主线
是否可直接用于生成代码:是
是否含可直接照写示例:是
是否含不可照写反例:是
遇到不确定时:先按本页候选页继续判断;[03_values_and_literals.md](03_values_and_literals.md)、[06_expressions_and_operators.md](06_expressions_and_operators.md)、[05_functions_and_calls.md](05_functions_and_calls.md)、[16_lexical_structure_and_compile_options.md](16_lexical_structure_and_compile_options.md)、[11_pitfalls.md](11_pitfalls.md);仍不命中时回到语法路由中心 [index.md](index.md);如果问题已经超出语法层,回到 TSL 总入口 [../index.md](../index.md)
这一篇收拢变量与常量初始化规则。
## 本篇职责
回答“普通变量怎样直接使用、`var` 在什么位置出现、常量必须怎样初始化、哪些名字一旦绑定就不能再赋值”。
## 智能体变量/常量判断流程
1. 普通变量默认直接用 `:=` 首次赋值,不要先补一个没有需求证据的 `var` 段。
2. 只有用户要求显式声明或遇到 `{$explicit+}` 时才优先写 `var`
3. 常量声明必须同时初始化;默认只生成 `const name = value;`,顶层脚本常量需要后续脚本语句时,仍按 `.tsl` 语句区规则组织。
4. 多参数赋值按 `[a, b] := array(...)` 写;单变量拆包必须写成 `[name, ] := array(...)`
5. 如果问题已经变成表达式求值、函数参数、运行时类型 / 转换、或 `{$explicit+}` 之外的编译选项,不要留在变量页硬推断;分别跳到 [06_expressions_and_operators.md](06_expressions_and_operators.md)、[05_functions_and_calls.md](05_functions_and_calls.md)、[17_types_and_conversions.md](17_types_and_conversions.md) 或 [16_lexical_structure_and_compile_options.md](16_lexical_structure_and_compile_options.md)。
6. 没有对应代码块时不要发明变量/常量写法;尤其不要从 Pascal 的声明习惯反推 TSL 必须先声明变量。
## 核心规则
- 默认变量模型是“直接赋值即得到变量”,不要求先写 `var`
- 例如:`a := 1; b := array(1, 2, 3);` 这种写法可直接编译。
- `var name;` 只作为显式声明写法,不能当成默认必需步骤。
- `const name = value;` 是常量初始化的默认生成写法;`const` 不能只声明名字而不初始化。
- 文档规则:
- `const name = value;` 适合顶层脚本常量、函数内部 `const` 段、`unit` 接口常量、类成员常量。
- 顶层脚本里的 `const name = value;` 可以放在后续脚本语句之前;单独只写一行 `const name = value;` 不作为可运行脚本骨架。
- 函数内部 `const = expr`、以及松散脚本顶层的 `const = expr`,都可以把右侧写成常量表达式。
-`const =` 初始化的这些常量不能再次赋值。
- 多参数赋值文档明确写法是 `[a, b] := array(...)`
- 左侧只有一个变量时,末尾逗号不能省略,必须写成 `[a, ] := array(...)`
- 当左侧变量数大于右侧数组长度时,多出的变量会得到 `nil`
- 右侧数组元素也可以是数组;拆出来的变量会直接得到对应子数组。
- 多参数赋值也可以出现在函数调用参数里。
- `{$explicit+}` 开启后,后续变量必须先用 `var` 声明;未声明变量会报 `variable not defined`
## 可直接照写示例
使用这些示例时遵守:
- 默认生成普通变量时直接用 `:=`;只有用户明确要求或 `{$explicit+}` 场景才复制 `var`
- 普通示例默认按 `.tsl` 脚本语句区书写;需要函数或类型时,放在后置声明区。
- 复制常量示例时只使用 `const name = value;`
### 普通变量与显式声明
默认变量模型:
代码块身份:可直接照写示例
```tsl
a := 1;
b := array(1, 2, 3);
writeLn(a);
writeLn(b[1]);
```
代码块身份:输出片段
```text
1
2
```
显式 `var` 写法:
代码块身份:可直接照写示例
```tsl
var a;
a := 1;
```
`{$explicit+}` 下的显式声明:
代码块身份:可直接照写示例
```tsl
{$explicit+}
var a;
a := 1;
writeLn(a);
```
代码块身份:输出片段
```text
1
```
### 常量初始化
顶层最稳的常量写法:
代码块身份:可直接照写示例
```tsl
const value = 1;
echo value;
```
代码块身份:输出片段
```text
1
```
顶层 `const =` 也可以写常量表达式:
代码块身份:可直接照写示例
```tsl
const value = 1 + 2 * 3;
writeLn(value);
```
代码块身份:输出片段
```text
7
```
函数内部 `const` 段:
代码块身份:可直接照写示例
```tsl
function Demo();
const max_retries = 1 + 2 * 3;
begin
return max_retries;
end;
```
`unit` 接口常量:
代码块身份:可直接照写示例
```tsl
unit DemoUnit;
interface
const value = 1;
function GetValue();
implementation
function GetValue();
begin
return value;
end;
end.
```
类成员常量:
代码块身份:可直接照写示例
```tsl
type DemoType = class
public
const value = 1;
end;
```
顶层 `const =` 只有在“后面继续接可执行语句”的脚本模型里才成立:
代码块身份:可直接照写示例
```tsl
const max_retries = 3 + 4;
value := max_retries;
```
### 多参数赋值
多参数赋值:
代码块身份:可直接照写示例
```tsl
[r1, r2] := array(1, 3, 5, 7, 9);
writeLn(r1);
writeLn(r2);
```
代码块身份:输出片段
```text
1
3
```
单变量拆包时,末尾逗号不能省略:
代码块身份:可直接照写示例
```tsl
[re, ] := array(1, 2, 3, 4);
writeLn(re);
```
代码块身份:输出片段
```text
1
```
左侧变量比右侧数组更长时,多出的变量为 `nil`
代码块身份:可直接照写示例
```tsl
[r1, r2] := array(1);
writeLn(r1);
writeLn(r2 = nil);
```
代码块身份:输出片段
```text
1
1
```
右侧元素也可以是数组:
代码块身份:可直接照写示例
```tsl
[r1, r2] := array((1, 2), (3, 4));
writeLn(r1[0]);
writeLn(r1[1]);
writeLn(r2[0]);
writeLn(r2[1]);
```
结果说明:
- 依次输出 `1`、`2`、`3`、`4`
代码块身份:输出片段
```text
1
2
3
4
```
函数返回数组后也可以直接拆包:
代码块身份:可直接照写示例
```tsl
[error, re] := PairAdd(3, 4);
writeLn(error);
writeLn(re);
function PairAdd(a, b);
begin
return array(0, a + b);
end;
```
结果说明:
- 依次输出 `0`、`7`
代码块身份:输出片段
```text
0
7
```
函数参数里也可以使用多参数赋值:
代码块身份:可直接照写示例
```tsl
writeLn(Test(e := 3, [f, g] := array(1, 2), g));
function Test(a, b, c);
begin
return a + b + c;
end;
```
结果说明:
- 输出 `6`
代码块身份:输出片段
```text
6
```
## 默认生成模板
普通变量和顶层常量的默认骨架如下:
代码块身份:可直接照写示例
```tsl
const max_retries = 3;
counter := max_retries;
items := array(1, 2, 3);
```
## 禁止项
- 以为普通变量必须先写 `var` 才能使用。
- 把常量初始化写成普通变量赋值风格。
- 以为 `const =` 顶层单独写一行就一定成立。
- 以为 `const` 可以只声明名字,不写初始化表达式。
- 以为单变量拆包可以写成 `[a] := array(...)`
- 以为 `{$explicit+}` 开启后仍然可以继续直接写未声明变量。
代码块身份:反例 / 不可照写
```text
const value = 1;
value := 2;
```
上面这类写法会编译失败,报错点在重新赋值这一行。
代码块身份:反例 / 不可照写
```text
const value = 1;
```
上面这类“顶层单独一行”的 `const =` 也会编译失败。
代码块身份:反例 / 不可照写
```text
const value;
```
上面这类没有初始化表达式的 `const` 会编译失败;`const` 必须写初始化值。
错误原因是常量语句缺少初始化表达式。
代码块身份:反例 / 不可照写
```text
[re] := array(1, 2, 3);
```
上面这种单变量拆包写法会报 `left side can not be assign to`。单变量时必须写成 `[re, ] := ...`
代码块身份:反例 / 不可照写
```text
{$explicit+}
a := 1;
```
上面这类写法会编译失败,主因是 `variable not defined`
代码块身份:输出片段
```text
variable not defined
```