playbook/docs/tsl/syntax_book/01_language_basics.md

110 KiB
Raw Blame History

01 语言基础

本章收录 TSL 语言基本构成、数据类型、变量/表达式、字符串与块环境语句等基础语法。

目录

基础知识

大小写无关规则

重要特性TSL 语言是大小写无关Case-Insensitive的编程语言。

这意味着:

  • 关键字BeginbeginBEGIN 完全等价
  • 函数名Date()date()DATE() 是同一个函数
  • 变量名myVarmyvarMYVAR 引用同一个变量
  • 类型名IntegerintegerINTEGER 是相同的类型

示例代码:

function TestCaseInsensitive(): string
begin
    var my_value := 100;
    my_value := my_value + 50;
    if my_value > 200 then
        return "large";
    return "small";
end;

注意事项:

  • 字符串内容区分大小写:'Hello''hello' 不相同。
  • 文件路径是否区分大小写取决于操作系统。

TSL 程序的基本构成

TSL 程序以函数定义体为基本结构,一个定义体内可包含多个函数。

function FunctionName(param1, param2)
begin
    statement;
end;

例子:显示当天日期。

function HelloTsl(): string
begin
    today := Date();
    message := "Hello, today is " + DateToStr(today) + "!";
    return message;
end;

简化写法:

function HelloTsl(): string
begin
    return "Hello, today is " + DateToStr(Date()) + "!";
end;

TSL 的符号

保留字(关键字)

保留字是指在 TSL 语言中具有特定的含义,是编写使用前必须了解的。目前 TSL 语言中的保留字一共有:

PROGRAM

程序开始的入口,一般用户不需要使用,在作为独立的 TSL 脚本时可以使用。

通常来说,倘若编写 TSL 代码作为 CGI 执行运行,系统默认以 PROGRAM 模式运行。虽然 TSL 可以省略 PROGRAM 关键字,但是使用 PROGRAM 关键字可以使得 TSL 代码里包含子函数。

例如:

program
Test;
function sub1();
begin
    Writeln('Execute Sub1');
end;
begin
    Sub1();
end.
FOR

循环子句,可组成 FOR TO DO 或者 FOR DOWNTO DO 循环子句。

参见TODOWNTO

REPEAT

循环子句,可组成 REPEAT 循环子句。

SYSTEM

系统函数前缀,因为系统函数可能被用户函数重载,如果要指定调用系统函数,可以用 system.funcname 的模式。例如 system.close()是指定调用系统的收盘价函数。

参见:用户和函数相关函数

TO

循环子句往后,可组成 FOR TO DO 循环子句,也可以用于 CASE 和 SELECT 语句中。

参见FORDOWNTO

DO

循环子句做,可组成 FOR TO/DOWNTO DO 或者 WHILE DO 循环子句。此外DO 还可以组成 WITH DO 设置语句的运行时环境。

参见WHILEFORWITH

THEN

条件子句那么,可组成 IF THEN 或者 IF THEN ELSE 条件子句,详细见 IF

BEGIN

语句段开始,与 END 组合成 BEGIN END 语句段,详细见 END

WITH

为语句设置运行时环境,可组成 WITH DO在类 SQL 语法中,可在 JOIN 子句中使用 with(expLeft…,expRight)作为快速条件。

参见DOWith 设值运算符块环境设置语句WITH ON 条件。

STEP

for 循环语句里的步长for 的缺省步长为 1。

参见FORTODOWNTO

ELSE

条件子句否则,可组成 IF THEN ELSE

参见IFTHEN

REALPARAMCOUNT

系统内置变量,用来得到函数调用时实际被送入的参数个数。用于下标方式获取参数。

例如:

a := function(i1, i2)
begin
    echo '实际送入的参数:', realparamcount;
    echo '参数:', paramcount;
    return 1;
end;
return Call(a, 1);

结果:返回 1。

参见PARAMS

FUNCTION

函数声明开始,组成类似于 function Xxx(): ... begin ... end; 的函数块。

PARAMS

系统内置变量,按序列访问函数参数。Params[1] 表示第一个参数,Params[n] 表示第 n 个参数;参数个数可用 ParamCount 获取。通常优先使用参数名称,仅在不定参数个数的场景使用 Params

参见PARAMCOUNT

IF

条件子句如果,可组成 IF THEN 或者 IF THEN ELSE 条件子句,详细见 THEN

END

语句段结束,与 begin/end 组合成语句块。

WHILE

循环子句,可组成 while ... do 循环语句。

参见TO

PARAMCOUNT

系统内置变量,用来得到函数的参数个数。用于下标方式获取参数。

参见PARAMS

DOWNTO

循环子句往前,可组成 FOR DOWNTO DO 循环子句,

参见FORTO

SYSPARAMS

用语系统环境变量存贮的数组。

除了特殊的环境变量以外,直接访问 SysParams 的下标利用 SetSysParam 和 GetSysParam 的功能相同。

SysParams["MM"] := 1;等同于 SetSysParam("MM", 1);

直接使用 SysParams 来访问和设置系统环境的值的性能会优越于 SetSysParam 和 GetSysParam 函数,尤其当某系统环境变量的值是一个数组的时候。

参见:系统参数设置和获取函数

PROCEDURE

与 FUNCTION 类似,但是在函数头后不允许加返回类型值。

Shl

左移位运算符X Shl Y二进制 X 左移 Y 位

范例:

return 6 shl 3;
// 结果48
// 返回十进制 (6=110) 左移3位后得到二进制110000十进制是48
DEBUGRETURN

调试返回,后面跟返回值,可在任何地方直接将结果返回到客户端,而不是象 RETURN 一样返回到上一级别。

BREAK

终止循环语句段,可用于 FOR 或者 WHILE 循环中,

参见循环语句FORWHILE

GOTO

无条件跳转语句,需要 LABEL 配合,参见 GOTO

Ror

循环右移位运算符X Ror Y二进制 X 循环右移 Y 位, 原来的低 Y 位变成高 Y 位

范例:

return 18 Ror 1;
// 结果9
// 返回十进制 (18=10010) 右移1位后得到二进制1001十进制是9
NIL

数据类型为 NIL 的常量

OR
CASE

多分支条件语句CASE OF

DebugRunEnvDo
LABEL

无条件跳转的标号定义语句,参见 GOTO

DebugRunEnv
FALSE
UNTIL

循环子句 REPEAT 的结束判断符。

RETURN

函数返回,后面可跟返回值

EXIT
TRUE
CONTINUE

循环语句段中继续,可用于 FOR 或者 WHILE 循环中,

参见循环语句FORWHILE

AND
ARRAY

数组类型初始化方法为Array(下标 0:取值 0,下标 1:取值 1,…,下标 N:取值 N),当下标省略的时候,默认下标从整数 0 开始,例如 Array(1,2,3,4)相当于 Array(0:1,1:2,2:3,3:4),数组下标支持字符串,例如 Array('a':1, 'b':2)多维数组的声明例子Array(('a':1,'b':2),('a':2,'b':3)),其规则为取值为数组,在 ARRAY 声明中的数组取值不再需要 ARRAY 关键字。注意:如果即无下标也无取值,则为空数组,例如 Array(),数组类型

参见ArrayTableArray 数组类型

Shr

右移位运算符X Shr Y二进制 X 右移 Y 位

范例:

return 18 shr 3;
// 结果2
// 返回十进制 (18=10010) 右移3位后得到二进制10十进制是2
Rol

循环左移位运算符X Rol Y二进制 X 各位左移 Y 位,原来的高 Y 位变成低 Y 位

范例:

return 6 rol 3;
// 结果48
// 返回十进制 (6=110) 左移3位后得到二进制110000十进制是48
OVERLOAD
EXCEPTOBJECT

获得异常对象实例,异常对象包含 ErrNo,ErrLine,ErrInfo 三个属性。参见错误控制,以及调试语句

LIKEEPS

LIKE 的精度设置,可以用 := 进行设置,也可以获得其值,默认值为 1e-6。 参见LIKE,LIKEEPSRATE

CDECL
Sqlin

行记录的存在于判断。

语法v SQLIn R

其中v 一定是 R 的一条记录才是真。以一条记录为判定单位。

范例一:

v := array(1, 2);
r := array((1, 2), (2, 3));
return v sqlin r; // 返回结果为1

范例二:

v := array(1, 3);
r := array((1, 2), (2, 3));
return v sqlin r; // 返回结果为0

范例三:与 In 的区别。

a := array();
a["1 ifin array(1,2)"]['In'] := 1 in array(1, 2);
a["1 ifin array(1,2)"]['sqlin'] := 1 sqlin array(1, 2);
a["1 ifin array((1,2))"]['In'] := 1 in array((1, 2));
a["1 ifin array((1,2))"]['sqlin'] := 1 sqlin array((1, 2));
a["array(1,2) ifin array((1,2),(2,3))"]['In'] := array(1, 2) in array((1, 2), (2, 3));
a["array(1,2) ifin array((1,2),(2,3))"]['sqlin'] := array(1, 2) sqlin array((1, 2), (2, 3));
a["array(1,3) ifin array((1,2),(2,3))"]['In'] := array(1, 3) in array((1, 2), (2, 3));
a["array(1,3) ifin array((1,2),(2,3))"]['sqlin'] := array(1, 3) sqlin array((1, 2), (2, 3));
return a;
// 返回:
Not sqlin

Not sqlin 是与 Sqlin 相对应的操作符,两者语法结构相似但逻辑功能完全相反。

功能:以行的模式来判定是否不存在于数组中。

语法v Not SQLIn R

其中v 一定不是 R 的一条记录才是真。以一条记录为判定单位。

示例:

v := array(1, 2);
r := array((1, 2), (2, 3));
return v not sqlin r; // 返回结果为0

v := array(1, 3);
r := array((1, 2), (2, 3));
return v not sqlin r; // 返回结果为1
LIKE

字符串模式匹配判定符,判定是否匹配于指定的正则表达式。

例如: '2009-1-1' like "\d+-\d+-\d+" 返回为真

实数 LIKE例如 1.000001 like 1 为真,实数相等一般要避免用等于(=)来判断

Like 的计算方法如下:

如果两个数的差的绝对值<likeepslike 为真

或者该绝对差/两个数的绝对数的平均值< likeepsrate like 为真

默认的 likeeps 以及 likeepsrate 均为 1e-6。

在 TSL 中, Like 符和 SQL 的 Like 的含义不同, TSL 的 Like 的右元素为正则表达式,左边运算元素为匹配串,结果是左边串是否匹配右边的正则表达式

参见:正则表达式,LIKEEPS,LIKEEPSRATE

Not Like

2025/8 月版本开始TSL 语言中支持 NOT LIKE 这样 not(a like b)可以简单写成 a not like b

Not Like 是与 FAQLIKE 相对应的模式匹配操作符,两者语法结构相似但逻辑功能完全相反。

字符串中的 Not Like

Not Like 可用于判断某个字符串是否不符合指定的模式规则,当且仅当字符串与给定模式不匹配时返回 True。

例如:

if a not like"\\d{4}-\\d{2}-\\d{2}"then
begin
    return "a不包含'YYYY-MM-DD'格式日期字符串";
end
else return "包含'YYYY-MM-DD'格式";

数值中的 Not Like

Not Like 可用于判断某个数值是否和指定值相似,当且仅当数值与给定模式不相似时返回 True。

例如 1.000001 not like 1 为 False。

计算方法和 FAQLIKE 类似,可参考其中的介绍。

IN

存在于判定符,判定是否在数组中。是以最小的元素进行判断。

例如 1 in array(1,2,3) 返回真0 in array(1,2,3)返回假。

IN 也支持判定是否在多维数组中存在。

Not IN

Not IN 用于判断指定元素是否不存在于数组中。若该元素在数组中未被找到,则返回 True否则返回 False。

与 IN 操作符的使用方式类似,但意义相反。同时也支持如下特性:

1、支持被查找数组为多维数组如 1 not in array((1),(2))返回值为 False

2、支持子集判断如 array(1,2) not in array(1,2,3,4)返回值为 False

示例:

t1 := 1 not in array(1, 2, 3);
t2 := 0 not in array(1, 2, 3);
return array(t1, t2);

返回结果array(0,1)

SUDO
RAISE

出错并终止运行,参见错误控制,以及调试语句

EXCEPT

异常处理。参见错误控制,以及调试语句

SETUID
DIV

除取整运算符,详见算术运算符

LIKEEPSRATE

LIKE 的精度设置,可以用 := 进行设置,也可以获得其值,默认值为 1e-6。 参见LIKE,LIKEEPS

NOT
FINALLY

最终执行处理。参见错误控制,以及调试语句

MOD

余运算符,与%的意义相同,详见算术运算符

PASCAL
TRY

定义被保护的块。参见错误控制,以及调试语句

FASTCALL
VAR

在调用函数的时候,在参数之前标识,与 OUT 一样,标明是可修改参数。除非系统使用 VarByRef 编译选项,否则默认为可修改参数。

var 还可以用来声明变量,在使用 Explicit 编译选项的时候,变量必需提前声明。

ECHO

输出内容(输出到控制台或者 WEB 应用输出到浏览器语法echo [,…],输出的内容必需是简单类型,否则仅仅只是输出一个基本的类型信息。

echo "Today is ", DateToStr(Today()), "now->", TimeToStr(Now());

如果在平台运行,会输出信息到客户端

固定功能用法:

Echo #127 可清除掉客户端 ECHO 窗口中的内容。

范例:

范例一:

// 打印当前时间
echo "现在时间:", DateTimeToStr(Now());

范例二:

// 清除打印窗口的信息,清除命令会在30s后执行
echo "待清除信息";
Sleep(30000);
echo #127;
EXTERNAL

说明函数是外部函数,来自于其他的动态库。

THISFUNCTION

THISFUNCTION 返回一个函数类型

THISFUNCTION 可得到当前运行的函数

THISFUNCTION(FuncName)可以得到指定 FuncName 的函数

THISFUNCTION(Object.FuncName)可以得到指定对象的方法。

THISFUNCTION(CLASS(ClassName).FUNCNAME)可以得到指定类的类方法。

参考:表达式相关函数

STATIC 静态计算

语法STATIC Expression [name nameExpression]

功能:指定后边的表达式为静态计算(常量计算),该计算只计算一次,用于加速。

其中,

Expression :为表达式

nameExpression为该静态计算标识符每个标识符代表的表达式只执行一次即若该标识符在第二次被调用时则直接返回第一次执行的结果。

例 1

begin
    return Teststatic("BBBB");
end;
function teststatic(key);
begin
    // 这个变量A的值是一个不需要每次调用重新构造的静态计算即它右边的表达式不管上层调用多少次它只执行一次
    A := 
    static array("ABCD":1, "BBBB":2, "CCCC":3……..);
    return A[key];
end;

例 2

begin
    return staticstockname("SZ000002");
end;
function staticstockname(key);
begin
    return
    static StockName(key) name"stkname"$key;
    // 每个股票代码求名称都只要计算一次假如StockName函数耗费的时间比较长则静态计算可以加速。
end;
SAFECALL
NAN

NOT A NUMBER,非数字,例如 0.0/0.0 的结果就是 NAN

INF

无穷大,例如 1/0.0 的结果就是 INF

OUT

在调用函数的时候,在参数之前标识,与 VAR 一样,标明是可修改参数。除非系统使用 VarByRef 编译选项,否则默认为可修改参数。

STDCALL
CONST

功能一:与 OUT 相反,标明是不可修改参数。

功能二:定义常量的关键字。

REGISTER
GLOBAL

指定后边的变量类型为全局变量,全局变量可以在不同的函数中访问到。

例:

begin
    global x, y;
    x := 100;
    testglobal();
end;
function testglobal();
begin
    global x;
    echo x;
end;
// 使用了全局变量之后,以上的 testglobal 函数会打印出 100。
MYMEM

应用已经使用的内存大小。

MAXMEM

应用允许使用的最大内存大小。

MTIC

高精度计算时间,得到 CPU 运行过的计数周期,并设置默认的开始计时,一般会利用 MTOC 来计算耗费的时间。

MTIC;
A := 0;
for i := 0 to 99999 do A++;
return MTOC;

以上代码可以计算所耗费的秒数

MTOC

高精度计算时间,得到和上一次调用 MTIC 到当前所耗费的时间,为一个浮点秒数,也可以带参数指定计算的开始点,一般开始点由 MTIC 获得。

MTOC

获得与上一次调用 MTIC 之间的秒数。

MTIC;
A := 0;
for i := 0 to 99999 do A++;
return MTOC;

以上代码可以计算 mtic 和 mtoc 之间的代码执行所耗费的秒数。

MTOC(TICK:double)获得与某个指定的 TICK 之间的秒数

默认 MTOC 和上次 MTIC 匹配,但是也可以指定某个 MTIC 的返回来计算时间

T1 := MTIC;
for i := 0 to 9999 do A++;
TE1 := MTOC(T1);
MTIC;
for j := 0 to 9999 do A++;
TE2 := MTOC(T1);
return array(TE1, TE2, MTOC);
__line__

关键字,获得当前的行号。

例如:echo __line__;

__stack_frame

说明:获得调用的堆栈的函数名以及行号

返回结构解析

面向对象支持的保留字

关于各个关键字的详细内容见面向对象 TSL 章节

参见Object TSL

TYPE

用来定义类:

type abcd = class

Public

V:Integer;
function return V();
begin
    return V;
end;
end;
CLASS

类关键字,用来定义类,见 TYPE。

也可以返回指定的类,如 class(abcd)

也可以用来得到强制类型的对象,例如 class(abcd,aObject),返回强制对象 aObject 为 abcd 类。

FINDCLASS

查找类,与 CLASS 用法类似。

返回指定名称的类FindClass("abcd")

得到强制类型的对象FindClass("abcd",aObject),返回强制对象 aObject 为 abcd 类。

与 CLASS 关键字不同,当不存在指定的类的时候,或者不是指定对象的父类时,并不抛出异常,而是返回一个整数 0。

FINDFUNCTION

查找函数,与 FINDCLASS 用法类似。

返回指定名称的函数FindFunction("Function1")。

返回指定对象的类方法FindFunction("Method1",aObject),返回对象 aObject 的 Method1 方法。

返回指定单元对象findfunction(UnitName),返回单元对象 UnitName。

当找不到指定的函数,并不抛出异常,而是返回一个整数 0。

FACKCLASS

虚假类,仅仅用来做类描述。

IS

判断符,判断对象是否是指定类的实例。

语法Object Is Class

返回:真假。

####### Not IS

not is 用于判断是否不是指定对象的实例。若不是指定对象的实例,则返回 True否则返回 False。

与 FAQIS 语法结构相似但逻辑功能完全相反。

示例:

a := 1;
ret := a not is class(TStringList);
return ret; // 返回结果1

a := new TStringList();
ret := a not is class(TStringList);
return ret; // 返回结果0
PROPERTY

定义对象的属性,详细内容见面向对象 TSL。

property A[:String] read GETA write SETA;
property A[:String] Index 1 read GETA Write SETA;
SELF

SELF 得到当前的对象。

VIRTUAL

虚方法关键字,指定的函数可被重写。

OVERRIDE
PROTECTED

可视域为受保护的,只有自己当前类和继承类可以访问。

PUBLIC

可视域为公共的,大家都可以访问

PRIVATE

可视域为私有的,只有自己当前类可以访问。

PUBLISHED

可视域为发布,与 PUBLIC 可视域相同

STATIC

指定后边的成员变量类型为静态,静态成员变量是所有的实例共享的全局的,当一个类需要使用到公共变量的时候可以采用静态成员变量。

远程调用客户端函数的保留字
RDo2

注: Rdo2 在客户机运行的时候,需要用户在权限上许可,具体可参考:平台模型远程调用客户端函数,访问客户端的资源

范例

if Rdo2 InputQuery("nput", "Hint", Value) then return Value
else return "Canceled";

可以在本机弹出一个输入的对话框并返回输入的串如果取消则返回“Canceled”

RDo
SQL 语法支持保留字
相关的关键字 含义
Select 查询语句指定由查询返回的列。详细使用方法见SELECT查询
VSelect 查询语句,返回一个值
SSelect 查询语句,返回一维数组
MSelect 查询语句返回Matrix类型数组
Distinct 取唯一值,去重复项
SelectOpt 返回指定类型值selectopt(type)type值不同返回不同类型的值。详见select字句语法。
DRange Drange(N to M): 从查询结果集中输出从N到M之间的行
Drange(N of M): 从查询结果集中输出从 (N-1)/M * 记录总条数到 N/M * 记录总条数 -1 之间的行;
As 对列命名
From 数据的来源表
MarketTable 高频行情数据表
InfoTable 财务类数据表关键字
TradeTable 交易明细数据表
SqlTable 用于与第三方数据库交互的关键字
HugeSqlTable
KeepNull
Datekey 取行情数据时,指定日期关键字
Of 取数据时,指定股票关键字
Order 排序与by同时使用order by
By 排序与order同时使用order by
Where 条件
Desc 排序,逆序
Asc 排序,正序
Group 分组
Having 聚集
ChecksumOf 生成哈希索引
Countof 记录统计
SumOf
MaxOf 最大值
StdevOf 标准差
VarOf 在险价值
TotalVarOf
NormOf 平方和的平方根
MedianOf 中值
AveDevOf 离散度
GeoMeanOf 几何平均值
SkewOf 偏度
KurtosisOf 峰度
Skew2Of 偏度
Kurtosis2Of 峰度
LargeOf 最大值
PercentileOf 百分点
QuartileOf 四分位的值
TrimMeanOf 返回数据集中的内部平均值,从头尾去掉指定百分比的数据点,再求平均值
CountOf 记录个数
AvgOf 平均值
MinOf 最小值
AggOf 利用回调函数计算聚集的值
StdevpOf 统计标准偏差
VarpOf 统计方差
ModeOf 众数
DevSqOf 样本平均值偏差的平方和
HarMeanOf 调和平均值
Checksum_AggOf 校验和
SmallOf 最小值
PercentRankOf 数据集中X排位的百分点的值
Rankof 排位
FrequencyOf 数值区间在数据集中出现的频率
ProductOf
RefOf 往前取数据
RefsOf
AggValue 当前参与聚集的列的值
ThisGroup 指定分组
ThisRow 当前行的数据值
ThisRowIndex 当前行的相对索引值
ThisOrder 当前排序
Insert 插入数据
InsertFields 插入数据的字段列表
Values 设置或者取得Name=Value模式串中指定的Name的Value值
Update 修改数据
Set 修改数据
Delete 删除数据
DeleteOpt 设定删除数组时是否自动修改下标SelectOpt对稀疏矩阵用的时候很有意义
FetchFirst 查询第一条记录
FetchNext 查询下一条记录
被系统保留未被使用的保留字

EXPORTS

DISPINTERFACE

LIBRARY

ASM

RECORD

RESOURCESTRING

THREADVAR

CONSTRUCTOR

DESTRUCTOR

INLINE

PACKED

ABSTRACT

INHERITED

标识符

标识符的定义:标识符就是以字母开头的字母数字序列,大小写不敏感。可以用来标示常量、变量、程序、函数等。

例如 TSL 程序的基本构成示例中的 HelloTSLdToday(变量名)、strToday(变量名)都是标识符。

标识符的命名规则:

1选用的标识符不能和保留字相同。

2在定义标识符时可以用的字符

A—Za—z0—9

注释符

注释符是什么呢?注释符注释的部分是仅仅给程序员注释所用,对于运行而言是毫无意义的,只是为了阅读程序理解更方便。

TSL 语言支持四种主要的注释符:

  • //:行注释,直到行尾。
  • #!:为 CGI 场景保留,语义与 // 相同。
  • { ... }:块注释。
  • (* ... *):块注释,可用于与 { ... } 交替实现嵌套。

嵌套注释示例:

value := 0;
{
    value := value + 1;
    (* 小段注释 *)
    value := value * value;
}

编译选项与注释符

在 TSL 语言中,为了改变编译的行为,会有一些编译选项,而编译选项也是利用注释符来实现的。

TSL 语言的编译选项和 PASCAL 语言类似,目前支持下列几种用法:

{$CompileOption}编译选项模式

例如{$VarByRef-}可以改变函数调用的入口参数为形参,而{$Explicit+}则可以使得变量使用前必需申明,{$GridCompute-}可以关闭掉#的网格操作符。这些内容将在后续相关章节中详细提到。

在 C 语言中,采用#CompileOption 的形式作为编译选项,大部分语言都支持编译选项以改变编译的默认行为。

参考VarByRef 编译选项Explicit 编译选项

条件编译

TSL 语言使用{$DEFINE} {$IFDEF}{$IFNDEF} {$ELSE} {$ENDIF} {$UNDEF}进行条件编译。

{$DEFINE Identifier} 定义标识

{$UNDEF Identifier} 取消定义标识

{$IFDEF Identifier} 判定是否定义了标识

{$IFNDEF Identifier} 判定是否未定义标识

{$ELSE} 在{$IFDEF}以及{$IFNDEF}条件编译中作为否定条件进入语句

{$ENDIF} {$IFDEF}或者{$IFNDEF}条件编译的结束

例如:

{$DEFINE SampleID}
// 定义一个名为SampleID的编译标识
{$IFDEF SampleID}
// 判定是否定义了SampleID
echo "Defined SampleID";
{$else}
// 否则(如果未定义)
echo "1 Undefined SampleID";
{$ENDIF}
// IFDEF的结束
{$UNDEF SampleID}
// 此处将SampleID的定义取消
{$IFDEF SampleID}
Error codesdjk sdcnkjsdnksd//此处是错误代码由于在条件编译中未定义SampleID所以此块不会被编译不会出错
{$else} //否则(如果未定义)
echo "2 Undefined SampleID";
{$ENDIF}

运行打印结果为:

Defined SampleID

2 Undefined SampleID

文件包含

支持通过 {$I} / {$INCLUDE} 将外部文件内容插入当前编译单元。

示例:

{$I "common.tsl"}
条件编译和 IF THEN 语句的差异

条件编译是仅仅编译符合条件的内容,而不像 IF 需要运行时判断,效率更高。

条件编译中的不需要编译的代码即便是有错误代码块也可以编译成功,这样可以很简单用条件编译的模式处理那些未完成代码。(比使用注释好,采用注释存在注释嵌套问题)

预定义的条件编译标识

tslnewtechcache

如果定义了该标识,表明技术指标相关函数支持缓存定义

tslver31

如果定义了该标识,表明当前支持版本 3.1 的语法

nilinvoke

如果定义了该标识,表明当前支持 calcctrlword 功能

cov

如果定义了该标识,表明当前支持双序列聚集和时间序列函数

commaexpr

如果定义了该标识,表明当前支持逗号表达式

weakptr

如果定义了该标识,表明支持弱引用

AutoWeak

如果定义了该标识,表明支持自动弱引用

例如:

{$IFDEF commaexpr} //判定是否定义了commaexpr
Echo (a := 1, a += 100, a);
{$else} //否则(如果未定义)
echo "不支持逗号表达式";
{$ENDIF} //IFDEF的结束

若当前环境支持逗号表达式则打印结果为101

否则打印结果为:不支持逗号表达式

{$dependency class1,class2}依赖关系编译选项

一个类如果依赖另上一个类,在类的定义里的第一行可以用该编译选项加入依赖关系。

语法:{$dependency class1,class2}

目前,该依赖关系仅在 TSL 的编辑器中用于自动完成中。用来决定当前开发中的函数自动完成的标识符和函数需要引入哪些其它的类。

例如,在编辑一个函数时,给函数的参数中输入一个实例对象变量,由于天软中变量都是弱类型,无法通过该变量快速查找到目标类的相关方法等信息。

此时,可通过在函数中增加{$dependency class1,class2}语句,对指定类的信息进行加载,使得在编辑函数时,能够通过不完整的函数名自动搜索到相关模型,并补充完整。

具体表现如:

TSL 的数据类型

数据是程序设计的一个重要内容,一般的高级程序语言都提供了种类繁多的数据类型,用来完成不同特色的程序设计。数据类型复杂,往往会使初学者不容易全面和深入掌握。

TSL 语言的数据类型从简洁和实用性考虑目前提供的数据类型分为简单类型、扩展类型其中简单类型分为整型、实型、布尔型、字符串型和日期型二进制流类型扩展类型分为数组型、NIL 型、TGraph 型、TGraphGroup 型和表达式型。

Integer

整数型一个整型数据用来存放整数TSL 语言中的整型数据是一个带符号的 32 位数据,其可以表达的数值范围是-2147483648..2147483647。

整数的基础含义每个读者都明白,和实数类似的,在 TSL 中,用户一般也无需理解整数的具体表达方式,直接使用就可以了。

但是和实数一样,当处理字节流或者和 DLL 发生关系的时候,整数在计算机中的具体表示就显得重要了。

其他计算机里的整数类型:

Byte,ShortInt字节短整顾名思义存贮为 8 位,占用一个字节。可表达的范围为:有符号为-128 127无符号为 0 255。所谓有符号或者无符号是指出是否需要可以表达负数。

Word,SmallInt小整存贮为 16 位,占用两个字节。可表达的范围为:有符号-32768 32767无符号 0 65535。

Dword,LongInt双子长整存贮为 32 位,占用四个字节。可表达的范围为:有符号-2147483648 2147483647无符号 0 4294967295。所谓 2G4G 就是指的有符号或者无符号下 32 位最大的表达大小。

Int6464 位整数,存贮为 64 位,占用 8 个字节,可表达范围为:-2^63 2^63-1。

TSL 里默认采用的整数是有符号的 LongInt但是提到整数运算不得不提到关于绝大多数语言里的整数溢出问题。例如在 C 语言中,如果整数运算越界了,例如无符号的 4294967295+1结果为 0。在 TSL 语言中,为了应用开发的简便性,当溢出的时候,数据会自动转为浮点数来处理,变为浮点的 4294967296。所以一般情况下用户可以不用理会数据运算溢出问题。当然特殊情况下例外假如用户需要将运算结果存贮到数据库或者文件字节流中由于运算溢出了而原来设计存贮的数据类型不足以表达依旧会产生问题。

TSL 语言的整数常量支持如下四种表达方法:

直接由数字表达十进制,如 100 表示十进制 100。

由数字 E 密表达十进制,如 1E2 表示十进制 100。

由 0x 开头表示十六进制,如 0x100 表示十进制的 256。

由 0b 开头表示二进制,如 0b100 表示十进制的 4。

由 0o 开头表示八进制,如 0o100 表示十进制的 64。

Int64

64 位整数,当 Integer 常量超过 32 位表达的时候,会自动采用 64 位整数,除此以外,也可以采用在整数常量末尾加上 L 来表示 64 位整数,例如 100L 表示值为 100 的 64 位整数。

Real

实数型一个实型数据用来存放实数TSL 语言中的实型数据是一个带符号的 64 位数据,其可以表达的数值范围是 5.0e-324..1.7e308,有效位数是 15..16。由于 TSL 的类型无需要申明因此实数常量的书写方法必须带有小数点例如0 表示整数 0而 0.00 则表示实数 0。

TSL 语言的实数常量除了直接使用数字加小数点外,还可以用例如 1.10E10 这种科学记数法。

此外,当一个未含有小数点的 10 进制整数表达方式超过了 32 位整数所表达的范围TSL 语言将自动将其当做实数常量。

实数的含义

实数的基础含义每个读者都应明白,为了用户使用方便,在 TSL 中用户一般不需要去理解关于实数更多的知识,只需要使用就可以了。

但有例外,一个是将实数处理成为要存贮的字节流,写入文件或者和其他的外部程序发生交互的时候,需要理解实数在计算机中的具体存贮大小。二是和外部 DLLDynamic Link Library一种在 Windows 中经常使用并依赖的开发技术,使得开发可以模块式开发)发生关系的,必需明确实数的计算机里的标准类型。

在计算机里,实数一般是以浮点的方式来表达的,关于浮点的表达方式在这里就不做具体描述。我们只关注下具体的数据类型,一般存在如下数据类型:

Single 也叫 Float单精度浮点数存贮位数为 32 位,也就是占用四个字节的存贮空间,可表达的范围为:负数为-3.4E38 -1.4E-45正数为 1.4E-45 3.4E38,有效精度为十进制的 7 位。目前,由于浮点协处理已经成为计算机的标准配置,计算机内存以及硬盘以及变得很大,这种损失精度的类型已经很少使用了,主要应用在那种对存贮空间很苛刻,对于精度要求不大的应用中。

Double双精度浮点数存贮位数位 64 位,也就是占用八个字节的存贮空间,可表达的范围为:正数为 5.0E-308 1.7E+308负数为-1.7E+308~-5.0E-308有效精度为十进制的 16 位。Double 是目前应用得最广的数据类型,也是 TSL 语言中默认采用的实数数据类型。当和 DLL 发生关系的时候又或者执行字节流处理的TSL 会自动按照指定的位数或者类型采用 DOUBLE 或者 Single 类型。

其他实数数据类型:有占用 16 个字节的 Long Double 类型,有占用 10 个字节的 Extended Double 类型,这些类型可以描述更精确地数字,可以表达更大的数据范围,但是这些类型并不常用。

特殊的实数

在实数中,有几个特殊的实数,-INF,+INF,NaN。也就是负无穷大正无穷大以及错误的数据(Not a Number)。

这些为什么放在实数中呢?事实上这些数据往往都是实数运算产生的结果,例如正数处以 0 得到的是+INF负数处以 0 得到的是-INF而 0 处以 0 则是 NaN。

TSL 语言提供了一些方法给用户去判断是否是这些特殊的实数。

科学计数法

支持科学计数法

a*10^n 可以用 aEn 来表达。

例如 123 可以写为 1.23e20.01234 可以写成 1.234e-2。

Boolean

在计算机中Boolean 类型事实上是被使用得最多的类型,只是很多是隐式使用。例如所有的逻辑判断的结果都是一个 Boolean 类型,而计算机程序中到处都充斥着逻辑判断。

TSL 的 Boolean 型的内部存贮其实只是一个整数1 为真0 为假。但是与 C 语言类型类似,对于数字,非 0 为真0 为假。此外NIL 类型也被判断为假。

其他语言中,按照布尔类型占用的大小,有 LongBool 和 ByteBool 之分,其实,要表达一个布尔类型,一个位就足够了,这也叫 BitBool事实上在汇编语言中经常判断某个指定位是否为 1这其实就是 BitBool。BitBool 基本上不存在在独立的数据类型只是某种按存贮的方式。TSL 如果要和外部的语言的 Boolean 类型打交道,最可靠地方法是采用具体位数的整数。

Boolean 型的常量有两个TRUE 表示为真FALSE 表示为假。

在 TSL 中,除了 0 为假以外NIL 类型、空字符串、空数组在作为逻辑判断的时候也是为假的。

TDateTime

日期类型是一种难以表达的类型,也许有的人说是用某年某月某日的表达方法不是很简单吗,但是计算机语言是无国界的,有的国家是用 12/30/2010 来表达 2010 年 12 月 31 日,有的国家则是用 2010-12-30 来表达,所以采用字符串的方式来描述是难以行得通的。

TSL 中采用数字来描述的日期类型,具体规则如下:

整数部分表示自从 1899 年 12 月 30 日以来经历的天数,小数部分表示一天中均匀的分割 24 小时的时间所形成的时间。

例如:

0 表示 1899 年 12 月 30 日 000

2.75 表示 1900 年 1 月 1 日下午 600

-1.25 表示 1899 年 12 月 29 日 600

要得到一个指定日期的日期值,可以通过如 StrToDate('2002-1-1')的方法获得。

如果要显示一个日期型,请参考相关函数,如 DateToStr,DateTimeToStr 等

TSL 还支持 20101231.0931T 表达 2012 年 12 月 31 日 9 点 31 分。

这种表达方式可以达到最高 10 毫秒的精度。没有小数点即为 0 点 0 分, 20111231T 则为 2011 年 12 月 31 日 0 点 0 分。

采用这样的方式,历史和未来的任何一天的时间都可以很容易地表达。这种表达方式也是一种国际标准,像 Object pascal 语言Excel 等通用开发语言和工具中的日期类型也都采纳了这个标准。

其他语言的日期时间表达方式

大多语言和 TSL 的日期表达相近,也是从某天以来的天数,但是有的也存在基准时间点不同。

如 Excel 采用的日期标准和 TSL 语言相同。

而 MATLAB 的时间和 TSL 语言的时间相差一个常量,比天软大 693960相当于 MATLAB 的日期从公元前 1900 年开始。

SQLServer 中的日期和 TSL 语言相差 1基点为 1899 年 12 月 31 日开始。

有的语言采用从某个时间点以来发生的秒数作为日期时间的表达方式,这种表达除了基点 0 不同以外,一天的表达也相差了 24*3600 倍。还有的语言,日期不是采用数据方式。例如 JAVA 这种语言,就是采用“对象”来表达日期的。

String

在 TSL 语言中,字符串常量的表达方式是采用单引号或者双引号括起来。例如 123 表示数值为 123 的整数,而"123"则表示内容为 123 的的字符串,是两种不同的数据类型。由于字符串中可能会包括一些特殊的字符,例如回车换行以及引号,就采用\符进行转义,例如\r 表示回车字符,\n 表示换行字符。采用\符进行转义后,字符串里的\字符就得用\来表达了。例如'ab\r'd\r\n'表示的字符串内容是带回车换行结尾的 ab\r'd 字符串。

\转义的几个特殊字符为:

\表示\r 表示回车

\n 表示换行

\t 表示制表符 Tab

\0 表示 ASCII 值为 0 的字符

\跟其他任何字符都为该字符的本身

因此,'就是'"就是"

字符串中引号的另一种表示方法

TSL 语言还支持在字符串常量中表达'或者"的另外的方式,例如字符串常量表达如下:

'This"s a book.'

这个表示字符串的内容为 This's a book.

在字符串常量中,连续的两个与字符串开始的引号相同的引号可代表一个引号。

也就是说,"This""s a book."代表 This"s a book.,但是"This''s a book."则代表的是 This''s a book.

号字符表达方式

有时候,程序中需要表达特殊的难以直接输入的 ASCII 符,例如笑脸符的 ASCII 值为 1要表达的时候就用#1 就可以表达了,而同样的,回车符的 ASCII 码值为 13可以用#13 来表达,而字符 0 的 ASCII 码值为 48则#48 可以表达数字字符 0。

号字符表达方式可以和引号的字符串表达方式串起来表达字符串常量,例如:'The first

line,Hello!'#13#10'The second line,Yeah!'#13#10'The End.'描述的字符串内容为:

The first line,Hello!

The seond line,Yeah!

The End.

TSL 字符串采用带长度的表示,可包含 ASCII 0 字符;理论上最大长度为 4G受内存限制

字符串的表达

ATSL 语言中,采用的是 GBK 编码。

一般用引号(单引号或双引号)"xxx"表示一个字符串,为多字节字符串,如:

s := "Hello 天软!";

Unicode 字符串:

支持 L"xxx"方式表示一个 Unicode 字符串,也称宽字节字符串,如:

s := L"Hello 天软!";

字符串内转义\u 可以表达 unicode 字符,\u 后默认为 16 进制的 Unicode 码,如:

s := L"\u5929\u8F6F";

表示字符串 L"天软"

在 Unicode 字符串连接中被自动识别为 Unicode 码,默认为 10 进制,因而需要使用 0x 代表 10 进制,如:
s := L"AA"#0x5929#0x8F6F

s 结果为 L"AA 天软"

UTF8 字符串:

支持 U"xxx"方式表示一个 UTF8 字符串,如:

s := U"Hello 天软!";

也可以通过非转义方式描述:

如多字符字符串表示“我们”

s := %% 我们%%;

宽字节字符串表示“我们”

s := L%% 我们%%;

UTF8 字符串表示“我们”

s := U%% 我们%%;
ANSI 字符串、Unicode 字符串与 UTF8 字符串

TSL 语言默认的字符串均是 ANSI 类型的。

但在语言内核已经支持了 UNICODE在字符串引号前加 L 就代表是一个 UNICODE 字符串。如 L”CCC”表明为 Unicode 的字符串”CCC”。

字符串内转义\u 可以表达 unicode 字符,\u 后默认为 16 进制的 UNicode 码,如 L"\u5929\u8F6F"表达 L"天软"

在 Unicode 字符串连接中被自动识别为 Unciode 码,默认为 10 进制,因而需要用 0x 代表 16 进制,如 L""#0x5929#0x8F6F 表达 L"天软"

TSL 许多字符串相关函数都支持 Unicode函数版本一般是以 W 结尾,例如 InttoStrW(1)的结果是 L”1”。

WideString 函数可以把 ANSI 串转换为 UNICODE 串,例如 WideString(“AAA”)的结果是 L”AAA”。

类似的, String 函数可以把 Unicode 串转换为 MBCS 多字节 ANSI 串。在处理特殊的符号,以及一些特殊的中文时 UNICODE 具备一定的优势。

如果不特殊说明,缺省 TSL 字符串均是 ANSI 的。

Unicode 和 Ansi 字符串的运算返回以左操作数的类型为准,如"喜欢"+L"天软"的结果是"喜欢天软",L"喜欢"+"天软"的结果是 L"喜欢天软".

TSL 语言中大多数的算符、基础函数及基类都支持 Unicode 字符串。如字符串的拼接、比较等运算,字符串的截取与转换等基础模型,数据库、文件、邮件等处理基类等。

TSL 语言没有独立的 UTF8 字符串类型UTF8 字符串的本质就是一个字节串,也就是一种特殊的字符串。在绝大多数其他语言中也是如此。

但 TSL 语言对 UTF8 的常量串提供了支持,和 UNICODE 字符串的前导符 L 类似的UTF8 字符串的字符前导为 U例如 U"天软"表达的是 UTF8 串的天软。除此以外, TSL 还提供了丰富的函数支持 UTF8。

具体示例可参考FAQ字符串的表达

字符串转义

为了支持字符串包含一些特殊的字符,例如回车换行以及引号,就采用\符进行转义,例如\r 表示回车字符,\n 表示换行字符。

采用\符进行转义后,字符串里的\字符就得用\来表达了。例如'ab\r'd\r\n'表示的字符串内容是带回车换行结尾的 ab\r'd 字符串。

\转义的几个特殊字符为:

\表示\

\r 表示回车

\n 表示换行

\t 表示制表符 Tab

\x 之后跟 2 两个 16 进制字符,代表字符 ANSI 值,例如\x30 就是字符 0。

\u 之后跟 4 个 16 进制字符,对于 unicode 串而言表示 unicode 的值,对于 UTF8 串而言表示该 Unicode 值字符的 UTF8 串,对于 ansi 串而言代表两个字符。

\0 表示 ASCII 值为 0 的字符

\a 表示响铃符,等同于\x07

\b 表示回退符,等同于\x08

\f 表示换页符,等同于\x0c

\v 表示垂直制表符,等同于\x0b

\’表示’

\”表示”

\跟其他非转义字符的值都为该字符的本身,而不会报错。但由于转义符号未来可能会

扩展,所以除了已知的转义以外,不应利用此特性混淆字符串

字符串的非转义表达%%

如果有程序代码或者其他多行的字符串,转移表达描述起来相当复杂, TSL 使用%%符来支持不需要转义的原始串, %%支持 UNICODE 和 UTF-8 的 L 和 U 前导串。

说明:定义一个字符串,字符串中的转义字符无效。

定义:%%[标识符]<回车换行|回车|tab|空格>字符串原始串%%[标识符]

注:

1、通过标识符进行配对若没有则配对第一个%%。

2、字符串中的任何转义字符都是无效的比如%% a\r\nb%%它就是字符串'a\r\nb'。

例如:

%% \r\nABC%%

代表字符串 \\r\\nABC,无标识符,字符串中包含 \" 等转义符号。

%%__
printf("%%smc")%%__

代表字符串 printf("%%smc"),其中 __ 为标识符。

L%% abcd%%

代表 Unicode 字符串 L"abcd"

更多示例如:

范例 1%%的用法

a := %%f1 ABCDEFG%%f1;
return a;

结果:返回字符串 ABCDEFG

a := %%a ABCDE\\FG%%a;
return a;

结果:返回字符串 ABCDE\\FG

范例 2%%与引号的区别

// 表示字符串 This's a book
s := %%s1 This's a book %%s1;
q := 'This\'s a book';
return s = q;

结果:返回 1。

// 路径中的应用
LJ := "C:\\Program Files\\Tinysoft\\Analyse.NETplug\\log";
LJ2 := %% C:\program Files\Tinysoft\Analyse.NETplug\log%%;
return LJ = LJ2;

结果:返回 1。

字符串中#表达

有时候,程序中需要表达特殊的难以直接输入的 ASCII 符TSL 支持以#号加 ASCII 码来描述该字符。

例如笑脸符的 ASCII 值为 1要表达的时候就用#1 就可以表达了,

而同样的,回车符的 ASCII 码值为 13可以用#13 来表达,而字符 0 的 ASCII 码值为 48则#48 可以表达

数字字符 0。

号字符表达方式可以和引号的字符串表达方式串起来表达字符串常量。

例如:

'The first line,Hello!'#13#10'Thesecond line,Yeah!'#13#10' The End.'

描述的字符串内容为:

The first line,Hello!

The seond line,Yeah!

The End.

Binary

二进度制类型,二进制类型是用来存贮文件等二进制流的数据类型,这种类型主要用于象生成位图以及在 CGI 应用中返回位图以及文件。

取二进制串里的字符或者设置指定下标的字符,使用[]操作符,下标从 0 开始。

s[0] := "C";
s[1] := 0x30; // char '0'
c := s[0];

c 为长度 1 的字符串。

ArrayTableArray 数组类型

数组型,数组是程序中最常用的结构数据类型,用来描述一定数目的元素集合。数组的每个元素和下标相关联,根据下标指示数组中元素的位置。

数组中的元素可以是任意类型的数据;

一个数组在使用之前,首先必须初始化为数组。数组支持自动扩展:设置新下标会扩展长度或维度。

arr := array();
arr[0] := -1;
arr[0][0][0] := 0;

TSL 的数组,有两种下标,一种为整数,一种为字符串。允许有字符型下标存在,其优点是可以直接描述一张数据表。例如 arrTable 是一个二维数组,我们可以设定其中的一个元素值如下:

arr_table[0]["日期1"] := "2002-04-15";
arr_table[0]["日期2"] := "2002-06-23";

这其中表示 arrTable 是一个二维数组,第一维为整数下标,第二维为字符串下标,这种类型的数组我们又称之为 TableArray在系统中象 Nday,NDay2 等函数以及 TGraph 的数据类型里依赖这种特殊的数组类型。

矩阵只是一种特殊的二维数字数组。

一维数组的理解

一维数组是按整数下标组织的有序元素集合,元素通过下标访问与修改。

多维数组的理解

多维数组由多层下标组成,每一维表示不同层级的索引,可视为“数组的数组”。

字符串也可以作为数组的下标

TSL 允许字符串作为下标,常用于表示表格型或键值型数据,便于按名称直接访问元素。

数组的表达方式
  • array(2, 3, 5, 7, 11) 创建 0 起始的一维数组。
  • array(0:2, 1:3, 2:5, 3:7, 4:11) 显式指定下标,与上例等价。
  • array("身高": 1.75, "体重": 85, "姓名": "李四") 使用字符串下标。
  • 多维数组可用嵌套表达,内部可省略 array
  • 数组项允许混合类型,也允许表达式作为元素。

示例:

a := array(2, 3, 5, 7, 11);
b := array(0:2, 1:3, 2:5, 3:7, 4:11);
c := array("身高": 1.75, "体重": 85, "姓名": "李四");
m := array((0, 1, 2, 3, 4), (3, 4, 5, 6, 7));
d := array(1 + 3, 2 + 5);
empty := array();
Array of 类型注解(可选)

类型注解可用 array of <label> 标注元素含义,支持嵌套(如 array of array of real)。注解仅用于说明,编译器不做类型检查。

function Clone(items: array of string): array of string;
begin
    return items;
end;
TSL 语言的数组和其他语言相比的特性
  • 支持字符串下标,便于按名称访问。
  • 支持混合类型数组。
  • 维度和长度可在赋值时自动扩展。

示例:

a := array();
a[0] := -1; // 自动扩展为长度 1

b := array();
b[0][0][0] := 0; // 自动扩展为三维数组

TMatrix

矩阵类型,特指二维实数数组,即 array of array of real。

Matrix

特殊的二维数组结构,第一维为整数下标,且从 0 开始,第二维可为字符串或者整数,该类型主要是内存紧凑,在处理特大数据集合的时候可以节省内存和加速建立集合。

与其相关的函数有IfMatrix 判定类型是否为 Matrix 类型CreateMatrix 来创建 Matrix 类型,对 Matrix 类型而言,一样可以用 matrix1[0]["字段1"] 这样来访问指定行列的数据,但是不能用 matrix1[0] 来访问其第一行。

Matrix 类型非常类似于 Array 类型,但是对于许多操作符,仅仅只支持 Array 类型,可以使用 MatrixToArray 函数进行转换。

NIL

NIL 类型是一种很特殊的类型,在 TSL 语言中NIL 往往用来判断数据是否存在。与 NIL 相关的函数有 IfNil 用来判定是否为 NIL。

当然,和 DLL 程序的外部交互的时候NIL 是有用的,例如外部要一个空指针,和 COMWindows 开发中的一种组件开发模式Component Object Model组件对象模型交互的时候也是有意义的。

绝大多数的语言都没有 NIL 类型,但是有 NIL 值,例如 C 语言中的 NULLPASCAL 中的 NIL。但那些都只是空指针的概念。TSL 中的 NIL 更接近于 COM 中的 Unassigned。

在 TSL 中,在判定数组中某个下标中是否有值会非常有用。这个数组概念有些类似于矩阵,矩阵中有非完全矩阵的概念。数组的具体知识在之后会讲到。

在 2018 年的 TSL 版本中,允许 NIL 和数字进行运算,以支持非完全矩阵的计算,而以前此类计算会导致异常。

TGraph

图形型TGraph 是 TSL 特有的一种数据类型它是由特定终端程序来解释和显示Graph()函数是它对应的数据生成函数。TGraph 在特定终端显示出来的是一种图形,图形的形状、颜色、大小等等完全由函数的参数来指定。

TGraphGroup

图形组型,同 TGraph 数据类型相似TGraphGroup 也是 TSL 中特有的数据类型。它是多个 TGraph 数据的集合。对应的图形生成函数是 GraphGroup()。

TExpression

表达式型,表达式是 TSL 中一种特定的数据类型,它是一种在函数执行中被动态求值的变量,你可以通过运算符@和&来得到一个表达式。如果要对一个表达式求值,需要调用函数 EVAL()。

TFUNCTION

函数型,存贮一个函数指针,一般用 ThisFunction 来获取。

函数型可以用 Call 来调用。

参见Call

TCLASS

类型,存贮一个 TSL 类指针,一般用 Class 来获取,获取方法为 Class(类名)。

TCLASS 型数据可以用.操作符来调用类方法。

class(ClassA).MethodA(a, b);

ComObj

对象型,对象型是 TSL 中的一种 Com 对象数据类型,用这种类型可以操作各种 Com 对象,如 Excel 以及 Word 对象。

对象的操作方法:

访问属性:

value := com_obj.PropertyName;

设置属性:

com_obj.PropertyName := value;

访问带参数的属性:

value := com_obj.PropertyName(param1, param2);

设置带参数的属性:

com_obj.PropertyName(param1, param2) := value;

访问方法:

com_obj.MethodName(param1, param2);

如果对象的属性或者方法返回的仍然是对象,支持嵌套调用:

ComObjVariant.<PropName|MethodName>[(P1,P2...)]. <PropName|MethodName>[(P1,P2...)]...

NIL 和 COM 的兼容性:由于 Excel,Matlab,S-PLUS 等多种统计软件对空类型的支持不尽相同,导致出现兼容性问题,

无法用统一的转换类型来解决,现在通过增加一个 COM 组件属性 NilTrans 来解决此问题,由用户自由选择转换的方法

假定天软的 COM 组件实例为 ts

具体操作如下:

设置 ts.NilTrans 属性为下列值(默认为 0

 0: 转换为Unassigned

 1: 转换为Empty

 2: 转换为NULL

 3: 转换为NaN

 4: 转换为整数0

 5: 转换为浮点0

 6: 转换为空字符串

 7: 转换为'NIL'字符串

 8: 转换为'NULL'字符串

 9: 转换为'-'字符串

10: 转换为'/'字符串

其他值: 转换为 Unassigned

BTW:

假如用户在其他的本地应用中与外部 COM 打交道,例如调用 EXCEL那么用户可以在 TSL 中使用

SetSysParam('NilTrans',N);//N 为 0-10 的上述值

的方式来调整 NIL 对 COM 数据类型转换的行为。

参考GetOleObject,CreateComObject,ifObj,SysGetPidOfCom

COM 方法的调用例子

COM 的调用和 TSL 的对象的调用是没有差异的,也非常类似于 PASCAL 以及 VB 等对 COM 对象的调用。每个 COM 对象方法或者属性的调用要么返回一个 TSL 基本类型如数组数字字符串NIL 等,要么返回一个 COM 对象。

例如:

GetOleObject("Excel.Application", 0, Obj);
Obj.WorkBooks.Open("C:\\1.xls");

上边的代码 Obj.WorkBooks 隐含着返回了一个 WorkBooks 对象

TSLObj

TSL 对象型TSL 对象型是 TSL 语言中特有的对象数据类型,可以由扩展的 TSL 语言插件注册该对象类型。也可以使用 TSL 语言编写 TSL 类,这种对象类型不同于操作系统的对象类型,但是操作的方法与 ComObj 类似。

创建该种对象的方法是 CreateObject。相关对象的使用方法见TSL 内置对象使用大全

参见函数CreateObjectifObj

常量与变量

变量是指在某个函数的运行过程中其值可以发生改变的标识符。

在 TSL 中的变量是不用初始化和预先声明的,你可以把某一个变量赋值为一种数据类型后,在接下来的语句中再改为另一种数据类型而不必做任何的声明,这和一般的高级语言不同。

变量类型为弱类型,变量类型与运行时相关。

TSL 语言标示符大小写无关,未初始化的变量为整数 0。

默认情况下所有的函数参数均为变参,请注意对入口参数的保护。

可以通过编译选项控制变量需要预先申明具体用法可参考Explicit 变量声明的编译选项

注:面向对象 TSL 的对象成员变量初始值为 nil因成员变量必定预先申明与此规则并无相悖。

一个变量的赋值语句为:

var_name := expression;

例如:

today := Date();

变量 dToday 通过 Date()函数的返回,被赋值为一个 TDateTime 类型的数据。

类型注解

类型注解使用 : 指定标识符类型,常见于函数参数/返回值、类字段和属性声明。

注解名称不参与编译检查,可任意命名,仅用于说明与阅读。

function Open(path: string; flags: options = 0): result;
begin
    return array("path": path, "flags": flags);
end;

常量即同 C 语言中的常量,它是一个固定值,在程序执行期间不会改变。

截止到 2023-07 月份前的 TSL 版中TSL 中没有特定的常量标识符,即不支持通过 const 来定义一个常量标识符。

而在 2023-07 月份之后,天软新一代 TSL 语言中支持常量的使用,并提供了一般常与运行时常量。

全局变量

全局变量时指在当前运行的任务中,无论在哪个函数中均可以访问。

全局变量必需预先声明。

例如 global x,y,z;声明了三个全局变量 X,Y,Z。在每个引用全局变量的函数中均需要采用 global 进行声明不进行声明TSL 会假设其为局部变量。

例如:

function Main(): void
begin
    global x, y;
    x := 100;
    TestGlobal();
end;
function TestGlobal(): void
begin
    global x;
    echo x;
end;

运行后输出 100

常量

常量是一个固定值,在程序执行期间不会被改变。

TSL 中支持常量、常量成员、运行时常量。

其中,常量成员是指类中的成员变量也可以定义成常量成员;

运行时常量又叫编译时常量,它的初始化与一般常量不同,是在程序运行时才被确定,但仍有初始化后不会被改变的特性。

常量及常量成员的定义与初始化

常量与常量成员的赋值符用的是“=”

其定义与初始化:

const 标识符 = 基础数据类型的常数或常量参与的运算结果;

如下:

【常量定义】

const a = "Hello";
const b = a + "Tinysoft";

【常量成员定义】

type C = class
const a = "Hello"; // 可以在声明中进行定义初始化
const b = a + "Tinysoft";
C := a + b + "from TSL";
end;

常量定义与常量成员定义的值可以是一个常数也可以是常量参与的计算。

其中,常数的数据类型及运算符如下:

数据类型整数、实数、字符串、nil、数组(包括 FMarray)、其它常量、常量成员。

运算符:如+、$、-、*、/、等基础运算符,具体如下图所示。

常量成员的初始化:有别于类的一般属性只能在构造函数中进行初始化,常量成员可以在成员声明这一层直接对该常量进行初始化。

作用与意义:

1 天软的变量在通过参数传入函数后,是允许对该变量进行修改的,所以,为了避免某些需要常量意义的变量不被修改,可以定义这样子的一个常量进行,它有不能被修改的特性。

2 常量在使用过程中比一般的变量的性能更高,尤其是在它存储的值较大的情况下。

运行时常量的定义与初始化

与常量的差异是运行时常量定义的内容是在每次计算时确定,其值的计算没有约束,支持任意类型、任意运算。

// 运行时常量的赋值符号为”:=”

其定义与初始化:

const 标识符 := 任何数据类型的常数或任意表达式的返回值;

例如:

const a := 1;
C := 100;
const b := C + now();

则此时的 b 只有在第一次运行时才能知道它的具体值。

特性说明:

1 初始化时允许任意表达式计算得到(即可调用任意函数、变量做任意运算)

2 函数每次运行都重新计算

3 和普通常量一样不允许被修改

4 普通常量又称之为编译时常量

5 未做类成员运行时常量支持

常量与运行时常量的对比
特点 常量定义 运行时常量定义 变量定义
声明关键字 Const Const 存在编译选项时用var
初始化赋值符 = := :=
初始化数据 常量或常量参与的计算 任意表达式 任意表达式
初始化计算种类 类型与算符有限制,具体可参考:计算种类 无约束 无约束
初始化生效 初始化时确认,是预知的 第一次运行时确认 运行时确认
初始化后是否可被修改 不能被修改 不能被修改 能被修改
是否支持类成员 支持 不支持 支持
是否支持声明中初始化类成员 支持 不支持 不支持
运算效率 存储数据较大时效率相对较高 存储数据较大时效率相对较高 一般
定义初始化举例 Const a="Hello"; Const a:=100+now(); a:=100+now();
常量的产生对其它功能的扩展帮助

1 允许 External 常量而非仅支持字符串,对于跨平台的动态库有帮助。

2 Com 性能提升

GetEstSize

范例

t := rand(10, 10);
return GetEstSize(t);

返回整数8800

注意:其结果是一个估算值,当变量占用空间较小时,可能会返回 0属于正常现象。

弱类型语言与变量类型运行时相关

对于 TSL 语言而言,使用一个变量不需要预先进行变量类型申明,一个变量既可以是数字,也可以是字符串,可以先是一个类型,运行后又是另外一个类型。

例如:

A := "My test string"; // 先给 A 赋值字符串
A := length(A);
// 取 A 的长度赋予 AA 变成了整数 14函数 length 可以得到字符串 A 的长度,关于 TSL 语言的函数参阅后面章节
A := A\ * 10.0; // A 的类型运算后变成了实数 140.0
A := array(A); // A 的类型变成了数组,内容为 array(140.0)

这种变量类型可以改变的特性,我们称之为变量类型的运行时相关,也就是说,变量类型只有在运行到具体位置的时候才可以被明确,这种特性在计算机术语里也可以称之为 TSL 语言是弱类型的语言。

TSL 语言变量的初始值

在 TSL 语言中,假定引用一个未赋初始值的变量,该变量的值会被系统初始成整数 0对于数组的项或者类成员变量未初始的值则为 NIL。

弱类型与强类型

弱类型是相对于强类型语言而言的,绝大多数编译型语言是强类型语言,其类型在变量申明的时候就已经确定了。

在强类型语言中,一般来说,变量必须预先申明,例如:

在 C 里:

int I;//申明 I 为整数类型

I = 0; // 赋值语句C 语言的=和 TSL 的 := 意义是一样的

先得申明 I 变量为整数,才可以使用 I。

而且这样是不正确的:

int I;
I := "1234";

这样 C 语言会报告类型不匹配的错误,因为无法把一个字符串类型赋给一个整数类型的变量,这就是强类型语言,而且,无论在哪里,只要使用 I都可以知道 I 是整数类型。

数据类型转换与算符重载

但是,对于某些其他语言而言,刚才这样的语句却是可行的。由于 I 是整数类型有的语言会自动转换字符串”1234”为 1234 并赋予 I。这种语言事实上还是强类型语言只是做了强制类型转换这【类似】于语言中的另外一个概念叫算符重载,也就是说,可以根据类型信息决定运行的行为(算符重载在本质上和这个是一样的,但是从很多“专业”的意义上,两者是不同概念,实质上,一个是编译器语言内核做的算符重载,另外一个则是语言应用层面上的重载,实质是相同的)。拥有这种特性的语言有点意思,写起代码来很优雅,尤其是很多计算机编程的初学者都喜欢这种特性,但是对于一个开发经验丰富的人而言,这种特性带来的坏处往往超过了其带来的便利性,我们来看一个 TSL 语言的例子:

A := "1234";
B := 1;
C := B + StrToInt(A);
// StrToInt 是一个把字符串转换成为整数的函数,我们先不去理解 TSL 的函数的表达方式,函数会在后续章节中讲解

在 TSL 中,我们很明确地先把字符串 A 用相关函数转换成为整数再和 B 相加,这样我们很清楚 C 的结果是 1+1234=1235。

假定是支持数据自动转换的语言,这样写就可以了:

A := "1234";
B := 1;
C := B + A;

编译器会自动识别 B 为整数,整数和后边的类型相加,自动把后边的数据类型转换成为整数,这样我们可以称之为语言内核已经重载了+运算符使得自动执行了将第二操作数自动转换成为和第一个操作数相同的类型也就是自动把字符串”1234”自动转换成为和整数 1 一样的类型的功能。

以上代码看起来显然比没有自动类型转换的看起来要简单和优雅,但是我们再看下一个代码:

A := "1234";
B := 1;
C := A + B;

这个 C 的结果会是什么呢?由于 A 是字符串,所以先要把 B 的结果 1 先变成字符串"1"再和 A 相加,结果就变成了"12341"。

这样就出现了很意想不到的效果了A+B 的结果与 B+A 的结果是完全不相同的,假定开发人员写的时候稍有不注意,这类的错误是非常容易犯下并难以查找的。

正因为数据类型的自动转换和算符重载功能是一把双刃剑,因此 TSL 语言并不支持数据类型的自动转换和算符重载。所有算符重载的功能都可以使用不重载的方法来实现,仅仅只是代码看起来繁杂一点,但是其意义的明确带来的好处完全可以抵消代码繁杂的坏处。

2016 年下半年的 Object TSL 已经支持对象的算符重载,以支持 TSL 的对象利用标准算符进行计算。

Explicit 变量声明的编译选项

无需提前声明就可以直接使用的特性使得 TSL 语言非常容易使用但是这种便捷的特性同时也会带来负面的影响。当开发大型程序的时候用户比较容易在写程序的时候遇到写错变量名的情况由于在程序的语法而言并不是一种错误只是运行的结果和预期不一致这种错误很难查找TSL 语言提供了另外一个选择给用户,即允许用户选择必需先申明后使用的模式。

用户在程序代码前加上编译选项{$Explicit+}就可以让出现在这个编译选项之后的所有代码(无论是否在一个函数内,只要是同一个源代码文件内)都会遵循先声明后使用的模式,要关闭这个选项则使用{$Explicit-}。

TSL 采用 Var 作为前缀来声明变量,具体使用方法为:

Var varName[:Type][:=InitExpression];

类型可以省略,即便声明,目前 TSL 语言也不采用该类型信息,当需要赋初值,则使用在变量后加赋值语句。

如果有多个变量同时声明,则采用:

var varName1, varName2[:type];

例如:

D := 1;
{$Explicit + }
var A, B; // 同时定义两个变量A和B
var E := D + 10; // 给变量E赋初始值
A := 1;
B := 2;
C := 3; // 该变量未预先声明

系统会提示 C := 3 这一行“变量未声明”。 是否启用 {$Explicit+} 取决于项目要求;启用后所有变量必须先声明。

运算符

运算符是用于运算的符号,例如+号A+B 表示对 A 和 B 进行加法运算,参与运算的元素个数为几,就叫几元运算符,大多数运算符号都为二元运算符号,也就是需要两个元素参与运算,例如加法需要两个元素参与。一元运算符号有++,--,&,@,Not 等,他们只有一个参与运算的元素。

事实上,+和-也可以是一元运算符。例如 1+-2 的结果是-1因为-作为一元运算符的时候只是负号,相当于 0-1-+2 的结果是-1因为+作为一元运算符号的时候只是表示正号。但是值得注意的是 1++2 是不可被支持的,虽然按理结果为 3但是由于++也是算符,这样就产生了歧义,因此必须采用 1+(+2)才是正确的同理1--2 也是不成立的。

三元运算符在 TSL 中只有一个,就是?:表达式a>b?1:2当前的条件为真时候返回:前的表达式值,否则返回后边的。

TSL 的算符有的存在.前导,例如 .&,.>等等,需要注意的是,由于.是实数的小数部的分割符,所以当运算常量数字的时候,要格外小心,否则非常容易出现意想不到的结果。例如 1.&2 会出错,因为系统将 1.理解成为实数 1类似于(1.)&2因此需要用户写成 1 .& 2也就是在 1 和.&之间使用空格分隔。

此外,还有一些带.的算符可能在读者看起来毫无存在的必要,例如既然存在=算符,.=的计算也和=一样,为什么还要.=呢?这是有原因的,因为=和.=在对数组类型运算的时候表现将不一致,但是这些差异我们在这一节将不做详细解释,等到后边的矩阵计算的相关章节再做详细说明。

赋值运算

a := 1;

TSL 中 = 是关系运算符,赋值使用 :=

多参数赋值运算

说明:将数组中的值依次赋值给对应位置的变量

[变量 1, 变量 2, ...] := array(值 1, 值 2, ...);

注意:

  • 右侧数组第一维下标必须为自然数,可为多维数组。
  • 左侧变量从第一个开始依次赋值,前面的变量不能为空;同名变量以后者覆盖前者。
  • 变量个数只有一个时,末尾逗号不能省略:[r1, ] := array(1)
  • 右侧必须是数组,可为多维数组。
  • 左侧变量数大于数组行数时,多出的变量为 nil
  • 支持在函数传参中使用,见范例 05。

范例:

范例 01一般用法

说明:将数组中的值依次赋给对应变量。

[r1, r2] := array(1, 3, 5, 7, 9);
return r1 + r2; // 返回 4其中 r1=1r2=3

范例 02变量最少的用法

说明:将数组第一个值赋给变量。

[re, ] := array(1, 2, 3, 4);
return re; // 返回 1

范例 03变量个数多于数组长度的示例

说明:数组中只有一个值。

[r1, r2] := array(1);
return r2; // 返回 nil

范例 04二维数组

[r1, r2] := array((1, 2), (3, 4));
return r2; // 返回 array(3, 4)

范例 05函数传参

说明:参数表达式按位置依次求值,再传入函数。

function Test(a, b, c)
begin
    return a + b + c;
end;

return Test(e := 3, [f, g] := array(1, 2), g); // 返回 6

return Test(e := 3, [f, g] := array(1, 2)); // 返回 4c 为 nil

注:此用法不常见。

范例 06实用场景当我们执行某个程序之后想要返回多个指标值可以采用这种方式进行处理会更快便捷。比如在数据库交互过程中做一个数据提取的操作的时候我既希望可以返回提取的数据结果集又希望在提取失败时返回具体失败的信息方便查看失败的原因。那此时就希望能一次返回两个结果一个代表数据库操作是否成功的 Bool 类型结果,另一个代表的是提取的结果集或报错信息。那通过数组绑定变量的方式就可以很好地读取到对应的信息值,大致过程如下所示:

说明:返回状态码 + 结果。

function Add(a, b)
begin
    if not (ifreal(a) and ifreal(b)) then
        return array(1, "参数不是实数");
    return array(0, a + b);
end;

调用方式如下:

[error, re] := add(3, 4); // error变量取到的是计算结果是否异常re代表的是执行结果或报错信息
if error then begin
    ...//异常处理
end;
...//获取到的正常值继续进行计算

算术运算符

算术运算符是最常用的运算符,用于从事算术运算,除了四则运算+-*/以外TSL 还支持求余,整除,自加一,自减一等。

运算符 运算 运算对象 结果类型 例子
+ 加,正号,可为一元或者二元运算符 整型、实型、字符串 只要有一个运算对象是实型,结果就是实型,如果全部的运算对象都是整型并且运算不是除法,则结果为整型,若运算是除法或者左除,则结果是实型 2+1结果为3
"222"+"888"结果为"222888"
- 减,负号,可为一元或者二元运算符 整型、实型 2-1结果为1
* 整型、实型 2*2结果为4
/ 整型、实型 2/4结果为0.5
注意:在某些语言中,整数常量相除的结果为整数,如果要结果为实数需要其中一个参与运算的元素为实数
\ 左除 整型、
实型
3\2的结果为0.666666…
左除这种运算符只有少数语言支持,大部分语言并不支持
%,Mod 取余 整型 整型 例如4 % 3的结果为1
Div 除取整 整型、实型 整型 例如7 div 3的结果为2
++ 自加1一元运算符 整型、实型 整型、实型 见下例
-- 自减1一元运算符 整型、实型 整型、实型 见下例
! 求倒数,一元运算符 整型、实型 实型 !A相当于1/A
^ 求幂 整型、实型 实数 2^3等于2的3次方为8
~ 求对数 整型、实型 实数 8~2等于8对2求对数为3

示例:

A := 2;
A++;
// A = 3

A := 2;
A--;
// A = 1

关系运算符

关系运算符主要是用于进行逻辑判断的,返回值为逻辑真假值。

基础判断符如=,<>,<,>,<=,>=,like

支持矩阵判断符如.=,.<>,.<,.>,.<=,.>=

在=,<>,<,>,<=,>=等运算符都可以对字符串类型进行判断,判断是以字符串中的字符的 ASCII 码值的大小为原则的,比较原则:从第一个字符开始比,相同则比下一个字符,如果两个字符串不一样长,且短串长度部分都相等,则长串大。此外,大小写的 ASCII 码值是不同的,小写的码值大,因此字符串比较是大小写相关的,例如"A"<"a"为真。

运算符 运算 运算对象 例子
=,.= 等于 简单类型 1=2的结果为假
注意:在有些语言中,=被用于赋值符,这样等于判断符就使用==
<>,.<> 不等于 简单类型 1<>2的结果为真
注意:在有些语言中,采用!=作为不等于的判断符
<,.< 小于 简单类型 1<2的结果为真
>,.> 大于 简单类型 1>2的结果为假
"B">"A"结果为真
<=,.<= 小于等于 简单类型 1<=2的结果为真
>=,.>= 大于等于 简单类型 1>=2的结果为假
LIKE 模式匹配判断 字符串 "2009-1-1" like "\d+-\d+-\d+"为真
在TSL中Like符和SQL的Like的含义不同TSL的Like的右元素为正则表达式左边运算元素为匹配串结果是左边串是否匹配右边的正则表达式关于正则表达式可以参阅的相关正则表达式的章节

逻辑运算符

逻辑运算符就是对真假的 Boolean 类型进行运算的算符。

运算符 运算 运算对象 结果类型 例子
Not,.!! 逻辑非,一元运算符 布尔型 布尔型 逻辑非,对真运算则为假,对假运算则为真
Not 1的结果为0
在C语言或者类C语言中Not的表达为!
And,.&& 逻辑与 布尔型 布尔型 逻辑与,真与真与为真,真与假或者假与假与都为假
1 and 2结果为1
1 and 0结果为0
在C语言或者类C语言中And的表达为&&
Or,.|| 逻辑或 布尔型 布尔型 逻辑或,只要一个为真,运算结果就为真
1 or 2 结果为1
1 or 0结果为1
0 or 0结果为0
在C语言或者类C语言
Or的表达为II

TSL 在很多方面和 Pascal 语言比较类似,但是在 Pascal 语言中Not,And,Or 同时可以作为位运算符,由于 TSL 为弱类型语言为了避免冲突TSL 取消了这一特性,位运算有相应的一组算符和函数。

Not 作为结合算符的支持

判断别算法 In,SQLIn,Like,Is 没有对应的反向含义算符,如果需要判定非则需要使用 not( a in b)这样的模式。

为了更简便书写和可读性,特别在 2025/8 月版本中增加了 not 和这四个算法的结合。

即 not in,not sqlin,not like,not is

如 a not in b 相对于 not (a in b)的可读性更好

三目运算符

?:判断求值三元运算符

语法 1Condition?TrueResult:FalseResult

功能:?前的逻辑表达式的运算结果如果为真,则?表达式的结果为?后的值,否则为:后的值。

具体使用案例:

1>2?"大于":"不大于"

该运算的结果是"不大于"

2>1?"大于":"不大于"

该运算的结果是"大于"

2>1?1:0

该运算的结果是 1

1>2?1:0

该运算的结果是 0

容易被忽略的使用方法:

2?1:0

该运算的结果为 1

0?1:0

该运算的结果为 0

以上正确的原因是由于 2 的本身是非 0自身就可以成为逻辑表达式结果为真而 0 为假。

因此,当遇到 a?1:0 的时候,实际上是判断 a 是否非 0 非 nil

?:省略真表达式的三元运算符的特殊用法

语法 2Condition?:FalseResult

说明:若 Condition 为真,则返回 Condition若 Condition 为假,则返回 FalseResult。

在计算中,经常会出现有 NIL 值NIL 值的产生原因是多种多样的,例如多表的 SELECT JOIN 查询产生(我们姑且先不去理解什么是 SELECT JOIN当产生 NIL 值以后NIL 无法和其他数据类型进行运算,会产生错误。用户可能会希望在计算的时候利用 0 或者空字符串来替代掉 NIL使得计算可以继续。

例如:

Aexp+1由于 Aexp 可能是 NIL 会出错,那么用户可能会采用如下来替代:

(Aexp?Aexp:0)+1

有时候 Aexp 计算的表达式很长,这样写起来就会很不方便,运算的时候效率也会比较低,因为 Aexp 可能需要计算两次在这种特殊用途中TSL 语言支持省略掉真表达式,缺省认为为真的时候的值就是?之前表达式的本身的计算结果,这种写法就可以缩略为:

(Aexp?:0)+1

示例计算一个序列中各元素的和nil 值用 0 替代。

a := array(1, 2, 3, nil, 100);
s := 0;
for i, v in a do s += v ?: 0;
return s;

返回结果为106

支持矩阵的.?:表达式

语法 1ConditionMatrix.?TrueResult:FalseResult

说明对数组进行判断取值TrueResult 和 FalseResult 既可以是数组类,也可以是标量。

语法 2Condition.?:FalseResult

说明:为真的单元格采用条件的自身单元格,为假的单元格用 FalseResult 或者 FalseResult 内的单元格替代。

支持矩阵,可以对 fmarray 或 array 的单元格进行判别,组装出对应的矩阵,这样的做法可以避免有些需要循环判别的矩阵操作。对于全数字而言,以前实现需要使用 bool 矩阵和值矩阵相乘后相加来完成:如(a.>3)

  • a1 + (a.<=3)*a2或者采用 mfind 得到的数组取子矩阵来操作。

新的支持会使得这种操作极为便捷,也符合思维定式

a := array(1, 2, 3, 4, 5);
b := a . > 3 .? array(2, 3, 4, 5, 6) : array(3, 4, 5, 6, 7);

b 的结果为array(3,4,5,5,6)

即,对集合 a 中每个元素的集进行判断取值,然后得到对应的结果返回到结果集中。

同样,这种方式也支持 FMArray。

但是需要注意,当 f.?A:B 中A 与 B 都是 FMArray 时,两者的维度与大小必须是一致的。

当 A 与 B 的单元数据类型不一致时,也符合 FMArray 矩阵合并时对单元格数据类型的包容处理规则。

例如:将矩阵 f1 中不大于 3 的值用矩阵 f2 中的元素替换

f1 := fmarray[[1, 2], [0, 4], [1, 2], [5, 6]];
f2 := fmarray[[100, 10], [200, 20], [300, 30], [400, 40]];
b := f1 . > 3 .? f1:f2;
return b;

结果:

例如:将整型矩阵中不符合条件的值用浮点数 10.5 替换

f1 := fmarray[[1, 2], [0, 4], [1, 2], [5, 6]];
b := f1 . > 3 .? f1:10.5;
return b;

返回的矩阵中,所有单元格类型都为浮点数:

注:即便判断中没有出现符合条件的单元,数据类型不同时也会做包容处理。

位运算符

位运算符顾名思义,就是按照位来进行运算,在目前计算机的内部,都是以二进制方式对数据进行存贮,例如十进制 4 的二进制的写法则为 0b100十进制 5 的二进制写法为 0b101位运算的规则

1 与 1 为 1否则为 0

0 或 0 为 0否则为 1

非:非 1 为 0非 0 为 1

异或1 异或 0 为 1否则为 0

按照与的规则4 和 5 与的结果为 0b100结果为十进制的 4。

下述所说的整数为 32 位整数或 64 位整数。

运算符 运算 运算对象 结果类型 例子
.& 位与 整数 整数 位与例如3 .& 1 的结果13 .& 4的结果为0在C或者类C语言中.&的表达为&
.| 位或 整数 整数 位或例如3 .| 1 的结果33 .| 4的结果为7在C或者类C语言中.|的表达为|
.! 位非(取反) 整数 整数 位非,.! 1的结果为-2.! 0的结果为-1在C或者类C语言中位非运算符为~
.^ 位异或 整数 整数 位异或按位异或相同位为0不同的位为11 .^ 1为01 .^ 5 的值为4在C或者类C语言中.^的表达为^
SHL 位左移 整数 整数 位向左移动最高移动的N位舍弃
ROL 左循环移位 整数 整数 位向左移动最高移动的N位卷滚到最低位
SHR 位右移 整数 整数 位向右移动最低移动的N位舍弃
ROR 右循环移位 整数 整数 位向右移动最低移动的N位卷滚到最高位

[]运算符

[]是对特殊的运算符,和()类似,他们总是成对出现的,有[的时候,必定有匹配的]作为结尾。

我们在前面的章节里看到过 MOV EAX,[EBP+4]这样的指令,这里的[]是取地址 EBP+4 位置存贮的双字,而前面我们也提到计算机的内存也像是一个数组一样,地址像是数组的下标。

在 TSL 语言中,[]可以有三个用途,一个指示数组的下标项。

例如:

A := array(1, 2, 3);
B := A[0];
// 那么 B 的结果是 1;

[]中的内容就是数组的下标。

如果我们替换掉一行语句

A := array(1, 2, 3);
A[0] := 2;

那么数组 A 的内容就变成了 array(2,2,3)。

[]的另外一个作用就是指示字符串中指定下标的字符。

字符串在概念上与字符数组很类似,例如:

"ABCD"这个字符串,我们可以类似地认为是一个长度为 4 的字符数组,我们也借助数组中的下标与项的概念,认为字符串是数字下标数组,而项的类型则为字符。在 TSL 中,字符串的下标是从 1 开始的。在其他语言中Pascal 语言的字符串的下标也是以 1 开始,但是 C 以及类 C 的语言中,字符串的下标从 0 开始。

在 TSL 中,并不存在字符这种类型,字符类型被以长度为 1 的字符串所替代,要么以其 ASCII 码值来替代。

例如:

A := "ABCD";
B := A[1];

那么 B 的结果是"A"

如果要给字符串的某个项来赋值:

A := "ABCD";
A[1] := "B";

那么 A 的值变成了"BBCD"

我们也可以用下列方法给字符串的某项赋值:

A := "ABCD";
A[1] := 0x30;

结果 A 的值变成了"0BCD",因为 16 进制的 30 就是字符 0 的 ASCII 值。

此外,[]算符还可以用于在类 SQL 语法中取字段结果,这个内容在今后的数据查询与 SQL 语言中将会进行具体描述。

[]算符在数组和字符串下标运算中还可以支持子矩阵和子串运算,在这里我们也不做说明,留在后边章节做详细说明。

表达式运算符

说到表达式运算符,就需要提到什么是表达式,常量、变量、函数等都是表达式,用算符连接起来的常量、变量、函数等仍然是表达式。

什么是表达式运算符呢?

例如

a := 1;
b := a + 1;

那么我们知道 b 的结果会是 2我们知道 a+1 就是一个表达式,下边的代码:

A := 1;
B := @A + 1;
C := eval(B);

这里边出现了一个表达式求值的函数,叫 eval而 B 是表达式类型,利用表达式求值函数对 B 这个表达式求值得到的结果就是 2也就是 C 的结果是 2。

执行下边的代码可以得到同样的结果:

A := 1;
B := &”A + 1”;
C := eval(B);

@符是一个表达式前导符,用于指定后边的内容是一个表达式,而&符号则是把一个字符串的内容编译成为一个表达式使用。

运算符 运算 运算对象 结果类型
@ 前导符,直接声明为表达式,一元运算符 语句 表达式型
& 转换字符串为表达式,一元运算符 字符串类型 表达式型

表达式运算用于那些需要动态执行而传值无法实现的特殊情况。

对象访问符

该算符在后续章节中会使用到

运算符 运算 运算对象 结果类型
. 对象运算符号,访问对象的属性和方法 对象类型 任意

矩阵运算符

矩阵运算符号主要是用于矩阵数组TSL 语言中的基础算符 +,-,*,/,,%,mod,div,^,~,.=,.>,.<,.<>,.>=,.<=,.!,.&,.|,.^,.||,.&&,.!!,like,++,--都支持矩阵的计算用。

关于矩阵的计算我们将在矩阵计算专门章节里讲述。

运算符 运算
:* 矩阵乘
:/ 矩阵除
:\ 矩阵左除
:^ 矩阵幂
! 矩阵逆和伪逆
| 矩阵并右方
:| 矩阵并右方(空列不错位,支持非完全矩阵)
Union 矩阵并下方
:: 矩阵循环
::= 矩阵循环赋值
-> 生成序列数组

集合运算符

和矩阵计算不同,集合运算的结果会去重复记录,因此集合运算的 Union2 与矩阵计算的 Union 的结果是不同的。同样,在其他集合操作中,结果集均具有去重操作。

有关集合运算的内容在集合运算章节中会独立讲述。

运算符 运算
IN 判断元素是否存在或者判定是否是子集
Union2 集合并集
Intersect 集合交集
Outersect 对称差集
Minus 集合补集
SQLIN 行判断,判别是否是元素

过滤集运算符

过滤集运算是过滤掉二维结果集中某列的内容存在或者不存在于某个一维结果集中的运算,返回的结果可以是过滤后的二维结果集,也可以是二维结果集的下标列表,事实上,结果集过滤完全可以利用 TS-SQL 的 Select 来实现,但是相对于 Select 而言,结果集过滤的效率会更高效也更容易理解。

过滤集运算也可以对二维结果集的整行内容进行过滤,这样的操作非常类似于集合运算,过滤在过滤集内的类似于交集,而反之类似于减集。但是过滤运算和集合运算的最大差异是:集合运算的结果内容不允许有重复项,而过滤运算的结果允许有重复项,也就是说于被过滤集中的重复项目是不会被自动去重的。

运算符 运算
FilterIn 过滤在其中
FilterNotIn 过滤不在其中

运算赋值运算符

什么是运算赋值运算符呢?我们先来看一条语句:

A := A + B;

这条语句的意思读者已经很清楚了,就是将 A+B 的值赋予 A。对于这类运算我们可以用另外一条语句替代

A += B;
// 这就叫运算赋值运算符,基本上就是用算符=来替代掉原来的 A:=A 算符 B 的功能。

这种算符在 C 以及类 C 语言中被广泛支持,但是或许有的读者会有一个疑问,除了获得代码编写上的简便以外,似乎没有其他用处。其实不然,在很多情况下,采用运算赋值运算符还可以获得效率上的提高。

我们拿字符串的加法来做一个例子:

A := "";
for i := 0 to 9999 do A := A + "A";

A := "";
for i := 0 to 9999 do A += "A";

这两种写法在计算机的执行上是完全不同的,+=并不仅仅只是写法的简便那么简单。

// 在执行 A:=A+"A"的时候,计算机先取出 A 的值,然后执行+的操作,也就是需要先分配一个足够的内存空间存贮 A+"A"的结果,然后再将 A 原来存贮的字符串进行释放,释放以后再把计算的字符串结果复制到 A。

而执行 A+="A"的时候,计算机仅仅只需要先把 A 的字符串的存贮空间扩大一个字节,然后再把"A"复制进末尾。

// 简单说起来,就是+=可以利用原有变量的存贮空间,而:=则不行,这样就可以基本保障几乎所有类似的运算采用运算赋值运算符会更快速。这个效率尤其在之后的矩阵计算中会体现得更加明显(由于矩阵的每一次计算的结果集相比标量而言都非常庞大,导致:=会不断地生成新结果集和删除旧结果集,使得效率很低)
运算符 算符名称 备注
+= 加等于
-= 减等于
*= 乘等于
/= 除等于
= 左除等于
^= 幂等于
~= 对数等于
%= 模等于
Div= 整除等于 很特殊的算符,字母和符号并用的算符
.|= 位或等于
.&= 位与等于
.||= 逻辑或等于
.&&= 逻辑与等于
.^= 位异或等于
|= 矩阵右并等于
:|= 矩阵右并等于 和|=的差异见后续章节中|和:|的差异
&= 矩阵下并等于 很特殊的算符,因为&是表达式,所以利用&=来实现union等于的功能
:*= 矩阵乘等于
:/= 矩阵除等于
:= 矩阵左除等于
:^= 矩阵幂等于
Union2= 集合并等于 很特殊的算符,字母和符号并用的算符
Intersect= 集合交集等于 很特殊的算符,字母和符号并用的算符
Outersect= 集合对称差集= 很特殊的算符,字母和符号并用的算符
Minus= 集合补集等于 很特殊的算符,字母和符号并用的算符

With 设值运算符

运算符 运算 运算对象 结果类型
=> 设置With的系统参数 任意类型 无结果

算符优先级

什么是优先级呢?

例如 1+23是先计算 23再计算 1+,这就表示的优先级高于+1+23 等同于 1+(2*3)。

2>2+1是先计算 2+1再计算 2>,结果 2>3 为假,表示+的运算优先级高于>2>2+1 等同于 2>(2+1)。

当优先级一样的时候则从左至右计算。

以下会出现一些在上边未使用的算符,这些算符将会在矩阵计算等章节中详细解释。运算符的优先级有两个特例,就是+-号的优先级分为两种,一个是一元运算符的正负号的优先级,一个是做加减法计算的优先级。

运算符 优先级
(),[],.,?. 0(最高)
&,Not,++,--,`,!,.!,.!! 1
$ 2
^,~,:^ 3
,/,,%,Mod,Div, :,:/,:,SHL,SHR,ROL,ROR 4
+,-,|,:|,union,union2,Intersect,Outersect,Minus,.|,.||,.&,.&&, ->,.^,正号+,负号- 5
IS,IN,Like,=,<,>,>=,<=,<>,.=,.<>,.>=,.<=,.<,.>,
:>, :<,:<>,:==,:>=,:<=,::>,::<,::<>,::==,::>=,::<=
6
And,Or 7
: 8
:=, +=, -=, =, /=, =, ^=, ~=, %=, div=, |=, :|=, union2=, intersect=, Outersect=, Minus=, &=, :=, :/=, :=, :^=, ::=, ::, .|=, .&=, .||=, .&&=, .^= 9
@ 10(最低)

值得注意的是,虽然 TSL 语言的算符和 Pascal 语言在很多方面相似,但是算符的优先级却不尽相同,有一个很典型的例子:

在 Pascal 语法中A>1 and B<2 这样的表达式是不成立的,这是因为在 Pascal 中 And 可作为位与算符,而且优先级比>要高,那么 A>1 and B<2 等同于 A>(1 and B)<2这样的表达式显然是不对的。

而在 TSL 语法中And 为逻辑运算符,而且优先级低于>和<,这样 A>1 and B<2 就等同于(A>1) and (B<2)。在 C 或者类 C 语言中,由于逻辑运算符与位运算符是独立的,所以与 TSL 类似,在 C 语言中 A>1 && B<2 也是成立的。

正是由于运算的优先级存在很多的语言差异,而且算符优先级即使对于一个有着丰富经验的程序员而言,也是经常容易混淆的。正因为如此,笔者建议读者在不确认甚至于无论是否确认,也是用圆括弧来把优先级标出来。例如 A>1 and B<2 我们就应该写成(A>1) and (B<2),这样不仅仅阅读起来一目了然,形成这个良好的习惯对于转换于多种计算机语言开发的人也可以起到很大的帮助。

?.模式

即是对对象引用与数组访问的一个容错处理。

该功能也可以通过系统参数 CalcCTRLWord 值为 0x100 和 0x200 进行指定,与?.模式区别为,系统参数设置为全局有效,而?.模式只是在具体使用时有效。

FAQpn_CalcCTRLWord

具体为:

1、a?.b?.c 对象引用中,允许对 nil 调用返回 nil即当对象存在时则等同于 a.b.c否则返回 nil方便缺省时未创建对象在引用时可能引发报错的问题

2、t?.[]数组访问中,支持对 nil 类型操作返回 nil即当 t 为 nil 时 t?.[1]返回 nil当 t 为数组时t?.[1]等同于 t[1]

注:仅支持数组访问时,不支持数组写入时如此引用。

算符优先级:与[].等算符同级

示例 01

如对一个 nil 类型进行引用时返回 nil而非报错

objA := new tstringlist();
objB := nil;
return array(objA?.Count, objB?.Count);

返回结果为 array(0,nil)

示例 02

如对一个 nil 类型进行指定下标访问时返回 nil而非报错

A := array((1, 2), (2, 3));
B := nil;
return array(A?.[1][1], B?.[1], A[2]?.[1]);

返回结果为array(3,nil,nil)

其中B?.[1]因 B 为 nil 所以返回 nil

A[2]?.[1]因 A[2]不存在所以返回 nil

示例 03

连用模式如:

type AAA = class
tt;
bb;
function Create();
begin
    tt := array(1, 2, 3, 4);
    bb := new BBB();
end;
end;
type BBB = class
s;
function Create();
begin
    s := "BBB";
end;
end;
// 调用
obj := new AAA();
echo obj?.tt?.[1]; // 若对象obj或数组tt不存在时则返回nil
echo obj?.bb?.s; // 若对象obj或bb不存在时则返回nil
return 1;

打印:

2

BBB

表达式

算术表达式

算术表达式是由算术运算符连接常量、变量、函数的式子。算术表达式中各个运算次序为: ( ),函数,算符

布尔表达式

TSL 提供的布尔表达式可以含有逻辑运算和关系运算。

逗号表达式

语法:(exp1,exp2[,exp3,...,expN])

逗号表达式是将几个表达式串联起来,可以看做是一个一起提交的语句块,用英文括号封装。编译器先从左到右进行编译,即运算的顺序是从左到右的。如:

return (a := 1, b := 2, c := 3, a + b + c);

这个语句先对 a 赋值,在对 b 赋值,接下来是 c最后执行 a+b+c。类似于

a := 1;
b := 2;
c := 3;
return a + b + c;

也支持作为表达式参与计算,如:

A := (b := 2, c := 3, b * c) * c;

这个结果是 233 结果为 18

链式表达式

TSL 语言中目前支持的关系运算符:

关系运算符 大于 小于 不等于 等于 大于等于 小于等于 说明
标量关系算符 > < <> = >= <= 非链式a<b<c相当于(a<b)<c
标量链式算符 :> :< :<> :== :>= :<= 链式a:<b:<c相当于(a<b) and (b<c)
矩阵关系算符 .> .< .<> .= .>= .<= 非链式a.<b.<c相当于(a.<b).<c
矩阵链式算符 ::> ::< ::<> ::== ::>= ::<= 链式a::<b::<c相当于(a.<b) .&& (b.<c)
// 注:由于:=是赋值符号,因此链式算符等于符为:==;由于::=是循环赋值等于符号,因此矩阵链式等于为::==
标量链式算符

链式比较表达式形式:a :< b :< cx :== y :>

运算过程中会将其隐式转换为逻辑与and连接的多个独立比较。

执行顺序:从左到右依次计算每个比较

支持任意比较运算符::>, :<, :<>, :==, :>=, :<=,且可混合使用。

如:1 :< x :< 10 等价于 (1 < x) and (x < 10)

a :== b :< c :<> d 等价于 (a = b) and (b < c) and (c <> d)。 示例:

a := 10;
b := 20;
return 0 : < a : < b : < 100;

返回1

矩阵链式算符

矩阵链式比较表达式形式:a ::< b ::< cx ::== y ::>

运算过程中会将其隐式转换为逻辑与(.&&)连接的多个独立比较。

执行顺序:从左到右依次计算每个比较

支持任意比较运算符:::>, ::<, ::<>, ::==, ::>=, ::<=,且可混合使用。

如:a ::<= b ::<= c 等价于 (a .<= b) .&& (b .<= c)。 示例:

a := array((1, 2), (11, 12), (22, 23));
b := 12;
c := array((0, 12), (1011, 2), (4, 23));
return a :: <= b :: <= c;

返回:

数组与矩阵的链式算符的使用方法:

数组与矩阵的链式算符是基于每个数据点进行链式判别获得Array(1,2,-1)::<array(2,1,0)::<array(3,2,1)的结果为 array(1,0,1),

与其它矩阵算法一样也支持和标量进行计算例如array(1,2,-1)::<2::<array(3,2,1)的结果是 array(1,0,0)

TSL 还可以支持混用 fmarray 和 array 以及标量

字符串运算

字符串的子串处理的算符支持

和矩阵的子矩阵计算类似TSL 的字符串也支持子串计算。

为了避免产生概念混淆我们需要再重复一次TSL 中的字符串下标是从 1 开始的,而数组是从 0 开始的。

字符串取指定位置的字符 StringValue[Index]

字符串取单个字符的方法是 StringValue[Index]的模式,例如"ABCD"[1]的结果为"A"子串是,表明取第一个字符。

字符串取子串 StringValue[StartIndex:EndIndex]

字符串取子串则采用 StringValue[StartIndex:EndIndex]的模式,例如:

S := "AAABBB";
S1 := S[3:4];

S1 的结果为"AB",为第三个字符到第四个字符的值。

字符串替换子串 StringValue[StartIndex:EndIndex]:=SubString

用户往往有替换字符串的需求,我们也可以采用和取子串类似方法处理:

S := "AAABBB";
S[3:4] := "111";

S 的结果为"AA111BB"

含义是替换掉第三个字符到第四个字符的值为"111"。

字符串删除子串 StringValue[StartIndex:EndIndex]:=""

和字符串替换子串类似,假定我们要删除第三个到第四个字符串的值,我们可以给其赋值空字符串。

S := "AAABBB";
S[3:4] := "";
S 的结果则为"AABB";

字符串插入子串 StringValue[Index:0]:=SubString

假定我们要在指定的位置插入字符串,我们可以采用类似于替换的方法,但是结束索引号设置为 0例如

S := "AAABBB";
S[4:0] := "111";

S 的结果为"AAA111BBB"

含义是在第四个字符的位置插入"111"

字符串计算的算符支持

TSL 语言的算符支持的字符串运算主要是串相加运算取子串运算子串删除和子串替换运算。绝大多数的语言对字符串的处理采用字符串支持函数的方式除了直接的算符支持以外TSL 语言同样支持丰富的字符串函数。

字符串替换子串的功能,在 TSL 语言中有相关的函数支持,例如 AnsiReplaceStr这种确定的字符串替换的功能可以利用这些函数但是如果所需替换的字符串是模式匹配串正则表达式串那这种函数就失去作用了TSL 的正则表达式支撑函数 ParseRegExpr 可以返回匹配串所在的坐标,而这些坐标正好可以用于字符串计算算符。

基础算符对字符串的支持

字符串支持的基础算符有:+、$、[]、+=、>,<,>=,<=,<>、Like、not Like

字符串数组支持算符:+、+=、.>,.<,.>=,.<>,.=Like、not Like 等

同时Unicode 字符串也支持以上算符。

+字符串相加

基础算符中支持字符串运算的是+号,顾名思义,字符串相加。例如,"AAA"+"BBB"的结果是"AAABBB"。

字符串比较判断

,<,>=,<=,<>,.>,.<,.>=,.<>,.=等对于字符串的比较大小的判断是使用 ASCII 码序列从第一个字符开始逐个进行大小比较。

LIKE 字符串正则匹配判定

字符串模式匹配 LIKE

字符串"2009-1-1" like "\d+-\d+-\d+"为真

在 TSL 中Like 符和 SQL 的 Like 的含义不同TSL 的 Like 的右元素为正则表达式,左边运算元素为匹配串,结果是左边串是否匹配右边的正则表达式

在匹配字符串时,默认情况下不区分大小写,若需要区分大小写,可通过 FAQSetLikeFlag 进行设置后再判断。

如默认情况下'a' like 'A' 结果为真,而设置 LikeFlag 为 0 后,结果为否,如:

SetLikeFlag(0); // 区分大小写
return 'a'like'A'; // 结果为0

参见LIKE

$处理字符串转换与拼装

对于多个简单类型,如果要拼接为字符串,可以采用$算符。

例如

A := 1;
B := 2.34;
C := "AAA";
D := "A="$A$"B="$B$"C="$C;

D 的结果为 A=1B=2.34C=AAA

注:当拼接的对象是实数时,表现与 floattostr 保持一致,保留最高精度。

其中,当数值为纯小数时,表达方式可能为科学记数法

如:

return "_"$(0.0000005)$",";

返回字符串"_5E-7,"

而:

return "_"$(1.0000005)$",";

返回字符串"_1.0000005,"

块环境设置语句

块环境设置语句简介

在 TSL 中,有一些系统环境变量是非常重要的,例如当前的股票、当前日期时间。对于这些系统环境变量,系统提供了系统函数对其进行操作。

WITH DO 则提供了一个方便的功能。

With *,SysParamArray Do与 With **,SysParamArray Do

临时修改系统参数并执行一段逻辑时,可使用 with *, SysParamArray do

params := array(
    pn_stock(): "SZ000002",
    pn_cycle(): cy_1m(),
    pn_date(): Today() + 10 / 24
);
with *, params do
begin
    close_value := Close();
    // ...
end;

若希望执行期间不继承原有系统参数,可使用 with **, SysParamArray do,执行结束后恢复原环境。

With => Do

其语法如下:

with [S => StockID, T => Time] do <statements>;

例如:

with [S => "SZ000001", T => IntToDate(20020601)] do
begin
    close_value := Close();
    ref_close := Ref(close_value, 1);
    diff := close_value - ref_close;
end;

其工作原理如下:

先根据 S 和 T 设置当前的股票和日期,然后执行 DO 后的语句,执行完后恢复股票和日期的环境。

参见:系统参数设置和获取函数