📄 compile_hss.pas
字号:
unit Compile_Hss;
//////////////////////////////////////////////////////////////////////////
// //
// 数学函数动态编译器TCompile类 作者:侯思松 2002.4-2002.11 //
// (包括数学函数、布尔运算和定积分函数) //
// 有改进意见或发现任何错误请转告我,本人不胜感激。 //
// E-Mail:HouSisong@263.net //
// ( 转载时请保留本说明:) ) //
// //
//////////////////////////////////////////////////////////////////////////
///<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<///
/// <<功能简介>>: ///
/// TCompile可以在程序运行过程中动态完成数学函数表达式字符串的编译执行, ///
/// (可以带参数,布尔运算,定积分;动态生成机器码执行,不是解释执行)执行速度超快!!! ///
///>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>///
{
<<使用方法>>:
var
Compilation : TCompile; // 声明Compilation为数学函数动态编译器TCompile类的实例
str : string;
xValue : Extended;
begin
Compilation:=TCompile.Create; //创建类
try
str:='x+sin(y*PI/2)*3';
Compilation.SetText(str); //str为要 求值的数学表达式字符串
......
//如果有参数,可以获得参数地址,并赋值 (默认值为0)
//如: PExtendedX:=Compilation.GetParameterAddress('x');
//如: PExtendedY:=Compilation.GetParameterAddress('y');
// if (PExtendedX<>nil) then PExtendedX^:=1.5;
// if (PExtendedY<>nil) then PExtendedY^:=0.5;
//也可以一次获得所有的参数列表:Compilation.GetParameterList(PList);
......
xValue:=Compilation.GetValue(); //获得表达式的值,
//可以多次改变参数值并多次调用(如放在循环中),这样才能显示出效率:)
......
finally
Compilation.Free; //释放类
end;
end;
}
interface
uses SysUtils,Forms, Classes, Math;
(*
简要声明:
任何用户使用本软件属于个人自愿选择,作者不会对用户使用本软件所引起
的对用户的任何形式的损失负责,作者也不承诺提供对本类的维护和服务等义务。
本类可以自由拷贝和使用,但必须包含完整的代码和说明,任何修改和用于
商业化目的的行为都应该尽量与作者取得联系,并得到授权。
( E-Mail: HouSisong@263.net )
本类的编写目的只是在程序运行过程中能够多次的快速的执行用户输入的
数学表达式,程序优化的目标是速度和高精度,所以基本数据类型采用的是80
位浮点型,很多地方的处理是以速度为首要目标,这样在计算的准确性和错误
处理方面就有所损失。
------------------------------------------------------------------------------
希望大家能帮忙测试一下本编译类,特别是当把它用到了某些关键性计算事务中时,这
非常关键!一个小的bug就足以致命!!!您可以就某些方面进行测试,甚至是其中的一个函数,
然后把发现的错误的具体情况告诉我,以便修改;测试时没有发现错误也把测试情况告诉我,
万分感谢!!!
我的 E-Mail: HouSisong@263.net QQ: 9043542
------------------------------------------------------------------------------
*)
{
作者以前写过一个解释执行数学函数表达式的程序,因为最近使用到而解释执行太慢了满足不了要求,
所以编写了本编译类单元(数学函数动态编译器TCompile类)。
TCompile可以完成数学函数表达式的动态编译和执行(动态生成机器码),编译后的执行
速度比以前解释执行的版本快了5000倍左右!在多次执行和表达式复杂情况下,TCompile在程序
执行过程中动态编译的函数执行速度与Delphi6在程序设计阶段静态编译后的函数执行速度
相当,在有些情况下甚至快很多(注:测试时速度比一般在50%-180%之间,表达式简单的时候类TCompile
的调用开销太大,影响了测试结果,但只从代码实际执行部分来看,TCompile比Delphi6编译的快很多!!!)。
测试环境包括:Windows95、Win98、WindowsMe、Windows2000、WindowsXP。
测试过的CPU包括:奔腾、赛扬A、奔腾3、赛扬2、奔腾4。
}
(*
//2002.11.28-12.05
*改进参数传递和调用 (但仍保持与以前兼容,以前使用的方法可以继续使用)
// 调用函数返回表达式的值(实参数值列表);
function GetFunctionValue(const PList: array of Extended): Extended;
// 设置需要编译的字符串(要编译的字符串,虚参数列表字符串); 比如:Value:='Sqr(x)+Sqr(y)'; ParameterList:='x,y' ;
function SetText(Const Value:string;const ParameterList : string=''):boolean;
*增加函数 IfHaveUnDefineParameter():boolean; // 测试是否使用了未定义的变量
*增加强大的预处理宏 Define(const Key,Value : string):boolean
可以用来处理常数定义,甚至定义新的函数!
如 Key:='a'; Value:='-0.5*2' , 或 Key:='f(x,y)',Value:='Max(x,Sin(y))' 等;
常数预处理 DefineConst(const Key,Value: string):boolean;
处理常数定义(要代换的标识符,代换的值) // 常数定义, Value必须是一个可计算的值
如 Key:='a'; Value:='2' , 或 Key:='b' , Value:='2*sin(PI/2)' 等;
该功能完全可以用预定义宏(Define)来代替,
但当值为常数时这样处理有可能使最后得到的编译函数速度更快,并加快编译速度
*共享外部变量支持
设置一个外部变量函数, 这样就可以和Delphi或另一个TCompile共享变量了
SetExteriorParameter(const PName:string;const PAddress:PExtended):boolean; //(编译前调用,如果是在编译后,需要调用RefreshExeAddressCodeInPointer刷新地址)
*进行了消除堆栈调用的优化工作,代码速度进一步提升!
即编译时跟踪到有这样的操作序列: "ST压入堆栈->变量或常数载入ST->弹出数据到ST->二元运算"
优化为: "变量或常数载入ST->视情况交换ST与ST(1)->二元运算"
各种优化现在可以通过属性关闭或打开; (默认为打开)
EnabledOptimizeDiv : Boolean; // 类的属性: 是否要优化常数浮点除法运算 (除以一个常数变为乘以一个常数)
EnabledOptimizeStack : Boolean; // 类的属性: 是否要优化堆栈调用
EnabledOptimizeConst : Boolean; // 类的属性: 是否要优化常数运算
*增加GetUserParameterCount和GetUserParameterList两个函数,使之只返回变量的地址,而不再包含常数地址
以前使用的是:GetParameterCount和GetParameterList两个函数
(其实使用上面的传递参数列表调用方式更方便,这里只是为当参数很多而又每次只更新部分变量值的时候使用)
*对常数除法进行了优化(除以一个常数变为乘以一个常数: x/Const => x*(1/Const)),因为除法运算太慢
增加控制属性:EnabledOptimizeDiv :boolean; //是否要优化常数浮点除法运算 默认True
*增加版本管理
*修正bug: 忘记处理 '>','<','=' 前后的空格
*增加IF函数 格式为: If(s,r1,r2) 等价于高级语言的: If (s) Then Result:=r1 Else Result:=r2;
*增加平方和函数SqrAdd; SqrAdd(x,y)=x*x+y*y;
*增加整数次方函数IntPower函数, IntPower(x,N); N属于整数,且Abs(N)<2^31
*增加优化指数函数,当次方数为常整数时如:x^0,X^1,X^2,X^N,分别进行优化
改进Max、Min使速度加快;
-------------------------------------------------------------------------
某周一决定写一个表达式编译器 (具体日期忘了)。 程序第一版很快完成了,用
了两天,是编译型,但采用的是函数调用方式实现,动态编译的代码只是函数调用的接
口部分,速度比Delphi6慢5倍,但这还是比解释执行版本快了上千倍!后来将所有的函
数调用消除,而采用动态生成全部函数体的方式,并封装成类,这时的程序速度接近于
Delphi6,到此差不多用了6天(后面两天增加了定积分函数),代码量约为5千行左右。
作者不是计算机专业出身的,所以这是作者第一次接触和使用汇编,当然也很少
有机会接触编译原理方面的知识,所以只好 一边按照自己的设想构造一个一个的函
数,一边查看大块头的讲述 CPU 指令集的汇编书籍,一边查看和跟踪 Delphi编译器
生成的机器码(可以显示对应的汇编码,感谢Borland的强大编译器),一边参看 VC和
Delphi(感谢Borland的源码和开明)对这些函数的实现方式... 这一周是我大学生活
中所经历的最紧张繁忙的一周,但也是最有趣的一周:)
( 写于2002.11.29,怀念这一周 侯思松 )
-------------------------------------------------------------------------
*)
(*
//2002.11.5-11.8
改进ArcCotH、ArcCscH、ArcSecH使速度加快,
以前采用的是函数调用,现在采用化简的直接公式计算;
改进ArcCot函数;
改进编译后函数的调用方式(采用函数变量调用),
这对于表达式较短时会加快速度,但对于表达式较长时会慢5%左右;
改进系统内部使用的常量和系统函数的命名方式,使之更统一
'Boolean??' 改为: 'TCmSYS_Boolean??' ,
'Const_SYS_*' 改为: 'TCmSYS_Const_*' ,
'Const_SYS_ff_x_*' 改为: 'TCmSYS_Const_ff_x_*' ,
'FF_SYS_*' 改为: 'TCmSYS_FF_*' ;
//2002.11.5-11.8
*)
(*
//2002.8
最近更新,列表如下:
*增加了布尔运算; 引入逻辑运算符和比较运算符 (使用参见详细说明)
逻辑常量 真 true =1
逻辑常量 假 false =0
逻辑运算 与 AND
逻辑运算 或 OR
逻辑运算 异或 XOR
逻辑运算 非 NOT
相等 =
不等于 <>
小于 <
大于 >
小于等于 <=
大于等于 >=
!标识符 PI 现在被当作系统常量 PI=3.1415926...
标识符 e 系统给它的默认值为2.718281828...但程序可以重新赋值,与PI不同
!现在给出一个运算符优先级表:
由高到低
() (包括各种函数)
^
* \ / mod
+ -
= <> < > <= >=
AND、OR、XOR (NOT 可以看作函数)
//2002.8
*)
(*
//2002.7-2002.8
最近做了一些修改和除错工作,见下:
<<更新列表>>:
*做了一些优化,速度又加快了25%! 现在编译代码的执行效率更高(考虑了数据对齐)。
*为了减少TCompile类运行时占用的内存空间,而采用了动态的内存空间申请方式,
并且对能编译的文本长度几乎不再限制(只是受内存和编译时间等系统影响),
作者曾经测试过上百K的表达式编译和运算:);
*增加了错误号,以利于将错误描述翻译为其他语言(给出了两个翻译例子:中文繁体BIG5码的错误描述和英文版的错误描述);
*现在允许在表达式中使用注释(TCopmile.EnabledNote:=true;默认为false),注释写法为:
单行注释: 双斜杠// 开始到一行结束(即遇到回车换行符)
长段注释: '{'、'}' 或'/*'、'*/' 之间的部分
!!!关键字中不允许插入空格等字符;
以前版本 如 "Si n (P I/2)" 可以正常运行 等于"Sin(PI/2)", "d 45"被认为是"d45",现在不再允许!
!!!为了避免混乱,参考数学手册重新对表达式中的数学函数名称和别名做了修订,使用过以前版本的请注意一下;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -