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