欢迎访问 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动态库的输出函数 FcDll32W：int _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# my Module; 语句，就将 my Module 传送给该函数进行处理）；若不传送该函数，MForcal就不能编译模块。该函数接受一个模块名，然后返回该模块的编译代码，代码的意义与函数 ComModule(...)返回的编译代码意义相同（实际上，只有-6和-7两个代码须由该函数处理）。该函数必须能判断是否进行了模块的递归调用。任一线程均可设置该函数。

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

在主调程序中可用Forcal的加锁函数 LockKeyType 锁定一个键的类型为字符串，然后将被锁定的键 KeyType 用InsertKey("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日