====================================================================

			Lu接口库：MLu32.dll

====================================================================
#MLu使用说明{
[MLu32.dll] MLu使用说明

    MLu是程序和Lu核心库之间的一个接口库，MLu会自动加载Lu核心库和动态加载多个Lu扩展库，简化了Lu系统的使用；MLu可对Lu源程序进行模块化编译，能够编译运行具有固定格式的源程序（字符串表达式），源程序中可以使用C++风格的注释。此外，MLu还提供了多个线程互斥地访问Lu资源的功能；提供了Lu运行监视器，以退出耗时较长的程序；提供在脚本中动态编译函数的功能；提供错误处理函数，实现错误（异常）处理的恢复模型等等。
}#
--------------------------------------------------------------------
#MLu模块源文件{
[MLu32.dll] MLu模块源文件

1 源程序的一般格式

    在MLu源文件中，可以有多个Lu表达式，表达式之间用分号“;”隔开。两个连续的分号“;;”表示前面的表达式在执行时不会输出结果。
    若表达式以mvar:开头，表示自该表达式开始，后面的表达式允许使用未定义的模块变量；若表达式以unmvar:开头，表示取消这种设置。mvar:和unmvar:仅在当前模块起作用。
    在源文件中可以进行注释，注释方法与C++语言相似，即：每行两个//后的内容为注释内容；在一行内或者多行之间/*...*/之间的内容为注释内容。注释不是源程序的有效部分，但可以使程序更易读。

    举例如下：

    //这是一个例子！灰色部分为注释，不会被执行；
    2+3;            //整数表达式
    2.2+3.3;        //实数表达式
    sin(2.2+3.3i);  //复数表达式
    2+3;/* 从这里开始，连续几行注释：
    333+222;
    . . . . . . ;
    555-222;
    */ sin(2.5);
    exp(2.);

2 程序的执行

    由于表达式有些带有参数，有些不带参数，MLu在进行处理时，对于有参数的表达式只进行编译，不进行计算。
    MLu只顺序执行不带参数的表达式。如果该表达式以两个连续的分号“;;”结尾，则该表达式只执行，不输出计算结果。
    但是，如果表达式以~~~开头，则无论是有参表达式还是无参表达式，都是只编译，不执行，格式如下：

    ~~~ 2+3;            //整数无参表达式，只编译，不执行
    ~~~ f1()=2+3;       //整数无参表达式，只编译，不执行
    ~~~ 2.0+3;          //实数无参表达式，只编译，不执行
    ~~~ f2()=2+3.0;     //实数无参表达式，只编译，不执行
    ~~~ 2+3i;           //复数无参表达式，只编译，不执行
    ~~~ f3()=2+3i;      //复数无参表达式，只编译，不执行

    无参表达式f1、f2和f3可以在其他可执行的表达式中被调用。

    另外，如果无参表达式以感叹号!!!开头，则编译后立即执行，且以后执行模块时不再自动执行。格式如下：

    !!! 2+3;            //整数无参表达式，编译成功，立即执行，以后不再自动执行
    !!! f1()=2+3;       //整数无参表达式，编译成功，立即执行，以后不再自动执行
    !!! 2.0+3;          //实数无参表达式，编译成功，立即执行，以后不再自动执行
    !!! f2()=2+3.0;     //实数无参表达式，编译成功，立即执行，以后不再自动执行
    !!! 2+3i;           //复数无参表达式，编译成功，立即执行，以后不再自动执行
    !!! f3()=2+3i;      //复数无参表达式，编译成功，立即执行，以后不再自动执行

    无参表达式f1、f2和f3可以在其他可执行的表达式中被调用。

3 源程序中的模块

    编译时，将源程序中的表达式编译为一个或多个模块，MLu会对每一个模块进行加锁。编译时首先设置起始模块，也称主模块（并非Lu的0#模块，恰恰相反，MLu不会将任何一个表达式编译为0#模块，定义主模块是为了与源程序中的其他模块相区别），以后每当遇到#MODULE#，开始编译为一个新的模块，称为子模块，而每当遇到#END#，回到主模块的编译。即#MODULE#和#END#之间的表达式定义为一个子模块，子模块之间不能嵌套定义。注意#MODULE#和#END#必须位于表达式的开头。

    在模块中，以:::开头的表达式被编译为公有表达式（全局表达式或全局函数），能被其他模块访问到，其余的表达式均被编译为私有表达式（私有函数），其他模块无法访问。所有模块的模块号由MLu或程序员指定，不会重复，也不会被编译为0#模块。在源程序的任何地方，可用指令#USE#调用另一个模块。

    模块源文件的格式如下：

      //单行注释：模块名：myModule
      /*
        多行注释：在同一模块源文件中的所有表达式属于同一个模块；
        多行注释：以~开头的表达式可被其他模块的表达式所访问；
        多行注释：不以~开头的表达式只能被该模块的表达式所访问。
      */
      #MODULE#               //定义一个子模块；
        a(x)=10+x;           //私有函数，只能被该模块的表达式所访问；
        !!!b()=100+100i;     //私有函数，只能被该模块的表达式所访问，该表达式是在编译时执行的；
        :::c(x)=x-5;         //全局函数，任意模块包括本模块均可访问；
        :::f(x)=a(x)+b();    //全局函数，任意模块包括本模块均可访问；
        :::g(x)=a(x)+c(x);   //全局函数，任意模块包括本模块均可访问；
        #USE# Module1;       //使用模块Module1；
        ff(5)+gg(6);         //函数ff()和gg()在模块Module1中定义；
      #END#                  //子模块定义结束，可缺省；
      #MODULE#               //定义一个子模块；
        a(x)=10-x;           //私有函数，只能被该模块的表达式所访问；
        :::ff(x)=a(x);       //全局函数，任意模块包括本模块均可访问；
      #END#                  //子模块定义结束，不可缺省；
      f(1);                  //主模块中的表达式。
      ff(1);                 //主模块中的表达式。

    在其他模块中使用该模块的格式如下：

      #USE# myModule;        //关键字USE必须为大写，myModule是模块名称；
      f(2)+g(3);             //调用myModule模块中定义的函数；

4 编译指令的位置和次序

    在MLu中使用的 #MODULE#、#END#、#USE#、mvar:、unmvar:、:::、!!!、~~~ 等称为编译指令，用以确定一个表达式所在模块、是否私有函数等属性。这些编译指令必须位于表达式的开头，有些指令能同时使用，有些指令不能同时使用，并且在使用时有一定的次序，按先后顺序依次为：

    1）编译指令#MODULE#、#END#、#USE#、mvar:和unmvar:之间没有先后顺序，可混合使用，但这些指令必须在表达式的最前面，一般单独成行。注意mvar:和unmvar:只在本模块内有效。

    2）编译指令!!!、~~~不能混合使用，只能使用其中的一个。~~~表示该表达式只编译，不执行；!!!表示该表达式编译后立即执行，但以后执行模块时不再自动执行。

    3）:::表示该表达式是一个全局表达式，否则是私有表达式。

    如果表达式前没有使用任何一个编译指令，则按缺省表达式的类型编译为私有表达式，若该表达式是无参表达式，则执行模块时将自动执行。
}#
--------------------------------------------------------------------
#mluver{
[MLu32.dll] mluver()：MLu版本信息。
}#
--------------------------------------------------------------------
#err{
[MLu32.dll] err()：检测输出并清除Lu运行错误。

    检测到错误时，该函数返回错误类型代码。错误类型代码如下：

    0：没有运行错误!
    1：表达式运行错误!
    2：父表达式（基表达式）被删除!
    3：二级函数被删除!
    其他非0值：其他运行错误!
}#
--------------------------------------------------------------------
#geterr{
[MLu32.dll] geterr(FunName,&FunCode,&ForHandle)：获得Lu运行错误。

    FunName：Lu字符串。返回出错函数名；若字符串长度太小，仅返回部分函数名；若不是一个字符串，什么也不做。当返回值等于2或3时，返回空字符串。
    FunCode：当返回值等于1时，返回函数错误代码。当返回值等于2或3时，返回0。
    ForHandle：返回出错表达式的句柄，该句柄即编译表达式时获得的句柄。若ForHandle=0，则运行出错的表达式已被销毁。

    返回值：运行错误的类型。返回值=0：没有运行错误；返回值=1：表达式运行错误；返回值=2：父表达式被删除，父表达式即该表达式中所调用的表达式，也称基表达式；返回值=3：该表达式中所调用的二级函数被删除；返回值=其它值：其它类型运行错误。

    例子：f(:a,b,c,d)= seterr[66,"aaa",23], a=geterr[b="\[10]",&c,&d], o[b];
}#
--------------------------------------------------------------------
#seterr{
[MLu32.dll] seterr(ErrType,FunName,FunCode)：设置Lu运行错误。

    ErrType：设置运行错误的类型。ErrType=0：没有错误；ErrType=1：表达式运行错误；不要设置ErrType=2或ErrType=3，这两个值由系统自动设置；ErrType=其它值：其它类型运行错误。
    FunName：设置出错的函数名，要求传递一个Lu字符串（长度小于255）。
    FunCode：设置函数错误代码。

    返回值：false：非法的Lu字符串地址、ErrType=2或ErrType=3；true：成功返回。

    例子：f(:a,b,c,d)= seterr[66,"aaa",23], a=geterr[b="\[10]",&c,&d], o[b];
}#
--------------------------------------------------------------------
#clearerr{
[MLu32.dll] clearerr()：清除Lu运行错误。
}#
--------------------------------------------------------------------
#stop{
[MLu32.dll] stop[]：停止函数。

    该函数并不立即停止执行程序，只是在执行完本表达式后停止程序的执行。
    若要立即停止程序的执行，需要在stop[]函数后紧跟一个从表达式立即返回的函数return[]。
    例如：

      {stop[],o["停止程序！"]}; //输出了字符串之后，停止程序执行；
      {stop[].return[],o["本字符串不会输出！"]}; //立即停止程序执行；
}#
--------------------------------------------------------------------
#SetTalk{
[MLu32.dll] SetTalk[bool]：输出控制函数。

    缺省情况下，MLu的ExeModule()函数每计算完一个表达式，就输出该表达式的值。SetTalk[]函数用于设置是否输出表达式的值。当bool为true时，输出表达式的值；当bool为false时，不输出表达式的值，直到再一次遇到SetTalk[true]。注意当bool为false时，并不关闭其他函数的输出。
}#
--------------------------------------------------------------------
#ComModule{
[MLu32.dll] ComModule(Str,&nModule,&hModule,&err1,&err2)：编译源程序为一个模块。

    Str：源程序字符串；
    nModule：当nModule为0时，所有模块的模块号自动指定（MLu初始化时，设置最初的起始模块号为1，以后不断递增，但不一定连续，因为每次执行该函数，不管是否执行成功，模块号均加1），不会重复，也不会被编译为0#模块。任何时候，可用nModule传送给MLu一个起始模块号（必须大于0），以后的模块号将在该模块号的基础上递增，从而改变模块号序列。若要自己指定模块号，则每次编译源程序前，均指定一个起始模块号。MLu编译的多个模块序号是递增的，但不一定连续，因为如果MLu加锁一个模块不成功，就会继续加锁下一个模块号。函数返回时，nModule返回多个模块的最小模块号。一般返回主模块号，如果主模块中没有表达式，就返回有表达式的最小子模块号。
    hModule：返回模块的句柄。当该模块不用时，就用delete(hModule)立即销毁该模块，否则将导致某些全局函数无法编译。
    err1和err2：返回编译出错位置，该值是否有意义取决于函数的返回值（返回值为-1、0、1、3、-2时无意义）。

    该函数返回值的意义参考MLu的输出函数ComModule的说明。

    例子：

mvar:
main(:a)=
  t0=clock(), s=0, i=0,
  while{++i<=10000,
    //动态编译模块，该模块有2个函数，f为私有函数，ff为全局函数
    ComModule["f(x)=x+1; :::ff(x,y)=f(x)+y;",0,&me,0,0],
    a=HFor("ff"), //获得函数ff的句柄
    s=s+a[1,1],   //动态调用函数ff
    delete[me]    //立即销毁模块
  },
  s;
[clock()-t0]/1000.;
}#
--------------------------------------------------------------------
