Lu32.DLL V1.0 编程指南(测试版)

 

欢迎访问Lu程序设计

Lu32.DLL V1.0 编程指南(测试版)

目  录

1概述
2 基础知识
3 输出函数
4 简单计算
5Lu二级函数设计
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

1 概述[返回页首]

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

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

    Lu是一个可对字符串表达式进行动态编译和运行的动态链接库(dll),是一种易于扩展的轻量级嵌入式脚本,提供自动内存管理,也可以手动管理内存。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

    本文将用到以下脚本函数:

    (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和扩展数据类型VTypeBType决定了实际的数据结构;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
9 nan/NaN
10 self
9 luStaData_string 由系统解释 由系统解释 由系统解释 "..." GetStr 静态字符串
10 luStaData_intarray 由系统解释 由系统解释 由系统解释 intss("...") GetArray 静态64位一维整数数组
11 luStaData_realarray 由系统解释 由系统解释 由系统解释 realss("...") GetArray 静态64位一维实数数组
系统定义动态类型
-251 luDynData_class 前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       用户可查询和删除的私有健最大值 键的类型由系统定义,但用户可以使用。键的类型均被锁定。都是动态数据类型,即系统定义的动态数据类型。
-251 luDynData_class       类,未启用
-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。可加锁。

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);

    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

    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 *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 *Para,LuData *pLD);

    hFor:从二级函数获得的表达式句柄。不能使用编译表达式得到的表达式句柄。
   
Para:二级函数的第二个参数。首次调用时Para设为NULL。
   
pLD:要保存的动态对象指针。Para为NULL时忽略该参数。

    二级函数设计中,若要保存若干动态对象,需用该函数通知Lu。首次调用时参数Para设为NULL,进行初始化,以后每次调用保存一个Lu动态对象。

3.14 判断一个表达式是否有效:IsFor

    int _stdcall IsFor(wchar_t *ForName,luVOID nModule,void *hFor,void *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_stdcallGetFor(wchar_t *ForName,int ForType,void *hFor,luVOID &nModule,void *&myFor,void *&Para,luINT &PareNum);

    用法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 _stdcallDeleteFor(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);

    用法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);

    用法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

    int _stdcallInsertKey(char *KeyStr,luINT ByteNum,luKEY KeyType,void *KeyValue,void (_stdcall *DelMe)(void *),void (_stdcall *SignKey)(void *),int ReNew,void *&NowKey);

    KeyStr:键的名称。区分大小写,可包含任意字符,包括NULL('\0')。
   
ByteNum:键的长度。ByteNum<0表示指针键(32位平台上,指针键有4个字节),键长自动取4;ByteNum>0表示非指针键。
   
KeyType:用户自定义键的类型。KeyType>=luPubKey_User(公有键、普通键)或者KeyType<=luPriKey_User(私有键)约定用luPubKey_UserluPriKey_User保存一些特殊的数据,作为多个函数、线程、动态库等之间联系的通道。
   
KeyValue:键值。由用户定义,标识某种数据类型。
    DelMe:删除该键值的函数指针。该函数由用户定义,但由Lu调用,即:DelMe(KeyValue); 可将KeyValue删除。如果不需要Lu删除该键值,该参数可设为NULL
   
SignKey:标记子键值(对象)的函数指针,所有标记过的子对象都是有效(可达)对象,不会被垃圾收集器GC所回收。该函数由用户定义,但由Lu调用,即:SignKey(KeyValue); KeyValue若包含另外的对象,则调用该函数可对其他对象做标记,应在函数SignKey中对每一个对象调用函数SignGoodObj做标记。若KeyValue不包含子对象,该参数设为NULL。若键长不等于4(只有ByteNum=0ByteNum=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
      7:参数
SignKey非法。

    删除函数InsertKey插入的键:(1)使用函数DeleteKey;(2)用函数LockKey解锁键时将删除所有指定类型的键值;(3)系统自动销毁临时对象(临时对象都是指针键);(4)被垃圾收集器GC自动回收(仅回收用指针键);(5)使用deldelete函数销毁;(6)重新初始化Lu时由函数InitLu销毁所有键值;(7)释放Lu时由函数FreeLu销毁所有键值。

    说明:

    (1)只有指针键才能被垃圾收集器GC回收。

    (2)Lu内置一个计数器,自动记录指针键的数目,在用函数InsertKey插入一个指针键(插入非指针键不检查)时,若指针键总数超过设定值(默认值是10000,可通过函数ExMsgWithLu的6号功能修改此值,设定值小于等于0时不启动垃圾收集器),将自动调用垃圾收集器。

    当用函数InsertKey连续插入两个指针键时,前一个指针键可能被垃圾收集器销毁。故在二级函数设计中,每当用函数InsertKey插入一个指针键,就要调用函数FunSaveObjFunReObj通知Lu,此指针键是有效的。在主程序中要连续插入两个指针键时,可调用GC(4)暂时关闭垃圾收集器,或者使用函数ExMsgWithLu的6号功能暂时关闭垃圾收集器。

    (3)用函数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 *_stdcallSearchKey(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 *DeleteKey)(void *),int iBuf);

    KeyStr:键的名称。区分大小写,可包含任意字符,包括NULL('\0')。
   
ByteNum:键的长度。ByteNum>0
   
KeyType:用户自定义键的类型或者系统定义的特殊键的类型。当KeyType>=luPubKey_User(公有键、普通键)或者luPriKey_User<KeyType<=luPriKey_UserSearch(系统定义私有键)时,参数DeleteKey被忽略;当KeyType<=luPriKey_User(私有键)时,必须提供删除函数DeleteKey。可以删除的其他系统特殊键的类型如下(参数DeleteKey均被忽略):

      当KeyType=luKey_Const时,将删除由函数SetConst设置的常量;
      当
KeyType=luKey_Function时,将删除由函数SetFunction设置的二级函数。

   
DeleteKey:删除该键值的函数指针。该指针与插入键时用到的函数指针相同,否则不能删除键值。
    iBufiBuf!=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,表示解锁指定的键。
   
OpLockluOperator类型的函数指针,用于对象(用指针标识)的运算符重载,该参数不可为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~43 未定义        
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(void *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);

    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时不会自动启动垃圾收集器。

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

阅读更多
文章标签: dll Lu 脚本 动态库
个人分类: Lu[推荐]
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭