Lu64.DLL V2.0 编程指南
目 录
1
概述 2 基础知识 3 输出函数 4 简单计算 |
5 Lu二级函数设计 6 Lu的基本用法 7 用Lu保存和检索数据 |
Lu 输 出 函 数 |
|
3.1
版本信息函数:LuVer 3.2 初始化Lu:InitLu 3.3 释放Lu:FreeLu 3.4 获得Lu运行错误:GetRunErr 3.5 测试Lu运行错误:TestRunErr 3.6 设置Lu运行错误:SetRunErr 3.7 编译表达式:LuCom 3.8 计算表达式的值:LuCal 3.9 锁定一个模块:LockModule 3.10 设置外部二级函数:SetFunction 3.11 设置常量:SetConst 3.12 二级函数返回一个动态对象:FunReObj 3.13 二级函数保存动态对象:FunSaveObj 3.14 判断一个表达式是否有效:IsFor 3.15 获得表达式信息:GetFor 3.16 判断表达式的自变量是否重新赋值:ParaModify 3.17 删除一个表达式:DeleteFor |
3.18 获得字符串:GetStr 3.19 获得数组:GetArray 3.20 申请系统内置动态对象:NewSysObj 3.21 重置系统内置动态对象:SetSysObj 3.22 插入一个键:InsertKey 3.23 查找一个键:SearchKey 3.24 删除一个键:DeleteKey 3.25 枚举指定键值类型所对应的所有字符串及键值:EnumKey 3.26 锁定键的类型:LockKey 3.27 按指定类型执行运算符重载函数:ExeOperator 3.28 垃圾收集时标记一个指针键(对象):SignGoodObj 3.29 垃圾收集:GC 3.30 从缓冲池中获取一个对象:GetBufObj 3.31 与Lu交换信息:ExMsgWithLu 3.32 由字符串获得一个唯一的整数:StrToUniInt 3.33 由一个唯一的整数获得字符串:UniIntToStr |
Lu是一个可对字符串表达式进行动态编译和运行的动态链接库(dll),是一种易于扩展的轻量级嵌入式脚本, 支持协程,提供自动内存管理,也可以手动管理内存,Lu是强类型的脚本。Lu可用于连接各种语言编写的模块。Lu的优势在于简单易用和可扩展性强。Lu2.0与Lu1.0完全兼容,Lu2.0可对运行错误准确定位。
Lu用stdcall调用协议输出了动态库函数,可供C/C++、VB、delphi、FORTRAN等程序使用。
除了GetRunErr()、TestRunErr()和SetRunErr()三个函数及函数GC()的部分功能外,其余的函数只能在单线程中使用(不允许两个及两个以上的线程同时运行这些函数),在设计多线程应用程序时必须注意这一点。
Lu不抛出异常,如果在主调程序中检测到了异常,可能来自于外部模块向Lu注册的函数,此时,可尝试对Lu重新进行初始化。
以下是本文中使用的一些类型及常量定义(参考头文件Lu64.h):
typedef
__int64 luIFOR;
//Lu表达式中的整数类型定义
typedef __int64 luVOID;
//Lu的指针型整数定义
typedef __int32 luINT;
//Lu的通用整数定义
typedef __int32 luKEY;
//Lu的键值定义
#define
True 1
//逻辑真,定义为非0值
#define False 0 //逻辑假,定义为0
#define
luKey_Function 3 //二级函数标志
#define
luKey_Const 4 //常量标志
本文将用到以下脚本函数(在Lu脚本中使用的函数):
(1)HFor(...):获得函数句柄。
(2)intss("..."):将静态字符串转换为一维静态整数数组。
(3)realss("..."):将静态字符串转换为一维静态实数数组。
(4)cast(Data,MyType):强制将数据Data转换为MyType类型。
(5)new(...):生成新对象。
(6)del(...):将对象放到垃圾对象缓冲池中,由系统决定何时彻底销毁这些对象。
(7)delete(...):彻底销毁对象。
2.1 Lu模块及表达式
表达式是Lu程序的基本单元,如下例:
2+3
如果给表达式起一个名字,即成为函数,如下例:
f()=2+3
Lu表达式有无名表达式和有名表达式两种,有名表达式即函数。函数可以被其他表达式所调用,函数可以有0个或多个参数等等,不再赘述。
Lu表达式属于一个模块,具有全局或私有属性,全局表达式(实际上是全局函数)可被其他任意表达式所访问,私有表达式(实际上是私有函数)只能被本模块的表达式所访问。
Lu模块由一个或多个表达式组成。模块用一个整数标识,整数可正可负,只要绝对值相等,就属于同一个模块。一般用正整数表示该模块名。模块共有两类,即主模块(0#模块)和普通模块(其他标号的模块)。
同一模块中,模块号为负的表达式称私有表达式,只能被本模块的表达式所访问(即调用),在其他模块中是不可见的;模块号为正的表达式称公有表达式或全局表达式,能被任何一个表达式所访问。主模块(0#模块)中的表达式都是私有表达式。任何一个表达式,既可以访问本模块中的表达式,也可以访问其他模块中的全局表达式,如果本模块中的一个私有表达式与其他模块的一个全局表达式重名,将优先调用本模块中的私有表达式。
可以给模块创建一个命名空间,通过命名空间输出的表达式可被任意表达式所调用。
2.2 Lu基本数据类型
Lu表达式中会存在许多类型的数据,如整数、实数、字符串、逻辑值、函数句柄等等,但在其内部实现上,所有的数据类型都使用同一种结构形式,即:
struct LuData{ //Lu基本数据结构
luIFOR x; //存放数据。对于动态数据类型,对象指针约定保存在x中
luIFOR y; //存放数据
luIFOR z; //存放数据
luKEY VType; //扩展数据类型,决定重载函数
luKEY BType; //基本数据类型,决定数据结构
};
任何Lu数据在赋值时,必须提供基本数据类型BType和扩展数据类型VType。BType决定了实际的数据结构;VType指出要调用哪一个重载函数。
对象指针约定保存在数据x中,保存一个指针的代码如下:
LuData lud;
//定义一个Lu基本数据
void *p;
//定义一个指针
p=... ...;
//指针赋值
lud.x=(luIFOR)p;
//将指针p保存在x中
根据实际需要,Lu基本数据类型也可以定义为以下形式或者其他形式:
struct LuDataF{ //Lu基本数据结构
double x;
double y;
double z;
luKEY VType; //扩展数据类型,决定重载函数
luKEY BType; //基本数据类型,决定数据结构
};
Lu基本数据类型BType参见表1-1。
表1-1 Lu基本数据类型
类别 | 基本数据类型 | 标识符 | LuData::x | LuData::y | LuData::z | 生成对象 | 查询对象 | 销毁对象 | 意 义 |
静态类型 | 0 | luStaData_nil | 未定义的Lu数据或操作失败 | ||||||
1 | luStaData_forhandle | 保存指针 | LuCom | GetFor | DeleteFor | 表达式句柄。由编译符@或函数HFor获得。 | |||
2 | luStaData_int64 | 64位整数 | 不含小数点的数字 | 直接使用 | 无 | 64位整数 | |||
3 | luStaData_double | 双精度实数 | 含小数点的数字 | 直接使用 | 无 | 64位双精度实数 | |||
4 | luStaData_complex | 复数实部 | 复数虚部 | 以i结尾的数字/运算符$ | 直接使用 | 无 | 复数 | ||
5 | luStaData_vector | x | y | z | 运算符$ | 直接使用 | 无 | 三维向量 | |
6 | luStaData_longdouble | 以r结尾的数字 | 直接使用 | 无 | 长精度实数,未启用 | ||||
7 | luStaData_logical | 64位整数 | true/flase/关系运算或逻辑运算等 | 直接使用 | 无 | 逻辑值,x为0表示逻辑假,否则为逻辑真 | |||
8 | luStaData_speconst | 0 | all | 直接使用 | 无 | 特殊常量,由系统定义,直接在脚本中使用,其意义由使用这些常量的函数解释 | |||
1 | data | ||||||||
2 | public | ||||||||
3 | private | ||||||||
4 | protected | ||||||||
5 | virtual | ||||||||
6 | row | ||||||||
7 | rank | ||||||||
8 | inf/INF | ||||||||
-8 | -inf/-INF | ||||||||
9 | nan/NaN | ||||||||
10 | self | ||||||||
11 | method | ||||||||
12 | inc | ||||||||
13 | range | ||||||||
9 | luStaData_string | 由系统解释 | 由系统解释 | 由系统解释 | "..." | GetStr | 无 | 静态字符串 | |
10 | luStaData_intarray | 由系统解释 | 由系统解释 | 由系统解释 | intss("...") | GetArray | 无 | 静态64位一维整数数组 | |
11 | luStaData_realarray | 由系统解释 | 由系统解释 | 由系统解释 | realss("...") | GetArray | 无 | 静态64位一维实数数组 | |
12 | luStaData_struniint | 64位整数 | 编译符# | 由字符串生成的唯一的64位整数 | |||||
13 | luStaData_pointer | 保存指针 | 由其他指针转换而来 | 无类型指针 | |||||
系统定义动态类型 | |||||||||
-252 | luDynData_realarray | 保存指针 | NewSysObj | SearchKey/GetArray | DeleteKey | 动态64位实数数组 | |||
-253 | luDynData_intarray | 保存指针 | NewSysObj | SearchKey/GetArray | DeleteKey | 动态64位整数数组 | |||
-254 | luDynData_string | 保存指针 | NewSysObj | SearchKey/GetStr | DeleteKey | 动态字符串 | |||
-255 | luDynData_lu | 保存指针 | NewSysObj | SearchKey | DeleteKey | 动态Lu数据 | |||
用户定义动态类型 | < -256 | < luPriKey_User | 保存指针 | InsertKey/GetBufObj | SearchKey | DeleteKey | 用户自定义的私有动态数据类型,均小于-256 | ||
> 256 | > luPubKey_User | 保存指针 | InsertKey/GetBufObj | SearchKey | DeleteKey | 用户自定义的公有动态数据类型,均大于256 |
2.3 Lu键树
在Lu中,表达式、常量、函数、任意自定义数据等等,所有的东西都放到一棵键树中。键的类型KeyType有两种:公有键(KeyType>=luPubKey_User)和私有健(KeyType<=luPriKey_User)。
Lu所支持的程序可能很复杂,程序可以动态加载许多功能模块,每一个模块都可以向Lu键树注册数据,若注册为公有键,则该键可以被任意的功能模块所删除;若注册为私有健,则只有自己可以删除该键。
键的类型可以用函数LockKey加锁。锁定键的类型后,该键只能存储一种数据类型。如果没有锁定键的类型,则该键可以存储任意多种类型的数据。
公有键luPubKey_User和私有健luPriKey_User用来存放一些约定的键,不能加锁。 核心库约定使用如下函数输出信息:
typedef void (_stdcall * luMessage) (wchar_t *); //显示Lu信息的函数指针
主程序或其他模块,需设计如下输出信息的函数:
void _stdcall LuMessage(wchar_t *pWStr) //输出Lu信息,该函数注册到Lu,由Lu及二级函数调用
{
... ...
}
设置 luVOID iVOID=0;
然后使用变量iVOID将该函数用InsertKey((char *)&iVOID,sizeof(luVOID),luPubKey_User,LuMessage,NULL,NULL,1,v)注册到Lu。约定Lu及所有二级函数都使用该函数显示信息。任一线程均可根据需要设置该函数。查询该函数的方法为:
luMessage pMessage=(luMessage)SearchKey((char *)&iVOID,sizeof(luVOID),luPubKey_User);
Lu键的类型如表1-2所示。
表1-2 Lu键的类型
类别 | 键的类型 | 标识符 | 生成对象 | 查询对象 | 销毁对象 | 说明 | ||
系统键 |
255~-199 |
系统使用。 | ||||||
-128~-255 | -200 | luPriKey_UserSearch | 用户可查询和删除的私有健最大值 | 键的类型由系统定义,但用户可以使用。键的类型均被锁定。都是动态数据类型,即系统定义的动态数据类型。 | ||||
-252 | luDynData_realarray | NewSysObj | SearchKey/GetArray | DeleteKey | 动态64位实数数组 | |||
-253 | luDynData_intarray | NewSysObj | SearchKey/GetArray | DeleteKey | 动态64位整数数组 | |||
-254 | luDynData_string | NewSysObj | SearchKey/GetStr | DeleteKey | 动态字符串 | |||
-255 | luDynData_lu | NewSysObj | SearchKey | DeleteKey | 动态Lu数据 | |||
用户键 | 256 | luPubKey_User | InsertKey/GetBufObj | SearchKey | DeleteKey | 公有键。用来存放一些约定的键。不能加锁。 | ||
-256 | luPriKey_User | InsertKey/GetBufObj | SearchKey | DeleteKey | 私有键。用来存放一些约定的键。不能加锁。 | |||
> 256 | > luPubKey_User | InsertKey/GetBufObj | SearchKey | DeleteKey | 由用户定义的公有键。均大于256。可加锁。 | |||
< -256 | < luPriKey_User | InsertKey/GetBufObj | SearchKey | DeleteKey | 由用户定义的私有键。均小于-256。可加锁。 | |||
-1073741824 | luPoiKey_User | InsertKey/GetBufObj | SearchKey | DeleteKey | 指针键最小值。小于luPoiKey_User的非指针键只有自己可以销毁。 |
2.4 Lu扩展数据类型
Lu扩展数据类型通过函数LockKey定义,被加锁的键KeyType即扩展数据类型,加锁键时必须提供运算符重载函数OpLock(即加锁函数)。设被锁定的键KeyType为-300,为了便于使用,将-300注册为整型常量(参考函数SetConst),常量名为MyType。
对Lu扩展数据(简称为对象)的基本操作有四种:(1)生成对象;(2)对象的运算符及函数重载;(3)对象操作函数;(4)销毁对象。
(1)生成对象
根据对象基本数据类型的不同,分两种情况:1)基本数据类型为静态类型;2)基本数据类型为动态类型。
1)基本数据类型为静态类型
有以下几种但不限于这几种方法:
a、在脚本中直接定义。例如:2是一个整数;2.0是一个实数;2i或(2$5)是复数;(2$5.3$7)是一个向量;"..."是一个字符串等等。
b、使用函数cast(StaData,MyType)强制将静态类型数据StaData转换为MyType类型。
c、使用函数new(MyType,...)生成新对象。函数new是被函数OpLock所重载的。
2)基本数据类型为动态类型
有以下几种但不限于这几种方法:
a、使用函数cast(DynData,MyType)强制将动态类型数据DynData转换为MyType类型。要求DynData的基本类型与MyType的基本类型一致,否则转换后的新数据不能使用。
b、使用函数new(MyType,...)生成新对象。函数new是被函数OpLock所重载的。
c、提供专用函数NewMyType(...)生成新对象。
(2)对象的运算符及函数重载
Lu运算符有单目运算符和双目运算符两种,最多有两个操作数。Lu程序在运行时,如果遇到运算符,先查看第一操作数的扩展类型,找到其重载函数调用之,若第一操作数没有重载函数,再查看第二操作数的扩展类型,找到其重载函数调用之,若仍没有找到重载函数,将报告一个运行错误。
Lu部分内置函数允许重载,对于一元函数或参数不确定函数,都是由第一操作数的扩展类型决定重载函数的调用,或者报告一个找不到重载函数的运行错误;对于二元函数,先查看第一操作数的扩展类型,找到其重载函数调用之,若第一操作数没有重载函数,再查看第二操作数的扩展类型,找到其重载函数调用之,若仍没有找到重载函数,将报告一个运行错误。
运算符及函数重载的详细情况请参考函数LockKey的定义 。
(3)对象操作函数
即设计专门的函数对对象进行操作,不赘述。
(4)销毁对象
多数情况下,由Lu的垃圾收集器GC收集并销毁垃圾对象。可直接调用函数del(...)销毁一个或多个对象。或者设计专门的函数用于对象的销毁。
2.5 Lu核心库系统结构及输出函数
Lu核心库中主要包含一个编译器LuCom、一个执行器LuCal和一个键树用以保存各种数据。Lu核心库系统结构及输出函数如图1所示。
图1 Lu核心库系统结构及输出函数关系图
3.1 版本信息函数:LuVer
const wchar_t * _stdcall LuVer(void);
3.2 初始化Lu:InitLu
int _stdcall InitLu(void);
在使用Lu前必须先用该函数进行初始化。初始化成功时返回True,否则返回False。
可在任何时刻使用该函数对Lu进行重新初始化,使Lu恢复到第一次调用该函数时的状态。
重新初始化时,若在Lu键树中有未删除的表达式,在删除表达式之前将不会重置静态变量free为1并执行该表达式,也不会销毁由静态变量free或函数free标识的对象。要实现删除表达式之前的这种自动执行效果,应使用函数DeleteFor自己删除表达式。关于在删除一个表达式前要做的这些工作,请参考函数DeleteFor的说明。
3.3 释放Lu:FreeLu
void _stdcall FreeLu(void);
只要使用了Lu动态库函数,在卸载动态库之前,必须用该函数释放动态库。
释放Lu时,若在Lu键树中有未删除的表达式,在删除表达式之前将不会重置静态变量free为1并执行该表达式,也不会销毁由静态变量free或函数free标识的对象。要实现删除表达式之前的这种自动执行效果,应使用函数DeleteFor自己删除表达式。关于在删除一个表达式前要做的这些工作,请参考函数DeleteFor的说明。
3.4 获得Lu运行错误:GetRunErr
void _stdcall GetRunErr(int &ErrType,wchar_t *&FunName,int &FunCode,void *&ForHandle); //C++定义
void _stdcall GetRunErr(int *ErrType,wchar_t **FunName,int *FunCode,void **ForHandle); //C定义
ErrType:返回运行错误或警告的类型。ErrType=0:没有运行错误及警告;ErrType<0:运行警告;ErrType>0:运行错误。abs(ErrType)<=255:
由系统定义错误类型;abs(ErrType)>255:由用户定义错误类型。ErrType=1:表达式运行错误;ErrType=2:父表达式被删除,父表达式即该表达式中所调用的表达式,也称基表达式;ErrType=3:该表达式中所调用的二级函数被删除;ErrType=4:用户强制退出运行;ErrType=其它值:其它类型运行错误。
FunName:返回出错函数名,或者运行错误说明(ErrType=2或者ErrType=3时,FunName=FunDeleted,FunCode=1;堆栈溢出时,ErrType=1,FunName=StackOverFlow,FunCode=1或者FunCode=2)。
FunCode:返回函数错误代码。
ForHandle:返回出错表达式的句柄,该句柄即编译表达式时获得的句柄。在使用ForHandle前,必须用函数GetFor验证该句柄是否有效
(立即使用时不必验证)。若发现运行错误(ErrType!=0),可以用ExMsgWithLu函数的11号功能获得错误发生时在源代码表达式中的位置。
Lu在运行时,将记录出现的第一个运行错误或警告。
若先出现警告,后出现错误,仅保存第一个运行错误。
通常在编译表达式前后和执行表达式前后使用该函数。使用该函数后,Lu将恢复为无错状态。
在编译和执行表达式后使用该函数,是为了检测发生的运行错误。之所以还要在编译和执行表达式前使用该函数(在编译和执行表达式前,应保证没有运行错误,如果确信无错,也可以不用该函数),是因为该函数可将Lu恢复为无错状态;当然,也可以用SetRunErr()函数直接设为无错状态。
3.5 测试Lu运行错误:TestRunErr
int _stdcall TestRunErr(void);
该函数返回Lu运行错误的类型,当返回0时表示没有错误。该函数仅仅测试是否有运行错误,并不改变错误的状态。
通常在二级函数设计中要用到该函数。
3.6 设置Lu运行错误:SetRunErr
void _stdcall SetRunErr(int ErrType,wchar_t *FunName,int FunCode,int HandleType,void *ForHandle);
ErrType:设置运行错误的类型。ErrType=0:没有运行错误;ErrType=1:函数运行错误;不要设置ErrType=2或ErrType=3,这两个值由系统自动设置;ErrType=4:用户强制退出运行;ErrType=其它值:其它类型运行错误。
FunName:设置出错的函数名,要求传递一个静态字符串,不可为NULL。
FunCode:设置函数错误代码。
HandleType:句柄类型。HandleType=0:从二级函数获得的表达式句柄;HandleType!=0:编译表达式时获得的句柄。
ForHandle:设置出错表达式的句柄。该参数可以为NULL。如果ErrType=4,该参数必须为NULL,
此时Lu系统将尽可能定位退出时的运行位置;如果存在无限循环,可捕捉到无限循环的位置。
在设计自定义的二级函数时,可以用该函数设置Lu运行错误。由于Lu只保存出现的第一个运行错误或警告,故Lu有可能忽略本次设置。若先设置了警告,后设置错误,则警告信息将被丢弃。
注意1:当设置ErrType=0时,表示清除以前的错误设置。只有没有出现过运行错误,或者已经对以前的运行错误进行了正确的处理,才可以设置ErrType=0。
注意2:若要完全恢复刚刚由函数GetRunErr获得的运行错误,必须设置HandleType!=0,ForHandle为由GetRunErr获得的句柄。即便此前使用了ExMsgWithLu函数的11号功能,不影响本设置。
Lu允许另一个线程修改运行时的出错状态,以便退出Lu运行 (ErrType=4),因此可在其它线程中使用SetRunErr()函数设置运行错误。
3.7 编译表达式:LuCom
//C++定义
int _stdcall LuCom(wchar_t *ForStr,luVOID nModule,int
UseMVar,void (_stdcall
*ModuleLock)(void),void *&hFor,luINT &nPara,LuData
*&Para,luINT &ErrBegin,luINT &ErrEnd);
//C定义
int _stdcall LuCom(wchar_t *ForStr,luVOID nModule,int
UseMVar,void (_stdcall
*ModuleLock)(void),void **hFor,luINT *nPara,LuData **Para,luINT *ErrBegin,luINT
*ErrEnd);
ForStr:字符串表达式。
nModule:模块号。每一个模块都有自己的模块变量空间,为了提高存储效率,编译时尽量减少模块的数目。
UseMVar:UseMVar=0:所有用到的模块变量必须预先声明;UseMVar!=0时使用未定义的模块变量(若一个标识符没有定义,且不是常量,将被解释为模块变量)。
ModuleLock:加锁模块的函数指针,可以为NULL。如果预先用函数LockModule(hModule,ModuleLock,True)对一个模块hModule加锁,则编译表达式时必须提供加锁函数的指针,否则无法编译。缺省情况下,Lu对任何模块都不会加锁,此时ModuleLock设为NULL。通常ModuleLock定义为空函数,如下例:
void _stdcall
NullLock(void){}; //加锁模块的函数定义;
hFor:该参数必须为变量,不能为常量。该参数返回一个表达式句柄,标识一个表达式,计算表达式的值时要用到该参数。
nPara:该参数必须为变量,不能为常量。该参数返回表达式的自变量个数。当nPara=-1时表示有0个自变量,当nPara=0时表示有1个自变量,当nPara=1时表示有2个自变量,依次类推。
Para:该参数必须为变量,不能为常量。该参数返回用于输入自变量的数组指针。计算表达式的值时要用到该参数。当然,也可以不用该参数,而用自己的数组输入自变量。
ErrBegin:该参数必须为变量,不能为常量。该参数返回出错的初始位置。
ErrEnd:该参数必须为变量,不能为常量。该参数返回出错的结束位置。
该函数返回编译代码,当返回值为0时,表示没有错误,当返回其它值时,表示存在编译错误,出错位置由ErrBegin和ErrEnd确定。编译错误代码的意义如下:
-2:模块加锁函数不正确。该返回值由程序员进行处理!
-1:未用!
0:没有错误,编译成功!
1:内存分配失败!
2:括号不成对!
3:(等号后)没有表达式!
4:非法的函数句柄!
5:字符串中转义字符错误!
6:字符串无效,即"..."不匹配!
7:不可识别字符!
8:表达式名称定义错误!
9:不可识别的自变量,自变量只能以字母、中文字符或下画线开头!
10:不可识别的自变量定义方法,“(,:,:,:,:,...)”冒号过多!
11:自变量定义错误!
12:continue()函数只能有0个参数!
13:只能在while,until中使用continue函数!
14:break()函数只能有0个参数!
15:只能在while,until中使用break函数!
16:if,while,until,which中的参数个数至少为2个!
17:表达式中的数字错误!
18:&单目取地址运算符只能用于单独的变量!
19:单目运算符++、--错误!
20:括号内没有数字!
21:单目运算符+、-、!、!!错误!
22:赋值“=”错误!
23:不正确的运算方式或其他语法错误!
24:不可识别变量名或常量名!
25:不可识别函数名!
26:一级函数参数不匹配!
27:二级函数参数不匹配!
28:关键字Static或Common的位置非法!
29:(模块中)表达式有重名!
30:对形如“-2^3”的式子,须用括号给前置单目运算符“-”和乘方运算符“^”(或点乘方运算符“.^”)指出运算顺序!
31:类成员运算符(函数参数运算符)后只能是变量名、字符串、括号运算符或者类成员函数!
32:后单目运算符'、.'错误!
33:调用表达式时参数不匹配!
34:未用!
35:自变量重名!
36:因检测到运行错误而退出!
37:未用!
38:未用!
39:源代码太长或字符串太多!
在编译时该函数将自动设置使Lu处于无错状态,编译后 若错误代码为36,须用GetRunErr()检测运行错误。其他线程的错误设置可使Lu退出漫长的编译过程,返回错误代码36。
Lu支持表达式的模块化编译。在用Lu编译表达式时,要给该表达式指定一个模块号,模块号用整数进行标识。如果用模块加锁函数LockModule对一个模块号进行了加锁,则编译表达式时必须提供加锁函数。
在Lu中,一个模块由一个或多个表达式组成。模块用一个整数标识,整数可正可负,只要绝对值相等,就属于同一个模块。一般用正整数表示该模块名。模块共有两类,即主模块(0#模块)和普通模块(其他标号的模块)。
同一模块中,模块号为负的表达式称私有表达式,只能被本模块的表达式所访问(即调用),在其他模块中是不可见的;模块号为正的表达式称公有表达式或全局表达式,能被任何一个表达式所访问。主模块(0#模块)中的表达式都是私有表达式。任何一个表达式,既可以访问本模块中的表达式,也可以访问其他模块中的全局表达式,如果本模块中的一个私有表达式与其他模块的一个全局表达式重名,将优先调用本模块中的私有表达式。
由以上规定可以看出,主模块可以访问本模块中的表达式,也可以访问其他模块中的全局表达式。因此,主模块常常用在主程序中。
表达式hFor的销毁:(1)使用函数DeleteFor销毁表达式;(2)重新初始化Lu时由函数InitLu销毁所有表达式;(3)释放Lu时由函数FreeLu销毁所有表达式。
3.8 计算表达式的值:LuCal
LuData _stdcall LuCal(void *hFor,LuData *d);
计算表达式hFor,hFor是表达式的句柄;数组d中依次存放自变量,参数d不可为NULL。可以用任意的数组存放自变量参数。
注意自变量可能被重新赋值。可以用ParaModify()函数判断一个表达式的自变量是否重新赋值。
通常在计算前用SetRunErr()函数或GetRunErr()函数使Lu处于无错状态,计算后用GetRunErr()检测运行错误。
3.9 锁定一个模块:LockModule
int _stdcall LockModule(luVOID hModule,void (_stdcall *NullLock)(void),int bLock);
hModule:模块号。
NullLock:模块加锁函数指针。模块加锁函数在主程序中定义,格式如下:
void _stdcall NullLock(void){}; //加锁模块的函数定义;
bLock:bLock=True,加锁模块;bLock=False,解锁模块。
返回值:True表示操作成功,False表示操作失败。
如果预先用函数LockModule(hModule,NullLock,True)对一个模块hModule加锁,则编译表达式时必须提供加锁函数的指针,否则无法编译。缺省情况下,Lu对任何模块都不会加锁。
通常,模块加锁后无需解锁,模块中没有表达式时将自动解锁。但如果模块加锁后从未使用(没有一个表达式编译成功),则需用函数LockModule(hModule,NullLock,False)进行解锁。
加锁一个模块使得其他线程无法向该模块中增加表达式,这在设计多线程程序时特别有用。
3.10 设置外部二级函数:SetFunction
int _stdcall SetFunction(wchar_t *FunName,void *Fun,luINT ParaNum);
FunName:二级函数名称,要符合Lu标识符的命名规定。
Fun:二级函数指针。格式如下:
LuData Fun(luINT m,LuData *Para,void *hFor); //Lu标准二级函数;
其中m为Lu传递给该函数的实际的参数个数,-1表示有0个自变量,0表示有1个自变量,依次类推。Para为存放参数的数组。hFor为调用该函数的表达式句柄(与编译表达式时返回的表达式句柄并不相同)。
在Lu表达式中,函数的一般形式为FunName(X1,X2,...,Xn)。
ParaNum:存放二级函数的自变量个数。数组ParaNum的对应项存放每个函数的自变量个数,其中-2表示有不确定的多个自变量,-1表示有0个自变量,0表示有1个自变量,依次类推。
该函数返回值的意义如下:
0:设置成功。
1:已存在该函数。
2:内存分配失败。
3:不能用空字符串作为函数名。
4:非法的二级函数类型标识。
例子:
LuData _stdcall asd(luINT ,LuData *,void
*); //自定义二级函数;
SetFunction(luKey_Function,"asd",asd,2);
//设置二级函数;
注意:
1、通常在初始化Lu之后,立即进行二级函数的设置。
2、设计二级函数必须使用_stdcall调用协议
。
3、函数参数如果包含指针,约定指针保存在参数相关数据的前8个字节中。
4、在设计二级函数时,通常要用SetRunErr(...)向Lu报告运行错误。
5、在二级函数中用到的Lu动态对象都需要验证其有效性。例如字符串对象用函数GetStr验证;表达式对象用函数GetFor验证;系统产生的大多数对象或用户定义对象用函数SearchKey验证等等。
6、如果在二级函数中调用了函数LuCal,则以后使用的Lu动态对象仍需要验证其有效性,因为在使用LuCal计算过程中,有可能销毁动态对象。
7、如果二级函数的返回值是一个动态对象,在返回前必须用函数FunReObj通知Lu;如果用函数参数Para返回一些动态对象,必须用函数FunSaveObj通知Lu。
取消设置的外部二级函数:(1)使用函数DeleteKey((char *)FunName,ByteNum,luKey_Function,NULL),其中ByteNum为字符串FunName长度的2倍,luKey_Function标识二级函数;(2)重新初始化Lu时由函数InitLu取消所有外部二级函数;(3)释放Lu时由函数FreeLu取消所有外部二级函数。
3.11 设置常量:SetConst
int _stdcall SetConst(wchar_t *ConstName,LuData *ConstValue);
ConstStr:常量名,要符合Lu标识符的命名规定。
ConstValue:常量。
该函数返回值的意义如下:
0:设置成功。
1:已存在该常量。
2:内存分配失败。
3:不能用空字符串作为常量名。
4:非法的常量类型标识。
例如:
LuDataF pi;
pi.BType=LuData_double; pi.VType=LuData_double;
pi.x=3.1416; //设置常量的值;
SetConst(luKey_Const,"pi",pi);
//设置常量;
销毁设置的常量:(1)使用函数DeleteKey((char *)ConstName,ByteNum,luKey_Const,NULL),其中ByteNum为字符串ConstName长度的2倍,luKey_Const标识常量;(2)重新初始化Lu时由函数InitLu销毁所有常量;(3)释放Lu时由函数FreeLu销毁所有常量。
3.12 二级函数返回一个动态对象:FunReObj
void _stdcall FunReObj(void *hFor);
hFor:从二级函数获得的表达式句柄。不能使用编译表达式得到的表达式句柄。
二级函数设计中,若函数返回一个对象,需调用该函数通知Lu。
3.13 二级函数保存动态对象:FunSaveObj
void _stdcall FunSaveObj(void *hFor,LuData *pLD,LuData *Para);
hFor:从二级函数获得的表达式句柄。不能使用编译表达式得到的表达式句柄。
pLD:
要保存的动态对象指针。首次调用时pLD设为NULL。
Para:
二级函数的第二个参数。pLD为NULL时忽略该参数。
二级函数设计中,若要保存若干动态对象,需用该函数通知Lu。首次调用时参数pLD设为NULL,进行初始化,以后每次调用保存一个Lu动态对象。
3.14 判断一个表达式是否有效:IsFor
int _stdcall IsFor(wchar_t *ForName,luVOID nModule,void *hFor,LuData *Para,luINT PareNum);
用法1:
ForName:表达式的名称,只能是简单名称,不能包含模块命名空间访问符::。
nModule:表达式所属的模块。
hFor:表达式句柄,该句柄即编译表达式得到的表达式句柄,可用于表达式的计算。
Para:存放表达式自变量的数组指针。
ParaNum:表达式自变量的个数。
该函数返回True时表达式有效,返回False不是一个有效的表达式。
用法2:
ForName:ForName=NULL。
nModule:表达式所属的模块。
hFor:表达式句柄,该句柄即编译表达式得到的表达式句柄,可用于表达式的计算。
Para:存放表达式自变量的数组指针。
ParaNum:表达式自变量的个数。
该函数返回True时表达式有效,返回False不是一个有效的表达式。
3.15 获得表达式信息:GetFor
int _stdcall GetFor(wchar_t *ForName,int ForType,void *hFor,luVOID &nModule,void *&myFor,LuData *&Para,luINT &PareNum); //C++定义
int _stdcall GetFor(wchar_t *ForName,int ForType,void *hFor,luVOID *nModule,void **myFor,LuData **Para,luINT *PareNum); //C定义
用法1:
ForName:所查询的表达式的名称。可以是简单名称,也可以是包含模块命名空间访问符::(该访问符可简化为一个或多个:)的复杂名称
,取决于ForType。
ForType:所查询的表达式的类型。ForType!=0:ForName只能是简单名称;ForType=0:可以是包含模块命名空间访问符::(该访问符可简化为一个或多个:)的复杂名称
。
hFor:若hFor!=NULL,hFor必须是从二级函数获得的表达式句柄,不能使用编译表达式得到的表达式句柄。若hFor=NULL,只能获得0号模块中的表达式的信息,或者是一个全局表达式的信息。
nModule:该参数必须为变量,不能为常量。该参数返回一个表达式所属的模块。
myFor:该参数必须为变量,不能为常量。该参数返回一个表达式句柄,该句柄与编译表达式得到的表达式句柄的类型相同,可用于表达式的计算。
Para:该参数必须为变量,不能为常量。该参数返回一个存放表达式自变量的数组指针,可用于输入自变量。
ParaNum:该参数必须为变量,不能为常量。该参数返回表达式自变量的个数。当ParaNum=-1时表示有0个自变量,当ParaNum=0时表示有1个自变量,当ParaNum=1时表示有2个自变量,依次类推。
该函数返回True时查询成功,否则返回False。
注意:若hFor!=NULL,则只能在二级函数中使用该函数;若hFor=NULL,可在任何时候使用该函数。
用法2:
ForName:ForName=NULL。
ForType:无意义,被忽略。
hFor:使用编译表达式得到的表达式句柄。
nModule:该参数必须为变量,不能为常量。该参数返回一个表达式所属的模块。
myFor:该参数必须为变量,不能为常量。该参数返回一个表达式句柄,该句柄即hFor,可用于表达式的计算。
Para:该参数必须为变量,不能为常量。该参数返回一个存放表达式自变量的数组指针,可用于输入自变量。
ParaNum:该参数必须为变量,不能为常量。该参数返回表达式自变量的个数。当ParaNum=-1时表示有0个自变量,当ParaNum=0时表示有1个自变量,当ParaNum=1时表示有2个自变量,依次类推。
该函数返回True时查询成功,否则返回False。
3.16 判断表达式的自变量是否重新赋值:ParaModify
int _stdcall ParaModify(void *vFor);
vFor:编译表达式时得到的表达式句柄。
若表达式的自变量重新赋值,该函数返回True,否则返回False。
该函数不能判断是否对局部变量或全局变量进行了赋值。
在设计某些程序时,如果在表达式中不对自变量进行重新赋值,可以简化程序的设计,并可加快程序的执行速度。对表达式有此要求者可用该函数进行判断。
3.17 删除一个表达式:DeleteFor
int _stdcall DeleteFor(void *hFor,void (_stdcall *ModuleLock)(void));
hFor:表达式句柄,该句柄为编译表达式时获得的句柄。
ModuleLock:加锁模块的函数指针,参考函数LuCom的说明。如果预先用函数LockModule(hModule,ModuleLock,True)对一个模块hModule加锁,则删除表达式时必须提供加锁函数的指针。
返回值:删除成功返回True,否则返回False。如果在二级函数中使用DeleteFor函数,当表达式正在运行时将返回False,可使用函数ExMsgWithLu的4号功能测试一个表达式是否正在运行。
设有如下表达式定义:
f(x:y,p,static,a1,a2,a3,free,b1,b2:c) = ..., free(p), ...;
当用函数DeleteFor删除该表达式时,将设置静态变量free=1,然后按缺省参数自动调用该表达式 ,但也可能由于某种原因表达式不可执行(父表达式或该表达式中所调用的二级函数被删除)。任何情况下,在static与free之间的静态变量a1,a2,a3标识的对象将被销毁。
若表达式中没有静态变量free,将不会自动调用该表达式,也不会自动销毁任何静态变量所标识的对象。
若使用二级函数free(p)对p做了标记,在用函数DeleteFor删除该表达式时,将销毁对象p。
注意:为了使Lu在删除表达式时能按正确的次序自动执行表达式,要遵守后编译的表达式要先删除的原则。但即便没有遵循“后进先出”的删除原则,也不会产生灾难性的后果。
3.18 获得字符串:GetStr
wchar_t * _stdcall GetStr(LuData *Str,void *hFor,luVOID &StrMax); //C++定义
wchar_t * _stdcall GetStr(LuData *Str,void *hFor,luVOID *StrMax); //C定义
用法1:
Str:Lu基本数据指针。
hFor:从二级函数获得的表达式句柄。不能使用编译表达式得到的表达式句柄。
StrMax:返回字符串缓冲区最大长度。该参数必须为变量,不能为常量。
返回值:字符串指针。返回NULL表示操作失败。
注意:只能在二级函数中使用该函数。
用法2:
Str:Lu基本数据指针。
hFor:hFor=NULL。
StrMax:返回字符串缓冲区最大长度。该参数必须为变量,不能为常量。
返回值:字符串指针。返回NULL表示操作失败。
注意:字符串指针应及时使用。
说明:该函数既可获得luStaData_string类型的静态字符串,也可获得luDynData_string类型的动态字符串。
3.19 获得数组:GetArray
void * _stdcall GetArray(LuData *Array,void *hFor,luVOID &Max); //C++定义
void * _stdcall GetArray(LuData *Array,void *hFor,luVOID *Max); //C定义
用法1:
Array:Lu基本数据指针。
hFor:从二级函数获得的表达式句柄。不能使用编译表达式得到的表达式句柄。
Max:返回数组缓冲区最大长度。该参数必须为变量,不能为常量。
返回值:数组指针。返回NULL表示操作失败。
注意:只能在二级函数中使用该函数。
用法2:
Array:Lu基本数据指针。
hFor:hFor=NULL。
Max:返回数组缓冲区最大长度。该参数必须为变量,不能为常量。
返回值:数组指针。返回NULL表示操作失败。
注意:数组指针应及时使用。
说明:该函数既可获得luStaData_intarray或luStaData_realarray类型的静态数组,也可获得luDynData_intarray或luDynData_realarray类型的动态数组。如果动态数组是多维的,该函数将视为一维数组。静态数组都是一维数组。
3.20 申请系统内置动态对象:NewSysObj
void * _stdcall NewSysObj(luKEY Type,luVOID Len1,luVOID Len2);
Type:动态对象类型,只能取luDynData_lu(动态Lu数据)、luDynData_string(动态字符串)、luDynData_intarray(动态整数数组)和luDynData_intarray(动态实数数组)。
Len1:其意义取决于Type。luDynData_lu或luDynData_string类型对象的缓冲区长度;luDynData_intarray或luDynData_intarray类型对象的数组缓冲区长度。
Len2:其意义取决于Type。luDynData_intarray或luDynData_intarray类型对象的数组维数长度。luDynData_lu或luDynData_string类型对象将忽略此参数。
返回值:动态对象指针,其类型取决于Type。
//luDynData_lu(动态Lu数据)
struct luLu
{
LuData *Lu; //允许为NULL
luVOID Len; //Lu数据缓冲区长度
};//luDynData_string(动态字符串)
struct luString
{
wchar_t *Str; //不会为NULL
luVOID Len; //字符串缓冲区长度
};//luDynData_intarray(动态整数数组,多维数组采用C/C++格式存取)
struct luIntArray
{
luIFOR *Array; //数组缓冲区,不会为NULL
luVOID ArrayLen; //数组缓冲区长度
luVOID *Dim; //数组维数,不会为NULL,实际长度最少为2,以便于将一维数组转换为矩阵
luVOID DimLen; //数组维数长度
};//luDynData_realarray(动态实数数组,多维数组采用C/C++格式存取)
struct luRealArray
{
double *Array; //数组缓冲区,不会为NULL
luVOID ArrayLen; //数组缓冲区长度
luVOID *Dim; //数组维数,不会为NULL,实际长度最少为2,以便于将一维数组转换为矩阵
luVOID DimLen; //数组维数长度
};
说明:该函数将优先使用缓冲池中的垃圾对象。返回的对象已注册到了Lu键树中,可使用函数SearchKey查询。按谁申请谁释放的原则,用户不可释放这些内置对象的数据缓冲区,可以使用函数SetSysObj重置内置对象的数据缓冲区,或者重新生成一个新的对象。
3.21 重置系统内置动态对象:SetSysObj
int _stdcall SetSysObj(luKEY Type,void *theObj,luVOID Len1,luVOID Len2,int iCopy);
Type:动态对象类型,只能取luDynData_lu(动态Lu数据)、luDynData_string(动态字符串)、luDynData_intarray(动态整数数组)和luDynData_intarray(动态实数数组)。
theObj:动态对象指针,取决于Type。
Len1:对象缓冲区的新长度,其意义取决于Type。luDynData_lu或luDynData_string类型对象的缓冲区长度;luDynData_intarray或luDynData_intarray类型对象的数组缓冲区长度。
Len2:对象缓冲区的新长度,其意义取决于Type。luDynData_intarray或luDynData_intarray类型对象的数组维数长度。luDynData_lu或luDynData_string类型对象将忽略此参数。
iCopy:iCopy非0表示复制原先的数据,否则不复制数据。在复制数据时,若新的数据缓冲区小,原先的数据将被截断;若新的缓冲区大,缓冲区剩余部分不进行任何初始化操作。
返回值:操作成功返回True,否则返回False。
3.22 插入一个键:InsertKey
//C++定义
int _stdcall InsertKey(char
*KeyStr,luINT ByteNum,luKEY KeyType,void *KeyValue,void (_stdcall
*DelKey)(void *),void (_stdcall *SignKey)(void *),int ReNew,void *&NowKey);
//C定义
int _stdcall InsertKey(char
*KeyStr,luINT ByteNum,luKEY KeyType,void *KeyValue,void (_stdcall
*DelKey)(void *),void (_stdcall *SignKey)(void *),int ReNew,void **NowKey);
KeyStr:键的名称。区分大小写,可包含任意字符,包括NULL('\0')。
ByteNum:键的长度。ByteNum<0表示指针键(64位平台上,指针键有8个字节
;指针键类型KeyType>=luPoiKey_User),键长自动取8;ByteNum>0表示非指针键。
KeyType:用户自定义键的类型。KeyType>=luPubKey_User(公有键、普通键)
或者 KeyType<=luPriKey_User(私有键)。约定用luPubKey_User和luPriKey_User保存一些特殊的数据,作为多个函数、线程、动态库等之间联系的通道。
KeyValue:键值。由用户定义,标识某种数据类型。
DelKey:删除该键值的函数指针。该函数由用户定义,但由Lu调用,即:DelKey(KeyValue);
可将KeyValue删除。如果不需要Lu删除该键值,该参数可设为NULL。
SignKey:标记子键值(对象)的函数指针,所有标记过的子对象都是有效(可达)对象,不会被垃圾收集器GC所回收。该函数由用户定义,但由Lu调用,即:SignKey(KeyValue);
KeyValue若包含另外的对象,则调用该函数可对其他对象做标记,应在函数SignKey中对每一个对象调用函数SignGoodObj做标记。
若KeyValue不包含子对象,该参数设为NULL。
若键长不等于8(只有ByteNum<0或ByteNum=8时键长为8),该参数必须为NULL。标记子键值(对象)的函数SignKey格式如下:
void _stdcall SignKey(void *me)
{
...
SignGoodObj(p); //p是me的子对象,用函数SignGoodObj标记该子对象
...
}
ReNew:更新键值请求,用于存在同名同类型的键时的处理。ReNew=0:不更新键值,否则请求更新键值。
NowKey:该参数必须为变量,不能为常量,用于返回已存在的键值。即:
ReNew=0,不更新键值,NowKey返回已存在的键值。
ReNew!=0,且KeyType>=luPubKey_User:更新键值。
ReNew!=0,且KeyType<=luPriKey_User,新键和老键的删除函数相同:更新键值。
ReNew!=0,且KeyType<=luPriKey_User,新键和老键的删除函数不相同:不更新键值,NowKey返回已存在的键值。
该函数返回值的意义如下:
0:插入成功。
1:已存在该键但没有更新键值。参数ReNew=0,没有更新键值,由参数NowKey返回已存在的键值。或者ReNew!=0,KeyType<=luPriKey_User(私有键),且新键和老键的删除函数不相同,没有更新键值,参数NowKey返回了已存在的键值。
2:已存在该键且更新了键值。参数ReNew!=0,当KeyType>=luPubKey_User时,更新了键值;或者当KeyType<=luPriKey_User(私有键),且新键和老键的删除函数相同时,更新了键值。
3:键的类型参数KeyType非法。
4:键值类型被唯一指定,删除该键值的函数指针DeleteKey非法,必须使用被唯一指定的函数指针。
5:内存分配失败。
6:ByteNum=0,或者指针键类型KeyType<luPoiKey_User。
7:参数SignKey非法。
删除函数InsertKey插入的键:(1)使用函数DeleteKey;(2)用函数LockKey解锁键时将删除所有指定类型的键值;(3)系统自动销毁临时对象(临时对象都是指针键);(4)被垃圾收集器GC自动回收(仅回收指针键);(5)使用del或delete函数销毁;(6)重新初始化Lu时由函数InitLu销毁所有键值;(7)释放Lu时由函数FreeLu销毁所有键值。
说明:
(1)当键类型KeyType<luPoiKey_User时,只能是非指针键,这些键只有自己可以销毁;而指针键可被自动销毁。
(2)只有指针键才能被垃圾收集器GC回收。
(3)Lu内置一个计数器,自动记录指针键的数目,在用函数InsertKey插入一个指针键 (插入非指针键不检查)时,若指针键总数超过设定值(默认值是10000,可通过函数ExMsgWithLu的6号功能修改此值,设定值小于等于0时不启动垃圾收集器),将自动调用垃圾收集器。
当用函数InsertKey连续插入两个指针键时,前一个指针键可能被垃圾收集器销毁。故在二级函数设计中,每当用函数InsertKey插入一个指针键,就要调用函数FunSaveObj或FunReObj通知Lu,此指针键是有效的。在主程序中要连续插入两个指针键时,可调用GC(4)暂时关闭垃圾收集器,或者使用函数ExMsgWithLu的6号功能暂时关闭垃圾收集器。
(4)用函数DeleteFor删除一个表达式时,只有指针键才能被静态变量free或函数free所销毁。
设有如下表达式定义:
f(x:y,p,static,a1,a2,a3,free,b1,b2:c) = ..., free(p), ...;
当用函数DeleteFor删除该表达式时,将设置静态变量free=1,然后按缺省参数自动调用该表达式,但也可能由于某种原因表达式不可执行 (父表达式或该表达式中所调用的二级函数被删除)。任何情况下,在static与free之间的静态变量a1,a2,a3标识的对象将被销毁。
若表达式中没有静态变量free,将不会自动调用该表达式,也不会自动销毁任何静态变量所标识的对象。
若使用二级函数free(p)对p做了标记,在用函数DeleteFor删除该表达式时,将销毁对象p。
3.23 查找一个键:SearchKey
void * _stdcall SearchKey(char *KeyStr,luINT ByteNum,luKEY KeyType);
KeyStr:键的名称。区分大小写,可包含任意字符,包括NULL('\0')。
ByteNum:键的长度。ByteNum>0。
KeyType:用户自定义键的类型。KeyType>=luPubKey_User
或者
KeyType<=luPriKey_UserSearch
或者
KeyType==luKey_Const。
如果查找成功,该函数返回键值的指针,否则返回False。
3.24 删除一个键:DeleteKey
int _stdcall DeleteKey(char *KeyStr,luINT ByteNum,luKEY KeyType,void (_stdcall *DelKey)(void *),int iBuf);
KeyStr:键的名称。区分大小写,可包含任意字符,包括NULL('\0')。
ByteNum:键的长度。ByteNum>0。
KeyType:用户自定义键的类型或者系统定义的特殊键的类型。当KeyType>=luPubKey_User(公有键、普通键)或者luPriKey_User<KeyType<=luPriKey_UserSearch
(系统定义私有键)时,参数DelKey被忽略;当KeyType<=luPriKey_User(私有键)时,必须提供删除函数DelKey。可以删除的其他系统特殊键的类型如下(参数DelKey均被忽略):
当KeyType=luKey_Const时,将删除由函数SetConst设置的常量
;
当KeyType=luKey_Function时,将删除由函数SetFunction设置的二级函数。
DelKey:删除该键值的函数指针。该指针与插入键时用到的函数指针相同,否则不能删除键值。
iBuf:iBuf!=0时,删除的指针键暂时存放到缓冲池中;iBuf=0时,立即删除指定的键。该参数仅对指针键有效。
返回值:操作成功返回True,否则返回False。
3.25 枚举指定键值类型所对应的所有字符串及键值:EnumKey
void _stdcall EnumKey(luKEY KeyType,char *KeyStr,int nKeyStrMax,int (_stdcall *GetKeyTypeValue)(char *,int ,void *));
KeyType:指定键的类型。KeyType>=luPubKey_User(公有键、普通键)
或者 KeyType<=luPriKey_UserSearch(私有键)。
KeyStr:存放键的名称的缓冲区。如果缓冲区不够用,仅存放键的名称的前面部分字符。
nKeyStrMax:存放键的名称的缓冲区的最大长度(nKeyStrMax>=1)。
GetKeyTypeValue:回调函数。每当找到指定键的类型对应的键时就调用该函数。该回调函数的说明格式如下:
int
_stdcall GetKeyTypeValue(char *KeyStr,int
ByteNum,void *KeyValue);
KeyStr:键的名称。区分大小写
,可包含任意字符,包括NULL('\0')。该函数被调用时,将自动在键的名称后面添加一个NULL('\0')。
ByteNum:键的长度。同时指出键的名称的完整性。当ByteNum=nKeyStrMax-1时,键的名称可能不完整,仅存放了名称的前面部分字符。使用更大的缓冲区可使该函数返回键的完整名称。
KeyValue:键值。标识某种数据类型。
当GetKeyTypeValue返回True时,继续枚举其他键值,否则停止枚举。
注意:不要在该函数中调用除了GetRunErr()、TestRunErr()和SetRunErr()三个函数之外的Lu输出函数。
3.26 锁定键的类型:LockKey
int _stdcall LockKey(luKEY KeyType,void (_stdcall *DeleteKey)(void *),luOperator OpLock);
KeyType:被锁定的键的类型。KeyType>luPubKey_User(公有键、普通键)或者 KeyType<luPriKey_User(私有键)。
DeleteKey:删除键值的函数指针,用于标识要加锁的键。该函数由用户定义,但由Lu调用。若DeleteKey=NULL,表示解锁指定的键。
OpLock:luOperator类型的函数指针,用于对象(用指针标识)的运算符重载,该参数不可为NULL。解锁和加锁所用的OpLock函数必须相同。参考[注1]。
如果加锁或解锁成功,该函数返回0,否则返回非0值。
说明:锁定键的类型后,该键只能存储一种数据类型,该数据类型用删除函数标识。如果没有锁定键的类型,则该键可以存储任意多种类型的数据。只有欲加锁的键没有使用,也没有存储任何类型的数据时,才能将该键加锁;解锁时将删除所有的键值,将键恢复为加锁前未用时的状态。通常在主调程序中锁定的键无需解锁,Lu会自动删除键树中的所有数据。在动态库中锁定的键必须解锁,否则将导致不可预知的错误。
[注1]:运算符重载函数luOperator函数格式如下 (与Lu二级函数相比,仅多了一个参数theOperator):
//m指出数组Para的参数个数(也即操作数的个数,0表示1个,1表示2个,以此类推)。
//hFor为调用该函数的表达式句柄(与二级函数中的表达式句柄相同)。
//theOperator指出运算符的类型或操作类型:+、-、*、/、^、... ...。
LuData (_stdcall *luOperator)(luINT m,LuData *Para,void *hFor,int theOperator);
LuData _stdcall OpLock(luINT m,LuData *Para,void *hFor,int theOperator)
{
//... ...
switch(theOperator)
{
case 0: //重载运算符+
//... ...
case 1: //重载运算符-
//... ...
case 2: //重载运算符*
//... ...
case 3: //重载运算符%
//... ...
case 4: //重载运算符/
//... ...
... ...
}
}
如果不打算给加锁的键提供运算符或函数重载功能,须使用函数SetRunErr向Lu报告运行错误。
Lu中可重载的运算符、函数及操作如下表:
theOperator | 运算符 | 功 能 | 参数个数m+1 | 引起OpLock被调用的操作数 | 说 明 |
0 | + | 加 | 2 | 第一操作数优先,其次第二操作数 | |
1 | - | 减 | 2 | 第一操作数优先,其次第二操作数 | |
2 | * | 乘 | 2 | 第一操作数优先,其次第二操作数 | |
3 | % | 求模 | 2 | 第一操作数优先,其次第二操作数 | |
4 | / | 左除 | 2 | 第一操作数优先,其次第二操作数 | |
5 | \ | 右除 | 2 | 第一操作数优先,其次第二操作数 | |
6 | ^ | 乘方 | 2 | 第一操作数优先,其次第二操作数 | |
7 | - | 负 | 1 | 第一操作数 | |
8 | ' | 转置 | 1 | 第一操作数 | |
9 | ++o | 前置自增 | 1 | 第一操作数 | 通常先对当前对象自增,再返回当前对象。 |
10 | --o | 前置自减 | 1 | 第一操作数 | 通常先对当前对象自减,再返回当前对象。 |
11 | o++ | 后置自增 | 1 | 第一操作数 | 通常返回新对象。 |
12 | o-- | 后置自减 | 1 | 第一操作数 | 通常返回新对象。 |
13 | << | 左移位 | 2 | 第一操作数优先,其次第二操作数 | |
14 | >> | 右移位 | 2 | 第一操作数优先,其次第二操作数 | |
15 | > | 大于 | 2 | 第一操作数优先,其次第二操作数 | |
16 | >= | 大于等于 | 2 | 第一操作数优先,其次第二操作数 | |
17 | < | 小于 | 2 | 第一操作数优先,其次第二操作数 | |
18 | <= | 小于等于 | 2 | 第一操作数优先,其次第二操作数 | |
19 | == | 等于 | 2 | 第一操作数优先,其次第二操作数 | |
20 | != | 不等于 | 2 | 第一操作数优先,其次第二操作数 | |
21 | & | 与 | 2 | 第一操作数优先,其次第二操作数 | |
22 | | | 或 | 2 | 第一操作数优先,其次第二操作数 | |
23 | ~ | 异或 | 2 | 第一操作数优先,其次第二操作数 | |
24 | ! | 非 | 1 | 第一操作数 | |
25 | .* | 点乘 | 2 | 第一操作数优先,其次第二操作数 | |
26 | ./ | 点左除 | 2 | 第一操作数优先,其次第二操作数 | |
27 | .\ | 点右除 | 2 | 第一操作数优先,其次第二操作数 | |
28 | .^ | 点乘方 | 2 | 第一操作数优先,其次第二操作数 | |
29 | .' | 点转置 | 1 | 第一操作数 | |
30 | && | 按位与 | 2 | 第一操作数优先,其次第二操作数 | |
31 | || | 按位或 | 2 | 第一操作数优先,其次第二操作数 | |
32 | ~~ | 按位异或 | 2 | 第一操作数优先,其次第二操作数 | |
33 | !! | 按位非 | 1 | 第一操作数 | |
34 | $ | 并 | 2 | 第一操作数优先,其次第二操作数 | |
35~38 | 未定义 | ||||
39 | sum | 重载函数 | 不确定 | 第一操作数 | 求和。 |
40 | pro | 重载函数 | 不确定 | 第一操作数 | 求积。 |
41 | deepcopy | 重载函数 | 不确定 | 第一操作数 | 深复制对象。 |
42 | get | 重载函数 | 不确定 | 第一操作数 | 获得对象(成员)。 |
43 | set | 重载函数 | 不确定 | 第一操作数 | 设置对象(成员)。 |
44 | len | 重载函数 | 不确定 | 第一操作数 | 返回对象长度。 |
45 | copy | 重载函数 | 不确定 | 第一操作数 | 复制对象。 |
46 | new | 重载函数 | 不确定 | 第一操作数 | 第一操作数是一个整数,表示一种扩展数据类型。该函数返回一个新对象。 |
47 | oset | 重载函数 | 不确定 | 第一操作数 | 对象赋值。 |
48 | oget | 重载函数 | 不确定 | 第一操作数 | 获得对象的值。 |
49 | o | 重载函数 | 1 | 第一操作数 | 获得对象信息。 |
50 | sqrt | 重载函数 | 1 | 第一操作数 | |
51 | exp | 重载函数 | 1 | 第一操作数 | |
52 | ln | 重载函数 | 1 | 第一操作数 | |
53 | lg | 重载函数 | 1 | 第一操作数 | |
54 | sin | 重载函数 | 1 | 第一操作数 | |
55 | cos | 重载函数 | 1 | 第一操作数 | |
56 | tan | 重载函数 | 1 | 第一操作数 | |
57 | asin | 重载函数 | 1 | 第一操作数 | |
58 | acos | 重载函数 | 1 | 第一操作数 | |
59 | atan | 重载函数 | 1 | 第一操作数 | |
60 | sinh | 重载函数 | 1 | 第一操作数 | |
61 | cosh | 重载函数 | 1 | 第一操作数 | |
62 | tanh | 重载函数 | 1 | 第一操作数 | |
63 | abs | 重载函数 | 1 | 第一操作数 | |
64 | floor | 重载函数 | 1 | 第一操作数 | |
65 | ceil | 重载函数 | 1 | 第一操作数 | |
66 | itor | 重载函数 | 1 | 第一操作数 | |
67 | rtoi | 重载函数 | 1 | 第一操作数 | |
68 | con | 重载函数 | 1 | 第一操作数 | |
69 | atan2 | 重载函数 | 2 | 第一操作数优先,其次第二操作数 | |
70 | fmod | 重载函数 | 2 | 第一操作数优先,其次第二操作数 |
函数返回luStaData_nil类型的LuData数据表示操作失败,并返回警告信息,其中LuData::x的意义如下:
LuData::x |
意 义 |
0 | 没有定义该运算符或函数的操作。 |
1 | 定义了该运算符或函数的操作,但操作失败。 |
3.27 按指定类型执行运算符重载函数:ExeOperator
LuData _stdcall ExeOperator(luINT m,LuData *Para,void *vFor,int theOperator,luKEY Type);
其中m为Lu传递给该函数的实际的参数个数,-1表示有0个自变量,0表示有1个自变量,依次类推。Para为存放参数的数组。vFor为调用该函数的表达式句柄( 从二级函数获得的表达式句柄,与编译表达式时返回的表达式句柄并不相同) 。theOperator标识对哪一个运算符进行重载。Type为键的类型,标识一种对象。
函数返回luStaData_nil类型的LuData数据表示操作失败,并返回警告信息,其中LuData::x的意义如下:
LuData::x |
意 义 |
0 | 没有定义该运算符或函数的操作。 |
1 | 定义了该运算符或函数的操作,但操作失败。 |
2 | 没有找到Type类型的对象的运算符操作函数。 |
3.28 垃圾收集时标记一个指针键(对象):SignGoodObj
int _stdcall SignGoodObj(LuData *me);
通知Lu的垃圾收集器,me是一个可达的(有效的)对象。标记成功返回True。
3.29 垃圾收集:GC
int _stdcall GC(int n);
参数 n | 返回值 | 说 明 |
0 | 0:运行成功;非0:没有运行垃圾收集器。 | 若垃圾收集器标志为4,不运行垃圾收集器,否则运行Lu垃圾收集器。默认是可以运行垃圾收集器的。只能在单线程中使用该功能(不允许两个及两个以上的线程同时使用该功能,也不能与其他Lu函数一起使用)。 |
1 | 0:运行成功;非0:没有运行垃圾收集器。 | 若垃圾收集器标志为5,运行Lu垃圾收集器,否则不运行垃圾收集器。运行完垃圾收集器后,垃圾收集器标志设为0;若没有运行垃圾收集器,垃圾收集器标志的值保持不变。只能在单线程中使用该功能(不允许两个及两个以上的线程同时使用该功能,也不能与其他Lu函数一起使用)。 |
2 | 当前垃圾收集器标志的值:0、4、5 | 测试垃圾收集器标志的值。可以在任意线程中使用该功能。 |
3 | 以前垃圾收集器标志的值 | 设置垃圾收集器标志为默认值0。通常该功能由主程序使用。可以在任意线程中使用该功能。 |
4 | 以前垃圾收集器标志的值 | 设置垃圾收集器标志为4。通常该功能由主程序使用。可以在任意线程中使用该功能。 |
5 | 以前垃圾收集器标志的值 | 设置垃圾收集器标志为5。通常该功能由主程序使用。可以在任意线程中使用该功能。 |
其他值 | 0 | 什么也不做。 |
说明:垃圾收集器内部有一个标志,默认值为0,可以重新设置该标志的值,该标志将影响垃圾收集器的行为。Lu重新初始化时将自动设置垃圾收集器标志为0。
Lu使用标记清除算法进行垃圾回收。只有指针键才能被垃圾收集器回收。
Lu系统中的静态变量、模块变量、全局变量以及正在运行的表达式的数据区(自变量、动态变量及数据堆栈)中存在的动态对象是有效的,否则视为垃圾对象,会被垃圾收集器所回收。
一个对象是否包含子对象,可参考InsertKey函数的参数SignKey的说明。所有用函数SignKey标记过的对象都是有效(可达)对象,不会被垃圾收集器所回收。
注意:任意对象可被delete之类的函数手动销毁,包括可达对象。
3.30 从缓冲池中获取一个对象:GetBufObj
void * _stdcall GetBufObj(luKEY Type,char *pStr);
Type:
对象类型。
pStr:
在64位平台上,pStr是8字节长的字符串缓冲区,用于接收键的名称。
返回值:NULL表示失败,
否则返回一个对象指针。如果不打算使用该对象,须使用函数DeleteKey删除它。
3.31 与Lu交换信息:ExMsgWithLu
void * _stdcall ExMsgWithLu(int Type,void *hMsg,void *&Msg1,void *&Msg2); //C++定义
void * _stdcall ExMsgWithLu(int Type,void *hMsg,void **Msg1,void **Msg2); //C定义
Type:输入参数,信息交换的类型。
hMsg:输入参数,信息句柄,其意义取决于Type。
Msg1:该参数必须为变量,不能为常量。输入值及返回值取决于Type。
Msg2:该参数必须为变量,不能为常量。输入值及返回值取决于Type。
返回值:返回值的意义取决于Type。
说明:与Lu进行信息交换,向Lu传送信息或者从Lu获得信息。信息的类型及意义如下表所示:
Type | hMsg | Msg1 | Msg2 | 返回值 | 说明 |
1 | 从二级函数获得的表达式句柄 |
返回指向表达式模块号的指针 |
返回表达式句柄 | 指向表达式名称的字符串指针 | 得到表达式名称、模块号、句柄 |
2 | 编译表达式得到的表达式句柄 |
返回指向表达式模块号的指针 |
无意义 | 指向表达式名称的字符串指针 | 得到表达式名称、模块号 |
3 | luVOID类型指针,模块号 | luVOID类型指针,返回模块中的表达式数目 | 无意义 | 无意义 | 得到模块中的表达式数目 |
4 | 编译表达式得到的表达式句柄 |
无意义 |
无意义 | 将返回值取整数,表示表达式的状态。 | 返回值表示表达式状态:0:普通表达式;1:普通表达式正在运行;2:普通表达式递归运行;5:协程;6:协程正在运行;7:协程挂起;8:协程终止。 |
5 | 无意义 | 无意义 | 无意义 | 无意义 | 清空缓冲池 |
6 | luIFOR类型指针,设置对象总数 | luIFOR类型指针,返回以前设置的对象总数 | 无意义 | 无意义 | 设置对象(指针键)总数为多少时自动启动垃圾收集器GC。当*hMsg小于等于0时不会自动启动垃圾收集器。 |
7 | 函数指针:void (_stdcall *GcEndCallMe)(void); | 非0表示设置函数指针,0表示取消设置 | 无意义 | 非0表示操作成功。 | 设置一个函数GcEndCallMe,该函数将在垃圾收集器的最后调用。 |
8 | 编译表达式得到的表达式句柄 | 无意义 | 无意义 | 非0表示操作成功。 | 中止正处于挂起状态的协程。协程中止后,可以再次从头开始执行。 |
9 | 编译表达式得到的表达式句柄 | 无意义 | 无意义 | 非0表示操作成功。 | 终止处于正常等待运行状态或者挂起状态的协程。协程终止后将不能执行,执行时总返回nil。 |
10 | 编译表达式得到的表达式句柄 | 无意义 | 无意义 | 非0表示操作成功。 | 重启一个终止的协程,重启后可以运行。 |
11 | 编译表达式得到的表达式句柄,该句柄必须是GetRunErr函数的最后一个参数 | 返回出现运行错误时源代码表达式中的当前位置,当前位置通常有一个或多个字符,其末位置由参数Msg2返回 | 返回出现运行错误时源代码表达式中的末位置 | 无意义 | 得到出现运行错误时在源代码中的位置,该位置为当前位置,故错误是当前位置的前一个运算(在该位置附近,有可能在前面,也有可能在后面)引起的。该函数必须在GetRunErr函数后发现有运行错误时使用。若无运行错误,该函数的返回值无效。 |
3.32 由字符串获得一个唯一的整数:StrToUniInt
luVOID _stdcall StrToUniInt(char *Str,luINT StrLen);
Str:字符串。区分大小写,可包含任意字符,包括NULL('\0')。
StrLen:字符串长度。StrLen>0。
成功时返回非0值,返回0表示操作失败。
函数StrToUniInt和UniIntToStr的参数是相对应的。
3.33 由一个唯一的整数获得字符串:UniIntToStr
char * _stdcall UniIntToStr(luVOID k,luINT &len); //C++定义
char * _stdcall UniIntToStr(luVOID k,luINT *len); //C定义
k:
整数。
len:返回字符串长度。
区分大小写,可包含任意字符,包括NULL('\0')。
成功时返回字符串指针,返回NULL表示操作失败。
函数StrToUniInt和UniIntToStr的参数是相对应的。
二级函数的一般形式是:
LuData _stdcall Fun2Name(luINT n,LuData *d,void *hFor);
相应地,在表达式中二级函数的使用形式是:
Fun2Name(X1,X2,... ...,Xn)
数组d中依次存放各个自变量X1,X2,... ...,Xn;整数n指出自变量的个数,其大小为自变量的个数减1。
hFor是由Lu传送过来的调用该二级函数的表达式的句柄,该句柄与编译表达式所获得的句柄不同。由该句柄可以获得更多的信息。
以下是二级函数设计中可能用到的Lu输出函数及注意事项:
1、必须使用_stdcall调用协议
。
2、函数参数如果包含指针,约定指针保存在参数相关数据的前4个字节中。
3、用TestRunErr()测试是否有运行错误。由于Lu允许另一个线程修改运行时的出错状态,以便退出Lu运行,因此在二级函数设计中,在可能导致无限循环的循环体内,要使用TestRunErr()函数检测运行错误,如果检测到任何错误,就退出该二级函数。
4、在设计二级函数时,通常要用SetRunErr(...)向Lu报告运行错误。
5、在二级函数中用到的Lu动态对象都需要验证其有效性。例如字符串对象用函数GetStr验证;表达式对象用函数GetFor验证;系统产生的大多数对象或用户定义对象用函数SearchKey验证等等。
6、如果在二级函数中调用了函数LuCal,则以后使用的Lu动态对象仍需要验证其有效性,因为在使用LuCal计算过程中,有可能销毁动态对象。
7、如果二级函数的返回值是一个动态对象,在返回前必须用函数FunReObj通知Lu;如果用函数参数Para返回一些动态对象,必须用函数FunSaveObj通知Lu。
8、Lu不鼓励在二级函数中抛出异常,二级函数中产生的异常最好在本函数内检测,然后转换成Lu运行错误。如果在二级函数中抛出了异常,通常主调程序需要重新初始化Lu。实际上,如果进行混合语言编程,二级函数中抛出异常可能导致不可预知的错误。
1、用InitLu()初始化Lu;
2、用LuCom(...)编译表达式;
3、用LuCal(...)计算表达式;
4、用FreeLu()释放Lu。
尽管Lu表达式的用法可以非常灵活,但以下两种用法可能是最基本的。
6.1 动态使用字符串表达式
程序在运行过程中,动态地使用字符串表达式并进行计算。例如根据需要计算字符串数学表达式的值,或者执行一定的命令等。
在这种方法中,对于一种特定的运算,仅在需要的时候使用Lu,而其他的代码要用C/C++或FORTRAN等高级语言来写。
用这种方法得到的数值计算程序,其执行速度约为C/C++或FORTRAN执行速度的50%左右。但程序一经生成,即可脱离高级语言编译器独立运行,像使用Matlab和Mathematics一样方便。
6.2 按次序依次执行表达式进行运算
这是用Lu进行编程的一种方式。程序文件中的表达式用分隔符(通常用分号“;”)分隔,按某种次序依次编译各个表达式,编译成功后,按编译次序依次执行各个表达式。
无论哪种使用方式,在编译表达式前后和计算表达式前后都要检查Lu运行错误。
Lu始终记着编译过的表达式,这使得同名的表达式将不能再被编译,除非你释放了该表达式所占据的空间。可以用DeleteFor(...)函数释放一个表达式所占据的空间。如果使用初始化函数InitLu(),将连同注册的二级函数、常量、申请的数组、注册的所有自定义数据等一并删除。
7 用Lu保存和检索数据 [返回页首]
Lu用键树保存和检索数据信息,具有存储效率高、查询速度快的优点。用户可将自定义的数据信息保存到该键树中,并与Lu融为一体。用户可通过InsertKey(...)、SearchKey(...)、DeleteKey(...)等函数往键树中插入、查询和删除数据信息。
版权所有© Lu程序设计
2011-2021,保留所有权利
E-mail: forcal@sina.com
QQ:630715621
最近更新:
2021年06月20日