mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-10 07:28:12 +02:00
2620 lines
67 KiB
ObjectPascal
2620 lines
67 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
TCTConfigScriptEngine implements an interpreter for a simple scripting
|
|
language, enough for configurations.
|
|
|
|
Working:
|
|
if, then, else, begin..end, ;
|
|
()
|
|
boolean operators: not, and, or, xor
|
|
operators: =, <>, >, <, <=, >=, +
|
|
variables
|
|
constants: decimal, $hex, &octal, %binary, 'string', #character
|
|
functions: string(), integer(), int64(), defined(), undefined()
|
|
procedures: undefine()
|
|
assignments: :=, +=
|
|
|
|
Not supported:
|
|
- floats
|
|
- types
|
|
- objects
|
|
- loops
|
|
- custom functions
|
|
}
|
|
unit CodeToolsCfgScript;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$inline on}
|
|
|
|
{off $Define VerboseCTCfgScript}
|
|
{off $DEFINE CheckCTCfgVars}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, TypInfo, AVL_Tree,
|
|
// Codetools
|
|
BasicCodeTools, KeywordFuncLists, FileProcs, CodeToolsStrConsts;
|
|
|
|
type
|
|
ECodeToolCfgScript = class(Exception);
|
|
|
|
TCTCSValueType = (
|
|
ctcsvNone,
|
|
ctcsvString,
|
|
ctcsvNumber
|
|
);
|
|
|
|
{ TCTCfgScriptVariable }
|
|
|
|
TCTCfgScriptVariable = record
|
|
Name: PChar;
|
|
ValueType: TCTCSValueType;
|
|
case Integer of
|
|
0: (StrStart: PChar; StrLen: integer);
|
|
1: (Number: int64);
|
|
end;
|
|
PCTCfgScriptVariable = ^TCTCfgScriptVariable;
|
|
|
|
|
|
{ TCTCfgScriptVariables }
|
|
|
|
TCTCfgScriptVariables = class
|
|
private
|
|
FItems: TAVLTree; // tree of PCTCfgScriptVariable sorted for name
|
|
function GetValues(const Name: string): string;
|
|
procedure SetValues(const Name: string; const AValue: string);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
function Equals(Vars: TCTCfgScriptVariables): boolean; reintroduce;
|
|
procedure Assign(Source: TCTCfgScriptVariables); overload;
|
|
procedure Assign(Source: TStrings); overload;
|
|
procedure AddOverrides(Source: TCTCfgScriptVariables);
|
|
procedure AddOverride(Source: PCTCfgScriptVariable);
|
|
function GetVariable(const Name: PChar;
|
|
CreateIfNotExists: Boolean = false): PCTCfgScriptVariable;
|
|
property Values[const Name: string]: string read GetValues write SetValues; default;
|
|
procedure Undefine(Name: PChar);
|
|
procedure Define(Name: PChar; const Value: string);
|
|
function IsDefined(Name: PChar): boolean;
|
|
property Tree: TAVLTree read FItems;
|
|
procedure WriteDebugReport(const Title: string; const Prefix: string = '');
|
|
end;
|
|
PCTCfgScriptVariables = ^TCTCfgScriptVariables;
|
|
|
|
type
|
|
TCTCfgScriptOperator = (
|
|
ctcsoNone,
|
|
ctcsoNot,
|
|
ctcsoAnd,
|
|
ctcsoOr,
|
|
ctcsoXOr,
|
|
ctcsoShL,
|
|
ctcsoShR,
|
|
ctcsoDiv,
|
|
ctcsoMod,
|
|
ctcsoPlus,
|
|
ctcsoMinus,
|
|
ctcsoMultiply,
|
|
ctcsoDivide,
|
|
ctcsoEqual,
|
|
ctcsoNotEqual,
|
|
ctcsoLowerThan,
|
|
ctcsoLowerOrEqualThan,
|
|
ctcsoGreaterThan,
|
|
ctcsoGreaterOrEqualThan
|
|
);
|
|
TCTCfgScriptOperators = set of TCTCfgScriptOperator;
|
|
const
|
|
CTCfgScriptOperatorLvl: array[TCTCfgScriptOperator] of integer = (
|
|
0, //ctcsoNone,
|
|
1, //ctcsoNot,
|
|
1, //ctcsoAnd,
|
|
2, //ctcsoOr,
|
|
2, //ctcsoXOr,
|
|
1, //ctcsoShL,
|
|
1, //ctcsoShR,
|
|
1, //ctcsoDiv,
|
|
1, //ctcsoMod,
|
|
2, //ctcsoPlus,
|
|
2, //ctcsoMinus,
|
|
1, //ctcsoMultiply,
|
|
1, //ctcsoDivide,
|
|
4, //ctcsoEqual,
|
|
4, //ctcsoNotEqual,
|
|
4, //ctcsoLowerThan,
|
|
4, //ctcsoLowerOrEqualThan,
|
|
4, //ctcsoGreaterThan,
|
|
4 //ctcsoGreaterOrEqualThan
|
|
);
|
|
type
|
|
TCTCfgScriptStackItemType = (
|
|
ctcssNone,
|
|
ctcssStatement,
|
|
ctcssBegin,
|
|
ctcssIf,
|
|
ctcssIfThen,
|
|
ctcssIfElse,
|
|
ctcssExpression,
|
|
ctcssRoundBracketOpen,
|
|
ctcssOperand,
|
|
ctcssOperator,
|
|
ctcssAssignment
|
|
);
|
|
const
|
|
ctcssAllStatementStarts = [ctcssNone,ctcssIfThen,ctcssIfElse,ctcssBegin];
|
|
type
|
|
TCTCfgScriptStackItem = record
|
|
Typ: TCTCfgScriptStackItemType;
|
|
StartPos: PChar;
|
|
Operand: TCTCfgScriptVariable;
|
|
end;
|
|
PCTCfgScriptStackItem = ^TCTCfgScriptStackItem;
|
|
|
|
type
|
|
{ TCTCfgScriptStack }
|
|
|
|
TCTCfgScriptStack = class
|
|
public
|
|
Items: PCTCfgScriptStackItem;
|
|
Top: integer; // current item, -1 = empty
|
|
TopTyp: TCTCfgScriptStackItemType;
|
|
Capacity: integer;
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
procedure Push(Typ: TCTCfgScriptStackItemType; const StartPos: PChar);
|
|
procedure Pop(Count: integer = 1);
|
|
procedure Delete(Index: integer);
|
|
function TopItem: PCTCfgScriptStackItem;
|
|
function TopItemOperand: PCTCfgScriptVariable;
|
|
{$IFDEF CheckCTCfgVars}
|
|
procedure CheckOperands;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TCTCfgScriptError }
|
|
|
|
TCTCfgScriptError = class
|
|
public
|
|
Msg: string;
|
|
Position: integer;
|
|
Line: integer;
|
|
Column: integer;
|
|
constructor Create(const aMsg: string; aPos, aLine, aCol: integer);
|
|
constructor Create(const aMsg: string);
|
|
end;
|
|
|
|
{ TCTConfigScriptEngine }
|
|
|
|
TCTConfigScriptEngine = class
|
|
protected
|
|
FVariables: TCTCfgScriptVariables;
|
|
FStack: TCTCfgScriptStack;
|
|
FErrors: TFPList; // list of TCTCfgScriptError
|
|
function GetErrors(Index: integer): TCTCfgScriptError;
|
|
procedure AddError(const aMsg: string; ErrorPos: PChar); overload;
|
|
procedure AddError(const aMsg: string); overload;
|
|
procedure PushNumberConstant;
|
|
procedure PushBooleanValue(b: boolean);
|
|
procedure PushNumberValue(const Number: int64);
|
|
function RunDefined(Negate: boolean): boolean;
|
|
function RunFunction: boolean;
|
|
procedure PushStringConstant;
|
|
procedure RunStatement(Skip: boolean);
|
|
procedure RunBegin(Skip: boolean);
|
|
procedure RunIf(Skip: boolean);
|
|
procedure RunUndefine(Skip: boolean);
|
|
procedure RunAssignment(Skip: boolean);
|
|
function RunExpression: boolean; // if true the stack top has an operand
|
|
function ExecuteStack(MaxLevel: integer): boolean;
|
|
function GetOperatorLevel(P: PChar): integer;
|
|
function IsKeyWord(P: PChar): boolean;
|
|
function IsFunction(P: PChar): boolean;
|
|
function IsCustomFunction({%H-}FunctionName: PChar): boolean; virtual;
|
|
procedure RunCustomSimpleFunction({%H-}FunctionName: PChar; {%H-}Value: PCTCfgScriptVariable); virtual;
|
|
public
|
|
Src: PChar;
|
|
AtomStart: PChar;
|
|
SrcStart, SrcEnd: PChar;
|
|
MaxErrorCount: integer;
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure ClearErrors;
|
|
property Variables: TCTCfgScriptVariables read FVariables;
|
|
function Execute(const Source: string; StopAfterErrors: integer = 1): boolean;// true if no errors
|
|
function ErrorCount: integer;
|
|
property Errors[Index: integer]: TCTCfgScriptError read GetErrors;
|
|
function GetAtom: string;
|
|
function GetAtomOrNothing: string;
|
|
function GetAtom(P: PChar): string;
|
|
function PosToLineCol(p: PChar; out Line, Column: integer): boolean;
|
|
function PosToStr(p: PChar): string;
|
|
function GetErrorStr(Index: integer): string;
|
|
|
|
procedure WriteDebugReportStack(Title: string);
|
|
end;
|
|
|
|
procedure RenameCTCSVariable(var Src: string; const OldName, NewName: string);
|
|
|
|
function CompareCTCSVariables(Var1, Var2: Pointer): integer;
|
|
function ComparePCharWithCTCSVariableName(Name, aVar: Pointer): integer;
|
|
function AreCTCSVariablesEqual(const V1, V2: PCTCfgScriptVariable): Boolean;
|
|
function AreCTCSVariablesExactEqual(const V1, V2: PCTCfgScriptVariable): Boolean;
|
|
function CompareCTCSVariables(const Left, Right: PCTCfgScriptVariable;
|
|
out Equal, LeftIsLowerThanRight: boolean): boolean;
|
|
function NewCTCSVariable: PCTCfgScriptVariable;
|
|
function NewCTCSVariable(CloneName: PChar): PCTCfgScriptVariable;
|
|
function CloneCTCSVariable(const V: PCTCfgScriptVariable): PCTCfgScriptVariable;
|
|
procedure FreeCTCSVariable(var V: PCTCfgScriptVariable);
|
|
procedure ClearCTCSVariable(const V: PCTCfgScriptVariable);
|
|
procedure SetCTCSVariableAsString(const V: PCTCfgScriptVariable; const s: string);
|
|
procedure SetCTCSVariableAsNumber(const V: PCTCfgScriptVariable; const i: int64);
|
|
procedure SetCTCSVariableValue(const Src, Dest: PCTCfgScriptVariable);
|
|
function GetCTCSVariableAsString(const V: PCTCfgScriptVariable): string;
|
|
procedure MakeCTCSVariableString(const V: PCTCfgScriptVariable);
|
|
procedure MakeCTCSVariableInt64(const V: PCTCfgScriptVariable);
|
|
procedure MakeCTCSVariableInteger(const V: PCTCfgScriptVariable);
|
|
procedure AddCTCSVariables(const AddVar, SumVar: PCTCfgScriptVariable);
|
|
function CTCSNumberEqualsString(const Number: int64; const P: PChar): boolean; inline;
|
|
function CTCSVariableIsTrue(const V: PCTCfgScriptVariable): boolean; inline;
|
|
function CTCSVariableIsFalse(const V: PCTCfgScriptVariable): boolean;
|
|
|
|
function CTCSStringToNumber(P: PChar; out Number: int64): boolean;
|
|
function AtomToCTCfgOperator(p: PChar): TCTCfgScriptOperator;
|
|
|
|
procedure CheckCTCSVariable(const V: PCTCfgScriptVariable);
|
|
function dbgs(const t: TCTCfgScriptStackItemType): string; overload;
|
|
function dbgs(const t: TCTCSValueType): string; overload;
|
|
function dbgs(const t: TCTCfgScriptOperator): string; overload;
|
|
function dbgs(const V: PCTCfgScriptVariable): string; overload;
|
|
|
|
|
|
implementation
|
|
|
|
procedure RenameCTCSVariable(var Src: string; const OldName, NewName: string);
|
|
var
|
|
p: PChar;
|
|
AtomStart: PChar;
|
|
SrcPos: PtrUInt;
|
|
begin
|
|
if (Src='') or not IsValidIdent(OldName) or (NewName='') then exit;
|
|
p:=PChar(Src);
|
|
//debugln(['RenameCTCSVariable START ',dbgstr(Src)]);
|
|
repeat
|
|
ReadRawNextPascalAtom(p,AtomStart,nil,false,true);
|
|
if (p=AtomStart) then break;
|
|
if IsIdentStartChar[AtomStart^]
|
|
and (CompareIdentifiers(PChar(OldName),AtomStart)=0)
|
|
then begin
|
|
SrcPos:=PtrUInt(AtomStart-PChar(Src))+1;
|
|
|
|
Src:=copy(Src,1,SrcPos-1)+NewName+copy(Src,SrcPos+PtrUInt(length(OldName)),length(Src));
|
|
p:=@Src[SrcPos]+length(NewName);
|
|
end;
|
|
until false;
|
|
//debugln(['RenameCTCSVariable END ',dbgstr(Src)]);
|
|
end;
|
|
|
|
function CompareCTCSVariables(Var1, Var2: Pointer): integer;
|
|
var
|
|
v1: PCTCfgScriptVariable absolute Var1;
|
|
v2: PCTCfgScriptVariable absolute Var2;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(v1);
|
|
CheckCTCSVariable(v2);
|
|
{$ENDIF}
|
|
Result:=CompareIdentifiers(v1^.Name,v2^.Name);
|
|
end;
|
|
|
|
function ComparePCharWithCTCSVariableName(Name, aVar: Pointer): integer;
|
|
var
|
|
n: PChar absolute Name;
|
|
v: PCTCfgScriptVariable absolute aVar;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}CheckCTCSVariable(v);{$ENDIF}
|
|
Result:=CompareIdentifiers(n,v^.Name);
|
|
end;
|
|
|
|
function AreCTCSVariablesEqual(const V1, V2: PCTCfgScriptVariable): Boolean;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(v1);
|
|
CheckCTCSVariable(v2);
|
|
{$ENDIF}
|
|
Result:=false;
|
|
case V1^.ValueType of
|
|
ctcsvNone:
|
|
exit; // invalid is never equal to anything
|
|
ctcsvString:
|
|
case V2^.ValueType of
|
|
ctcsvNone: exit;
|
|
ctcsvString:
|
|
if (V1^.StrLen<>V2^.StrLen)
|
|
or ((V1^.StrStart<>nil)
|
|
and (not CompareMem(V1^.StrStart,V2^.StrStart,V1^.StrLen)))
|
|
then exit;
|
|
ctcsvNumber:
|
|
if not CTCSNumberEqualsString(V2^.Number,V1^.StrStart) then exit;
|
|
end;
|
|
ctcsvNumber:
|
|
case V2^.ValueType of
|
|
ctcsvNone: exit;
|
|
ctcsvString:
|
|
if not CTCSNumberEqualsString(V1^.Number,V2^.StrStart) then exit;
|
|
ctcsvNumber:
|
|
if V1^.Number<>V2^.Number then exit;
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function AreCTCSVariablesExactEqual(const V1, V2: PCTCfgScriptVariable
|
|
): Boolean;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(v1);
|
|
CheckCTCSVariable(v2);
|
|
{$ENDIF}
|
|
Result:=false;
|
|
if V1^.ValueType<>V2^.ValueType then exit;
|
|
case V1^.ValueType of
|
|
ctcsvNone: ;
|
|
ctcsvString: if (V1^.StrLen<>V2^.StrLen)
|
|
or ((V1^.StrStart<>nil)
|
|
and (not CompareMem(V1^.StrStart,V2^.StrStart,V1^.StrLen)))
|
|
then exit;
|
|
ctcsvNumber: if V1^.Number<>V2^.Number then exit;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function CompareCTCSVariables(const Left, Right: PCTCfgScriptVariable; out
|
|
Equal, LeftIsLowerThanRight: boolean): boolean;
|
|
{ Rules:
|
|
If one of the values is invalid, return false
|
|
If both are numbers, compare as numbers
|
|
Otherwise compare as string alphabetically case sensitive A<B, A<AA
|
|
}
|
|
|
|
procedure CompareNumberWithString(Number: int64; p: PChar);
|
|
var
|
|
i: Integer;
|
|
Cnt: integer;
|
|
s: array[0..30] of char;
|
|
begin
|
|
if p=nil then begin
|
|
Equal:=false;
|
|
LeftIsLowerThanRight:=false;
|
|
exit;
|
|
end;
|
|
// convert number to decimal string
|
|
if Number=0 then begin
|
|
Cnt:=1;
|
|
s[0]:='0';
|
|
end else begin
|
|
Cnt:=0;
|
|
if Number<0 then begin
|
|
Cnt:=1;
|
|
s[0]:='-';
|
|
Number:=-Number;
|
|
end;
|
|
while Number>0 do begin
|
|
s[Cnt]:=chr(Number mod 10+ord('0'));
|
|
inc(Cnt);
|
|
Number:=Number div 10;
|
|
end;
|
|
end;
|
|
for i:=0 to Cnt-1 do begin
|
|
if p^<>s[i] then begin
|
|
Equal:=false;
|
|
LeftIsLowerThanRight:=s[i]<p^;
|
|
exit;
|
|
end;
|
|
inc(p);
|
|
end;
|
|
if p^=#0 then begin
|
|
Equal:=true;
|
|
LeftIsLowerThanRight:=false;
|
|
end else begin
|
|
Equal:=False;
|
|
LeftIsLowerThanRight:=true;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
V1: PChar;
|
|
V2: PChar;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(Left);
|
|
CheckCTCSVariable(Right);
|
|
{$ENDIF}
|
|
//debugln(['CompareCTCSVariables START Left=',dbgs(Left),' Right=',dbgs(Right)]);
|
|
Result:=false;
|
|
Equal:=false;
|
|
LeftIsLowerThanRight:=false;
|
|
case Left^.ValueType of
|
|
ctcsvNone:
|
|
exit; // invalid is never equal to anything
|
|
ctcsvString:
|
|
case Right^.ValueType of
|
|
ctcsvNone: exit;
|
|
ctcsvString:
|
|
begin
|
|
// compare two strings
|
|
V1:=Left^.StrStart;
|
|
V2:=Right^.StrStart;
|
|
if V1=nil then begin
|
|
if V2=nil then begin
|
|
Equal:=true;
|
|
LeftIsLowerThanRight:=false;
|
|
end else begin
|
|
Equal:=False;
|
|
LeftIsLowerThanRight:=true; // left is shorter than right
|
|
end;
|
|
end else begin
|
|
if V2=nil then begin
|
|
Equal:=False;
|
|
LeftIsLowerThanRight:=false; // left is longer than right
|
|
end else begin
|
|
repeat
|
|
if V1^=V2^ then begin
|
|
if V1^=#0 then begin
|
|
Equal:=true;
|
|
LeftIsLowerThanRight:=false;
|
|
break;
|
|
end else begin
|
|
inc(V1);
|
|
inc(V2);
|
|
end;
|
|
end else begin
|
|
Equal:=false;
|
|
LeftIsLowerThanRight:=V1^<V2^;
|
|
break;
|
|
end;
|
|
until false;
|
|
end;
|
|
end;
|
|
end;
|
|
ctcsvNumber:
|
|
begin
|
|
CompareNumberWithString(Right^.Number,Left^.StrStart);
|
|
LeftIsLowerThanRight:=not LeftIsLowerThanRight;
|
|
end;
|
|
end;
|
|
ctcsvNumber:
|
|
case Right^.ValueType of
|
|
ctcsvNone: exit;
|
|
ctcsvString:
|
|
CompareNumberWithString(Left^.Number,Right^.StrStart);
|
|
ctcsvNumber:
|
|
begin
|
|
Equal:=Left^.Number=Right^.Number;
|
|
LeftIsLowerThanRight:=Left^.Number<Right^.Number;
|
|
end;
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function NewCTCSVariable: PCTCfgScriptVariable;
|
|
begin
|
|
New(Result);
|
|
FillByte(Result^,SizeOf(TCTCfgScriptVariable),0);
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(Result);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function NewCTCSVariable(CloneName: PChar): PCTCfgScriptVariable;
|
|
var
|
|
l: LongInt;
|
|
begin
|
|
Result:=NewCTCSVariable();
|
|
l:=GetIdentLen(CloneName);
|
|
if l>0 then begin
|
|
Result^.Name:=GetMem(l+1);
|
|
System.Move(CloneName^,Result^.Name^,l);
|
|
Result^.Name[l]:=#0;
|
|
end;
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(Result);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function CloneCTCSVariable(const V: PCTCfgScriptVariable): PCTCfgScriptVariable;
|
|
var
|
|
l: LongInt;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(v);
|
|
{$ENDIF}
|
|
Result:=NewCTCSVariable(V^.Name);
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(Result);
|
|
{$ENDIF}
|
|
Result^.ValueType:=V^.ValueType;
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(Result);
|
|
{$ENDIF}
|
|
case V^.ValueType of
|
|
ctcsvNone: ;
|
|
ctcsvString:
|
|
begin
|
|
l:=V^.StrLen;
|
|
if l>0 then begin
|
|
Result^.StrLen:=l;
|
|
Result^.StrStart:=GetMem(l+1);
|
|
System.Move(V^.StrStart^,Result^.StrStart^,l);
|
|
Result^.StrStart[l]:=#0;
|
|
end;
|
|
end;
|
|
ctcsvNumber:
|
|
Result^.Number:=V^.Number;
|
|
end;
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(Result);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure SetCTCSVariableValue(const Src, Dest: PCTCfgScriptVariable);
|
|
var
|
|
l: LongInt;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(Src);
|
|
CheckCTCSVariable(Dest);
|
|
{$ENDIF}
|
|
if Src=Dest then exit;
|
|
case Src^.ValueType of
|
|
ctcsvNone:
|
|
ClearCTCSVariable(Dest);
|
|
ctcsvString:
|
|
begin
|
|
if Dest^.ValueType<>ctcsvString then begin
|
|
Dest^.ValueType:=ctcsvString;
|
|
Dest^.StrStart:=nil;
|
|
end;
|
|
l:=Src^.StrLen;
|
|
Dest^.StrLen:=l;
|
|
if l>0 then begin
|
|
ReAllocMem(Dest^.StrStart,l+1);
|
|
System.Move(Src^.StrStart^,Dest^.StrStart^,l);
|
|
Dest^.StrStart[l]:=#0;
|
|
end else
|
|
ReAllocMem(Dest^.StrStart,0);
|
|
end;
|
|
ctcsvNumber:
|
|
begin
|
|
case Dest^.ValueType of
|
|
ctcsvNone:
|
|
Dest^.ValueType:=ctcsvNumber;
|
|
ctcsvString:
|
|
begin
|
|
Dest^.ValueType:=ctcsvNumber;
|
|
if Dest^.StrStart<>nil then
|
|
Freemem(Dest^.StrStart);
|
|
end;
|
|
ctcsvNumber: ;
|
|
end;
|
|
Dest^.Number:=Src^.Number;
|
|
end;
|
|
end;
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(Src);
|
|
CheckCTCSVariable(Dest);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure FreeCTCSVariable(var V: PCTCfgScriptVariable);
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(v);
|
|
{$ENDIF}
|
|
ClearCTCSVariable(V);
|
|
ReAllocMem(V^.Name,0);
|
|
Dispose(V);
|
|
end;
|
|
|
|
procedure ClearCTCSVariable(const V: PCTCfgScriptVariable);
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(v);
|
|
{$ENDIF}
|
|
if V^.ValueType=ctcsvString then
|
|
ReAllocMem(V^.StrStart,0);
|
|
V^.ValueType:=ctcsvNone;
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(v);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure MakeCTCSVariableString(const V: PCTCfgScriptVariable);
|
|
var
|
|
s: String;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(V);
|
|
{$ENDIF}
|
|
case V^.ValueType of
|
|
ctcsvNone:
|
|
begin
|
|
V^.StrLen:=0;
|
|
V^.StrStart:=nil;
|
|
V^.ValueType:=ctcsvString;
|
|
end;
|
|
ctcsvString: ;
|
|
ctcsvNumber:
|
|
begin
|
|
s:=IntToStr(V^.Number);
|
|
V^.StrLen:=length(s);
|
|
V^.StrStart:=GetMem(length(s)+1);
|
|
System.Move(s[1],V^.StrStart^,length(s)+1);
|
|
V^.ValueType:=ctcsvString;
|
|
end;
|
|
end;
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(V);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure MakeCTCSVariableInt64(const V: PCTCfgScriptVariable);
|
|
var
|
|
i: Int64;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(V);
|
|
{$ENDIF}
|
|
case V^.ValueType of
|
|
ctcsvNone:
|
|
begin
|
|
V^.Number:=0;
|
|
V^.ValueType:=ctcsvNumber;
|
|
end;
|
|
ctcsvString:
|
|
begin
|
|
if V^.StrStart<>nil then begin
|
|
i:=StrToInt64Def(V^.StrStart,0);
|
|
FreeMem(V^.StrStart);
|
|
V^.Number:=i;
|
|
end else
|
|
V^.Number:=0;
|
|
V^.ValueType:=ctcsvNumber;
|
|
end;
|
|
ctcsvNumber: ;
|
|
end;
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(V);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure MakeCTCSVariableInteger(const V: PCTCfgScriptVariable);
|
|
var
|
|
i: integer;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(V);
|
|
{$ENDIF}
|
|
case V^.ValueType of
|
|
ctcsvNone:
|
|
begin
|
|
V^.Number:=0;
|
|
V^.ValueType:=ctcsvNumber;
|
|
end;
|
|
ctcsvString:
|
|
begin
|
|
if V^.StrStart<>nil then begin
|
|
i:=StrToIntDef(V^.StrStart,0);
|
|
FreeMem(V^.StrStart);
|
|
V^.Number:=i;
|
|
end else
|
|
V^.Number:=0;
|
|
V^.ValueType:=ctcsvNumber;
|
|
end;
|
|
ctcsvNumber: ;
|
|
end;
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(V);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure AddCTCSVariables(const AddVar, SumVar: PCTCfgScriptVariable);
|
|
{ If one of them is none, then save in sum the other value
|
|
If both are numbers, add them.
|
|
Otherwise concatenate as strings.
|
|
}
|
|
var
|
|
OldLen: LongInt;
|
|
s: String;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(AddVar);
|
|
CheckCTCSVariable(SumVar);
|
|
{$ENDIF}
|
|
case SumVar^.ValueType of
|
|
ctcsvNone:
|
|
SetCTCSVariableValue(AddVar,SumVar);
|
|
ctcsvString:
|
|
case AddVar^.ValueType of
|
|
ctcsvNone:
|
|
;
|
|
ctcsvString:
|
|
if AddVar^.StrLen>0 then begin
|
|
// append
|
|
OldLen:=SumVar^.StrLen;
|
|
SumVar^.StrLen+=AddVar^.StrLen;
|
|
ReAllocMem(SumVar^.StrStart,SumVar^.StrLen+1);
|
|
System.Move(AddVar^.StrStart^,SumVar^.StrStart[OldLen],AddVar^.StrLen+1);
|
|
end;
|
|
ctcsvNumber:
|
|
begin
|
|
// append as string
|
|
s:=IntToStr(AddVar^.Number);
|
|
OldLen:=SumVar^.StrLen;
|
|
SumVar^.StrLen+=length(s);
|
|
ReAllocMem(SumVar^.StrStart,SumVar^.StrLen+1);
|
|
System.Move(s[1],SumVar^.StrStart[OldLen],length(s)+1);
|
|
end;
|
|
end;
|
|
ctcsvNumber:
|
|
case AddVar^.ValueType of
|
|
ctcsvNone:
|
|
;
|
|
ctcsvString:
|
|
begin
|
|
// convert SumVar from number to string and append
|
|
s:=IntToStr(SumVar^.Number);
|
|
SumVar^.ValueType:=ctcsvString;
|
|
SumVar^.StrLen:=length(s)+AddVar^.StrLen;
|
|
SumVar^.StrStart:=GetMem(SumVar^.StrLen+1);
|
|
System.Move(s[1],SumVar^.StrStart^,length(s));
|
|
if AddVar^.StrStart<>nil then
|
|
System.Move(AddVar^.StrStart^,SumVar^.StrStart[length(s)],AddVar^.StrLen+1)
|
|
else
|
|
SumVar^.StrStart[SumVar^.StrLen]:=#0;
|
|
end;
|
|
ctcsvNumber:
|
|
try
|
|
SumVar^.Number+=AddVar^.Number;
|
|
except
|
|
end;
|
|
end;
|
|
end;
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(AddVar);
|
|
CheckCTCSVariable(SumVar);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function CTCSNumberEqualsString(const Number: int64; const P: PChar): boolean;
|
|
var
|
|
n: int64;
|
|
begin
|
|
Result:=CTCSStringToNumber(P,n) and (n=Number);
|
|
end;
|
|
|
|
function CTCSStringToNumber(P: PChar; out Number: int64): boolean;
|
|
var
|
|
n: int64;
|
|
Negated: Boolean;
|
|
begin
|
|
Result:=false;
|
|
if (P=nil) or (P^=#0) then exit;
|
|
try
|
|
n:=0;
|
|
if p^='-' then begin
|
|
Negated:=true;
|
|
inc(p);
|
|
end else
|
|
Negated:=false;
|
|
if p^='$' then begin
|
|
// hex
|
|
repeat
|
|
case p^ of
|
|
'0'..'9': n:=n*16+Ord(p^)-Ord('0');
|
|
'a'..'f': n:=n*16+Ord(p^)-Ord('a')+10;
|
|
'A'..'F': n:=n*16+Ord(p^)-Ord('A')+10;
|
|
#0: break;
|
|
else exit;
|
|
end;
|
|
inc(p);
|
|
until false;
|
|
end else if p^='%' then begin
|
|
// binary
|
|
repeat
|
|
case p^ of
|
|
'0': n:=n*2;
|
|
'1': n:=n*2+1;
|
|
#0: break;
|
|
else exit;
|
|
end;
|
|
inc(p);
|
|
until false;
|
|
end else begin
|
|
// decimal
|
|
repeat
|
|
case p^ of
|
|
'0'..'9': n:=n*10+Ord(p^)-Ord('0');
|
|
#0: break;
|
|
else exit;
|
|
end;
|
|
inc(p);
|
|
until false;
|
|
end;
|
|
if Negated then n:=-n;
|
|
except
|
|
exit;
|
|
end;
|
|
Number:=n;
|
|
Result:=true;
|
|
end;
|
|
|
|
function CTCSVariableIsTrue(const V: PCTCfgScriptVariable): boolean;
|
|
begin
|
|
Result:=not CTCSVariableIsFalse(V);
|
|
end;
|
|
|
|
function CTCSVariableIsFalse(const V: PCTCfgScriptVariable): boolean;
|
|
begin
|
|
case V^.ValueType of
|
|
ctcsvNone:
|
|
Result:=false;
|
|
ctcsvString:
|
|
Result:=(V^.StrLen=1) and (V^.StrStart^='0');
|
|
ctcsvNumber:
|
|
Result:=V^.Number=0;
|
|
end;
|
|
end;
|
|
|
|
function AtomToCTCfgOperator(p: PChar): TCTCfgScriptOperator;
|
|
begin
|
|
Result:=ctcsoNone;
|
|
case UpChars[p^] of
|
|
'A':
|
|
if CompareIdentifiers('and',p)=0 then Result:=ctcsoAnd;
|
|
'D':
|
|
if CompareIdentifiers('div',p)=0 then Result:=ctcsoDiv;
|
|
'M':
|
|
if CompareIdentifiers('mod',p)=0 then Result:=ctcsoMod;
|
|
'N':
|
|
if CompareIdentifiers('not',p)=0 then Result:=ctcsoNot;
|
|
'O':
|
|
if CompareIdentifiers('or',p)=0 then Result:=ctcsoOr;
|
|
'S':
|
|
case UpChars[p[1]] of
|
|
'H':
|
|
case UpChars[p[2]] of
|
|
'L': if CompareIdentifiers('shl',p)=0 then Result:=ctcsoShL;
|
|
'R': if CompareIdentifiers('shr',p)=0 then Result:=ctcsoShR;
|
|
end;
|
|
end;
|
|
'X':
|
|
if CompareIdentifiers('xor',p)=0 then Result:=ctcsoXOr;
|
|
'=':
|
|
Result:=ctcsoEqual;
|
|
'<':
|
|
case p[1] of
|
|
'>': Result:=ctcsoNotEqual;
|
|
'=': Result:=ctcsoLowerOrEqualThan;
|
|
else { < lower than } Result:=ctcsoLowerThan;
|
|
end;
|
|
'>':
|
|
case p[1] of
|
|
'=': Result:=ctcsoGreaterOrEqualThan;
|
|
else { > greater than } Result:=ctcsoGreaterThan;
|
|
end;
|
|
'*':
|
|
case p[1] of
|
|
'*': ;
|
|
'=': ;
|
|
else { * multiply } Result:=ctcsoMultiply;
|
|
end;
|
|
'/':
|
|
case p[1] of
|
|
'/': ;
|
|
'=': ;
|
|
else { / divide } Result:=ctcsoDivide;
|
|
end;
|
|
'+':
|
|
case p[1] of
|
|
'=': ;
|
|
else { + plus } Result:=ctcsoPlus;
|
|
end;
|
|
'-':
|
|
case p[1] of
|
|
'=': ;
|
|
else { - minus } Result:=ctcsoMinus;
|
|
end;
|
|
':':
|
|
case p[1] of
|
|
'=': ;
|
|
else { : colon } ;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure CheckCTCSVariable(const V: PCTCfgScriptVariable);
|
|
begin
|
|
if V=nil then
|
|
RaiseCatchableException('');
|
|
if (V^.Name<>nil) and (strlen(V^.Name)>255) then
|
|
RaiseCatchableException('');
|
|
case V^.ValueType of
|
|
ctcsvNone: ;
|
|
ctcsvString:
|
|
begin
|
|
if V^.StrLen=0 then begin
|
|
if V^.StrStart<>nil then
|
|
RaiseCatchableException('');
|
|
end else begin
|
|
if V^.StrStart=nil then
|
|
RaiseCatchableException('');
|
|
if strlen(V^.StrStart)<>V^.StrLen then
|
|
RaiseCatchableException('');
|
|
end;
|
|
end;
|
|
ctcsvNumber: ;
|
|
end;
|
|
end;
|
|
|
|
function dbgs(const t: TCTCfgScriptStackItemType): string;
|
|
begin
|
|
Result:=GetEnumName(typeinfo(t),ord(t));
|
|
end;
|
|
|
|
function dbgs(const t: TCTCSValueType): string;
|
|
begin
|
|
Result:=GetEnumName(typeinfo(t),ord(t));
|
|
end;
|
|
|
|
function dbgs(const t: TCTCfgScriptOperator): string;
|
|
begin
|
|
Result:=GetEnumName(typeinfo(t),ord(t));
|
|
end;
|
|
|
|
function dbgs(const V: PCTCfgScriptVariable): string;
|
|
var
|
|
l: Integer;
|
|
begin
|
|
Result:=GetIdentifier(V^.Name)+':';
|
|
case V^.ValueType of
|
|
ctcsvNone:
|
|
Result:=Result+'none';
|
|
ctcsvString:
|
|
begin
|
|
Result:=Result+'string=';
|
|
l:=length(Result);
|
|
if V^.StrLen>0 then begin
|
|
SetLength(Result,l+V^.StrLen);
|
|
System.Move(V^.StrStart^,Result[l+1],V^.StrLen);
|
|
end;
|
|
end;
|
|
ctcsvNumber:
|
|
Result:=Result+'int64='+IntToStr(V^.Number);
|
|
end;
|
|
end;
|
|
|
|
function GetCTCSVariableAsString(const V: PCTCfgScriptVariable): string;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(V);
|
|
{$ENDIF}
|
|
case V^.ValueType of
|
|
ctcsvNone: Result:='';
|
|
ctcsvString:
|
|
begin
|
|
SetLength(Result,V^.StrLen);
|
|
if Result<>'' then
|
|
System.Move(V^.StrStart^,Result[1],length(Result));
|
|
end;
|
|
ctcsvNumber: Result:=IntToStr(V^.Number);
|
|
else Result:='';
|
|
end;
|
|
end;
|
|
|
|
procedure SetCTCSVariableAsString(const V: PCTCfgScriptVariable; const s: string);
|
|
var
|
|
l: Integer;
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(v);
|
|
{$ENDIF}
|
|
if V^.ValueType<>ctcsvString then begin
|
|
V^.ValueType:=ctcsvString;
|
|
V^.StrLen:=0;
|
|
V^.StrStart:=nil;
|
|
end;
|
|
l:=length(s);
|
|
V^.StrLen:=l;
|
|
if l>0 then begin
|
|
ReAllocMem(V^.StrStart,l+1);
|
|
System.Move(s[1],V^.StrStart^,l+1); // +1 for the #0
|
|
end else
|
|
ReAllocMem(V^.StrStart,0);
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(v);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure SetCTCSVariableAsNumber(const V: PCTCfgScriptVariable; const i: int64);
|
|
begin
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(v);
|
|
{$ENDIF}
|
|
if (V^.ValueType=ctcsvString) and (V^.StrStart<>nil) then
|
|
Freemem(V^.StrStart);
|
|
V^.ValueType:=ctcsvNumber;
|
|
V^.Number:=i;
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckCTCSVariable(v);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ TCTCfgScriptVariables }
|
|
|
|
function TCTCfgScriptVariables.GetValues(const Name: string): string;
|
|
var
|
|
v: PCTCfgScriptVariable;
|
|
begin
|
|
if Name='' then
|
|
exit('');
|
|
v:=GetVariable(PChar(Name));
|
|
if v=nil then
|
|
exit('');
|
|
Result:=GetCTCSVariableAsString(v);
|
|
end;
|
|
|
|
procedure TCTCfgScriptVariables.SetValues(const Name: string;
|
|
const AValue: string);
|
|
var
|
|
v: PCTCfgScriptVariable;
|
|
begin
|
|
if Name='' then
|
|
exit;
|
|
v:=GetVariable(PChar(Name),true);
|
|
SetCTCSVariableAsString(v,AValue);
|
|
end;
|
|
|
|
constructor TCTCfgScriptVariables.Create;
|
|
begin
|
|
FItems:=TAVLTree.Create(@CompareCTCSVariables);
|
|
end;
|
|
|
|
destructor TCTCfgScriptVariables.Destroy;
|
|
begin
|
|
Clear;
|
|
FreeAndNil(FItems);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCTCfgScriptVariables.Clear;
|
|
var
|
|
Node: TAVLTreeNode;
|
|
Item: PCTCfgScriptVariable;
|
|
begin
|
|
Node:=FItems.FindLowest;
|
|
while Node<>nil do begin
|
|
Item:=PCTCfgScriptVariable(Node.Data);
|
|
FreeCTCSVariable(Item);
|
|
Node:=FItems.FindSuccessor(Node);
|
|
end;
|
|
FItems.Clear;
|
|
end;
|
|
|
|
function TCTCfgScriptVariables.Equals(Vars: TCTCfgScriptVariables): boolean;
|
|
var
|
|
Node1: TAVLTreeNode;
|
|
Node2: TAVLTreeNode;
|
|
Item1: PCTCfgScriptVariable;
|
|
Item2: PCTCfgScriptVariable;
|
|
begin
|
|
Result:=false;
|
|
if Vars=nil then exit;
|
|
if FItems.Count<>Vars.FItems.Count then exit;
|
|
Node1:=FItems.FindLowest;
|
|
Node2:=Vars.FItems.FindLowest;
|
|
while Node1<>nil do begin
|
|
Item1:=PCTCfgScriptVariable(Node1.Data);
|
|
Item2:=PCTCfgScriptVariable(Node2.Data);
|
|
if CompareIdentifiers(Item1^.Name,Item2^.Name)<>0 then exit;
|
|
if Item1^.ValueType<>Item2^.ValueType then exit;
|
|
case Item1^.ValueType of
|
|
ctcsvNone: ;
|
|
ctcsvString: if (Item1^.StrLen<>Item2^.StrLen)
|
|
or ((Item1^.StrStart<>nil)
|
|
and (not CompareMem(Item1^.StrStart,Item2^.StrStart,Item1^.StrLen)))
|
|
then exit;
|
|
ctcsvNumber: if Item1^.Number<>Item2^.Number then exit;
|
|
end;
|
|
Node1:=FItems.FindSuccessor(Node1);
|
|
Node2:=Vars.FItems.FindSuccessor(Node2);
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
procedure TCTCfgScriptVariables.Assign(Source: TCTCfgScriptVariables);
|
|
var
|
|
Node: TAVLTreeNode;
|
|
Item: PCTCfgScriptVariable;
|
|
NewItem: PCTCfgScriptVariable;
|
|
begin
|
|
if Self=Source then exit;
|
|
Clear;
|
|
Node:=Source.FItems.FindLowest;
|
|
while Node<>nil do begin
|
|
Item:=PCTCfgScriptVariable(Node.Data);
|
|
NewItem:=CloneCTCSVariable(Item);
|
|
FItems.Add(NewItem);
|
|
Node:=Source.FItems.FindSuccessor(Node);
|
|
end;
|
|
end;
|
|
|
|
procedure TCTCfgScriptVariables.Assign(Source: TStrings);
|
|
var
|
|
Name: string;
|
|
Value: string;
|
|
i: Integer;
|
|
begin
|
|
Clear;
|
|
for i:=0 to Source.Count-1 do begin
|
|
Name:=Source.Names[i];
|
|
if not IsValidIdent(Name) then continue;
|
|
Value:=Source.ValueFromIndex[i];
|
|
Define(PChar(Name),Value);
|
|
end;
|
|
end;
|
|
|
|
procedure TCTCfgScriptVariables.AddOverrides(Source: TCTCfgScriptVariables);
|
|
var
|
|
Item: PCTCfgScriptVariable;
|
|
Node: TAVLTreeNode;
|
|
begin
|
|
Node:=Source.FItems.FindLowest;
|
|
while Node<>nil do begin
|
|
Item:=PCTCfgScriptVariable(Node.Data);
|
|
AddOverride(Item);
|
|
Node:=Source.FItems.FindSuccessor(Node);
|
|
end;
|
|
end;
|
|
|
|
procedure TCTCfgScriptVariables.AddOverride(Source: PCTCfgScriptVariable);
|
|
var
|
|
Node: TAVLTreeNode;
|
|
Item: PCTCfgScriptVariable;
|
|
begin
|
|
Node:=FItems.Find(Source);
|
|
if Node<>nil then begin
|
|
Item:=PCTCfgScriptVariable(Node.Data);
|
|
SetCTCSVariableValue(Source,Item);
|
|
end else begin
|
|
Item:=CloneCTCSVariable(Source);
|
|
FItems.Add(Item);
|
|
end;
|
|
end;
|
|
|
|
function TCTCfgScriptVariables.GetVariable(const Name: PChar;
|
|
CreateIfNotExists: Boolean): PCTCfgScriptVariable;
|
|
var
|
|
Node: TAVLTreeNode;
|
|
begin
|
|
Node:=FItems.FindKey(Name,@ComparePCharWithCTCSVariableName);
|
|
if Node<>nil then
|
|
Result:=PCTCfgScriptVariable(Node.Data)
|
|
else if CreateIfNotExists then begin
|
|
Result:=NewCTCSVariable(Name);
|
|
FItems.Add(Result);
|
|
end else
|
|
Result:=nil;
|
|
end;
|
|
|
|
procedure TCTCfgScriptVariables.Undefine(Name: PChar);
|
|
var
|
|
Node: TAVLTreeNode;
|
|
Item: PCTCfgScriptVariable;
|
|
begin
|
|
Node:=FItems.FindKey(Name,@ComparePCharWithCTCSVariableName);
|
|
if Node=nil then exit;
|
|
Item:=PCTCfgScriptVariable(Node.Data);
|
|
FreeCTCSVariable(Item);
|
|
FItems.Delete(Node);
|
|
end;
|
|
|
|
procedure TCTCfgScriptVariables.Define(Name: PChar; const Value: string);
|
|
|
|
function IsNumber: boolean;
|
|
var
|
|
p: PChar;
|
|
begin
|
|
if Value='' then exit(false);
|
|
p:=PChar(Value);
|
|
if p^='-' then inc(p);
|
|
while (p^ in ['0'..'9']) do inc(p);
|
|
Result:=(p^=#0) and (p-PChar(Value)=length(Value));
|
|
end;
|
|
|
|
var
|
|
V: PCTCfgScriptVariable;
|
|
i: Int64;
|
|
begin
|
|
V:=GetVariable(Name,true);
|
|
if Value='' then
|
|
ClearCTCSVariable(V)
|
|
else if IsNumber and TryStrToInt64(Value,i) then
|
|
SetCTCSVariableAsNumber(V,i)
|
|
else
|
|
SetCTCSVariableAsString(V,Value);
|
|
end;
|
|
|
|
function TCTCfgScriptVariables.IsDefined(Name: PChar): boolean;
|
|
begin
|
|
Result:=GetVariable(Name)<>nil;
|
|
end;
|
|
|
|
procedure TCTCfgScriptVariables.WriteDebugReport(const Title: string;
|
|
const Prefix: string);
|
|
var
|
|
Node: TAVLTreeNode;
|
|
V: PCTCfgScriptVariable;
|
|
begin
|
|
debugln([Prefix,'TCTCfgScriptVariables.WriteDebugReport Count=',Tree.Count,': ',Title]);
|
|
Node:=FItems.FindLowest;
|
|
while Node<>nil do begin
|
|
V:=PCTCfgScriptVariable(Node.Data);
|
|
debugln([Prefix,' ',dbgs(V)]);
|
|
Node:=FItems.FindSuccessor(Node);
|
|
end;
|
|
end;
|
|
|
|
{ TCTConfigScriptEngine }
|
|
|
|
function TCTConfigScriptEngine.GetErrors(Index: integer): TCTCfgScriptError;
|
|
begin
|
|
Result:=TCTCfgScriptError(FErrors[Index]);
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.AddError(const aMsg: string; ErrorPos: PChar);
|
|
var
|
|
Err: TCTCfgScriptError;
|
|
Position: Integer;
|
|
Line: Integer;
|
|
Column: Integer;
|
|
begin
|
|
{$IFDEF VerboseCTCfgScript}
|
|
WriteDebugReportStack('ERROR: '+aMsg);
|
|
{$ENDIF}
|
|
Position:=-1;
|
|
Line:=0;
|
|
Column:=0;
|
|
if (ErrorPos<>nil) then begin
|
|
Position:=ErrorPos-Src;
|
|
PosToLineCol(ErrorPos,Line,Column);
|
|
end;
|
|
Err:=TCTCfgScriptError.Create(aMsg,Position,Line,Column);
|
|
FErrors.Add(Err);
|
|
if ErrorCount>=MaxErrorCount then
|
|
raise ECodeToolCfgScript.Create(GetErrorStr(ErrorCount-1));
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.AddError(const aMsg: string);
|
|
begin
|
|
AddError(aMsg,AtomStart);
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.RunStatement(Skip: boolean);
|
|
{ Examples:
|
|
begin..
|
|
if...
|
|
variable:=
|
|
}
|
|
|
|
procedure ErrorUnexpectedAtom;
|
|
begin
|
|
AddError(Format(ctsExpectedSemicolonOfStatementButFound, [GetAtomOrNothing]))
|
|
end;
|
|
|
|
var
|
|
Handled: Boolean;
|
|
StartTop: LongInt;
|
|
begin
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunStatement Atom=',GetAtom]);
|
|
{$ENDIF}
|
|
StartTop:=FStack.Top;
|
|
case AtomStart^ of
|
|
#0: ;
|
|
';': ; // empty statement
|
|
'a'..'z','A'..'Z':
|
|
begin
|
|
// identifier or keyword
|
|
Handled:=false;
|
|
case UpChars[AtomStart^] of
|
|
'B':
|
|
if CompareIdentifiers('BEGIN',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
RunBegin(Skip);
|
|
end;
|
|
'I':
|
|
if CompareIdentifiers('IF',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
RunIf(Skip);
|
|
end;
|
|
'U':
|
|
if CompareIdentifiers('Undefine',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
RunUndefine(Skip);
|
|
end;
|
|
end;
|
|
if (not Handled) then begin
|
|
if IsKeyWord(AtomStart) then begin
|
|
AddError(Format(ctsUnexpectedKeyword2, [GetAtom]));
|
|
end else if IsFunction(AtomStart) then begin
|
|
if not RunFunction then exit;
|
|
end else begin
|
|
// parse assignment
|
|
RunAssignment(Skip);
|
|
end;
|
|
end;
|
|
end;
|
|
else
|
|
ErrorUnexpectedAtom;
|
|
end;
|
|
// clean up stack
|
|
while FStack.Top>StartTop do FStack.Pop;
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.RunBegin(Skip: boolean);
|
|
{ Examples:
|
|
begin
|
|
end
|
|
begin
|
|
statement statement
|
|
end
|
|
}
|
|
var
|
|
BeginStart: PChar;
|
|
StartTop: LongInt;
|
|
|
|
procedure ErrorMissingEnd;
|
|
begin
|
|
//debugln(['ErrorMissingEnd BeginStart=',BeginStart]);
|
|
AddError(Format(ctsBeginAtWithoutEnd, [PosToStr(BeginStart)]));
|
|
end;
|
|
|
|
begin
|
|
BeginStart:=AtomStart;
|
|
StartTop:=FStack.Top;
|
|
FStack.Push(ctcssBegin,AtomStart);
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
repeat
|
|
if (AtomStart^=#0) then begin
|
|
ErrorMissingEnd;
|
|
break;
|
|
end else if CompareIdentifiers('END',AtomStart)=0 then begin
|
|
FStack.Pop;
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
break;
|
|
end else if AtomStart^=';' then begin
|
|
// skip
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
end else begin
|
|
RunStatement(Skip);
|
|
end;
|
|
until false;
|
|
// clean up stack (recover from errors)
|
|
while FStack.Top>StartTop do FStack.Pop;
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.RunIf(Skip: boolean);
|
|
{ Examples:
|
|
if expression then statement else statement
|
|
}
|
|
var
|
|
IfStart: PChar;
|
|
ExprIsTrue: Boolean;
|
|
StartTop: LongInt;
|
|
begin
|
|
IfStart:=AtomStart;
|
|
StartTop:=FStack.Top;
|
|
FStack.Push(ctcssIf,IfStart);
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
ExprIsTrue:=false;
|
|
if RunExpression then begin
|
|
ExprIsTrue:=CTCSVariableIsTrue(FStack.TopItemOperand);
|
|
FStack.Pop;
|
|
end;
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunIf expression=',ExprIsTrue]);
|
|
{$ENDIF}
|
|
|
|
// read then
|
|
if CompareIdentifiers(AtomStart,'then')<>0 then
|
|
AddError(Format(ctsThenExpectedButFound, [GetAtomOrNothing]));
|
|
// then statement
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
RunStatement(Skip or not ExprIsTrue);
|
|
if CompareIdentifiers(AtomStart,'else')=0 then begin
|
|
// else statement
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
RunStatement(Skip or ExprIsTrue);
|
|
end;
|
|
// clean up stack
|
|
while FStack.Top>StartTop do FStack.Pop;
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.RunUndefine(Skip: boolean);
|
|
var
|
|
VarStart: PChar;
|
|
begin
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
if AtomStart^<>'(' then begin
|
|
AddError(Format(ctsExpectedButFound, [GetAtomOrNothing]));
|
|
exit;
|
|
end;
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
if (not IsIdentStartChar[AtomStart^]) or IsKeyWord(AtomStart) then begin
|
|
AddError(Format(ctsExpectedIdentifierButFound, [GetAtomOrNothing]));
|
|
exit;
|
|
end;
|
|
VarStart:=AtomStart;
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
if AtomStart^<>')' then begin
|
|
AddError(Format(ctsExpectedButFound2, [GetAtomOrNothing]));
|
|
exit;
|
|
end;
|
|
if not Skip then
|
|
Variables.Undefine(VarStart);
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.RunAssignment(Skip: boolean);
|
|
{ Examples:
|
|
a:=3;
|
|
}
|
|
var
|
|
VarStart: PChar;
|
|
Variable: PCTCfgScriptVariable;
|
|
OperatorStart: PChar;
|
|
StartTop: LongInt;
|
|
begin
|
|
VarStart:=AtomStart;
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunAssignment ',GetIdentifier(VarStart)]);
|
|
{$ENDIF}
|
|
StartTop:=FStack.Top;
|
|
FStack.Push(ctcssAssignment,VarStart);
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunAssignment Operator=',GetAtom]);
|
|
{$ENDIF}
|
|
// read := or +=
|
|
if AtomStart^=#0 then begin
|
|
AddError(ctsMissing);
|
|
exit;
|
|
end;
|
|
OperatorStart:=AtomStart;
|
|
if (not (AtomStart^ in [':','+'])) or (AtomStart[1]<>'=') then begin
|
|
AddError(Format(ctsExpectedButFound3, [GetAtom]));
|
|
exit;
|
|
end;
|
|
// read expression
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
if RunExpression and (not Skip) then begin
|
|
Variable:=Variables.GetVariable(VarStart,true);
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunAssignment BEFORE ',GetIdentifier(VarStart),'=(Old=',dbgs(Variable),') ',GetAtom(OperatorStart),' ',dbgs(FStack.TopItemOperand)]);
|
|
{$ENDIF}
|
|
case OperatorStart^ of
|
|
':': // :=
|
|
SetCTCSVariableValue(FStack.TopItemOperand,Variable);
|
|
'+': // +=
|
|
AddCTCSVariables(FStack.TopItemOperand,Variable);
|
|
end;
|
|
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunAssignment AFTER ',GetIdentifier(VarStart),' = ',dbgs(Variable),' Atom=',GetAtom]);
|
|
{$ENDIF}
|
|
end;
|
|
// clean up stack
|
|
while FStack.Top>StartTop do FStack.Pop;
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.PushNumberValue(const Number: int64);
|
|
var
|
|
Operand: PCTCfgScriptVariable;
|
|
begin
|
|
FStack.Push(ctcssOperand,AtomStart);
|
|
Operand:=FStack.TopItemOperand;
|
|
Operand^.ValueType:=ctcsvNumber;
|
|
Operand^.Number:=Number;
|
|
ExecuteStack(1);
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.RunDefined(Negate: boolean): boolean;
|
|
var
|
|
VarStart: PChar;
|
|
b: Boolean;
|
|
begin
|
|
Result:=false;
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
if AtomStart^<>'(' then begin
|
|
AddError(Format(ctsExpectedButFound, [GetAtomOrNothing]));
|
|
exit;
|
|
end;
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
if (not IsIdentStartChar[AtomStart^]) or IsKeyWord(AtomStart) then begin
|
|
AddError(Format(ctsExpectedIdentifierButFound, [GetAtomOrNothing]));
|
|
exit;
|
|
end;
|
|
VarStart:=AtomStart;
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
if AtomStart^<>')' then begin
|
|
AddError(Format(ctsExpectedButFound2, [GetAtomOrNothing]));
|
|
exit;
|
|
end;
|
|
b:=Variables.GetVariable(VarStart)<>nil;
|
|
if Negate then b:=not b;
|
|
PushBooleanValue(b);
|
|
Result:=true;
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.RunFunction: boolean;
|
|
var
|
|
StartTop: LongInt;
|
|
Value: TCTCfgScriptVariable;
|
|
FunctionName: PChar;
|
|
begin
|
|
Result:=false;
|
|
FunctionName:=AtomStart;
|
|
StartTop:=FStack.Top;
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
if AtomStart^<>'(' then begin
|
|
AddError(Format(ctsExpectedButFound, [GetAtomOrNothing]));
|
|
exit;
|
|
end;
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
|
|
FStack.Push(ctcssRoundBracketOpen,AtomStart);
|
|
FillByte(Value{%H-},SizeOf(Value),0);
|
|
if RunExpression then
|
|
SetCTCSVariableValue(FStack.TopItemOperand,@Value);
|
|
if AtomStart^<>')' then begin
|
|
AddError(Format(ctsExpectedButFound2, [GetAtomOrNothing]));
|
|
exit;
|
|
end;
|
|
|
|
// clean up stack
|
|
while FStack.Top>StartTop do FStack.Pop;
|
|
|
|
// execute function
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunFunction FunctionName="',GetAtom(FunctionName),'" Parameter=',dbgs(PCTCfgScriptVariable(@Value))]);
|
|
{$ENDIF}
|
|
case UpChars[FunctionName^] of
|
|
'I':
|
|
if CompareIdentifiers(FunctionName,'int64')=0 then
|
|
MakeCTCSVariableInt64(@Value)
|
|
else if CompareIdentifiers(FunctionName,'integer')=0 then
|
|
MakeCTCSVariableInteger(@Value);
|
|
'S':
|
|
if CompareIdentifiers(FunctionName,'string')=0 then
|
|
MakeCTCSVariableString(@Value);
|
|
else
|
|
RunCustomSimpleFunction(FunctionName,@Value);
|
|
end;
|
|
|
|
// put result on stack as operand
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunFunction FunctionName="',GetAtom(FunctionName),'" Result=',dbgs(PCTCfgScriptVariable(@Value))]);
|
|
{$ENDIF}
|
|
FStack.Push(ctcssOperand,FunctionName);
|
|
SetCTCSVariableValue(@Value,FStack.TopItemOperand);
|
|
|
|
ClearCTCSVariable(@Value);
|
|
|
|
Result:=true;
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.PushStringConstant;
|
|
var
|
|
Operand: PCTCfgScriptVariable;
|
|
|
|
procedure Add(p: PChar; Count: integer);
|
|
var
|
|
OldLen: LongInt;
|
|
NewLen: Integer;
|
|
begin
|
|
if Count=0 then exit;
|
|
OldLen:=Operand^.StrLen;
|
|
NewLen:=OldLen+Count;
|
|
ReAllocMem(Operand^.StrStart,NewLen+1);
|
|
System.Move(p^,Operand^.StrStart[OldLen],Count);
|
|
Operand^.StrLen:=NewLen;
|
|
Operand^.StrStart[NewLen]:=#0;
|
|
end;
|
|
|
|
var
|
|
p: PChar;
|
|
StartPos: PChar;
|
|
i: Integer;
|
|
c: char;
|
|
begin
|
|
FStack.Push(ctcssOperand,AtomStart);
|
|
Operand:=FStack.TopItemOperand;
|
|
Operand^.ValueType:=ctcsvString;
|
|
Operand^.StrLen:=0;
|
|
Operand^.StrStart:=nil;
|
|
p:=AtomStart;
|
|
while true do begin
|
|
case p^ of
|
|
#0:
|
|
break;
|
|
'#':
|
|
begin
|
|
inc(p);
|
|
StartPos:=p;
|
|
i:=0;
|
|
while (p^ in ['0'..'9']) do begin
|
|
i:=i*10+ord(p^)-ord('0');
|
|
if (i>255) then begin
|
|
AddError(ctsCharacterConstantOutOfRange);
|
|
while (p^ in ['0'..'9']) do inc(p);
|
|
break;
|
|
end;
|
|
inc(p);
|
|
end;
|
|
c:=chr(i);
|
|
Add(@c,1);
|
|
end;
|
|
'''':
|
|
begin
|
|
inc(p);
|
|
StartPos:=p;
|
|
while not (p^ in ['''',#10,#13,#0]) do
|
|
inc(p);
|
|
if p^<>'''' then
|
|
AddError('missing end apostrophe of string constant');
|
|
Add(StartPos,p-StartPos);
|
|
if p^='''' then
|
|
inc(p);
|
|
end;
|
|
else
|
|
break;
|
|
end;
|
|
end;
|
|
ExecuteStack(1);
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.PushNumberConstant;
|
|
var
|
|
Item: PCTCfgScriptStackItem;
|
|
p: PChar;
|
|
Number: int64;
|
|
l: integer;
|
|
c: Char;
|
|
begin
|
|
FStack.Push(ctcssOperand,AtomStart);
|
|
Item:=FStack.TopItem;
|
|
p:=AtomStart;
|
|
c:=p^;
|
|
if not (c in ['0'..'9']) then inc(p);
|
|
Number:=0;
|
|
try
|
|
while true do begin
|
|
case c of
|
|
'%':
|
|
case p^ of
|
|
'0': Number:=Number*2;
|
|
'1': Number:=Number*2+1;
|
|
else break;
|
|
end;
|
|
'&':
|
|
case p^ of
|
|
'0'..'7': Number:=Number*8+ord(p^)-ord('0');
|
|
else break;
|
|
end;
|
|
'$':
|
|
case p^ of
|
|
'0'..'9': Number:=Number*16+ord(p^)-ord('0');
|
|
'a'..'f': Number:=Number*16+ord(p^)-ord('a')+10;
|
|
'A'..'F': Number:=Number*16+ord(p^)-ord('A')+10;
|
|
else break;
|
|
end;
|
|
else
|
|
// decimal or float
|
|
case p^ of
|
|
'0'..'9': Number:=Number*10+ord(p^)-ord('0');
|
|
else break;
|
|
end;
|
|
end;
|
|
inc(p);
|
|
end;
|
|
except
|
|
p:=AtomStart;
|
|
end;
|
|
if p=Src then begin
|
|
// a number
|
|
Item^.Operand.ValueType:=ctcsvNumber;
|
|
Item^.Operand.Number:=Number;
|
|
end else begin
|
|
// string constant
|
|
Item^.Operand.ValueType:=ctcsvString;
|
|
l:=Src-AtomStart;
|
|
Item^.Operand.StrLen:=l;
|
|
Item^.Operand.StrStart:=GetMem(l+1);
|
|
System.Move(AtomStart^,Item^.Operand.StrStart^,l);
|
|
Item^.Operand.StrStart[l]:=#0;
|
|
end;
|
|
ExecuteStack(1);
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.PushBooleanValue(b: boolean);
|
|
var
|
|
Operand: PCTCfgScriptVariable;
|
|
begin
|
|
FStack.Push(ctcssOperand,AtomStart);
|
|
Operand:=FStack.TopItemOperand;
|
|
Operand^.ValueType:=ctcsvNumber;
|
|
if b then
|
|
Operand^.Number:=1
|
|
else
|
|
Operand^.Number:=0;
|
|
ExecuteStack(1);
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.RunExpression: boolean;
|
|
{ Examples:
|
|
A is false if A=0 or A='0'
|
|
defined(A)
|
|
(A)
|
|
unary operators:
|
|
|
|
binary operators:
|
|
|
|
}
|
|
function OperandAllowed: boolean;
|
|
begin
|
|
case FStack.TopTyp of
|
|
ctcssExpression,ctcssOperator,ctcssRoundBracketOpen:
|
|
Result:=true;
|
|
else
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunExpression.OperandAllowed no']);
|
|
{$ENDIF}
|
|
AddError(Format(ctsOperatorExpectedButFound, [GetAtom]));
|
|
Result:=false;
|
|
end;
|
|
end;
|
|
|
|
function BinaryOperatorAllowed: boolean;
|
|
begin
|
|
case FStack.TopTyp of
|
|
ctcssOperand:
|
|
Result:=true;
|
|
else
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunExpression.BinaryOperatorAllowed no']);
|
|
{$ENDIF}
|
|
AddError(Format(ctsOperandExpectedButFound, [GetAtom]));
|
|
Result:=false;
|
|
end;
|
|
end;
|
|
|
|
function PushBinaryOperator: boolean;
|
|
begin
|
|
Result:=BinaryOperatorAllowed;
|
|
if not Result then begin
|
|
RunExpression:=false;
|
|
exit;
|
|
end;
|
|
ExecuteStack(GetOperatorLevel(AtomStart));
|
|
FStack.Push(ctcssOperator,AtomStart);
|
|
end;
|
|
|
|
var
|
|
ExprStart: PChar;
|
|
Handled: Boolean;
|
|
Item: PCTCfgScriptStackItem;
|
|
StartTop: LongInt;
|
|
v: PCTCfgScriptVariable;
|
|
begin
|
|
Result:=true;
|
|
ExprStart:=AtomStart;
|
|
StartTop:=FStack.Top;
|
|
FStack.Push(ctcssExpression,ExprStart);
|
|
while true do begin
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunExpression Atom=',GetAtom]);
|
|
{$ENDIF}
|
|
case AtomStart^ of
|
|
#0:
|
|
break;
|
|
'(':
|
|
begin
|
|
if not OperandAllowed then break;
|
|
FStack.Push(ctcssRoundBracketOpen,AtomStart);
|
|
end;
|
|
')':
|
|
begin
|
|
ExecuteStack(5);
|
|
if FStack.TopTyp=ctcssRoundBracketOpen then begin
|
|
// empty ()
|
|
AddError(Format(ctsOperandExpectedButFound2, [GetAtom]));
|
|
Result:=false;
|
|
break;
|
|
end else if (FStack.TopTyp=ctcssOperand)
|
|
and (FStack.Top>0) and (FStack.Items[FStack.Top-1].Typ=ctcssRoundBracketOpen)
|
|
then begin
|
|
FStack.Delete(FStack.Top-1);
|
|
end else
|
|
break;
|
|
end;
|
|
'=':
|
|
if not PushBinaryOperator then break;
|
|
'<':
|
|
if (Src-AtomStart=1) or (AtomStart[1] in ['=','>']) then begin
|
|
if not PushBinaryOperator then break;
|
|
end else begin
|
|
AddError(Format(ctsInvalidOperator, [GetAtom]));
|
|
Result:=false;
|
|
break;
|
|
end;
|
|
'>':
|
|
if (Src-AtomStart=1) or (AtomStart[1] in ['=']) then begin
|
|
if not PushBinaryOperator then break;
|
|
end else begin
|
|
AddError(Format(ctsInvalidOperator, [GetAtom]));
|
|
Result:=false;
|
|
break;
|
|
end;
|
|
'+':
|
|
if (Src-AtomStart=1) then begin
|
|
if not PushBinaryOperator then break;
|
|
end else begin
|
|
AddError(Format(ctsInvalidOperator, [GetAtom]));
|
|
Result:=false;
|
|
break;
|
|
end;
|
|
'a'..'z','A'..'Z':
|
|
begin
|
|
// a keyword or an identifier
|
|
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunExpression StackTop=',dbgs(FStack.TopTyp),' Atom=',GetAtom]);
|
|
{$ENDIF}
|
|
// execute
|
|
Handled:=false;
|
|
case UpChars[AtomStart^] of
|
|
'A':
|
|
if CompareIdentifiers('and',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
if not PushBinaryOperator then break;
|
|
end;
|
|
'D':
|
|
case UpChars[AtomStart[1]] of
|
|
'E':
|
|
if CompareIdentifiers('defined',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
if not OperandAllowed then break;
|
|
if not RunDefined(false) then break;
|
|
end;
|
|
'I':
|
|
if CompareIdentifiers('div',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
if not PushBinaryOperator then break;
|
|
end;
|
|
end;
|
|
'E':
|
|
case UpChars[AtomStart[1]] of
|
|
'L':
|
|
if CompareIdentifiers('else',AtomStart)=0 then
|
|
break;
|
|
'N':
|
|
if CompareIdentifiers('end',AtomStart)=0 then
|
|
break;
|
|
end;
|
|
'F':
|
|
if CompareIdentifiers('false',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
if not OperandAllowed then break;
|
|
PushBooleanValue(false);
|
|
end;
|
|
'M':
|
|
if CompareIdentifiers('mod',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
if not PushBinaryOperator then break;
|
|
end;
|
|
'N':
|
|
if CompareIdentifiers('not',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
if not OperandAllowed then break;
|
|
// Note: no execute, "not" is unary operator for the next operand
|
|
FStack.Push(ctcssOperator,AtomStart);
|
|
end;
|
|
'O':
|
|
if CompareIdentifiers('or',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
if not PushBinaryOperator then break;
|
|
end;
|
|
'T':
|
|
case UpChars[AtomStart[1]] of
|
|
'H':
|
|
if CompareIdentifiers('then',AtomStart)=0 then begin
|
|
break;
|
|
end;
|
|
'R':
|
|
if CompareIdentifiers('true',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
if not OperandAllowed then break;
|
|
PushBooleanValue(true);
|
|
end;
|
|
end;
|
|
'U':
|
|
if CompareIdentifiers('undefined',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
if not OperandAllowed then break;
|
|
if not RunDefined(true) then break;
|
|
end;
|
|
'X':
|
|
if CompareIdentifiers('xor',AtomStart)=0 then begin
|
|
Handled:=true;
|
|
if not PushBinaryOperator then break;
|
|
end;
|
|
end;
|
|
if (not Handled) and IsKeyWord(AtomStart) then begin
|
|
AddError(Format(ctsUnexpectedKeyword2, [GetAtom]));
|
|
Result:=false;
|
|
break;
|
|
end;
|
|
if (not Handled) then begin
|
|
if not OperandAllowed then break;
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunExpression ',GetAtom(AtomStart),' ',IsFunction(AtomStart)]);
|
|
{$ENDIF}
|
|
if IsFunction(AtomStart) then begin
|
|
// a function
|
|
if not RunFunction then begin
|
|
Result:=false;
|
|
break;
|
|
end;
|
|
end else begin
|
|
// a variable
|
|
FStack.Push(ctcssOperand,AtomStart);
|
|
Item:=FStack.TopItem;
|
|
v:=Variables.GetVariable(AtomStart);
|
|
if v<>nil then begin
|
|
SetCTCSVariableValue(v,@Item^.Operand);
|
|
end;
|
|
end;
|
|
ExecuteStack(1);
|
|
end;
|
|
end;
|
|
'#','''':
|
|
begin
|
|
if not OperandAllowed then break;
|
|
PushStringConstant;
|
|
end;
|
|
'0'..'9','$','%','&':
|
|
begin
|
|
// float, decimal, hex, octal, binary constant
|
|
if not OperandAllowed then break;
|
|
PushNumberConstant;
|
|
end;
|
|
else
|
|
if FStack.TopTyp in [ctcssOperator,ctcssRoundBracketOpen]
|
|
then begin
|
|
AddError(Format(ctsOperandExpectedButFound2, [GetAtom]));
|
|
Result:=false;
|
|
end;
|
|
break;
|
|
end;
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
end;
|
|
|
|
if Result then begin
|
|
if not ExecuteStack(10) then
|
|
Result:=false;
|
|
if FStack.Top=StartTop+1 then begin
|
|
// empty expression
|
|
AddError(Format(ctsOperandExpectedButFound2, [GetAtom]));
|
|
end else if (FStack.TopTyp<>ctcssOperand) or (FStack.Top<>StartTop+2) then begin
|
|
// unfinished expression
|
|
if FStack.TopTyp in [ctcssOperator,ctcssRoundBracketOpen]
|
|
then
|
|
AddError(Format(ctsOperandExpectedButFound2, [GetAtom]))
|
|
else
|
|
AddError(Format(ctsOperatorExpectedButFound2, [GetAtom]));
|
|
Result:=false;
|
|
end
|
|
else if Result then begin
|
|
// success
|
|
// delete ctcssExpression and keep the operand
|
|
FStack.Delete(FStack.Top-1);
|
|
Item:=FStack.TopItem;
|
|
inc(StartTop);
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.RunExpression Result="',dbgs(PCTCfgScriptVariable(@Item^.Operand)),'" ']);
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
// clean up stack
|
|
while (FStack.Top>StartTop) do FStack.Pop;
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.IsKeyWord(P: PChar): boolean;
|
|
begin
|
|
Result:=false;
|
|
if p=nil then exit;
|
|
case UpChars[p^] of
|
|
'A':
|
|
if CompareIdentifiers('and',p)=0 then exit(true);
|
|
'B':
|
|
if CompareIdentifiers('begin',p)=0 then exit(true);
|
|
'C':
|
|
if CompareIdentifiers('case',p)=0 then exit(true);
|
|
'D':
|
|
case UpChars[p[1]] of
|
|
'E':
|
|
if CompareIdentifiers('defined',p)=0 then exit(true);
|
|
'I':
|
|
if CompareIdentifiers('div',p)=0 then exit(true);
|
|
end;
|
|
'E':
|
|
case UpChars[p[1]] of
|
|
'L':
|
|
if CompareIdentifiers('else',p)=0 then exit(true);
|
|
'N':
|
|
if CompareIdentifiers('end',p)=0 then exit(true);
|
|
end;
|
|
'F':
|
|
case UpChars[p[1]] of
|
|
'A':
|
|
if CompareIdentifiers('false',p)=0 then exit(true);
|
|
'U':
|
|
if CompareIdentifiers('function',p)=0 then exit(true);
|
|
end;
|
|
'I':
|
|
case UpChars[p[1]] of
|
|
'F':
|
|
if CompareIdentifiers('if',p)=0 then exit(true);
|
|
'N':
|
|
if (CompareIdentifiers('in',p)=0) then exit(true)
|
|
end;
|
|
'M':
|
|
if CompareIdentifiers('mod',p)=0 then exit(true);
|
|
'N':
|
|
if CompareIdentifiers('not',p)=0 then exit(true);
|
|
'O':
|
|
case UpChars[p[1]] of
|
|
'F':
|
|
if CompareIdentifiers('of',p)=0 then exit(true);
|
|
'R':
|
|
if CompareIdentifiers('or',p)=0 then exit(true);
|
|
end;
|
|
'P':
|
|
if CompareIdentifiers('procedure',p)=0 then exit(true);
|
|
'S':
|
|
case UpChars[p[1]] of
|
|
'H':
|
|
case UpChars[p[2]] of
|
|
'L':
|
|
if CompareIdentifiers('shl',p)=0 then exit(true);
|
|
'R':
|
|
if CompareIdentifiers('shr',p)=0 then exit(true);
|
|
end;
|
|
end;
|
|
'T':
|
|
case UpChars[p[1]] of
|
|
'H':
|
|
if CompareIdentifiers('then',p)=0 then exit(true);
|
|
'R':
|
|
if CompareIdentifiers('true',p)=0 then exit(true);
|
|
end;
|
|
'X':
|
|
if CompareIdentifiers('xor',p)=0 then exit(true);
|
|
'U':
|
|
if CompareIdentifiers('undefined',p)=0 then exit(true);
|
|
end;
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.ExecuteStack(MaxLevel: integer): boolean;
|
|
{ execute all operators on stack with level <= maxlevel
|
|
}
|
|
var
|
|
OperatorItem: PCTCfgScriptStackItem;
|
|
Typ: TCTCfgScriptOperator;
|
|
OperandItem: PCTCfgScriptStackItem;
|
|
b: Boolean;
|
|
LeftOperandItem: PCTCfgScriptStackItem;
|
|
OperandsEqual: boolean;
|
|
LeftIsLowerThanRight: boolean;
|
|
|
|
procedure ErrorInvalidOperator;
|
|
begin
|
|
raise ECodeToolCfgScript.Create('TCTConfigScriptEngine.ExecuteStack invalid operator: '+GetAtom(OperatorItem^.StartPos));
|
|
end;
|
|
|
|
begin
|
|
Result:=true;
|
|
repeat
|
|
{$IFDEF VerboseCTCfgScript}
|
|
WriteDebugReportStack('ExecuteStack MaxLevel='+dbgs(MaxLevel));
|
|
{$ENDIF}
|
|
if (FStack.TopTyp<>ctcssOperand) or (FStack.Top<=0) then
|
|
exit;
|
|
OperatorItem:=@FStack.Items[FStack.Top-1];
|
|
if (OperatorItem^.Typ<>ctcssOperator)
|
|
or (GetOperatorLevel(OperatorItem^.StartPos)>MaxLevel) then
|
|
exit;
|
|
OperandItem:=FStack.TopItem;
|
|
|
|
// execute operator
|
|
Typ:=AtomToCTCfgOperator(OperatorItem^.StartPos);
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.ExecuteStack execute operator "',GetAtom(OperatorItem^.StartPos),'" Typ=',dbgs(Typ)]);
|
|
{$ENDIF}
|
|
case Typ of
|
|
|
|
ctcsoNot:
|
|
begin
|
|
b:=CTCSVariableIsTrue(@OperandItem^.Operand);
|
|
FStack.Pop(2);
|
|
PushBooleanValue(not b);
|
|
end;
|
|
|
|
ctcsoAnd,ctcsoOr,ctcsoXOr:
|
|
begin
|
|
b:=CTCSVariableIsTrue(@OperandItem^.Operand);
|
|
FStack.Pop(2);
|
|
if (FStack.Top>=0) then begin
|
|
OperandItem:=FStack.TopItem;
|
|
case Typ of
|
|
ctcsoAnd: b:=b and CTCSVariableIsTrue(@OperandItem^.Operand);
|
|
ctcsoOr: b:=b or CTCSVariableIsTrue(@OperandItem^.Operand);
|
|
ctcsoXOr: b:=b xor CTCSVariableIsTrue(@OperandItem^.Operand);
|
|
end;
|
|
FStack.Pop;
|
|
end;
|
|
PushBooleanValue(b);
|
|
end;
|
|
|
|
ctcsoEqual, ctcsoNotEqual, ctcsoLowerThan, ctcsoLowerOrEqualThan,
|
|
ctcsoGreaterThan, ctcsoGreaterOrEqualThan:
|
|
begin
|
|
b:=false;
|
|
if (FStack.Top>=2) then begin
|
|
LeftOperandItem:=@FStack.Items[FStack.Top-2];
|
|
if not CompareCTCSVariables(@LeftOperandItem^.Operand,@OperandItem^.Operand,
|
|
OperandsEqual,LeftIsLowerThanRight)
|
|
then begin
|
|
b:=false;
|
|
end else begin
|
|
case Typ of
|
|
ctcsoEqual:
|
|
b:=OperandsEqual;
|
|
ctcsoNotEqual:
|
|
b:=not OperandsEqual;
|
|
ctcsoLowerThan:
|
|
b:=(not OperandsEqual) and LeftIsLowerThanRight;
|
|
ctcsoLowerOrEqualThan:
|
|
b:=OperandsEqual or LeftIsLowerThanRight;
|
|
ctcsoGreaterThan:
|
|
b:=(not OperandsEqual) and not LeftIsLowerThanRight;
|
|
ctcsoGreaterOrEqualThan:
|
|
b:=OperandsEqual or not LeftIsLowerThanRight;
|
|
end;
|
|
end;
|
|
{$IFDEF VerboseCTCfgScript}
|
|
debugln(['TCTConfigScriptEngine.ExecuteStack ',GetCTCSVariableAsString(@LeftOperandItem^.Operand),' ',GetCTCSVariableAsString(@OperandItem^.Operand),' Equal=',OperandsEqual,' LT=',LeftIsLowerThanRight,' Result=',Result]);
|
|
{$ENDIF}
|
|
FStack.Pop(3);
|
|
end else begin
|
|
FStack.Pop(2);
|
|
end;
|
|
PushBooleanValue(b);
|
|
end;
|
|
|
|
ctcsoPlus:
|
|
begin
|
|
if (FStack.Top>=2) then begin
|
|
LeftOperandItem:=@FStack.Items[FStack.Top-2];
|
|
// add right operand to left operand on stack
|
|
AddCTCSVariables(@OperandItem^.Operand,@LeftOperandItem^.Operand);
|
|
// remove right operand and +
|
|
FStack.Pop(2);
|
|
end else begin
|
|
// unary operator
|
|
// just remove the +
|
|
FStack.Delete(FStack.Top-1);
|
|
end;
|
|
end;
|
|
|
|
else
|
|
ErrorInvalidOperator;
|
|
end;
|
|
until false;
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.GetOperatorLevel(P: PChar): integer;
|
|
begin
|
|
Result:=CTCfgScriptOperatorLvl[AtomToCTCfgOperator(P)];
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.IsFunction(p: PChar): boolean;
|
|
begin
|
|
Result:=false;
|
|
if (p=nil) or (not IsIdentStartChar[p^]) then exit;
|
|
case UpChars[p^] of
|
|
'I':
|
|
if (CompareIdentifiers(p,'integer')=0)
|
|
or (CompareIdentifiers(p,'int64')=0)
|
|
then exit(true);
|
|
'S':
|
|
if CompareIdentifiers(p,'string')=0 then exit(true);
|
|
end;
|
|
Result:=IsCustomFunction(p);
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.IsCustomFunction(FunctionName: PChar): boolean;
|
|
begin
|
|
Result:=false;
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.RunCustomSimpleFunction(FunctionName: PChar;
|
|
Value: PCTCfgScriptVariable);
|
|
begin
|
|
|
|
end;
|
|
|
|
constructor TCTConfigScriptEngine.Create;
|
|
begin
|
|
FVariables:=TCTCfgScriptVariables.Create;
|
|
FStack:=TCTCfgScriptStack.Create;
|
|
FErrors:=TFPList.Create;
|
|
end;
|
|
|
|
destructor TCTConfigScriptEngine.Destroy;
|
|
begin
|
|
ClearErrors;
|
|
FreeAndNil(FErrors);
|
|
FreeAndNil(FVariables);
|
|
FreeAndNil(FStack);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.ClearErrors;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to FErrors.Count-1 do
|
|
TObject(FErrors[i]).Free;
|
|
FErrors.Clear;
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.Execute(const Source: string;
|
|
StopAfterErrors: integer): boolean;
|
|
|
|
procedure ExpectedSemicolon;
|
|
begin
|
|
AddError(Format(ctsExpectedSemicolonOfStatementButFound, [GetAtomOrNothing]))
|
|
end;
|
|
|
|
var
|
|
Err: TCTCfgScriptError;
|
|
begin
|
|
FStack.Clear;
|
|
ClearErrors;
|
|
MaxErrorCount:=StopAfterErrors;
|
|
SrcStart:=#0;
|
|
SrcEnd:=SrcStart;
|
|
Src:=SrcStart;
|
|
AtomStart:=SrcStart;
|
|
|
|
if Source='' then exit(true);
|
|
|
|
SrcStart:=PChar(Source);
|
|
SrcEnd:=SrcStart+length(Source);
|
|
Src:=SrcStart;
|
|
AtomStart:=Src;
|
|
|
|
try
|
|
// execute all statements
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
while Src^<>#0 do begin
|
|
RunStatement(false);
|
|
if not (AtomStart^ in [#0,';']) then
|
|
ExpectedSemicolon;
|
|
ReadRawNextPascalAtom(Src,AtomStart,nil,false,true);
|
|
end;
|
|
except
|
|
on E: Exception do begin
|
|
// too many errors
|
|
if ErrorCount=0 then begin
|
|
Err:=TCTCfgScriptError.Create(E.Message);
|
|
FErrors.Add(Err);
|
|
end;
|
|
end;
|
|
end;
|
|
Result:=ErrorCount=0;
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.ErrorCount: integer;
|
|
begin
|
|
Result:=FErrors.Count;
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.GetAtom: string;
|
|
begin
|
|
if (AtomStart=nil) or (AtomStart>Src) then
|
|
exit('');
|
|
SetLength(Result,Src-AtomStart);
|
|
if Result<>'' then
|
|
System.Move(AtomStart^,Result[1],length(Result));
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.GetAtomOrNothing: string;
|
|
begin
|
|
if (AtomStart=nil) or (AtomStart>Src) then
|
|
Result:='nothing'
|
|
else begin
|
|
SetLength(Result,Src-AtomStart);
|
|
if Result<>'' then
|
|
System.Move(AtomStart^,Result[1],length(Result));
|
|
end;
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.GetAtom(P: PChar): string;
|
|
var
|
|
StartPos: PChar;
|
|
begin
|
|
if P=nil then
|
|
exit('');
|
|
ReadRawNextPascalAtom(P,StartPos,nil,false,true);
|
|
SetLength(Result,p-StartPos);
|
|
if Result<>'' then
|
|
System.Move(StartPos^,Result[1],length(Result));
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.PosToLineCol(p: PChar; out Line, Column: integer
|
|
): boolean;
|
|
var
|
|
run: PChar;
|
|
begin
|
|
Line:=1;
|
|
Column:=1;
|
|
if (p<SrcStart) or (p>SrcEnd) then exit(false);
|
|
run:=SrcStart;
|
|
while run<p do begin
|
|
if Run^ in [#10,#13] then begin
|
|
inc(Line);
|
|
Column:=1;
|
|
if (Run[1] in [#10,#13]) and (Run^<>Run[1]) then
|
|
inc(Run,2)
|
|
else
|
|
inc(Run);
|
|
end else begin
|
|
inc(Run);
|
|
inc(Column);
|
|
end;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.PosToStr(p: PChar): string;
|
|
var
|
|
Line: integer;
|
|
Column: integer;
|
|
begin
|
|
if PosToLineCol(p,Line,Column) then
|
|
Result:='('+IntToStr(Line)+','+IntToStr(Column)+')'
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TCTConfigScriptEngine.GetErrorStr(Index: integer): string;
|
|
var
|
|
Err: TCTCfgScriptError;
|
|
begin
|
|
Err:=Errors[Index];
|
|
Result:='Error: ';
|
|
if Err.Line>0 then
|
|
Result:=Result+'('+IntToStr(Err.Line)+','+IntToStr(Err.Column)+') ';
|
|
Result:=Result+Err.Msg;
|
|
end;
|
|
|
|
procedure TCTConfigScriptEngine.WriteDebugReportStack(Title: string);
|
|
var
|
|
i: Integer;
|
|
Item: PCTCfgScriptStackItem;
|
|
begin
|
|
debugln(['TCTConfigScriptEngine.WriteDebugReportStack FStack.Top=',FStack.Top,' ',Title]);
|
|
for i:=0 to FStack.Top do begin
|
|
dbgout(GetIndentStr(i*2+2));
|
|
Item:=@FStack.Items[i];
|
|
dbgout(dbgs(Item^.Typ),' StartPos=',GetAtom(Item^.StartPos));
|
|
if Item^.Typ=ctcssOperator then
|
|
dbgout(' level='+dbgs(GetOperatorLevel(Item^.StartPos)));
|
|
if Item^.Typ=ctcssOperand then
|
|
dbgout(' ',dbgs(PCTCfgScriptVariable(@Item^.Operand)));
|
|
debugln;
|
|
end;
|
|
end;
|
|
|
|
{ TCTCfgScriptStack }
|
|
|
|
constructor TCTCfgScriptStack.Create;
|
|
begin
|
|
Top:=-1;
|
|
end;
|
|
|
|
destructor TCTCfgScriptStack.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCTCfgScriptStack.Clear;
|
|
var
|
|
i: Integer;
|
|
Item: PCTCfgScriptStackItem;
|
|
begin
|
|
for i:=0 to Top do begin
|
|
Item:=@Items[i];
|
|
ClearCTCSVariable(@Item^.Operand);
|
|
if Item^.Operand.Name<>nil then
|
|
ReAllocMem(Item^.Operand.Name,0);
|
|
end;
|
|
Top:=-1;
|
|
TopTyp:=ctcssNone;
|
|
Capacity:=0;
|
|
ReAllocMem(Items,0);
|
|
end;
|
|
|
|
procedure TCTCfgScriptStack.Push(Typ: TCTCfgScriptStackItemType;
|
|
const StartPos: PChar);
|
|
var
|
|
OldCapacity: LongInt;
|
|
Item: PCTCfgScriptStackItem;
|
|
begin
|
|
inc(Top);
|
|
if Top>=Capacity then begin
|
|
OldCapacity:=Capacity;
|
|
if Capacity<10 then
|
|
Capacity:=10
|
|
else
|
|
Capacity:=Capacity*2;
|
|
ReAllocMem(Items,Capacity*SizeOf(TCTCfgScriptStackItem));
|
|
FillByte(Items[OldCapacity],(Capacity-OldCapacity)*SizeOf(TCTCfgScriptStackItem),0);
|
|
end;
|
|
Item:=@Items[Top];
|
|
Item^.Typ:=Typ;
|
|
Item^.StartPos:=StartPos;
|
|
TopTyp:=Typ;
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckOperands;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TCTCfgScriptStack.Pop(Count: integer);
|
|
|
|
procedure RaiseTooManyPop;
|
|
begin
|
|
raise ECodeToolCfgScript.Create('TCTCfgScriptStack.Pop too many pop');
|
|
end;
|
|
|
|
var
|
|
Item: PCTCfgScriptStackItem;
|
|
begin
|
|
if Top<Count-1 then
|
|
RaiseTooManyPop;
|
|
while Count>0 do begin
|
|
Item:=@Items[Top];
|
|
ClearCTCSVariable(@Item^.Operand);
|
|
if Item^.Operand.Name<>nil then
|
|
ReAllocMem(Item^.Operand.Name,0);
|
|
dec(Top);
|
|
if Top>=0 then
|
|
TopTyp:=Items[Top].Typ
|
|
else
|
|
TopTyp:=ctcssNone;
|
|
dec(Count);
|
|
end;
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckOperands;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TCTCfgScriptStack.Delete(Index: integer);
|
|
var
|
|
Item: PCTCfgScriptStackItem;
|
|
begin
|
|
if (Index<0) or (Index>Top) then exit;
|
|
Item:=@Items[Index];
|
|
ClearCTCSVariable(@Item^.Operand);
|
|
if Item^.Operand.Name<>nil then
|
|
ReAllocMem(Item^.Operand.Name,0);
|
|
if Index<Top then begin
|
|
System.Move(Items[Index+1],Items[Index],SizeOf(TCTCfgScriptStackItem)*(Top-Index));
|
|
Item:=@Items[Top];
|
|
Item^.Typ:=ctcssNone;
|
|
FillByte(Item^.Operand,SizeOf(Item^.Operand),0);
|
|
end;
|
|
dec(Top);
|
|
{$IFDEF CheckCTCfgVars}
|
|
CheckOperands;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TCTCfgScriptStack.TopItem: PCTCfgScriptStackItem;
|
|
begin
|
|
if Top<0 then
|
|
Result:=nil
|
|
else
|
|
Result:=@Items[Top];
|
|
end;
|
|
|
|
function TCTCfgScriptStack.TopItemOperand: PCTCfgScriptVariable;
|
|
begin
|
|
if Top<0 then
|
|
Result:=nil
|
|
else
|
|
Result:=@Items[Top].Operand;
|
|
end;
|
|
|
|
{$IFDEF CheckCTCfgVars}
|
|
procedure TCTCfgScriptStack.CheckOperands;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to Top do
|
|
CheckCTCSVariable(@Items[Top].Operand);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ TCTCfgScriptError }
|
|
|
|
constructor TCTCfgScriptError.Create(const aMsg: string; aPos, aLine,
|
|
aCol: integer);
|
|
begin
|
|
Msg:=aMsg;
|
|
Position:=aPos;
|
|
Line:=aLine;
|
|
Column:=aCol;
|
|
end;
|
|
|
|
constructor TCTCfgScriptError.Create(const aMsg: string);
|
|
begin
|
|
Msg:=aMsg;
|
|
Position:=-1;
|
|
Line:=0;
|
|
Column:=0;
|
|
end;
|
|
|
|
end.
|
|
|