23 KiB
TSL 表达式与运算符
文档类型:语法主线 是否可直接用于生成代码:是 是否含可直接照写示例:是 是否含不可照写反例:是 遇到不确定时:先按本页表格和专题入口继续判断;03_values_and_literals.md、04_variables_and_constants.md、05_functions_and_calls.md、07_control_flow.md、08_objects_and_classes.md、12_matrix_and_collections.md、16_lexical_structure_and_compile_options.md、11_pitfalls.md;仍不命中时回到语法路由中心 index.md;如果问题已经超出语法层,回到 TSL 总入口 ../index.md
这一篇集中放语言级表达式与运算符。智能体写代码时,只使用本页和对应专题页已经明确的表达式事实,不要把其他语言或外部资料里的运算符习惯直接搬进 TSL。
本篇职责
回答“赋值、算术、比较、逻辑、位运算、集合/类型关系、条件求值、表达式对象、访问/调用、点前缀运算符和专题运算符入口在 TSL 里怎样写”。
智能体表达式/运算符判断流程
- 先判断要写基础表达式运算符,还是集合、矩阵、对象重载、运行时调用或 TS-SQL 这类专题运算符。
- 普通变量赋值只能用
:=,不要把=当赋值写法;const name = value;是常量初始化规则,回看 04_variables_and_constants.md。 - 在普通表达式里,比较才用
=,并且把比较表达式放在writeLn(...)、条件或其他需要布尔值的位置。 - 函数签名里的默认参数
name = value不是比较表达式;默认参数规则回 05_functions_and_calls.md。 - 普通算术优先使用
+、-、*、/、%;左除用\;整数除法和取模可用div、mod;幂运算用^;对数用~;一元倒数用!x。 - 逻辑表达式优先使用
and、or、not;&&、||、.&&、.||、.!!也已可用,但默认不作为主写法;不要把!写成逻辑非。 - 位运算使用点前缀或移位关键字:
.&、.|、.!、.^、shl、shr、rol、ror;普通&、|、~不按位运算理解。 - 已有变量做原地更新时,才使用
+=、-=、*=、/=、\=、%=、^=、~=、div=、.&=、.|=、.^=、.&&=、.||=、a++;、a--;、++a;、--a;。 - 条件求值优先用
flag ? true_value : false_value;需要保留条件自身真值时可用value ?: fallback_value;需要 Pascal 风格时可用if condition then true_value else false_value的形态,但必须带else。 - 需要延迟求值或动态表达式对象时,才使用
@expr或&"...",并用本页明确的eval(...)形态求值。 - 空安全访问只照本页文档明确形态写:
a?.member、a?.[index]、以及c?.a?.[1]。不要外推成任意深度、任意组合都可写。 - 需要连续比较时,标量用
:</:>这组链式比较;数组逐元素比较用::</::>这组矩阵链式比较。 - 命中集合、矩阵、过滤、对象重载、函数值调用、网格调用、运行时后缀或 TS-SQL 时,按“专题运算符入口表”跳到对应页面。
{$ifdef ...}只作为能力探测;编译选项边界回 16_lexical_structure_and_compile_options.md。- 任务需要的运算符没有文档事实时,不要生成猜测写法;只能切到对应专题页、项目自身文档或项目专属规则,仍无结论时记录文档缺口。
核心规则
- 本页是 TSL 表达式与运算符的生成规则页;写代码时只使用本页或对应专题页明确记录的运算符和表达式形态。
本页直接生成的运算符
| 类别 | 运算符 / 形态 | 生成规则 |
|---|---|---|
| 普通赋值 | := |
变量赋值默认只用 :=。 |
| 多变量赋值 | [a, b] := array(...)、[a, ] := array(...) |
需要从数组按位置拆出多个值时使用;只有一个接收变量时保留末尾逗号。 |
| 常量初始化 | const name = value; |
这是常量声明规则,不按普通赋值处理。 |
| 函数默认参数 | name = value |
这是函数签名规则,不按比较表达式处理。 |
| 算术复合赋值 | +=、-=、*=、/=、\=、%=、^=、~=、div= |
已有变量需要原地更新时使用;div= 是整除复合赋值。 |
| 点前缀复合赋值 | .&=、.|=、.^=、.&&=、.||= |
位运算或点前缀逻辑需要原地更新时使用。 |
| 自增 / 自减 | a++、a--、++a、--a |
普通数字变量可直接用;对象重载语义回 24_object_overloads_and_iteration.md。 |
| 算术 | +、-、*、/、\、%、div、mod、^、~ |
普通数值计算使用;\ 是左除,^ 是幂运算,~ 是对数运算。 |
| 一元正负号 | +x、-x |
可直接用于数值表达式。 |
| 一元倒数 | !x |
整型、实型输入返回实型倒数;矩阵逆/广义逆回 22_matrix_deep_dive.md。 |
| 字符串连接 | +、$ |
字符串拼接两种写法都已写入文档;默认优先用 +。 |
| 比较 | =、<>、<、>、<=、>= |
= 只作比较,不作普通赋值。 |
| 点前缀比较 | .=、.<>、.<、.>、.<=、.>= |
标量比较可用;数组/矩阵样数据上的逐元素语义回 12_matrix_and_collections.md。 |
| 逻辑 | and、or、not、&&、||、.&&、.||、.!! |
默认优先写 and / or / not;不要用 ! 表示逻辑非。 |
| 位运算 | .&、.|、.!、.^、shl、shr、rol、ror |
生成代码时写成点前缀或关键字形态;普通 &、|、~ 不作为位运算写法。 |
| 成员 / 下标 / 调用 | obj.member、value[index]、Func(args) |
普通访问和调用可直接使用;类、对象、函数细节回对应专题。 |
| 空安全访问 | a?.member、a?.[index]、c?.a?.[1] |
只按已写入文档形态生成。 |
| 条件求值 | flag ? true_value : false_value |
普通条件表达式默认写法。 |
| 省略真值的条件求值 | value ?: fallback_value |
条件为真时返回条件自身的值;条件为假时返回后备值。 |
| Pascal 风格条件表达式 | if condition then true_value else false_value |
必须带 else。 |
| 表达式对象 | @expr、&"..." |
需要延迟求值或动态表达式对象时使用,并用 eval(...) 求值。 |
| 逗号表达式 | (exp1, exp2, ..., expN) |
从左到右求值,返回最后一个表达式结果。 |
| 集合 / 匹配 / 类型关系 | in、sqlin、like、is |
否定形态见下一行;集合运算回 12_matrix_and_collections.md。 |
| 否定关系 | not in、not sqlin、not like、not is |
直接使用这几种文档明确形态,不自行重组。 |
| 标量链式比较 | :<、:>、:<>、:==、:>=、:<= |
连续标量比较才使用。 |
| 矩阵链式比较 | ::<、::>、::<>、::==、::>=、::<= |
数组逐元素链式比较才使用;数组与矩阵样数据细节回 12_matrix_and_collections.md。 |
专题运算符入口
| 运算符 / 形态 | 责任页 | 生成规则 |
|---|---|---|
call(f, ...)、##f(...)、::FuncName(...) |
05_functions_and_calls.md | 函数值调用、变参转发和全局函数限定调用只按函数页生成。 |
调用点 in / out、变参 ... |
05_functions_and_calls.md | 这是参数传递 / 变参规则,不按普通表达式运算符处理。 |
#Func() with array(...) |
10_runtime_context_and_with.md | 运行时环境参数调用只按运行时上下文页生成。 |
#Func(args)、timeout N、dupvalue(...) |
10_runtime_context_and_with.md | 网格调用和运行时服务后缀只按运行时上下文页生成。 |
union2、intersect、minus、outersect、union2=、intersect=、minus=、outersect= |
12_matrix_and_collections.md | 行集合并、交、差、对称差及其复合赋值只按数组/集合页生成。 |
filterIn(...)、filterNotIn(...) |
13_resultset_and_filters.md | 结果集过滤只按过滤页生成,不当作去重型集合运算。 |
:*、:/、:\、:^、union、|、:|、|=、:|=、&=、:*=、:/=、:\=、:^= |
22_matrix_deep_dive.md、23_fmarray.md | 矩阵乘除、左右拼接、下方拼接和矩阵复合赋值只按矩阵专题页生成。 |
->、!matrix、.?、.?: |
22_matrix_deep_dive.md | 数列数组初始化、矩阵逆/广义逆、矩阵条件求值只按矩阵深水页生成。 |
::、::=、:.、:.= |
22_matrix_deep_dive.md、24_object_overloads_and_iteration.md | 矩阵遍历/深度遍历和对象遍历重载只按专题页生成,不在普通表达式里自行套用。 |
反引号转置 `value |
23_fmarray.md | FMArray 转置只按 FMArray 页生成。 |
operator +、operator <、operator[]、operator[0]、operator[1]、operator for、operator mrows/mcols/msize、operator++、operator += |
24_object_overloads_and_iteration.md | 对象运算符重载只按对象重载页生成。 |
select / sselect / vselect / mselect、where、group by、order by、join |
14_ts_sql.md | TS-SQL 是查询语法,不按普通表达式拼接。 |
默认生成规则:
- 普通变量赋值使用
:=;常量初始化不按普通赋值判断,见 04_variables_and_constants.md。 =在普通表达式里用于比较,不用于赋值。const name = value;和函数签名默认参数name = value不按本页普通表达式比较判断。- 普通算术使用
+、-、*、/、\、%、div、mod、^、~。 - 需要数值倒数时用
!x;整型和实型输入都会得到实型结果。 - 普通逻辑优先使用
and、or、not;&&、||已写入文档但不作为默认主写法。 - 普通条件求值默认用
flag ? true_value : false_value。 - 需要把条件自身作为真值返回时,使用
value ?: fallback_value。 - 需要 Pascal 风格表达式时,才用
if condition then true_value else false_value,且必须带else。 - 字符串拼接默认用
+;需要明确字符串连接时也可用$。 - 字符串匹配用
like时按正则理解,不按 SQL%通配理解。
按需生成规则:
- 已有变量需要原地更新时,才用
+=、-=、*=、/=、\=、%=、^=、~=、div=、.&=、.|=、.^=、.&&=、.||=、a++;、a--;、++a;、--a;。 - 需要矩阵逆/广义逆时才使用
!A,并回 22_matrix_deep_dive.md 确认矩阵生成规则。 - 位运算需要明确写成点前缀或关键字形态:
.&、.|、.!、.^、shl、shr、rol、ror;普通&不作为位与写法使用。 - 点前缀比较
.=、.<>、.<、.>、.<=、.>=命中数组/矩阵样数据时,先回 12_matrix_and_collections.md 判断逐元素语义。 - 需要延迟求值或动态表达式对象时,才用
@expr或&"...",并用eval(...)求值。 - 需要在一个表达式内按顺序执行多个子表达式时,才用逗号表达式
(exp1, exp2, ..., expN)。 - 空安全访问只按
a?.member、a?.[index]和本页示例里的c?.a?.[1]生成,不外推任意深链。 - 否定形式只照
not in、not like、not sqlin、not is这几种文档明确写法生成。 - 连续标量比较才用
:>、:<、:<>、:==、:>=、:<=。 - 数组逐元素链式比较才用
::>、::<、::<>、::==、::>=、::<=。 - 混合两类以上运算符时,优先用括号明确分组,不依赖跨语言记忆里的优先级。
边界规则:
- 没有文档事实的运算符,不生成猜测写法,也不从其他语言习惯反推 TSL 语法。
{$ifdef ifexp}可用于探测if ... then ... else ...表达式能力;不要写成普通业务分支。{$ifdef nilinvoke}可用于探测 nil 调用相关能力;编译选项细节见 16_lexical_structure_and_compile_options.md。
可直接照写示例
使用这些示例时遵守:
:=是普通变量赋值写法;常量初始化规则回 04_variables_and_constants.md。- 函数调用、命名参数、默认参数和函数值调用边界回 05_functions_and_calls.md;不要把函数签名里的
name = value当成比较表达式。 - 分支/循环语句回 07_control_flow.md;对象成员和类相关表达式回 08_objects_and_classes.md。
- 字符串字面量、拼接与文本边界回 03_values_and_literals.md;数组扩展和矩阵样数据回 12_matrix_and_collections.md。
{$ifdef ...}能力探测回 16_lexical_structure_and_compile_options.md,不要写成普通业务逻辑。
基础赋值和条件求值
代码块身份:可直接照写示例
a := 1;
b := 2;
flag := a < b;
value := flag ? 10 : 20;
writeLn(value);
代码块身份:输出片段
10
省略真值的条件求值:
代码块身份:可直接照写示例
writeLn(2 ?: 9);
writeLn(0 ?: 9);
代码块身份:输出片段
2
9
多变量赋值:
代码块身份:可直接照写示例
[a, b] := array(1, 2, 3);
writeLn(a);
writeLn(b);
[first_value, ] := array(4, 5);
writeLn(first_value);
代码块身份:输出片段
1
2
4
运算赋值:
代码块身份:可直接照写示例
a := 1;
a += 2;
writeLn(a);
代码块身份:输出片段
3
基础算术:
代码块身份:可直接照写示例
writeLn(1 + 2);
writeLn(5 - 2);
writeLn(3 * 4);
writeLn(8 / 2);
writeLn(3 \ 2);
writeLn(9 % 4);
writeLn(9 div 4);
writeLn(9 mod 4);
writeLn(2 ^ 3);
writeLn(8 ~ 2);
writeLn(-3 + 5);
writeLn(+3);
代码块身份:输出片段
3
3
12
4
0.666666666666667
1
2
1
8
3
2
3
基础比较:
代码块身份:可直接照写示例
writeLn(1 < 2);
writeLn(2 > 1);
writeLn(2 <= 2);
writeLn(2 >= 2);
writeLn(2 = 2);
writeLn(2 <> 3);
代码块身份:输出片段
1
1
1
1
1
1
点前缀比较:
代码块身份:可直接照写示例
writeLn(2 .= 2);
writeLn(2 .<> 3);
writeLn(1 .< 2);
writeLn(2 .> 1);
writeLn(2 .<= 2);
writeLn(2 .>= 2);
代码块身份:输出片段
1
1
1
1
1
1
一元倒数:
代码块身份:可直接照写示例
a := 2;
b := 4.0;
ra := !a;
rb := !b;
writeLn(ra);
writeLn(rb);
writeLn(dataType(ra));
writeLn(dataType(rb));
代码块身份:输出片段
0.5
0.25
1
1
说明:
- 整型
a和实型b都可以用!求倒数。 - 上面两个
dataType(...)都输出1,表示结果是实型。
逻辑运算:
代码块身份:可直接照写示例
writeLn((1 < 2) and (2 < 3));
writeLn((1 > 2) or (2 < 3));
writeLn(not (1 > 2));
writeLn(.!! 0);
writeLn((1 < 2) && (2 < 3));
writeLn((1 > 2) || (2 < 3));
writeLn(1 .&& 0);
writeLn(0 .|| 2);
代码块身份:输出片段
1
1
1
1
1
1
0
1
位运算:
代码块身份:可直接照写示例
writeLn(6 .& 3);
writeLn(4 .| 1);
writeLn(6 .^ 3);
writeLn(.! 1);
writeLn(1 shl 2);
writeLn(8 shr 1);
writeLn(1 rol 1);
writeLn(2 ror 1);
代码块身份:输出片段
2
5
5
-2
4
4
2
1
基础算术复合赋值:
代码块身份:可直接照写示例
a := 10;
a -= 3;
writeLn(a);
a *= 4;
writeLn(a);
a /= 7;
writeLn(a);
a %= 5;
writeLn(a);
b := 3;
b \= 2;
writeLn(b);
c := 8;
c ~= 2;
writeLn(c);
d := 7;
d div= 3;
writeLn(d);
代码块身份:输出片段
7
28
4
4
0.666666666666667
3
2
幂运算和位运算也可以使用复合赋值:
代码块身份:可直接照写示例
a := 2;
a ^= 3;
writeLn(a);
b := 6;
b .&= 3;
writeLn(b);
c := 4;
c .|= 1;
writeLn(c);
d := 6;
d .^= 3;
writeLn(d);
e := 0;
e .||= 2;
writeLn(e);
f := 1;
f .&&= 0;
writeLn(f);
代码块身份:输出片段
8
2
5
5
1
0
字符串同样支持 +=:
代码块身份:可直接照写示例
s := "A";
s += "B";
writeLn(s);
代码块身份:输出片段
AB
$ 也可以用于字符串连接:
代码块身份:可直接照写示例
writeLn("A" $ "B");
代码块身份:输出片段
AB
字符串拼接、比较和 like:
代码块身份:可直接照写示例
writeLn("222" + "888");
writeLn("A" < "a");
writeLn("AB" < "ABC");
writeLn("ABC" = "ABC");
writeLn("2009-01-01" like "\\d{4}-\\d{2}-\\d{2}");
代码块身份:输出片段
222888
1
1
1
1
说明:
- 字符串可以直接用
+拼接。 - 字符串比较区分字符序和大小写;当前例子里
"A" < "a"为真。 like的右侧可以直接写正则模式。
like 不要按 SQL % 通配去理解:
代码块身份:可直接照写示例
writeLn("abc" like "a.*");
writeLn("abc" like "a%");
代码块身份:输出片段
1
0
因此 like 更接近“正则匹配”,不是 SQL 那套 % / _ 通配语义。
自增与自减:
代码块身份:可直接照写示例
a := 1;
a++;
writeLn(a);
a--;
writeLn(a);
代码块身份:输出片段
2
1
前置自增与自减:
代码块身份:可直接照写示例
a := 1;
++a;
writeLn(a);
--a;
writeLn(a);
代码块身份:输出片段
2
1
if 表达式:
代码块身份:可直接照写示例
writeLn(if 2 > 1 then 2 else 1);
代码块身份:输出片段
2
if condition then true_value else false_value 必须带 else,否则不是本页可照写的表达式形态。
表达式对象
@ 表达式前导:
代码块身份:可直接照写示例
base_value := 1;
expr_value := @base_value + 1;
result_value := eval(expr_value);
writeLn(result_value);
代码块身份:输出片段
2
@base_value + 1 会得到一个可交给 eval(...) 求值的表达式对象。
&"..." 表达式常量:
代码块身份:可直接照写示例
base_value := 1;
expr_value := &"base_value + 1";
result_value := eval(expr_value);
writeLn(result_value);
代码块身份:输出片段
2
&"base_value + 1" 会把字符串编译成表达式对象,再由 eval(...) 求值。
逗号表达式:
代码块身份:可直接照写示例
writeLn(Demo());
function Demo();
begin
return (a := 1, b := 2, c := 3, a + b + c);
end;
代码块身份:输出片段
6
逗号表达式会按从左到右顺序执行前面的赋值,再返回最后一个表达式结果。
逗号表达式也可以继续参与外层计算:
代码块身份:可直接照写示例
result_value := (b := 2, c := 3, b * c) * c;
writeLn(result_value);
代码块身份:输出片段
18
逗号表达式本身可以作为一个普通子表达式继续参与后续运算。
空安全访问
代码块身份:可直接照写示例
a := nil;
writeLn(a?.value = nil);
h := new Holder();
h.value := 7;
writeLn(h?.value);
arr := nil;
writeLn(arr?.[0] = nil);
type Holder = class
value;
end;
代码块身份:输出片段
1
7
1
更深一层的混合空安全访问,本页只写入下面这个形态:
代码块身份:可直接照写示例
c := nil;
writeLn(c?.a?.[1] = nil);
代码块身份:输出片段
1
不要从这一段外推成所有深度、所有成员/下标组合都可写。
否定形式运算
代码块身份:可直接照写示例
writeLn(1 not in array(2, 3));
writeLn("2009-1-1" not like "\\d{4}-\\d{2}-\\d{2}");
writeLn(1 not sqlin array(2, 3));
obj := new A();
writeLn(obj not is class(B));
type A = class
end;
type B = class
end;
代码块身份:输出片段
1
1
1
1
标量链式比较
代码块身份:可直接照写示例
writeLn(1 :< 2 :< 3);
writeLn(3 :> 2 :> 1);
writeLn(1 :== 1 :== 1);
writeLn(3 :>= 2 :>= 2);
writeLn(1 :<= 2 :<= 3);
writeLn(1 :<> 2 :<> 3);
代码块身份:输出片段
1
1
1
1
1
1
矩阵链式比较
代码块身份:可直接照写示例
r := array(1, 2, -1) ::< array(2, 1, 0) ::< array(3, 2, 1);
writeLn(r[0]);
writeLn(r[1]);
writeLn(r[2]);
s := array(1, 2, -1) ::< 2 ::< array(3, 2, 1);
writeLn(s[0]);
writeLn(s[1]);
writeLn(s[2]);
代码块身份:输出片段
1
0
1
1
0
0
矩阵链式比较会按元素位置分别得到结果数组,并且可以和标量混用。
条件编译探测
代码块身份:可直接照写示例
{$ifdef ifexp}
writeLn(1);
{$else}
writeLn(0);
{$endif}
{$ifdef nilinvoke}
writeLn(1);
{$else}
writeLn(0);
{$endif}
代码块身份:输出片段
1
1
这只能作为能力探测示例使用;智能体不要把条件编译探测写成普通业务逻辑。
默认生成模板
如果你只需要最小的“比较 + 三目”例子,直接用这个:
代码块身份:可直接照写示例
flag := 1 < 2;
value := flag ? 10 : 20;
writeLn(value);
代码块身份:输出片段
10
禁止项
- 用
=当赋值运算符。 - 把
==/!=当成等值 / 不等值比较;本页比较使用=/<>。 - 把
!当成逻辑非;本页逻辑非使用not,!是一元倒数/矩阵逆相关运算符。 - 把普通
&当成位与;位与使用.&。 - 把
$=当成字符串复合赋值;字符串原地拼接使用+=。 - 把
if表达式写成没有else的半句。 - 把本页明确的
c?.a?.[1]外推成所有深链式空安全访问都可靠。 - 从其他语言推断 TSL 运算符能力。
代码块身份:反例 / 不可照写
a = 1;
代码块身份:输出片段
invalid statement
上面这种写法会编译失败,因为单独的 = 在这里会被当成不成立的表达式。
代码块身份:反例 / 不可照写
v := if 2 > 1 then 2;
代码块身份:输出片段
invalid statement
上面这种写法也会编译失败;if 表达式必须带 else。