672 lines
17 KiB
ObjectPascal
672 lines
17 KiB
ObjectPascal
{-----------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: JvSAL.PAS, released on 2002-06-15.
|
|
|
|
The Initial Developer of the Original Code is Jan Verhoeven [jan1 dott verhoeven att wxs dott nl]
|
|
Portions created by Jan Verhoeven are Copyright (C) 2002 Jan Verhoeven.
|
|
All Rights Reserved.
|
|
|
|
Contributor(s): Robert Love [rlove att slcdug dott org].
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.sourceforge.net
|
|
|
|
Known Issues:
|
|
-----------------------------------------------------------------------------}
|
|
// $Id: JvSAL.pas,v 1.25 2005/09/15 07:22:55 marquardt Exp $
|
|
|
|
unit JvSAL;
|
|
|
|
{$I jvcl.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF UNITVERSIONING}
|
|
JclUnitVersioning,
|
|
{$ENDIF UNITVERSIONING}
|
|
SysUtils, Classes, Windows, Messages, Graphics, Controls, Forms, Dialogs,
|
|
JvSALHashList, JvStrings, JvComponentBase;
|
|
|
|
const
|
|
StackLimit = 256;
|
|
// message are processed every 250 milliseconds
|
|
// use the stop procedure to stop a locked script
|
|
TimeOut = 250;
|
|
|
|
type
|
|
TOnGetUnitEvent = procedure(Sender: TObject; AUnit: string;
|
|
var AValue: string; var Handled: Boolean) of object;
|
|
|
|
TJvAtom = class(TObject)
|
|
private
|
|
FValue: Variant;
|
|
FActor: TJvSALProc;
|
|
procedure SetActor(const Value: TJvSALProc);
|
|
procedure SetValue(const AValue: Variant);
|
|
public
|
|
property Value: Variant read FValue write SetValue;
|
|
property Actor: TJvSALProc read FActor write SetActor;
|
|
end;
|
|
|
|
TJvSALProcAtom = class(TObject)
|
|
private
|
|
FParser: TJvSALProc;
|
|
FActor: TJvSALProc;
|
|
procedure SetActor(const Value: TJvSALProc);
|
|
procedure SetParser(const Value: TJvSALProc);
|
|
public
|
|
property Actor: TJvSALProc read FActor write SetActor;
|
|
property Parser: TJvSALProc read FParser write SetParser;
|
|
end;
|
|
|
|
TJvAtoms = class(TStringList)
|
|
public
|
|
procedure ClearAll;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TJvSAL = class(TJvComponent)
|
|
private
|
|
FStop: Boolean;
|
|
FCaption: string;
|
|
FSP: Integer;
|
|
FRSP: Integer;
|
|
FBSP: Integer;
|
|
FStack: array [0..StackLimit] of Variant;
|
|
FBStack: array [0..StackLimit] of Boolean;
|
|
FRStack: array [0..StackLimit] of Integer;
|
|
FProcs: TJvSALHashList;
|
|
FScript: string;
|
|
FUnits: TStringList;
|
|
FTicks: cardinal;
|
|
FOnGetUnit: TOnGetUnitEvent;
|
|
FVariableName: string;
|
|
FVariable: TJvAtom;
|
|
FSelection: Variant;
|
|
FUseDirective: string;
|
|
FBeginOfComment: string;
|
|
FEndOfComment: string;
|
|
FStringDelimiter: string;
|
|
FPC: Integer;
|
|
FAtoms: TJvAtoms;
|
|
FPCProc: Integer;
|
|
FToken: string;
|
|
procedure SetScript(const Value: string);
|
|
procedure SetGetUnit(const Value: TOnGetUnitEvent);
|
|
procedure SetVariable(const Value: TJvAtom);
|
|
procedure SetVariableName(const Value: string);
|
|
procedure SetSelection(const Value: Variant);
|
|
procedure SetUseDirective(const Value: string);
|
|
procedure SetBeginOfComment(const Value: string);
|
|
procedure SetEndOfComment(const Value: string);
|
|
procedure SetStringDelimiter(const Value: string);
|
|
procedure SetPC(const Value: Integer);
|
|
procedure SetToken(const Value: string);
|
|
procedure SetCaption(const Value: string);
|
|
protected
|
|
procedure ParseScript;
|
|
// return FStack methods
|
|
// SAL language
|
|
procedure xBoSub;
|
|
procedure xEoSub;
|
|
procedure xValue;
|
|
procedure xDefVariable;
|
|
procedure xVariable;
|
|
procedure xProc;
|
|
procedure xNoParser;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure ClearProcedures;
|
|
procedure AddProcedure(AName: string; AProcedure, AParser: TJvSALProc);
|
|
function APO(Op: string; AProc: TJvSALProc): Integer;
|
|
procedure Push(AValue: Variant);
|
|
function Pop: Variant;
|
|
procedure RPush(AValue: Integer);
|
|
function RPop: Integer;
|
|
procedure BoolPush(AValue: Boolean);
|
|
function BoolPop: Boolean;
|
|
procedure LoadFromFile(FileName: string);
|
|
procedure Execute;
|
|
procedure Stop;
|
|
property PC: Integer read FPC write SetPC;
|
|
property Atoms: TJvAtoms read FAtoms;
|
|
property PCProc: Integer read FPCProc;
|
|
property Token: string read FToken write SetToken;
|
|
property Script: string read FScript write SetScript;
|
|
property Caption: string read FCaption write SetCaption;
|
|
property Variable: TJvAtom read FVariable write SetVariable;
|
|
property VariableName: string read FVariableName write SetVariableName;
|
|
property TheSelect: Variant read FSelection write SetSelection;
|
|
property UseDirective: string read FUseDirective write SetUseDirective;
|
|
property BeginOfComment: string read FBeginOfComment write SetBeginOfComment;
|
|
property EndOfComment: string read FEndOfComment write SetEndOfComment;
|
|
property StringDelim: string read FStringDelimiter write SetStringDelimiter;
|
|
published
|
|
property OnGetUnit: TOnGetUnitEvent read FOnGetUnit write SetGetUnit;
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
const
|
|
UnitVersioning: TUnitVersionInfo = (
|
|
RCSfile: '$RCSfile: JvSAL.pas,v $';
|
|
Revision: '$Revision: 1.25 $';
|
|
Date: '$Date: 2005/09/15 07:22:55 $';
|
|
LogPath: 'JVCL\run'
|
|
);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
implementation
|
|
|
|
uses
|
|
JvConsts, JvResources, JvTypes;
|
|
|
|
const
|
|
// do not localize these strings
|
|
cSAL = 'SAL';
|
|
cUse = 'use::';
|
|
cLiteral = 'literal';
|
|
cProc = 'proc-';
|
|
cEndProc = 'end-proc';
|
|
cVar = 'var-';
|
|
|
|
//=== { TJvSAL } =============================================================
|
|
|
|
constructor TJvSAL.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FAtoms := TJvAtoms.Create;
|
|
FProcs := TJvSALHashList.Create(ITinyHash, HashSecondaryOne, SameText);
|
|
FUnits := TStringList.Create;
|
|
FCaption := cSAL;
|
|
FUseDirective := cUse;
|
|
FBeginOfComment := '{';
|
|
FEndOfComment := '}';
|
|
FStringDelimiter := '"';
|
|
end;
|
|
|
|
destructor TJvSAL.Destroy;
|
|
begin
|
|
FAtoms.Free;
|
|
FProcs.Free;
|
|
FUnits.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJvSAL.BoolPop: Boolean;
|
|
begin
|
|
Dec(FBSP);
|
|
if FBSP < 0 then
|
|
raise EJVCLException.CreateRes(@RsEBooleanStackUnderflow);
|
|
Result := FBStack[FBSP];
|
|
end;
|
|
|
|
procedure TJvSAL.BoolPush(AValue: Boolean);
|
|
begin
|
|
FBStack[FBSP] := AValue;
|
|
Inc(FBSP);
|
|
if FBSP > StackLimit then
|
|
raise EJVCLException.CreateRes(@RsEBooleanStackOverflow);
|
|
end;
|
|
|
|
procedure TJvSAL.Execute;
|
|
var
|
|
A: TJvAtom;
|
|
C: Integer;
|
|
begin
|
|
PC := 0;
|
|
FSP := 0;
|
|
FRSP := 0;
|
|
FBSP := 0;
|
|
C := Atoms.Count;
|
|
FStop := False;
|
|
FTicks := GetTickCount;
|
|
if C = 0 then
|
|
Exit;
|
|
repeat
|
|
A := TJvAtom(Atoms.Objects[PC]);
|
|
FPCProc := PC;
|
|
Inc(FPC);
|
|
A.Actor;
|
|
if (GetTickCount - FTicks) > TimeOut then
|
|
begin
|
|
FTicks := GetTickCount;
|
|
Application.ProcessMessages;
|
|
end;
|
|
if FStop then
|
|
raise EJVCLException.CreateRes(@RsEProgramStopped);
|
|
until PC >= C;
|
|
end;
|
|
|
|
procedure TJvSAL.ParseScript;
|
|
var
|
|
S: string;
|
|
// iprocs: Integer;
|
|
haveproc: Boolean;
|
|
AActor: TJvSALProc;
|
|
AParser: TJvSALProc;
|
|
I, P, P2: Integer;
|
|
fv: Double;
|
|
A: TJvAtom;
|
|
fn, TheUnit: string;
|
|
Handled: Boolean;
|
|
|
|
function CharFrom(From: Integer; AChar: Char; AText: string): Integer;
|
|
var
|
|
C: Integer;
|
|
begin
|
|
Result := 0;
|
|
C := Length(AText);
|
|
repeat
|
|
if AText[From] = AChar then
|
|
begin
|
|
Result := From;
|
|
Exit;
|
|
end;
|
|
Inc(From);
|
|
until From > C;
|
|
end;
|
|
|
|
begin
|
|
PC := 1;
|
|
S := FScript;
|
|
FUnits.Clear;
|
|
// process any includes
|
|
repeat
|
|
P := Pos(FUseDirective, S); // default use::
|
|
if P > 0 then
|
|
begin
|
|
P2 := CharFrom(P, ' ', S);
|
|
if P2 = 0 then
|
|
raise EJVCLException.CreateResFmt(@RsEUnterminatedIncludeDirectiveNears, [Copy(S, P, 50)]);
|
|
fn := Trim(Copy(S, P + Length(FUseDirective), P2 - P - Length(FUseDirective)));
|
|
if not Assigned(FOnGetUnit) then
|
|
raise EJVCLException.CreateRes(@RsEOngetUnitEventHandlerIsNotAssigned);
|
|
Handled := False;
|
|
fn := LowerCase(fn);
|
|
if FUnits.IndexOf(fn) = -1 then
|
|
begin
|
|
OnGetUnit(Self, fn, TheUnit, Handled);
|
|
if not Handled then
|
|
raise EJVCLException.CreateResFmt(@RsECouldNotIncludeUnits, [fn]);
|
|
TheUnit := StringReplace(TheUnit, Cr, ' ', [rfReplaceAll]);
|
|
Delete(S, P, P2 - P);
|
|
Insert(TheUnit, S, P);
|
|
FUnits.Append(fn);
|
|
end;
|
|
end;
|
|
until P = 0;
|
|
|
|
while S <> '' do
|
|
begin
|
|
if Pos(FBeginOfComment, S) = 1 then
|
|
begin // default= {
|
|
P := Pos(FEndOfComment, S); // default= }
|
|
if P = 0 then
|
|
raise EJVCLException.CreateResFmt(@RsEUnterminatedCommentNears, [S]);
|
|
Delete(S, 1, P + Length(FEndOfComment) - 1);
|
|
S := Trim(S);
|
|
end
|
|
else
|
|
if Pos(FStringDelimiter, S) = 1 then
|
|
begin // default = "
|
|
Delete(S, 1, Length(FStringDelimiter));
|
|
P := Pos(FStringDelimiter, S);
|
|
if P = 0 then
|
|
raise EJVCLException.CreateResFmt(@RsEUnterminatedStringNears, [S]);
|
|
Token := Copy(S, 1, P - 1);
|
|
Delete(S, 1, P + Length(FStringDelimiter) - 1);
|
|
S := Trim(S);
|
|
A := TJvAtom.Create;
|
|
A.Value := Token;
|
|
A.Actor := xValue;
|
|
Atoms.AddObject(cLiteral, A);
|
|
end
|
|
else
|
|
begin
|
|
P := Pos(' ', S);
|
|
if P = 0 then
|
|
begin
|
|
Token := S;
|
|
S := '';
|
|
end
|
|
else
|
|
begin
|
|
Token := Copy(S, 1, P - 1);
|
|
Delete(S, 1, P);
|
|
S := Trim(S);
|
|
end;
|
|
// take care of aliases
|
|
if Token = '.' then
|
|
Token := '+=';
|
|
// check for user procs
|
|
haveproc := FProcs.Hash(Token, AActor, AParser);
|
|
try // float
|
|
fv := StrToFloat(Token);
|
|
A := TJvAtom.Create;
|
|
A.Value := fv;
|
|
A.Actor := xValue;
|
|
Atoms.AddObject(cLiteral, A);
|
|
except
|
|
if Pos(cProc, Token) = 1 then
|
|
begin // begin of procedure
|
|
if Pos(cEndProc, S) = 0 then
|
|
raise EJVCLException.CreateResFmt(@RsEUnterminatedProcedureNears, [S]);
|
|
APO(Token, xBoSub);
|
|
end
|
|
else
|
|
if Token = cEndProc then
|
|
APO(Token, xEoSub)
|
|
else
|
|
if Copy(Token, Length(Token) - 1, 2) = '()' then
|
|
APO(Token, xProc) // proc call
|
|
else
|
|
if Pos(cVar, Token) = 1 then
|
|
begin // define variable
|
|
if Atoms.IndexOf(Token) <> -1 then
|
|
raise EJVCLException.CreateResFmt(@RsEVariablesAllreadyDefineds, [Token, S]);
|
|
A := TJvAtom.Create;
|
|
A.Actor := xDefVariable;
|
|
Atoms.AddObject(Token, A);
|
|
end
|
|
else
|
|
if Token[1] = '$' then
|
|
begin // variable value
|
|
// find address
|
|
I := Atoms.IndexOf(cVar + Copy(Token, 2, MaxInt));
|
|
if I = -1 then
|
|
raise EJVCLException.CreateResFmt(@RsEVariablesIsNotYetDefineds, [Token, S]);
|
|
A := TJvAtom.Create;
|
|
A.Value := I;
|
|
A.Actor := xVariable;
|
|
Atoms.AddObject(Token, A);
|
|
end
|
|
else
|
|
if haveproc then
|
|
begin
|
|
if Assigned(AParser) then
|
|
AParser
|
|
else
|
|
APO(Token, AActor);
|
|
end
|
|
else
|
|
raise EJVCLException.CreateResFmt(@RsEProceduresNears, [Token, S]);
|
|
end
|
|
end
|
|
end;
|
|
|
|
// now resolve procs()
|
|
if Atoms.Count = 0 then
|
|
Exit;
|
|
for I := 0 to Atoms.Count - 1 do
|
|
begin
|
|
S := Atoms[I];
|
|
if Copy(S, Length(S) - 1, 2) = '()' then
|
|
begin
|
|
S := cProc + Copy(S, 1, Length(S) - 2);
|
|
P := Atoms.IndexOf(S);
|
|
if P = -1 then
|
|
raise EJVCLException.CreateResFmt(@RsEUndefinedProcedures, [S]);
|
|
TJvAtom(Atoms.Objects[I]).Value := P;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TJvSAL.Pop: Variant;
|
|
begin
|
|
Dec(FSP);
|
|
if FSP < 0 then
|
|
raise EJVCLException.CreateRes(@RsEStackUnderflow);
|
|
Result := FStack[FSP];
|
|
end;
|
|
|
|
procedure TJvSAL.Push(AValue: Variant);
|
|
begin
|
|
FStack[FSP] := AValue;
|
|
Inc(FSP);
|
|
if FSP > StackLimit then
|
|
raise EJVCLException.CreateRes(@RsEStackOverflow);
|
|
end;
|
|
|
|
procedure TJvSAL.SetScript(const Value: string);
|
|
begin
|
|
FScript := Trim(StringReplace(Value, Cr, ' ', [rfReplaceAll]));
|
|
Atoms.ClearAll;
|
|
ParseScript;
|
|
end;
|
|
|
|
procedure TJvSAL.xDefVariable;
|
|
var
|
|
A: TJvAtom;
|
|
begin
|
|
A := TJvAtom(Atoms.Objects[PCProc]);
|
|
FVariableName := Atoms[PCProc];
|
|
FVariableName := '$' + Copy(FVariableName, 5, MaxInt);
|
|
FVariable := A;
|
|
end;
|
|
|
|
procedure TJvSAL.xValue;
|
|
begin
|
|
Push(TJvAtom(Atoms.Objects[PCProc]).Value);
|
|
end;
|
|
|
|
procedure TJvSAL.xVariable;
|
|
var
|
|
Index: Integer;
|
|
A: TJvAtom;
|
|
begin
|
|
A := TJvAtom(Atoms.Objects[PCProc]);
|
|
VariableName := Atoms[PCProc];
|
|
Index := A.Value;
|
|
Variable := TJvAtom(Atoms.Objects[Index]);
|
|
end;
|
|
|
|
procedure TJvSAL.Stop;
|
|
begin
|
|
FStop := True;
|
|
end;
|
|
|
|
procedure TJvSAL.LoadFromFile(FileName: string);
|
|
begin
|
|
Script := Loadstring(FileName);
|
|
end;
|
|
|
|
procedure TJvSAL.ClearProcedures;
|
|
begin
|
|
// FProcs.ClearAll;
|
|
FProcs.Clear;
|
|
end;
|
|
|
|
procedure TJvSAL.AddProcedure(AName: string; AProcedure, AParser: TJvSALProc);
|
|
//var
|
|
// A: TJvSALProcAtom;
|
|
begin
|
|
// A:=TJvSALProcAtom.Create;
|
|
// A.Actor:=AProcedure;
|
|
// A.Parser:=AParser;
|
|
// FProcs.AddObject(AName,A);
|
|
FProcs.AddString(AName, AProcedure, AParser);
|
|
end;
|
|
|
|
function TJvSAL.RPop: Integer;
|
|
begin
|
|
Dec(FRSP);
|
|
if FRSP < 0 then
|
|
raise EJVCLException.CreateRes(@RsEReturnStackUnderflow);
|
|
Result := FRStack[FRSP];
|
|
end;
|
|
|
|
procedure TJvSAL.RPush(AValue: Integer);
|
|
begin
|
|
FRStack[FRSP] := AValue;
|
|
Inc(FRSP);
|
|
if FRSP > StackLimit then
|
|
raise EJVCLException.CreateRes(@RsEReturnStackOverflow);
|
|
end;
|
|
|
|
// end of subroutine, marked with end-proc
|
|
|
|
procedure TJvSAL.xEoSub;
|
|
begin
|
|
PC := RPop;
|
|
end;
|
|
|
|
// begin of subroutine, marked with [
|
|
// loop to ]
|
|
|
|
procedure TJvSAL.xBoSub;
|
|
var
|
|
Op: string;
|
|
C: Integer;
|
|
begin
|
|
C := Atoms.Count;
|
|
repeat
|
|
Op := Atoms[PC];
|
|
Inc(FPC);
|
|
if Op = cEndProc then
|
|
Exit;
|
|
until PC >= C;
|
|
raise EJVCLException.CreateRes(@RsECouldNotFindEndOfProcedure);
|
|
end;
|
|
|
|
procedure TJvSAL.SetGetUnit(const Value: TOnGetUnitEvent);
|
|
begin
|
|
FOnGetUnit := Value;
|
|
end;
|
|
|
|
// function call
|
|
|
|
procedure TJvSAL.xProc;
|
|
var
|
|
Index: Integer;
|
|
begin
|
|
Index := TJvAtom(Atoms.Objects[PCProc]).Value;
|
|
RPush(PC);
|
|
PC := Index + 1;
|
|
end;
|
|
|
|
procedure TJvSAL.SetVariable(const Value: TJvAtom);
|
|
begin
|
|
FVariable := Value;
|
|
end;
|
|
|
|
procedure TJvSAL.SetVariableName(const Value: string);
|
|
begin
|
|
FVariableName := Value;
|
|
end;
|
|
|
|
procedure TJvSAL.SetSelection(const Value: Variant);
|
|
begin
|
|
FSelection := Value;
|
|
end;
|
|
|
|
procedure TJvSAL.SetUseDirective(const Value: string);
|
|
begin
|
|
FUseDirective := Value;
|
|
end;
|
|
|
|
procedure TJvSAL.SetBeginOfComment(const Value: string);
|
|
begin
|
|
FBeginOfComment := Value;
|
|
end;
|
|
|
|
procedure TJvSAL.SetEndOfComment(const Value: string);
|
|
begin
|
|
FEndOfComment := Value;
|
|
end;
|
|
|
|
procedure TJvSAL.SetStringDelimiter(const Value: string);
|
|
begin
|
|
FStringDelimiter := Value;
|
|
end;
|
|
|
|
procedure TJvSAL.SetPC(const Value: Integer);
|
|
begin
|
|
FPC := Value;
|
|
end;
|
|
|
|
function TJvSAL.APO(Op: string; AProc: TJvSALProc): Integer;
|
|
var
|
|
A: TJvAtom;
|
|
begin
|
|
A := TJvAtom.Create;
|
|
A.Actor := AProc;
|
|
Result := Atoms.AddObject(Op, A);
|
|
end;
|
|
|
|
procedure TJvSAL.SetToken(const Value: string);
|
|
begin
|
|
FToken := Value;
|
|
end;
|
|
|
|
procedure TJvSAL.SetCaption(const Value: string);
|
|
begin
|
|
FCaption := Value;
|
|
end;
|
|
|
|
procedure TJvSAL.xNoParser;
|
|
begin
|
|
// do nothing
|
|
end;
|
|
|
|
//=== { TJvAtom } ============================================================
|
|
|
|
procedure TJvAtom.SetActor(const Value: TJvSALProc);
|
|
begin
|
|
FActor := Value;
|
|
end;
|
|
|
|
procedure TJvAtom.SetValue(const AValue: Variant);
|
|
begin
|
|
FValue := AValue;
|
|
end;
|
|
|
|
//=== { TJvAtoms } ===========================================================
|
|
|
|
destructor TJvAtoms.Destroy;
|
|
begin
|
|
ClearAll;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJvAtoms.ClearAll;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to Count - 1 do
|
|
TJvAtom(Objects[I]).Free;
|
|
Clear;
|
|
end;
|
|
|
|
//=== { TJvSALProcAtom } =====================================================
|
|
|
|
procedure TJvSALProcAtom.SetActor(const Value: TJvSALProc);
|
|
begin
|
|
FActor := Value;
|
|
end;
|
|
|
|
procedure TJvSALProcAtom.SetParser(const Value: TJvSALProc);
|
|
begin
|
|
FParser := Value;
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
initialization
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
|
|
|
finalization
|
|
UnregisterUnitVersion(HInstance);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
end.
|
|
|