Scripting met Pascal
Vrijwel elke applicatie heeft te maken met configuratie. Instellingen worden daarbij vaak in ini-files opgeslagen of misschien tegenwoordig wat moderner in XML-files. Dit soort instellingen zijn vaak eenvoudig en enkelvoudig, zoals het pad naar een of andere map of misschien de connectiegegevens naar een database. Ook kunnen applicaties te maken hebben met maatwerk dat je niet zomaar in de executable wil ‘meecompileren’. Het bouwen van een plugin, vaak in de vorm van een DLL, is dan een oplossing. Beide technieken zijn bedoeld om een applicatie in een specifieke context op maat te laten functioneren.
Pascal Script is een techniek die zich bezig houdt met het op maat maken van een applicatie, zonder dat deze applicatie opnieuw gecompileerd hoeft te worden
Pascal Script [1] is een techniek die zich hier ook mee bezig houdt: het op maat maken van een applicatie, zonder dat deze applicatie opnieuw gecompileerd hoeft te worden.
Pascal Script
De aanleiding voor het gebruik van Pascal Script tijdens mijn werk is een project voor de bouw van een benchmark-applicatie. In deze applicatie wordt een groot aantal gegevens opgeslagen en worden allerlei benchmarks berekend op basis daarvan. De logica achter die berekeningen is van tevoren niet bekend, en daarnaast moet de logica door niet-programmeurs aanpasbaar zijn. De complexiteit van de logica is dermate groot, dat de mogelijkheden van een traditioneel ini-file veel te beperkt zijn. De eis van aanpasbaarheid zet de inzet van DLL’s daarbij ook buitenspel.
Pascal Script blijkt hiervoor een passende oplossing te zijn. Hiermee kan Pascal-code, in bovenstaand project soms uitgebreide berekeningen, runtime worden gecompileerd en uitgevoerd. Delphi is hierbij (runtime) niet nodig. Pascal Script is vrij te gebruiken (wel met bronvermelding) en de broncode is volledig beschikbaar. Delphi 6 en hoger worden ondersteund, evenals Kylix 3.

De scripting engine die voor het compileren en executeren zorgt, wordt meegecompileerd in de executable. Hieronder staat een voorbeeld dat duidelijk maakt hoe deze engine kan worden aangeroepen:
procedure ExecuteScript(const Script: string);
var
PSScript: TPSScript;
begin
PSScript := TPSScript.Create(nil);
try
PSScript.Script.Text := Script;
if not PSScript.Compile then
raise Exception.Create(...);
try
PSScript.Execute;
except
raise Exception.Create(...)
end;
finally
PSScript.Free;
end;
end;
De engine zit in de Delphi component TPSScript. Aan een instantie van deze component wordt Pascal-code aangeboden, die wordt gecompileerd en daarna uitgevoerd. De Pascal syntax die door de engine ondersteund wordt, is niet zo uitgebreid als die binnen Delphi beschikbaar is. De taal is echter krachtig genoeg voor de meeste toepassingen. Zo zijn taalconstructies begin/end, for/to/downto/do, case/do, repeat/until, while, uses, exit/continue/break aanwezig. Ook worden constanten, variabelen en functies ondersteund en zijn de meeste standaard datatypen beschikbaar.
De engine zit in het Delphi component TPSScript
Met bovenstaande procedure kan het volgende minimale script worden uitgevoerd:
var
s: string;
begin
s := 'Hello';
s := s + ' World';
end;
Leuk... Maar als je echt iets met scripting wilt, dan moet eerst een interface van het script naar buiten en andersom beschikbaar zijn. Hiervoor zijn allerlei methods en properties op TPPScript beschikbaar, waaronder:
In het volgende voorbeeld worden een aantal van bovengenoemde opties gebruikt. Stel dat we een applicatie willen bouwen, waarin door middel van scripts het doorrekenen van formules mogelijk moet zijn. De parameters bij de formules moeten daarbij door de eindgebruiker ingegeven kunnen worden. Het script voor de berekening van de lengte van de rechthoekszijde met behulp van de stelling van Pythagoras kan er dan als volgt uitzien:
const
FORMULE = 'Stelling van Pythagoras';
function Calculate: Double;
var
A, B: Double;
begin
if AskParameter(FORMULE,'Lengte rechthoekszijde 1',A)
and
AskParameter(FORMULE,'Lengte rechthoekszijde 2',B)
then
Result := sqrt(A * A + B * B)
else
ShowMessage(
'Fouten in parameters of onderbreking invoer');
end;
Om dit script te kunnen uitvoeren moet een tweetal functies gefaciliteerd worden in de Delphi applicatie: AskParameter en ShowMessage. Voor de laatste functie willen we gewoon gebruikmaken van de functie in de Dialogs unit; AskParameter zou zo kunnen worden gecodeerd:
function AskParameter(const Caption, Question: string;
out Answer: Double): Boolean;
begin
try
Answer := StrToFloat(InputBox(Caption,Question,''));
Result := True;
except
Result := False;
end;
end;
Beide functies moeten voor het compileren aan de engine worden gekoppeld; daarvoor is de OnCompile eventhandler de juiste plaats:
procedure TfrmMain.PSScriptCompile(Sender: TPSScript);
begin
Sender.AddFunction(@AskParameter,
'function AskParameter(
const Caption, Question: string;
out Answer: Double): Boolean;');
Sender.AddFunction(@Dialogs.ShowMessage,
'procedure ShowMessage(const Message: string);');
end;
Tenslotte kan de aanroep voor uitvoeren van het script worden gedaan:
procedure TfrmMain.ExecuteFunction;
var
CalculationResult: Variant;
begin
if PSScript.Compile then
begin
CalculationResult :=
PSScript.ExecuteFunction([], 'Calculate');
// ... doe iets met CalculationResult
end;
end;
Met dit voorbeeld wordt maar een beperkt deel van de mogelijkheden getoond, maar laat wel zien hoe de Delphi applicatie en de script engine met elkaar kunnen samenwerken.
Het compileren en executeren kan ook via twee afzonderlijke classes worden gedaan, waardoor het moment van compileren en executeren goed gescheiden kan worden
Overige noemenswaardige aspecten van Pascal Script zijn:
- Het compileren en executeren kan ook via twee afzonderlijke classes worden gedaan. De compiler levert daarbij zogenaamde bytecode op, die de executer weer als invoer gebruikt. De bytecode kan worden bewaard, waardoor het moment van compileren en executeren goed gescheiden kan worden;
- Debugging wordt ook ondersteund door een afgeleide class van TPSScript. Hiermee kunnen onder andere breakpoints worden ingesteld en kan regel voor regel door de code worden gelopen.
- (Windows-)DLL's kunnen geregistreerd worden bij de engine, zodat allerlei lagere API-calls in het script mogelijk zijn;
- Aanmaken en gebruiken van OLE-objecten wordt in een plugin gefaciliteerd;
- Ook zelf gedefinieerde typen (ook eigen classes) kunnen worden geregistreerd bij de engine;
- Include files en defines zoals bekend binnen Delphi zijn beschikbaar omdat een preprocessor op de engine aangezet kan worden;
- Voor gebruikers van de REM Objects SDK is functionaliteit beschikbaar om vanuit de script-omgeving services te benaderen;
Ontwikkelomgeving
De programmeur wordt binnen Delphi goed ondersteund bij het schrijven van syntactisch correcte code. Binnen de IDE zijn syntax highlighting, code completion, etc., daarbij handige tools. Uiteindelijk is er dan natuurlijk nog de compiler die de programmeur nog op fouten wijst. Design-time worden dus veel fouten afgevangen.
Bij Pascal Script gebeurt alles runtime, dus fouten in het script zullen dan ook meteen foutsituaties voor de eindgebruiker opleveren. Dit is een belangrijk aandachtspunt bij gebruik van deze techniek.
De scripts zou je natuurlijk in de editor van Delphi of zelfs in notepad kunnen opstellen, maar er is eigenlijk meer behoefte aan een ontwikkelomgeving voor Pascal Script.
SynEdit [2] kan goed als basis dienen voor zo’n ontwikkelomgeving. Het is een multi-line edit control voor onder andere Delphi en Kylix. Deze component ondersteunt onder andere syntax hightlighting voor Pascal (van tientallen talen overigens) en code completion. Het is ook open source. Pascal Script en SynEdit kunnen zo voor het schrijven en debuggen van script goed gecombineerd worden.
Conclusie
Pascal Script is een waardevolle aanvulling van de toolbox gebleken. Het is een stabiel product en de mogelijkheden zijn legio. In de projecten waarin het tot nu toe is toegpast, heeft het als techniek meer dan voldaan.
Natuurlijk moet bij de inzet van elke techniek getoetst worden, of het wel de juiste techniek is in de betreffende situatie en of er geen beter alternatief is. Download het product eens en speel er eens mee. Goede kans dat er eens een project voorbijkomt, waarin het zijn diensten kan bewijzen.
Literatuurverwijzingen
[1] Pascal Script™ 3.0, http://www.remobjects.com/products/
[2] SynEdit, http://synedit.sourceforge.net/