欢迎访问 Lu程序设计

Lu32.DLL V1.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

1 概述 [返回页首]

Lu来源于Forcal，可以说，没有Forcal就没有Lu，但学习Lu并不需要了解Forcal。

Lu是对Forcal的完善和发展，但与Forcal相比，Lu更简洁实用。Lu的运行效率，从单纯的数值计算和循环来说，是下降的，其他方面，特别在涉及动态对象的地方，Lu的效率会提高。Lu与Forcal本质的区别在于：Forcal以整数、实数、复数三种简单数据类型为基础，在描述复杂对象时，本身不带有数据类型信息，故是弱类型的；Lu以一种称为 LuData 的结构体作为基本数据类型，携带数据类型信息，故是强类型的。

Lu是一个可对字符串表达式进行动态编译和运行的动态链接库（dll），是一种易于扩展的轻量级嵌入式脚本，提供自动内存管理，也可以手动管理内存。Lu可用于连接各种语言编写的模块。Lu的优势在于简单易用和可扩展性强。

Lu 用Win32标准函数调用方式（stdcall调用协议）输出了动态库函数，可供C/C++、VB、delphi、FORTRAN等程序使用。

除了GetRunErr()、TestRunErr()和SetRunErr()三个函数及函数GC()的部分功能外，其余的函数只能在单线程中使用（不允许两个及两个以上的线程同时运行这些函数），在设计多线程应用程序时必须注意这一点。

Lu不抛出异常，如果在主调程序中检测到了异常，可能来自于外部模块向Lu注册的函数，此时，可尝试对Lu重新进行初始化。

2 基础知识 [返回页首]

以下是本文中使用的一些类型及常量定义（参考头文件Lu32.h）：

typedef __int64 luIFOR; //Lu表达式中的整数类型定义
typedef __int32 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 //常量标志

更多常量定义参考表1-1和表1-2。

本文将用到以下脚本函数（在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的前4个字节中
> luIFOR y; //存放数据
> luIFOR z; //存放数据
> luKEY VType; //扩展数据类型，决定重载函数
> luKEY BType; //基本数据类型，决定数据结构
> };

任何Lu数据在赋值时，必须提供基本数据类型 BType 和扩展数据类型 VType。BType 决定了实际的数据结构；VType 指出要调用哪一个重载函数。

在编写代码时，应注意能方便地移植到64位平台。为此约定：指针总是保存在数据x的前面的字节中，例如在32位平台上，指针应保存在数据x的前4个字节中。保存一个指针的代码如下：

LuData lud; //定义一个Lu基本数据
void *p; //定义一个指针

p=... ...; //指针赋值
*(luVOID *)&(lud.x)=(luVOID)p; //指针p保存在x的前面的字节中，无论32位平台还是64位平台均可正常编译

根据实际需要，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 | 前32位保存指针 |  |  | 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
method | method
9 | luStaData_string | 由系统解释 | 由系统解释 | 由系统解释 | "..." | GetStr | 无 | 静态字符串
10 | luStaData_intarray | 由系统解释 | 由系统解释 | 由系统解释 | intss("...") | GetArray | 无 | 静态64位一维整数数组
11 | luStaData_realarray | 由系统解释 | 由系统解释 | 由系统解释 | realss("...") | GetArray | 无 | 静态64位一维实数数组
12 | luStaData_struniint | 32位整数 |  |  | 编译符# |  |  | 由字符串生成的唯一的32位整数
13 | luStaData_pointer | 前32位保存指针 |  |  | 由其他指针转换而来 |  |  | 无类型指针
系统定义动态类型
-252 | luDynData_realarray | 前32位保存指针 |  |  | NewSysObj | SearchKey/GetArray | DeleteKey | 动态64位实数数组
-253 | luDynData_intarray | 前32位保存指针 |  |  | NewSysObj | SearchKey/GetArray | DeleteKey | 动态64位整数数组
-254 | luDynData_string | 前32位保存指针 |  |  | NewSysObj | SearchKey/GetStr | DeleteKey | 动态字符串
-255 | luDynData_lu | 前32位保存指针 |  |  | NewSysObj | SearchKey | DeleteKey | 动态Lu数据
用户定义动态类型 | < -256 | < luPriKey_User | 前32位保存指针 |  |  | InsertKey/GetBufObj | SearchKey | DeleteKey | 用户自定义的私有动态数据类型，均小于-256
> 256 | > luPubKey_User | 前32位保存指针 |  |  | 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及二级函数调用
> {
> ... ...
> }

然后将该函数用InsertKey("\0\0\0\0",4,luPubKey_User,LuMessage,NULL,NULL,1,v)注册到Lu。约定Lu及所有二级函数都使用该函数显示信息。任一线程均可根据需要设置该函数。查询该函数的方法为：

> luMessage pMessage=(luMessage)SearchKey("\0\0\0\0",4,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 输出函数 [返回页首]

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=其它值：其它类型运行错误。
FunName：返回出错函数名，或者运行错误说明。
FunCode：返回函数错误代码。
ForHandle：返回出错表达式的句柄，该句柄即编译表达式时获得的句柄。在使用ForHandle前，必须用函数 GetFor 验证该句柄是否有效。

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=其它值：其它类型运行错误。
FunName：设置出错的函数名，要求传递一个静态字符串，不可为NULL。
FunCode：设置函数错误代码。
HandleType：句柄类型。HandleType=0：从二级函数获得的表达式句柄；HandleType!=0：编译表达式时获得的句柄。
ForHandle：设置出错表达式的句柄。该参数可以为NULL。

在设计自定义的二级函数时，可以用该函数设置Lu运行错误。由于Lu只保存出现的第一个运行错误 或警告，故Lu有可能忽略本次设置。若先设置了警告，后设置错误，则警告信息将被丢弃。

注意：当设置ErrType=0时，表示清除以前的错误设置。只有没有出现过运行错误，或者已经对以前的运行错误进行了正确的处理，才可以设置ErrType=0。

Lu允许另一个线程修改运行时的出错状态，以便退出Lu运行，因此可在其它线程中使用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：源代码太长或字符串太多！

通常在编译前用SetRunErr()函数或GetRunErr()函数使Lu处于无错状态，编译后用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调用协议（Win32标准函数调用方式）。
3、函数参数如果包含指针，约定指针保存在参数相关数据的前4个字节中。
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 *Const Name,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 表示指针键（32位平台上，指针键有4个字节 ；指针键类型 KeyType>=luPoiKey_User），键长自动取4；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。 若键长不等于4（只有 ByteNum<0 或 ByteNum=4 时键长为4），该参数必须为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 n KeyStr Max,int (_stdcall *GetKeyTypeValue)(char *,int ,void *));

KeyType：指定键的类型。KeyType>=luPubKey_User（公有键、普通键） 或者 KeyType<=luPriKey_UserSearch（私有键）。
KeyStr：存放键的名称的缓冲区。如果缓冲区不够用，仅存放键的名称的前面部分字符。
n KeyStr Max：存放键的名称的缓冲区的最大长度（n KeyStrMax>=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： 在32位平台上，pStr 是4字节长的字符串缓冲区，用于接收键的名称。
返回值：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表示表达式正在运行 | 判断该表达式是否正在运行
5 | 无意义 | 无意义 | 无意义 | 无意义 | 清空缓冲池
6 | luIFOR类型指针，设置对象总数 | luIFOR类型指针，返回以前设置的对象总数 | 无意义 | 无意义 | 设置对象（指针键）总数为多少时自动启动垃圾收集器GC。当*hMsg小于等于0时不会自动启动垃圾收集器。
7 | 函数指针：void (_stdcall *GcEndCallMe)(void); | 非0表示设置函数指针，0表示取消设置 | 无意义 | 非0表示操作成功。 | 设置一个函数GcEndCallMe，该函数将在垃圾收集器的最后调用。

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 的参数是相对应的。

4 Lu二级函数设计 [返回页首]

二级函数的一般形式是：

LuData _stdcall Fun2Name(luINT n,LuData *d,void *hFor);

相应地，在表达式中二级函数的使用形式是：

Fun2Name(X1,X2,... ...,Xn)

数组d中依次存放各个自变量 X1,X2,... ...,Xn；整数n指出自变量的个数，其大小为自变量的个数减1。

hFor 是由Lu传送过来的调用该二级函数的表达式的句柄，该句柄与编译表达式所获得的句柄不同。由该句柄可以获得更多的信息。

以下是二级函数设计中可能用到的Lu输出函数及注意事项：

1、必须使用_stdcall调用协议（Win32标准函数调用方式）。
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。实际上，如果进行混合语言编程，二级函数中抛出异常可能导致不可预知的错误。

5 简单计算 [返回页首]

1、用InitLu()初始化Lu；
2、用 LuCom(...)编译表达式；
3、用 LuCal(...)计算表达式；
4、用FreeLu()释放Lu。

6 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-2013，保留所有权利
E-mail: forcal@sina.com QQ：630715621
最近更新: 2013年09月10日