欢迎访问 Forcal程序设计

MForcal32.dll V5.0 使用说明

目  录

MForcal说明 基于MForcal的Forcal扩展动态库
1 MForcal的输出函数
2 如何加载使用
MForcal
3 二级函数
4 模块源文件
5 例子

6 在软件中加入Forcal & MForcal支持
1 OpenFcGl:基于OpenGL的图形库
2 FcWin:基于Windows的窗口库

MForcal对Forcal源程序进行模块化编译,能够编译运行具有固定格式的源程序(字符串表达式),源程序中可以使用C++风格的注释。

MForcal使得Forcal能更好地用在没有指针的编程语言中,例如 VB 。


1 MForcal的输出函数  [目录]

    MForcal是一个标准的Forcal扩展动态库,共输出了六个函数,其中一个即标准的Forcal扩展动态库输出函数FcDll32W(...)。在加载MForcal并初始化之后,也可以用SearchKey("UseForcal",FC_PrivateKey_User);SearchKey("ComModule",FC_PrivateKey_User);SearchKey("ExeModule",FC_PrivateKey_User);SearchKey("DeleteModule",FC_PrivateKey_User);、SearchKey("ExeFcDll32W",FC_PrivateKey_User);获得另外五个函数的句柄。这五个函数的功能和用法如下:

1.1 申请进入或退出Forcal工作区int _stdcall UseForcal(int iUse);

    iUse=1时,表示要申请使用Forcal,若函数返回值 UseForcal=0:申请成功;UseForcal=1:申请不成功,某线程正在使用Forcal,稍后再进行申请;UseForcal=-1:申请不成功,表示应用程序要释放Forcal,因此要做好退出前的准备工作。
   
iUse=2时,表示要申请使用Forcal,如果其他线程正在使用Forcal,函数不返回,进行等待,直至申请成功。若函数返回值 UseForcal=0:申请成功;UseForcal=1:申请不成功,线程本身正在使用Forcal,不能重复进行申请;UseForcal=-1:申请不成功,表示应用程序要释放Forcal,因此要做好退出前的准备工作。
   
iUse=0时,表示要归还Forcal的使用权,函数返回值无意义。 
    iUse=3时,设置安全标志,表示Forcal运行正常,函数返回值无意义。 一般在二级函数中设置该标志,当该函数在较长时间内运行时,可用此标志通知Forcal,此时的运行是正常的,没有陷入无限循环等情况。
    iUse=4时,取消安全标志,表示Forcal运行处于不可控制阶段(有陷入无限循环的可能),函数返回值无意义。 
    iUse=5时,查询安全标志,UseForcal=0:运行正常;UseForcal=1:运行情况无法预测(有陷入无限循环的可能),这是Forcal运行的一般情况。 
   
注意:Forcal是极为重要而有限的资源,用完后要及时归还。UseForcal(1)(或者UseForcal(2))和UseForcal(0)必须成对使用,注意不能在二级函数中使用该功能,因为二级函数本身就是在Forcal工作区中运行的。UseForcal(3)UseForcal(4)也要成对使用,且一般在二级函数中使用。
    可以在主调程序或Forcal扩展动态库中使用该函数。在多线程程序中,必须通过申请Forcal工作区的方法来使用Forcal,因为在任何时刻,只允许一个线程使用Forcal(GetRunErr()、TestRunErr()和SetRunErr()三个函数除外)。

1.2 编译源程序:int _stdcall ComModule(wchar_t *FcStr,fcVOID &nModule,void *&hModule,fcINT &err1,fcINT &err2);

    编译时,将源程序中的表达式编译为一个或多个模块,MForcal会对每一个模块进行加锁。编译时首先设置起始模块,也称主模块(并非Forcal的0#模块,恰恰相反,MForcal不会将任何一个表达式编译为0#模块,定义主模块是为了与源程序中的其他模块相区别),以后每当遇到#MODULE#,开始编译为一个新的模块 ,称为子模块,而每当遇到#END#,回到主模块的编译。即#MODULE##END#之间的表达式定义为一个子模块,子模块之间不能嵌套定义。注意#MODULE##END#必须位于表达式的开头。 在模块中,以~开头的表达式被编译为正模块号表达式(公有表达式或公有函数,也称为全局函数),能被其他模块访问到,其余的表达式均被编译为负模块号表达式(私有表达式或私有函数),其他模块无法访问。 当nModule为0时,所有模块的模块号由该函数自动指定(MForcal初始化时,设置最初的起始模块号为1,以后不断递增,但不一定连续,因为每次执行该函数,不管是否执行成功,模块号均加1),不会重复,也不会被编译为0#模块。任何时候,可用nModule传送给MForcal一个起始模块号(必须大于0),以后的模块号将在该模块号的基础上递增,从而改变模块号序列。若要自己指定模块号,则每次编译源程序前,均指定一个起始模块号。MForcal编译的多个模块序号是递增的,但不一定连续,因为如果MForcal加锁一个模块不成功,就会继续加锁下一个模块号。在源程序的任何地方,可用指令#USE#调用另一个模块。
    FcStr:指向源程序字符串的指针;
    nModule:返回多个模块的最小模块号。一般返回主模块号,如果主模块中没有表达式,就返回有表达式的最小子模块号。
   
hModule:返回模块的句柄,用于执行该模块。
    err1和err2:返回编译出错位置,该值是否有意义取决于函数的返回值(返回值为-1、0、1、3、-2时无意义)。

    该函数返回值的意义如下:

    -7:递归调用指定的模块(由MfcLoadModule(...)函数判断,见本文第2部分:如何加载使用MForcal)
    -6:找不到指定的模块(由MfcLoadModule(...)函数判断,见本文第2部分:如何加载使用MForcal)
    -5:缺少模块名。
(由程序员处理,不返回给用户)
    -4:注释符号/* ... */不成对。
    -3:未使用模块编译功能,不能编译指定的模块。
    -2:无法加锁模块。
(由程序员处理,修改nModule为较小的值可能修正此错误)
    -1:未用!
    0:没有错误,编译成功!
    1:内存分配失败!
    2:括号不成对!
    3:(等号后)没有表达式!
    4:复数表达式中不能使用i作为参数!
    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:
运算符重载函数oo有0个自变量或嵌套使用
    38:未用!
    39:源代码太长或字符串太多!

    模块源文件的格式如下(没有定义子模块,子模块的例子请参考这里):
      //单行注释:模块名:myModule
      /*
        多行注释:在同一模块源文件中的所有表达式属于同一个模块;
        多行注释:以~开头的表达式的模块号为正,可被其他模块的表达式所访问;
        多行注释:不以~开头的表达式的模块号为负,只能被该模块的表达式所访问。
      */
      i:a(x)=10+x;         //模块号为负,私有函数,只能被该模块的表达式所访问;
      c:b()=100+100i;     
//模块号为负,私有函数,只能被该模块的表达式所访问;
      ~_c(x)=x-5;         
//模块号为正,全局函数,任意模块包括本模块均可访问;
      ~_f(x)=a(x)+b();
     //模块号为正,全局函数,任意模块包括本模块均可访问;
      ~i:g(x)=a(x)+_c(x);
  //模块号为正,全局函数,任意模块包括本模块均可访问;
     
#USE# Module1;       //使用模块Module1;
      _ff(5)+_gg(6);       //函数_ff()和_gg()在模块Module1中定义;
    在其他模块中使用该模块的格式如下:
      #USE# myModule;      //关键字USE必须为大写,myModule是模块名称;
      _f(2)+g(3);          //调用myModule模块中定义的函数;

1.3 执行程序:void _stdcall ExeModule(void *hModule,void (_stdcall *outl)(fcIFOR ),void (_stdcall *outd)(double ),void (_stdcall *outc)(_complex ));

    hModule:编译源程序时得到的模块的句柄。
    outl(输出整数表达式的值)、outd(输出实数表达式的值)、outc(输出复数表达式的值):这三个回调函数在执行表达式时被调用,输出信息,这三个参数也可设为NULL。
    注意1:该函数只执行模块中的无参表达式。
    注意2:
当Forcal键树中有一个被锁定为字符串的键给出运行错误说明时,MForcal将试图找出出现运行错误的原因。请参考“2.2 MForcal使用说明”部分。

1.4 删除模块:void _stdcall DeleteModule(void *hModule);

    hModule:编译源程序时得到的模块的句柄。

1.5 执行Forcal动态库的输出函数FcDll32Wint _stdcall ExeFcDll32W(int (_stdcall *pFcDll32W)(HINSTANCE ,bool ,void *),HINSTANCE hFC,bool bInit,void *me);

    pFcDll32W:指向Forcal扩展动态库输出函数FcDll32W的指针,该指针通常在加载Forcal扩展动态库后通过GetProcAddress(hFcDll,"FcDll32W")函数获得。
    hFC:Forcal32W.dll的句柄。
    bInit=true:初始化动态库,bInit=false:释放动态库。
    me:指向任意数据的指针,可用于验证用户能否使用该库,为了方便验证,约定该指针指向一个wchar_t类型的字符串。

    说明:该函数通常用在没有指针的编程语言中,例如 VB 。


2 如何加载使用MForcal  [目录]

2.1 MForcal的加载及初始化

    MForcal是一个标准的Forcal扩展动态库,其加载和初始化的方法与其他的Forcal扩展动态库相同,但为了使其他的Forcal扩展动态库能够使用MForcal,MForcal应紧跟在Forcal之后加载。MForcal应在Forcal卸载之前卸载。

2.2 MForcal使用说明

    在主调程序中可以设置一个全局变量bool MFC_Quit=false;。然后将该变量的地址用InsertKey("MFC_Quit",8,FC_PrivateKey_User,&MFC_Quit,DelKey,v)传送给Forcal。当MFC_Quit=true时将退出MForcal。规定仅在主线程中设置和修改该变量,但该变量可被任一线程所访问。

    在主调程序或任一个Forcal扩展动态库中均可以设置一个函数int _stdcall MfcLoadModule(wchar_t *ModuleName);。然后将该函数的地址用InsertKey("MFC_LoadModule",14,FC_Key_User,MfcLoadModule,NULL,v)传送给Forcal,这样MForcal就可以编译模块(每当遇到#USE# myModule语句,就将myModule传送给该函数进行处理);若不传送该函数,MForcal就不能编译模块。该函数接受一个模块名,然后返回该模块的编译代码,代码的意义与函数ComModule(...)返回的编译代码意义相同(实际上,只有-6和-7两个代码须由该函数处理)。该函数必须能判断是否进行了模块的递归调用。任一线程均可设置该函数。

    在主调程序或任一个Forcal扩展动态库中均可以设置一个函数void _stdcall FcMessage(wchar_t *);。然后将该函数的地址用InsertKey("FcMessage",9,FC_Key_User,FcMessage,NULL,v)传送给Forcal。约定所有Forcal扩展动态库都使用该函数发送信息。任一线程均可根据需要设置该函数。

    在主调程序中可用Forcal的加锁函数LockKeyType锁定一个键的类型为字符串,然后将被锁定的键KeyTypeInsertKey("MFC_LockKeyErrStr",17,FC_PrivateKey_User,KeyType,DelKey,v)传送给Forcal,这样MForcal就可以给出更详细的运行错误说明。实际上,当出现运行错误时,MForcal查找与出错函数名相同的键,键的类型为KeyType,其键值为一个字符串,该字符串包含了出错原因。该字符串格式如下:

    #-2:...; #-1:...; #1:...; #2:错误2; #3:...; ... ...

    例如,当运行错误代码为2时,将输出“#2:错误2;”中的“错误2”。每一个错误描述以“#”开头,后面紧跟错误代码和一个冒号“:”,冒号“:”后为错误说明,错误说明以分号“;”结尾。

    请参考1.3 执行程序:void _stdcall ExeModule...”和“3.2 检测Forcal运行错误:err...”部分。


3 二级函数  [目录]

    该库中的函数均为实数函数。

3.1 MForcal版本信息:MForcalVer();

3.2 检测Forcal运行错误:err();

    检测到错误时,该函数返回错误类型代码(逻辑真),否则返回 0 (逻辑假)。错误类型代码如下:

    1:整数表达式运行错误!
    2:实数表达式运行错误!
    3:复数表达式运行错误!
    4:父表达式(基表达式)被删除!
    5:二级函数被删除!
    其他非0值:
其他运行错误!

    注意:当Forcal键树中有一个被锁定为字符串的键给出运行错误说明时,MForcal将试图找出出现运行错误的原因。请参考“2.2 MForcal使用说明”部分。

3.3 输出控制函数:SetTalk[bool];

    缺省情况下,MForcal的ExeModule()函数每计算完一个表达式,就输出该表达式的值。SetTalk[]函数用于设置是否输出表达式的值。当bool为真时,输出表达式的值;当bool为假时,不输出表达式的值,直到再一次遇到SetTalk[真]。注意当bool为假时,并不关闭其他函数的输出。

3.4 停止函数:stop[];

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

      {stop[],printff["停止程序!"]}; //输出了字符串之后,停止程序执行;
      {stop[],return[0],printff["本字符串不会输出!"]}; //立即停止程序执行;


4 模块源文件  [目录]

4.1 源程序的一般格式  [目录]

    在MForcal源文件中,可以有多个FORCAL表达式,表达式之间用分号“;”隔开。
    由于FORCAL数学表达式有三种,即整数表达式、实数表达式和复数表达式。为便于区分,MForcal将以i:开头的表达式作为整数表达式,以r:开头的表达式作为实数表达式,以c:开头的表达式作为复数表达式,缺省为实数表达式。 缺省的表达式类型可以重新设置:若表达式以integer:开头,表示自该表达式开始,后面的缺省表达式为整数表达式;若表达式以real:开头,表示自该表达式开始,后面的缺省表达式为实数表达式;若表达式以complex:开头,表示自该表达式开始,后面的缺省表达式为复数表达式。
    若表达式以mvar:开头,表示自该表达式开始,后面的表达式允许使用未定义的模块变量;若表达式以unmvar:开头,表示取消这种设置。mvar:和unmvar:仅在当前模块起作用。
    同时,在源文件中可以进行注释,注释方法与C++语言相似,即:每行两个//后的内容为注释内容;在一行内或者多行之间/*...*/之间的内容为注释内容。注释不是源程序的有效部分,但可以使程序更易读。

    举例如下:

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

    integer:          //设置缺省表达式为整数表达式;
    22+3;             //这是整数数表达式;

    可以用加载MForcal的任何一个程序验证以上代码。

4.2 程序的执行  [目录]

    由于表达式有些带有参数,有些不带参数,MForcal在进行处理时,对于有参数的表达式只进行编译,不进行计算。
    MForcal只顺序执行不带参数的表达式。
    但是,如果表达式以冒号“ : ”开头,则无论是有参表达式还是无参表达式,都是只编译,不执行,格式如下:

    i:: 2+3;            //整数无参表达式,只编译,不执行;
    i:: f1()=2+3;       //整数无参表达式,只编译,不执行;
    c:: 2+3i;           //复数无参表达式,只编译,不执行;
    c:: f2()=2+3i;      //复数无参表达式,只编译,不执行;
    : 2+3;              //实数无参表达式,只编译,不执行;
    : f3()=2+3;         //实数无参表达式,只编译,不执行。

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

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

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

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

4.3 源程序的完整格式  [目录]

    编译时,将源程序中的表达式编译为一个或多个模块,MForcal会对每一个模块进行加锁。编译时 首先设置起始模块,也称主模块(并非Forcal的0#模块,恰恰相反,MForcal不会将任何一个表达式编译为0#模块,定义主模块是为了与源程序中的其他模块相区别),以后每当遇到#MODULE#,开始编译为一个新的模块 ,称为子模块,而每当遇到#END#,回到主模块的编译。即#MODULE##END#之间的表达式定义为一个子模块,子模块之间不能嵌套定义。注意#MODULE##END#必须位于表达式的开头。 在模块中,以~开头的表达式被编译为公有表达式(全局表达式或全局函数),能被其他模块访问到,其余的表达式均被编译为私有表达式(私有函数),其他模块无法访问。所有模块的模块号由MForcal或程序员指定,不会重复,也不会被编译为0#模块。在源程序的任何地方,可用指令#USE#调用另一个模块。

    模块源文件的格式如下:

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

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

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

4.4 编译指令的位置和次序  [目录]

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

    1)编译指令#MODULE#、#END#、#USE#、integer:、real:、complex:、mvar:和unmvar:之间没有先后顺序,可混合使用,但这些指令必须在表达式的最前面,一般单独成行。

    2)~表示该表达式是一个全局表达式,否则是私有表达式。

    3)编译指令i:、r:、c:不能混合使用,只能使用其中的一个,强制指定表达式的类型。如果都没有用,表达式按缺省的类型进行编译。

    4)编译指令:、!不能混合使用,只能使用其中的一个。:表示该表达式只编译,不执行;!表示该表达式编译后立即执行,但以后执行模块时不再自动执行。

    如果表达式前没有使用任何一个编译指令,则按缺省表达式的类型编译为私有表达式,若该表达式是无参表达式,则执行模块时将自动执行。


5 例子  [目录]

例子1:简单的数值计算:

2+sin[2+3*sqrt(3)]*exp[5];    //实数表达式
c:sin[2+3i]-ln[i];   
         //复数表达式

例子2:变步长辛卜生一元积分:

f(x)=sin[x]+0.8;              //定义一元函数
SimpIntegrate(1,2,0.0001,"f");

例子3:二元函数图象[须加载动态库扩展FcData32W.dll和OpenFcGl32W.dll]

f(x,y)=(x^2-2*x)*exp[-(x^2)-y^2-x*y];
(::Rot)={Rot=0};              //设置一个全局变量
Draw(::Rot)=
{
  glClear[],
  glLoadIdentity[],
  glTranslated[0,0,-20],
  glRotated[Rot++,1,1,1],     
//使图象连续旋转
  glColor3d[0,1,0],
  fgPlot3d[HFor("f"),-3,3,-3,3]
};
gleDrawScene{HFor("Draw")};


版权所有© Forcal程序设计 2002-2010,保留所有权利
E-mail: forcal@sina.com  QQ:630715621
最近更新:
2010年09月27日