在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 [email protected] 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 [email protected] 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中調用腳本裡的函數。如下函數被定義在腳本中,後面將會在delphi中被調用:
 
function TestFunction(Param1: Double; Data: String): Longint;
begin
 ShowNewMessage('Param1: '+FloatToString(param1)
                 +#13#10+'Data: '+Data);
 Result := 1234567;
end;
 
begin
end.
 
Before this scripted function can be used, it has to be checked to match its parameter and result types, which can be done in the OnVerifyProc event.
在使用調用這個函數之前,必須對其進行一個校驗,校驗其參數和返回值類型,在OnVerifyProc執行這個校驗。
 
procedure TForm1.CEVerifyProc(Sender: TPSScript;
                              Proc: TPSInternalProcedure;
                              const Decl: String;
                              var Error: Boolean);
begin
 if Proc.Name = 'TESTFUNCTION' then begin
    if not ExportCheck(Sender.Comp, Proc,
               [btS32, btDouble, btString], [pmIn, pmIn]) then begin
      Sender.Comp.MakeError('', ecCustomError, 'Function header for
      TestFunction does not match.');
      Error := True;
    end
    else begin
      Error := False;
    end;
 end
 else
    Error := False;
end;
 
The ExportCheck function checks if the parameters match. In this case, btu8 is a boolean (the result type), btdouble is the first parameter, and btString the second parameter. [pmIn, pmIn] specifies that both parameters are IN parameters. To call this scripted function, you have to create an event declaration for this function and call that.
ExportCheck函數檢查參數的匹配情況。在這個例子中,btu8是一個布爾型(返回值類型),btdouble是第一個參數,btString是第二個參數。[pmIn, pmIn]表示兩個參數都是輸入參數。要調用這個腳本函數,你需要為它創建一個函數類型聲明。
 
type
 TTestFunction = function (Param1: Double;
                            Data: String): Longint of object;
//...
var
 Meth: TTestFunction;
 Meth := TTestFunction(ce.GetProcMethod('TESTFUNCTION'));
 if @Meth = nil then
    raise Exception.Create('Unable to call TestFunction');
 ShowMessage('Result: '+IntToStr(Meth(pi, DateTimeToStr(Now))));
 
It's also possible to add variables to the script engine, which can be used from within the script. To do this, you have to use the AddRegisteredVariable function. You can set this in the OnExecute event :
還可以向腳本引擎中添加變量,然後就可以在腳本中使用這些變量 。要做到這一點,你需要使用AddRegisteredVariable函數。可以在OnExecute設置它:
 
procedure TForm1.ceExecute(Sender: TPSScript);
begin
 CE.SetVarToInstance('SELF', Self);
 // ^^^ For class variables
 VSetInt(CE.GetVariable('MYVAR'), 1234567);
end;
 
To read this variable back, after the script has finished executing, you can use the OnAfterExecute event:
若要再去讀取這個變量的值,在腳本執行完成後,在OnAfterExecute事件中訪問:
VGetInt(CE.GetVariable('MYVAR')).
 
Registering external variables to the script engine is also possible. It's a two step process, first, in the OnCompile event, add the types to the script using the AddRegisteredPTRVariable function.
注冊一個外部變量到腳本引擎中也是可以的。這需要兩個步驟,首先在OnCompile事件中使用AddRegisteredPTRVariable函數將變量類型添加到腳本中。
 
procedure TMyForm.PSScriptCompile(Sender: TPSScript);
begin
 Sender.AddRegisteredPTRVariable('MyClass', 'TButton');
 Sender.AddRegisteredPTRVariable('MyVar', 'Longint');
end;
 
This will register the external MyClass and MyVar variables. Second, attach a pointer to these variables in the OnExecute event:
這樣就注冊了MyClass 和 MyVar這兩個變量。第二步,在OnExecute中通過將變量值的地址指針傳給變量來實現給變量賦值:
 
procedure TMyForm.PSScriptExecute(Sender: TPSScript);
begin
 PSScript.SetPointerToData('MyVar', @MyVar, PSScript.FindBaseType(bts32));
 PSScript.SetPointerToData('Memo1', @Memo1, PSScript.FindNamedType('TMemo'));
end;
 
There are two types of variables in Pascal Script, base types, which are simple types (see the table below), and class types. Base types are registered in the uPSUtils.pas unit and can be located using the FindBaseType function. Class types have to be located by name, using the FindNamedType. Changes to these variables have a direct effect on the actual variable.
在Pascal Script中有兩種類型的變量,一種是基本類型,包含一些簡單的類型,下面會列出;另一種是類類型。基本類型是在uPSUtils.pas被注冊進去的,可以通過FindBaseType函數找到。類類型需要使用FindNamedType函數通過名稱找到。改變這些變量將直接地影響到實際的變量。
 
Base types:
btU8     Byte
btS8     Shortint
btU16    Word
btS16    Smallint
btU32    Longword
btS32    Longint
btS64    Int64
btSingle      Single
btDouble      Double
btExtended    Extended
btVariant     Variant
btString      String
btWideString WideString
btChar  Char
btWideChar    WideChar
 
 
The component version of Pascal Script also supports execution of scripted functions. This works by using the ExecuteFunction method.
Pascal Script組件同樣也支持腳本函數。這通過ExecuteFunction來調用。
 
ShowMessage(CompExec.ExecuteFunction([1234.5678, 4321,
                                      'test'],
                                     'TestFunction'));
 
This will execute the function called 'TestFunction' with 3 parameters, a float, an integer and a string. The result will be passed back to ShowMessage.
這個例子將執行一個名為TestFunction的函數,這個函數包含3個參數,一個float,一個integer和一個string。函數的返回值傳回給ShowMessage。
 
Notes:
For some functions and constants, it might be necessary to add: uPSCompiler.pas, uPSRuntime.pas and/or uPSUtils.pas to your uses list.
The script engine never calls Application.ProcessMessages by itself, so your application might hang, while the script is running. To avoid this, you can add Application.ProcessMessages to the TPSScript.OnLine event.
It's possible to import your own classes in the script engine. Pascal Script includes a tool to create import libraries in the /Unit-Importing/ directory.
It's possible to import your own classes in the script engine. Pascal Script includes a tool to create import libraries in the Bin directory.
For examples on how to use the compiler and runtime separately, see the Import and Kylix samples.
The Debug requires SynEdit http://synedit.sourceforge.net/.
注意:
一些必要的函數和常量應該被加入到uses 列表中:uPSCompiler.pas, uPSRuntime.pas, uPSUtils.pas;
腳本引擎不會自行調用Application.ProcessMessages,因此在腳本執行時你的應用程序可能會終止。要避免這一點,你可以將Application.ProcessMessages加入到TPSScript的OnLine事件中;
可能需要在腳本中引入你自己的類,Pascal Script包含一個工具以便創建引入的庫,這個工具在Unit-Importing目錄中;
可能需要在腳本中引入你自己的類,Pascal Script包含一個工具以便創建引入的庫,這個工具在Bin目錄中;
安裝目錄中可以找到單獨使用Comiler和Runtim的實例;
腳本調試需要SynEdit http://synedit.sourceforge.net/.

--
fey
2007/2/27夜
更多相关文章
  • .NET 動態腳本語言Script.NET 入門指南 Quick Start
    Script.NET是一种动态的脚本语言,它使得程序可扩展,可定制,和维护性好.和Offi
  • 


    		    使用Lua腳本語言開發出高擴展性的系統,AgileEAS.NET SOA中間件Lua腳本引擎介紹
    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Mi
  • 下载源码
  • 從遊戲腳本語言說起,剖析Mono所搭建的腳本基礎
    0x00 前言 在日常的工作中,我偶尔能遇到这样的问题:“为何游戏脚本在现在的游戏开发中变得不可或缺?”.那么这周我就写篇文章从游戏脚本聊起,分析一下游戏脚本因何出现,而mono又能提供怎样的脚本基础.最后会通过模拟Unity3D游戏引擎中的脚本功能,将Mono运行时嵌入到一个非托管(C/C++)程
  • 常用脚本语言Perl,Python,Ruby,Javascript一 Perl,Python,Ruby,Javascript Javascript现阶段还不适合用来做独立开发,它的天下还是在web应用上. 主要做数据挖掘算法的研究,应该会处理大量的文本. 提到文本处理,相信大部分人都会说应该用Per ...
  • 内容源自Delphi XE5 UPDATE 2官方幫助<Delphi Reference>,本人水平有限,欢迎各位高人修正相关错误! 也欢迎各位加入到Delphi学习资料汉化中来,有兴趣者可QQ:[email protected] Language Overview 第一章    语言概述 ...
  • 使用Jena RDF API 開發腳本語言管理資源描述框架模型
    摘要 资源描述框架(Resource Description Framework RDF)是一种以XML格式描述元数据的标准格式.Jena是一种用于将关系数据库或是文本文件中所表示的数据建立为元数据模型的框架方法. Protégé 用用来创建RDF元数据文档的编辑工具. 建立这种数据库和文件的元数据
  • 最近听说Cocos2d JS官方在组织写新的代码例子.并且林顺同学在开会时说,Cocos2d JS接口将回归JS风格,成员变量访问方式用".",以及初始化时用对象字面量{}.非常不错啊. 如果之前是Cocos2d-x的C++程序员,在使用Cocos2d JS时很容易沿用C++的思 ...
一周排行
  • 待完善... Linux上的任务计划: 任务计划: 在未来时间点一次性执行某任务:at, batch 周期性执行某任务: crontab 电子邮件服务: smtp: simple mail transmission
  • 编辑好cell,db节点信息文件all_group,dbs_group,cell_group [[email protected] exatest]# pwd /root/exatest [[email protected] exa
  • qt移植到开发板上 但有个问题就是我用QT4编译完的程序如:books 可以用minicom运行起来:#./home/plg/books/books -qws 问题是如何嵌入到QtE里面?就是如何在板上按一个按钮就可
  • 


    		    VMware Perl SDK error “Server Version Unavailable .. line 545”
    当我在centos6.6中运行perl脚本时候,出现"Server Versio ...
  • Echop後台分頁實現原理詳解
    ecshop后台开发,工作中分页中遇到的问题 1.通过筛选条件筛选出数据后,点击下一页返回
  • 简单实现UITableView索引功能(中英文首字母索引)(一) ByH罗 相关类: NSString+PinYing(获取中英文首字母)   参考上面链接 #import "ViewController. ...
  • 技术分享:VPS小内存LNMP服务器Nginx 502 Bad Gateway故障一分钟内自动恢复解决辦法 以下代码保存为.sh定时执行即可 #!/bin/sh if [ -e /home/wwwlogs/502 ]
  • 通過yeelink平台監控樹莓派CPU溫度變化
    通过yeelink平台监控树莓派温度,是很多派友入门第一课.作为一名刚入手树莓派裸机不久的
  • 


    		    sharepoint 錯誤 6398
    最近在sharepoint维护服务器是,发现系统日志里面每隔20分钟就会出现如下错误 这时
  • SQL Server5種常見的約束
    SQLServer 中有五种约束, Primary Key 约束. Foreign Key ...