目 录
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# 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锁定一个键的类型为字符串,然后将被锁定的键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.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.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可以在其他可执行的表达式中被调用。
编译时,将源程序中的表达式编译为一个或多个模块,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)编译指令:、!不能混合使用,只能使用其中的一个。:表示该表达式只编译,不执行;!表示该表达式编译后立即执行,但以后执行模块时不再自动执行。
如果表达式前没有使用任何一个编译指令,则按缺省表达式的类型编译为私有表达式,若该表达式是无参表达式,则执行模块时将自动执行。
例子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日