在delphi中嵌入脚本语言--(译)RemObjects Pascal Script使用说明(1)(译)
翻译这篇文章源於我的一个通用工资计算平台的想法,在工资的计算中,不可避免的需要使用到自定义公式,然而对於自定义公式的实现,我自己想了一些,也在网上搜索了很多,解决办法大致有以下几种:
1. 自己写代码去解析公式。这种方法的缺点是,解析的代码很难实现,如果公式的功能比较完整,如增加条件判断或自定义函数。不亚於实现了一个简单的语言编译嚣或解释嚣。所以,只能实现一些诸如加减乘除之类的简单公式。
2. 打包成SQL传给数据库去执行。这显然不是一种好办法。而且需要与特定的数据库和表结构进行适应。
3. 我想到在foxpro中有宏替换功能&,那不如就借用它的这个功能,即利用foxpro写一个dll,在这个dll中实现了将字符串转换成指令执行的功能,然後在delphi中加载这个dll,将公式传入dll中的函数执行。这应该是一个办法,但我还没有去实现它。
4. 内嵌脚本语言。
也只有第四种办法比较理想的,於是我就找到了RemObjects Pascal Script,安装,并翻译了这篇使用说明。
再把应用范围扩大一点,其实在编译型程序中嵌入脚本语言可以解决很多应用程序自动化的问题,在了解并实际写了几个RemObjects Pascal Script的实从程序後。内心还是蛮兴奋的。
PS01 - Using the RemObjects Pascal Script
使用RemObjects Pascal Script
This article provides an overview of the new RemObjects Pascal Script and explains how to create some simple scripts.
这篇文章提供了RemObjects Pascal Script的一个概览,以及说明了如何去创建一些简单的脚本。
Pascal Script comprises two different parts:
Compiler (uPSCompiler.pas)
Runtime (uPSRuntime.pas)
Pascal Script由两个部分组成:
编译嚣(UPSCompiler.pas)
运行时(uPSRuntime.pas)
The two parts have no interdependencies on each other. You can use them directly, or you can use them in the TPSScript component, which can be found in the uPSComponent.pas unit, and wraps them both in one easy to use class.
这两部分之间是没有相互依赖的。你可以直接使用她们,或才你可以透过TPSSCript组件来使用她们,TPSSCript组件在uPSComponent.pas单元中,她对上述两个部分进行一些包装以便我们可以很容易的使用。
To use the component version of Pascal Script, you must first place it on your form or data module, set or assign the script property, call the Compile method, and call the Execute method. Compile errors, warnings or hints can be found in the CompilerMessages array property, while runtime errors can be found by reading the ExecErrorToString property.
要使用Pascal Script组件,你首先要将它从组件面板中拖置窗体或module中,然後设置它的script属性,然後调用它的Compile方法进行编译,再然後调用它的Execute方法来执行脚本。编译的errors,hints,warnings可以通过其属性CompilerMessages取得,这个属性是一个数组。如果是运行时的错误,则可以通过属性ExecErrorToString取得。
The following example will compile and execute an empty script ("begin end."):
下面的例子将编译并执行一个空脚本("begin end."):
var
Messages: string;
compiled: boolean;
begin
ce.Script.Text := 'begin end.';
Compiled := Ce.Compile;
for i := 0 to ce.CompilerMessageCount -1 do
Messages := Messages +
ce.CompilerMessages[i].MessageToString +
#13#10;
if Compiled then
Messages := Messages + 'Succesfully compiled'#13#10;
ShowMessage('Compiled Script: '#13#10+Messages);
if Compiled then begin
if Ce.Execute then
ShowMessage('Succesfully Executed')
else
ShowMessage('Error while executing script: '+
Ce.ExecErrorToString);
end;
end;
By default, the component only adds a few standard functions to the scripting engine (the exact list can be found at the top of uPSComponents.pas).
缺省情况下,组件只加入一少部分标准的functions到脚本引擎中(具体可以在uPSComponents.pas单元头中找到)
Besides the standard functions, there are a few libraries included with Pascal Script:
TPSDllPlugin Allow scripts to use dll functions, the syntax is like:
function FindWindow(C1, C2: PChar): Longint; external 'FindWindowA@user32.dll stdcall';
TPSImport_Classes Import library for TObject and the Classes unit.
TPSImport_DateUtils Import library for date/time related functions.
TPSImport_ComObj Access COM Objects from your scripts.
TPSImport_DB Import library for db.pas.
TPSImport_Forms Import library for the Forms & Menus units.
TPSImport_Controls Import library to Controls.pas and Graphics.pas.
TPSImport_StdCtrls Import library for ExtCtrls and Buttons.
除了这些标准的functions之外,Pascal Script还包含了一少部分程式库:
TPSDllPlugin 允许脚本可以使用外部DLL函数,其调用语法类似下例:
function FindWindow(C1, C2: PChar): Longint; external 'FindWindowA@user32.dll stdcall';
TPSImport_Classes 导入对应於TObject和Classes单元的libraries;
TPSImport_DateUtils 导入日期时间相关的libraries;
TPSImport_ComObj 在脚本中访问COM对象;
TPSImport_DB 导入对应於db.pas单元的libraries;
TPSImport_Forms 导入对应於Forms和Menus单元的libraries;
TPSImport_Controls 导入对应於Controls.pas和Graphics.pas单元的libraries;
TPSImport_StdCtrls 导入对应於ExtCtrls和Buttons的libraries.
To use these libraries, add them to your form or data module, select the [...] button next to the plugins property of the TPSCompiler component, add a new item and set the Plugin property to the plugin component.
要使用这些libraries,将它们从组件面板中拖至窗体数据data module中,然後设置TPSCompiler的plugins属性,在其中增加条目,并将条目指向这些plugin组件。
Besides the standard libraries, you can easily add new functions to the scripting engine. In order to do that, create a new method you would like to expose to the scripting engine, for example:
除了这些标准的libraries之外,你还可以很方便地向脚本引擎中添加新的函数。要做到这一点,创建一个你要加入到脚本中的method,如下例:
procedure TForm1.ShowNewMessage(const Message: string);
begin
ShowMessage('ShowNewMessage invoked:'#13#10+Message);
end;
Then, assign an event handler to the OnCompile event and use the AddMethod method of TPSCompiler to add the actual method:
然後,在TPSCompiler 的OnCompile事件中将该方法添加入脚本中:
procedure TForm1.CECompile(Sender: TPSScript);
begin
Sender.AddMethod(Self, @TForm1.ShowNewMessage,
'procedure ShowNewMessage
(const Message: string);');
end;
A sample script that uses this function could look like this:
这样, 你就可以在脚本中使用这个函数,如下:
begin
ShowNewMessage('Show This !');
end.
Advanced Features
高级功能
Pascal Script includes a preprocessor that allows you to use defines ({$IFDEF}, {$ELSE}, {$ENDIF}) and include other files in your script ({$I filename.inc}). To enable these features, you must set the UsePreprocessor property to true and the MainFileName property to match the name of the script in the Script property. The Defines property specifies which defines are set by default, and the OnNeedFile event is called when an include file is needed.
Pascal Script包含了一个预处理程序,以便你可以在脚本中使用编译预定义(defines)({$IFDEF}, {$ELSE}, {$ENDIF}) 以及在脚本中包含其它脚本 ({$I filename.inc})。要达到这个功能,你需要设置UsePreprocessor属性为true,and the MainFileName property to match the name of the script in the Script property. 。Defines属性指定要缺省时定义哪些defines;OnNeedFile事件代码在需要包含的文件时被执行。
function TForm1.ceNeedFile(Sender: TObject;
const OrginFileName: String;
var FileName, Output: String): Boolean;
var
path: string;
f: TFileStream;
begin
Path := ExtractFilePath(ParamStr(0)) + FileName;
try
F := TFileStream.Create(Path, fmOpenRead or fmShareDenyWrite);
except
Result := false;
exit;
end;
try
SetLength(Output, f.Size);
f.Read(Output[1], Length(Output));
finally
f.Free;
end;
Result := True;
end;
When these properties are set, the CompilerMessages array property will include the file name these messages occur in.
当这些属性被设置以後,CompilerMessages属性可就将可能包含这些文件名。
Additionally, you can call scripted functions from Delphi. The following sample will be used as a script:
另外,你可以在Delphi中调用脚本里的函数。如下函数被�