DelphiWebScript II (DWSII) is a scripting engine for application scripting or for server side scripting. The script language of DWS is a subset of
Delphi(tm) pascal. DelphiWebScript is a component for the programming
system "Borland Delphi"
DelphiWebScript II is distributed under "Mozilla Public License 1.1" (MPL 1.1). Please refer to the DWS homepage for additional information.
Extract the archive (if not already done) to a directory of your choice - I'll call it "<DWSDIR>"
Start Delphi and load the right BorlandProjectGroup file according to
your version of Delphi.
e.g.:
Delphi 5 -> <DWSDIR>\Delphi5.bpg
Delphi 6 -> <DWSDIR>\Delphi6.bpg
Kylix -> <DWSDIR>\Kylix1.bpg
DWSII only supports Delphi 6, Delphi 5 and Kylix 1 at this time officially.
Delphi 4 might work with a few modifications too, but has not been tested.
For all Borland C++ Builder users: Please send feedback about your experiences
with DWSII to the DWS mailinglist!
If you don't see the Delphi ProjectManager, then open it. (DelphiMenu -> View -> ProjectManager)
Compile all dws2XXXRuntimeXX.bpl packages in the project
Install the dclXXXX.bpl package
If you need further assistance, please subscribe yourself to the DWS mailinglist.
You can do this simply by sending an email to following address: join.dwsforum@dwscript.com
ATTENTION: Write your list-messages to: dwsforum@dwscript.com
If you don't want to subscribe to the list, but still need support, you can
use the Web-Forum at sourceforge:
http://sourceforge.net/forum/forum.php?forum_id=64301
If DelphiWebScript II is installed correctly, a new tab "DWS2" appears in your Delphi component palette. To use DWSII in your project execute the following instructions:
uses
dws2Exprs, ...
[...]
var
[...]
prog: TProgram;
begin
[...]
prog := DelphiWebScriptII1.Compile('PrintLn(''Hello World'');');
try
prog.Execute;
ShowMessage(prog.Result.AsString);
finally
prog.Free;
end;
end;This is a very simple example. For more detailed information about using DelphiWebScript II please have a look at the demo program.
DWSII uses a language that is very similar to Delphi from Borland. So this reference only describes the differences to Delphi pascal. It is assumed that you already know the programming language of Delphi!
A Delphi program has the following structure:
program ProgramName;
Declarations
begin
Instructions
end.
Difference 1: A DWSII script program has no structure. You can place declarations and instructions everywhere you want. For this reason every declaration starts with a keyword and ends with a semicolon. Example:
var i: Integer;
i := 2;
type TPoint = record x, y: Integer; end;
type TRect = record x, y, w, h: Integer end;
var r: TRect;
r.x := 3;
procedure Proc(x: Integer);
begin
end;
Proc(x);
Difference 2: You can't place declarations in the procedure declaration. Example:
BAD:
procedure Proc(x: Integer);
type
TMyRec = record a, b: string end; // ERROR!!
begin
end;
GOOD:
procedure Proc(x: Integer);
begin
type TMyRec = record a, b: string end; // OK!!
end;
Possible declarations are:
const Name = Value;
type Name = TypeName;
type Name = record ... end;
type Name = array [x1..x2, ...] of TypeName;
type Name = class ... end;
procedure Name(Param: TypeName; ...);
begin
end;
function Name(Param: TypeName; ...): TypeName;
begin
end;
Delphi has many built in basic data types like "integer", "single", "double", "currency", "ansistring", "shorttring", ... DelphiWebScript II doesn't support all of these types but only the most important ones.
The basic data types of DelphiWebScript II are:
Integer: 32 bit integer (corresponds to Delphi's
"Integer")Float: 64 bit floating point value (corresponds to Delphi's
"Double")String: corresponds to Delphi's "AnsiString"Boolean: True or falseDateTime: corresponds to Delphi's "TDateTime" value
and is compatible to "Float"Variant: same as "Variant" in Delphi. You can
assign every other basic data type to a variable of type "Variant"In Delphi the case statement only works with integer types. But in DWSII you can use all comparative datatypes in the case-instruction:
var s: string;
s := 'Alpha';
case s of
'Alpha': DoSomething;
'Beta', 'Gamma': DoSomethingElse;
end;
Because you can declare a variable anywhere in a DWSII program it's also
possible to declare it inside a block (begin .. end).
The declaration of a variable is only visible in the actual block and in any sub-blocks:
var i: Integer;
if i = 0 then
begin
var j: Integer;
j := 2;
while j > 0 do
begin
var k: Integer;
k := 2;
j := j - k;
end;
end;
var j: String; // Variable "j" declared inside the
// if-block is not visible here!
It's possible to initialize a variable using the var-statement:
var s: Integer = 2;
var str: String = 'Hello' + IntToStr(s);
var i: Integer = 12;
is the same as
var i: Integer;
i := 12;
Delphi knows a "overload" directive for procedures. You can't declare overloaded functions in DWSII!
DWSII has no support for sets and enumerations
DWSII has only two compiler directives. Example:
{$I 'File'}
{$Include 'File'}
{$A 'File'}
{$ADAPTER 'File'}
Important: In difference to Delphi the quotes ('') around the filename are obligatory!
Low level features are not part of DWSII:
TDelphiWebScriptII is the main component in the DWSII package. It manages all information needed for the compilation. The property "Config" is storing the include paths, compiler options and so on...
TDelphiWebScriptII.Adapter
The adapter property stores a reference to an Tdws2Adapter component. An
adapter has two important purposes: Provide output functionality and process the
input (script programs). TDelphiWebScript is also derived from Tdws2Adapter and
can be assigned to this property. It offers the two output functions
"Print" and "PrintLn".
TDelphiWebScriptII.Config.CompilerOptions
Available compiler options are:
TDelphiWebScriptII.Config.MaxDataSize
All data in a DWSII script program is stored on a global stack. This stack
is able to grow and shrink dynamically at runtime. If you assign a value
different to 0 to this property the stack won't grow larger than this value in
bytes. If it still grows larger a runtime error is raised.
This property is useful to avoid recursive program to use up all memory and to
prevent users from defining large data structures. But it's not a general
protection! There are still many ways to write a script program that uses up all
memory!
TDelphiWebScriptII.Config.ScriptPaths
If you use a {$INCLUDE 'file.dws'} compiler switch in your script program and 'file.dws'
is not found the compiler tries to find 'file.dws' relative to every path in .ScriptPaths.
You can add multiple paths by writing every path on a new line. The path will
automatically be terminated by a backslash ("\" or foreslash
"/" for Linux).
TDelphiWebScriptII.Config.Timeout
If .Timeout is set to a value different to 0 the script execution
will be terminated after .Timeout seconds. This is useful to avoid
scripts with endless-loops using up all of the systems resources.
TDelphiWebScriptII.Version
The version of the DelphiWebScript II component package.
constructor TDelphiWebScriptII.Create(AOwner:
TComponent);
destructor TDelphiWebScriptII.Destroy;
Creates and destroys instances of the TDelphiWebScriptII component. If you
have to create TDelphiWebScriptII components dynamically for some reasons use
TDelphiWebScriptII.Create(nil);
function TDelphiWebScriptII.Compile(const Text: string): TProgram;
Compiles the script program in "Text" with the current settings
and returns a TProgram object. If compilation errors occurred you can read the
error messages in TProgram.Msgs (see als "TProgram properties" below
constructor TProgram.Create(SystemTable: TSymbolTable; Adapter: IAdapter);
destructor TProgram. Destroy; override;
Creates and destroys an instance of TProgram. You don't have to call Create()
yourself. This is done by the compiler. But the destruction of the TProgram
object is your task.
function TProgram.AddObj(Obj: TScriptObj): Integer;
procedure TProgram.FreeObj(Id: Integer);
function TProgram.GetObj(Id: Integer): TScriptObj;
function TProgram.HasObj(Id: Integer): Boolean;
function TProgram.EnumerateScriptObjs(EnumFunc: Enumerator): TScriptObj;
function TProgram.FindExternalObject(Obj: TObject): TScriptObj;
DelphiWebScript II maintains a list of object instances created during the
execution of a script program. These are the methods to add and remove such
object to the list and to query the list. In almost every case you don't need to
call these methods.
procedure TProgram.Execute
procedure TProgram.Execute(TimeoutValue: Integer);
Executes the script program. If you specify a "TimeoutValue"
different to 0 (default) this value overrides the one set by
TDelphiWebScriptII.Timeout and the script is terminated after TimeoutValue
seconds.
procedure TProgram.ExecuteParam(Params: array
of Variant);
procedure TProgram.ExecuteParam(Params: array of Variant; TimeoutValue:
Integer);
Same as TProgram.Execute but additionally you have the possibility to
pass parameters to the script program. In script code you can read the
parameters using the functions "ParamCount: Integer", "ParamStr(Index:
Integer): string" and "Param(Index: Integer): Variant".
Parameters are numbered from 0 to ParamCount - 1. You can only pass values of
simple types (string, integer, float, boolean, datetime). Example:
Prog.ExecuteParam([1, 'Hello', 3.4, false]);
procedure TProgram. Stop;
Stops the execution of the script program as soon as possible. Remember: If
TProgram is executing an external procedure the program won't stop until this
procedure returns.
property TProgram.Adapter: IAdapter;
Same as TDelphiWebScriptII.Adapter.
property TProgram. Debugger: IDebugger;
If you assign a value to this property the TProgram object starts to call the
debugger every time an instruction is executed. If a debugger is assigned the
flag IsDebugging is set to true if you call TProgram.Execute. You can change the
debugger at runtime, too. You can download debugger components on the DWS
homepage.
property TProgram.Expr: TExpr;
This is the root node of the expression tree that represents the compiled script
program. You shouldn't use this property!
property TProgram.IsDebugging: Boolean;
This property is set to True if a Debugger is assigned and Execute
is called. If you set this property to False at runtime the debugging is
disabled. If the program is not running (IsRunning = False) this property
has no effect.
property TProgram.IsRunning: Boolean;
This property is read-only. It's True if the program is running (TProgram.Execute)
and False otherwise
property TProgram. Level: Integer;
This property is only used by the compiler. It determines the nesting level
of this "Procedure" which is always 0 for the main program.
property TProgram.Msgs: TMsgs;
The Msgs property is very important. It tells you many things about
the state of the TProgram object. If you didn't yet execute the TProgram you can
check if there were compiler errors using Msgs.HasSyntaxErrors. The
number of error message is Msgs.Count. The first message is Msgs.Msgs[0]
and the last one is Msgs.Msgs[Msgs.Count - 1]. You can get the error
message unformatted with Msgs.Msgs[x].AsString or with additional
information with Msgs.Msgs[x].AsInfo.
property TProgram. Stack: TStack;
This property is only used by the compiler
property TProgram. Stopped: Boolean;
This property is set to True if you call TProgram.Stop. It remains true until
the program is executed again. Remember: It's possible that IsRunning and
Stopped are both True.
property TProgram.Table: TSymbolTable;
This property is only for experts.
property TProgram. Timeout: Integer;
This property is used to set and read the actual timeout value. The default
value is TDelphiWebScriptII.Timeout. You can also set a timeout value
calling TProgram.Execute(TimeoutValue);
property TProgram.TypBoolean: TSymbol;
property TProgram.TypFloat: TSymbol;
property TProgram.TypInteger: TSymbol;
property TProgram.TypNil: TNilSymbol;
property TProgram.TypObject: TClassSymbol;
property TProgram.TypString: TSymbol;
property TProgram.TypVariant: TSymbol;
These properties are only used by the compiler.
Tdws2Unit is mainly used to define external functions for DWS programs. If such a function is called in a script program the call is passed to the corresponding Delphi object (a Tdws2Function in this case) and this object fires it's OnEval event. You can write any Delphi code in this event handler.
But Tdws2Unit is much more powerful. You can also define arrays, records and classes. The definition of classes is mostly used to create wrapper classes for the classes of your Delphi program. The methods of your Tdws2Unit class call the methods of the Delphi class in their OnEval event handler.
property Tdws2Unit.Arrays: Tdws2Arrays;
You need the Arrays property if you want to pass an arrays as parameters
to your functions. The properties of Tdws2Array are used like this:
type Name = array [LowBound .. HighBound] of DataType
You can only define arrays of one dimension. If you need multi dimensional arrays you have to define every dimension extra:
TSingleDimension = array [0..9] of Integer;
TMultiDimension = array [0..9] of TSingleDimension;
property Tdws2Unit.Classes: Tdws2Classes;
The Classes property is used to define wrapper classes for Delphi
classes. The properties of Tdws2Class are used like this:
type Name = class (Ancestor)
Fields
Methods
Properties
end;
property Tdws2Unit.Constants: Tdws2Constants;
The Constants property is used to define constants in a Tdws2Unit. The
properties of Tdws2Constant are used like this:
const Name: DataType = Value;
property Tdws2Unit.Dependencies: TStrings;
If use datatypes defined in other Tdws2Units you can add the .UnitName of
these Tdws2Units here. E. g. if you'd like to use the record type "TPoint"
from Tdws2Unit "Graphics" and the object "TErrorMsg" from
Tdws2Unit "Errors" add the strings "Graphics" and
"Errors". Place multiple entries on single lines.
property Tdws2Unit.Forwards: Tdws2Forwards;
The Forwards property is used if you want to define cross-link declarations
of classes:
TClassB = class;
TClassA = class
FClassB: ClassB;
end;
TClassB = class
FClassA: TClassA;
end;
In this case you should add the name "TClassB" to the Forwards property. It's also necessary that TClassA is declared before TClassB in the Classes collection!
property Tdws2Unit.Functions: Tdws2Functions;
This property is used if you want to declare an external function for your DWS
scripts. The properties of Tdws2Function are used like this:
function Name (Params): DataType;
property Tdws2Unit.Records: Tdws2Records;
This property is used if you want to declare an external function for your DWS
scripts. The properties of Tdws2Function are used like this:
property Tdws2Unit.Script: TDelphiWebScriptII;
You have to assign this property to a TDelphiWebScriptII component on your
forms. The functions and data types declared in this Tdws2Unit are only known to
scripts that are compiled with that TDelphiWebScriptII component!
property Tdws2Unit.UnitName: string;
The name of this Tdws2Unit in DWSII programs and in the property .Dependencies
of other Tdws2Units. You can also use the syntax "unitname.datatype..."
in DWSII programs.
property Tdws2Unit.Variables: Tdws2Variables;
You can also declare variables using the Variables property for use in
your script program. But in difference to normal variables of a script these
variables have additional capabilities:
Property AutoInstantiate: If this property is set to True and the
property Datatype points to a class and you read this variable the first
time in the script this happens: An object of the class Datatype is
created and the event OnInstantiate is called. To the script programmer this
variable will behave like an object of type Datatype. If the class is a
wrapper class for a Delphi object you can assign the Delphi object in the
OnInstantiate event. If the script terminates all script objects will be
destroyed and the OnCleanup event of this variable will be fired. If necessary
you can now destroy the assigned Delphi object.
Alternatively you can set AutoDestroyExternalObjects to True. Now the
assigned Delphi object is automatically freed if the script terminates.
If you assign an event handler to the events OnReadVar and OnWriteVar every time
your read or write the variable in the script the corresponding even is fired.
You can't use OnReadVar/OnWriteVar together with AutoInstantiate
If you defined a function or a method using Tdws2Unit and this function is called in the script code an OnEval event is fired. To write the event handler you need information about the script that called the function. You need the values of the parameters, you have to set the result value or you may want to call another script function. For all this purposes a parameter "Info" of type TProgramInfo is passed to every OnEval event handler.
constructor TProgramInfo.Create(Table: TSymbolTable);
Creates a new TProgramInfo object that provides information about the
symboltable "Table"
function TProgramInfo.GetTemp(DataType: string): IInfo;
Creates an unnamed temporary variable of type "DataType".
Temporary variables are used to assign values to variables of complex types
(like records or arrays).
property TProgramInfo. Caller: TProgram;
The caller of this function.
property TProgramInfo.Data[s: string]: TData;
In most cases you'll use Value to assign values to a variable. But
if the value is larger than on variant (e. g. if it's a record/array) you use Data
and assign an array of Variant.
property TProgramInfo.Func[s: string]: IInfo;
Returns the interface representing the function, procedure or method
"s". See IInfo for more information.
property TProgramInfo.FuncSym: TFuncSymbol;
In most cases TProgramInfo is used to access the variables of the
function that was called. In this case FuncSym points to the TFuncSymbol
object that represents the Function in the symbol table.
property TProgramInfo.Method[s: string]: IInfo;
Returns the interface representing the function, procedure or method
"s". See IInfo for more information.
property TProgramInfo.Obj: TScriptObj;
If the TProgramInfo object is used to access the variables of a method Obj
points to the "Self" object. Info.Vars['self'].Obj points to the same
object.
property TProgramInfo.Value[s: string]: Variant; default;
This is a shortcut to Info.Vars[s].Value. Because Value is the
default property of TProgramInfo you can also omit the property name
"Value" an write Info['varname'].
property TProgramInfo.Vars[s: string]: IInfo;
Returns an interface representing the variable s. See IInfo for
more information.
function IInfo.Call: IInfo;
This method is only of interest if the IInfo interface was returned by
TProgramInfo.Func[], TProgramInfo.Method[] or IInfo.Method[]. This method
executes the call to that function or method. If the function has a result value
Call returns the interface representing that value. To pass parameters to
this function you should first use IInfo.Parameters[] to set them
function IInfo.Call(Params: array of Variant): IInfo;
This method is only of interest if the IInfo interface was returned by
TProgramInfo.Func[], TProgramInfo.Method[] or IInfo.Method[]. This method
executes the call to that function or method. If the function has a result value
Call returns the interface representing that value. To pass parameters to
this function you can pass an array of variants containing the parameter values.
If the parameter values are records or arrays you should use the Call
method without arguments and pass the variables using IInfo.Parameter[]
function IInfo.Element(const Indizes: array of Integer): IInfo;
This method is only of interest if the IInfo interface was returned by
TProgramInfo.Vars[] and denotes a variable of type array. To select an element
of the array you pass an array of integer indices to this function. The result
of Elements represents the selected element.
function IInfo.GetConstructor((MethName: string; ExtObject: TObject): IInfo;
This method is similar to Method[MethName]. It is used to create a script
object (using constructor MethdName) and connect it immediately to a
Delphi object ExtObject. Example: Info['Result'] :=
Info.Vars['TList'].GetConstructor('Create', TList.Create).Call.Value;
property IInfo. Data: TData;
If the IInfo represents a variable of a complex type (record or array)
you don't have to set every element of the array or every member of the record
one by one. You can also pass all elements of an array of Variants by assigning
it to IInfo.Data or get all members of a record in an array of Variants
by reading IInfo.Data
property IInfo.Member[s: string]: IInfo;
If the IInfo interface represents a variable of type record you can
select a single member using IInfo.Member[]
property IInfo.Method[s: string]: IInfo;
If the IInfo interface represents an object - variable you can get an
IInfo interface to call a method of this object.
property IInfo.Obj: TScriptObj;
If the IInfo represents a variable of type class IInfo.Obj points
to the corresponding TScriptObj object.
property IInfo.Parameter[s: string]: IInfo;
If the IInfo interface represents a function or method you can set the
parameters using IInfo.Parameter[].
property IInfo. Value: Variant;
If the IInfo represents a variable of a simple type you can set or read
its value using IInfo.Value.
If you place this component to the form several utility functions are added to every TDelphiWebScriptII component on the form. The utility functions in Tdws2GUIFunctions are all used to display different kind of message boxes.
If you place this component to the form several utility functions are added to every TDelphiWebScriptII component on the form. The utility functions in Tdws2FileFunctions are all used to read from or write to files.
Tdws2SimpleDebugger is the simplest possible debugger that you can imagine. It does nothing else than converting the calls to the debugger to events. More usable debuggers are available on the DWS homepage
property Tdws2SimpleDebugger.OnDebug: TOnDebugEvent;
This event is called for every instruction in the script program.
property Tdws2SimpleDebugger.OnDebugStart: TOnDebugStartStopEvent;
Is called before OnDebug is fired the first time
property Tdws2SimpleDebugger.OnDebugStop: TOnDebugStartStopEvent;
Is fired after OnDebug was fired the last time
property Tdws2SimpleDebugger.OnEnterFunc: TOnDebugEvent;
Is fired everytime a function is called
property Tdws2SimpleDebugger.OnLeaveFunc: TOnDebugEvent;
Is fired everytime a function call is finished.
If you declare functions or methods in a Tdws2Unit and also in the built-in function a TProgramInfo object is used to exchange data with the running script program. You can read and write parameteres, global variables and set the result value. But you can also call functions and create objects.
For the following explanations we assume that you have defined a function "Func" in a Tdws2Unit. The following code could be part of the OnEval event handler:
procedure TForm1.dws2UnitFunctionsFuncEval(Info: TProgramInfo);
begin
...
end;
The following declaration are supposed to be made in a Tdws2Unit but are given in DWSII syntax:
type TPoint = record x, y: Integer; end;
type TNames = array[1..10] of String;
var global: string;
function Func(i: Integer; point: TPoint; names: TNames): float;
begin
// OnEval event handler
end;
You can access the parameters and global variables by their name under the same rules as in a normal script function (e. g. you can't read local variables of other procedures).
Read the value of the parameter "i":
x := Info.Vars['i'].Value;
You can also use the default property of TProgram to shortcut the expression above:
x := Info['i'];
Of course you can also assign a value to a local variable
Info['i'] := 12;
Setting a result value for your function is very simple:
Info['result'] := 3.141592;
If the parameter or the variable is of a complex type (array, record) you can read an write values like this:
x := Info.Vars['point'].Member['x'].Value;
Info.Vars['point'].Member['y'].Value := 3;
x := Info.Vars['names'].Element([2]).Value;
Info.Vars['names'].Element([2]).Value := 'Hello World';
Of course it's also possible to cascade such expressions:
Info.Vars['xyz'].Element([2, 4, 5]).Member['aaa'].Element([1]).Value :=
'Hello World';
You can also call functions using the TProgramInfo object:
Info.Func['IntToStr'].Call([345]);
Another possibility to call a function is given below.
var
inf: IInfo;
res: Integer;
begin
inf := Info.Func['StrToInt'];
inf.Paramter['value'] := '123';
res := inf.Call.Value;
Use this way if you have to call a function that uses var-parameters and you're interested in the output value. You can use the "Parameter" property after the function call to read the output-value of the var-parameter.
You can also create objects using TProgramInfo. It's also possible to read and write properties and fields and to call methods.
var
inf: IInfo;
begin
inf := Info.Vars['TObject'].Method['Create'].Call;
inf.Member['field'].Value := 'Hello';
inf.Method['Free'].Call;
If you want to debug a DWSII script program you need a debugger component. A very simple debugger component is part of the DWSII package: Tdws2SimpleDebugger. More useful debugger components are available on the DWS homepage. To enable the debugging mode you have to assign an object that implements the IDebugger interface to TProgram.Debugger. For information about how to use the debugger please read the manual of the debugger component.
An adapter is also a component and has to be a descendant of Tdws2Adapter. An adapter is responsible for two different tasks:
The "naked" DWSII component can't talk to the outer world. You have to add functions to a Tdws2Unit component or to assign an adapter. But you may also want to use COM objects in you DWSII scripts or to call functions of your Delphi program. To connect DWSII to external technologies like COM or RMI the Connector concept was introduced.
A connector is a component that is assigned to the TDelphiWebScriptII component. If the compiler finds an element that belongs to a connector it asks the connector to handle it. The connector returns the "executable code" needed to use this element. The DWSII compiler inserts that "executable code" into the already compiled code not knowing what it exactly does.
At runtime the DWSII code is executed by DWSII and the connector-parts are executed by the responsible connector.