lazarus/components/codetools/expreval.pas
2024-07-17 21:37:14 +02:00

2156 lines
56 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. *
* *
***************************************************************************/
Author: Mattias Gaertner
Abstract:
Defines class TExpressionEvaluator
Used by Code Tools for compiler directives. For example $IF expression.
This class stores variables (case sensitive) of type string.
Boolean values are '0' for false and true else (except empty '' which is
invalid).
The function Eval evaluates expressions and understands the operators
AND, OR, XOR, NOT, (, ), =, <, >, <=, >=, <>
defined()
not defined V or undefined V
}
unit ExprEval;
{$ifdef FPC}{$mode objfpc}{$endif}{$H+}
{$I codetools.inc}
{ $DEFINE VerboseExprEval}
interface
uses
{$IFDEF MEM_CHECK}
MemCheck,
{$ENDIF}
Classes, SysUtils, KeyWordFuncLists, FileProcs, LazDbgLog;
const
ExternalMacroStart = '#';
//----------------------------------------------------------------------------
// compiler switches
const
CompilerSwitchesNames: array['A'..'Z'] of shortstring=(
'ALIGN' // A align fields
,'BOOLEVAL' // B complete boolean evaluation
,'ASSERTIONS' // C generate code for assertions
,'DEBUGINFO' // D generate debuginfo (debug lines), OR: $description 'text'
,'EXTENSION' // E output file extension
,'' // F
,'IMPORTEDDATA' // G
,'LONGSTRINGS' // H String=AnsiString
,'IOCHECKS' // I generate EInOutError
,'WRITEABLECONST' // J writable typed const
,'' // K
,'LOCALSYMBOLS' // L generate local symbol information (debug, requires $D+)
,'TYPEINFO' // M allow published members OR $M minstacksize,maxstacksize
,'' // N
,'OPTIMIZATION' // O enable safe optimizations (-O1)
,'OPENSTRINGS' // P deprecated Delphi directive
,'OVERFLOWCHECKS' // Q or $OV
,'RANGECHECKS' // R
,'' // S
,'TYPEADDRESS' // T enabled: @variable gives typed pointer, otherwise untyped pointer
,'SAFEDIVIDE' // U
,'VARSTRINGCHECKS'// V strict shortstring checking, e.g. cannot pass shortstring[3] to shortstring
,'STACKFRAMES' // W always generate stackframes (debugging)
,'EXTENDEDSYNTAX' // X deprecated Delphi directive
,'REFERENCEINFO' // Y store for each identifier the declaration location
,'' // Z
);
type
TOnValuesChanged = procedure of object;
TOnGetSameString = procedure(var s: string) of object;
ArrayOfAnsiString = ^AnsiString;
TEvalOperand = record
Value: PChar;
Len: PtrInt;
Data: array[0..3] of char;
Free: boolean;
end;
PEvalOperand = ^TEvalOperand;
{ TExpressionEvaluator }
TExpressionEvaluator = class
private
FChangeStamp: integer;
FErrorMsg: string;
FErrorPos: integer;
FNames, FValues: ArrayOfAnsiString; // always sorted in FNames and FNames uppercase
FCount: integer;
FCapacity: integer;
OldExpr: string;
OldCurPos, OldMax, OldAtomStart, OldAtomEnd, OldPriorAtomStart: integer;
FOnChange: TOnValuesChanged;
function OldReadTilEndBracket:boolean;
function CompAtom(const UpperCaseTag:string): boolean;
function OldReadNextAtom:boolean;
function EvalAtPos:string;
function CompareValues(const v1, v2: string): integer;
function GetVariables(const Name: string): string;
procedure SetVariables(const Name: string; const Value: string);
function IndexOfName(VarName: PChar; VarLen: integer; InsertPos: boolean): integer;
function IndexOfIdentifier(Identifier: PChar; InsertPos: boolean): integer;
procedure Expand;
public
property Variables[const Name: string]: string
read GetVariables write SetVariables; default;
property Count: integer read FCount;
procedure Undefine(const Name: string);
function IsDefined(const Name: string): boolean; inline;
function IsIdentifierDefined(Identifier: PChar): boolean; inline;
function Equals(AnExpressionEvaluator: TExpressionEvaluator): boolean; reintroduce;
procedure Assign(SourceExpressionEvaluator: TExpressionEvaluator);
procedure AssignTo(SL: TStringList);
function Eval(const Expression: string; AllowExternalMacro: boolean = false):string;
function EvalPChar(Expression: PChar; ExprLen: PtrInt;
out Operand: TEvalOperand; AllowExternalMacro: boolean = false): boolean;// true if expression valid
function EvalBoolean(Expression: PChar; ExprLen: PtrInt; AllowExternalMacro: boolean = false): boolean;
function EvalOld(const Expression: string):string;
property ErrorPosition: integer read FErrorPos write FErrorPos;
property ErrorMsg: string read FErrorMsg write FErrorMsg;
property OnChange: TOnValuesChanged read FOnChange write FOnChange;
function Items(Index: integer): string;
function Names(Index: integer): string;
function Values(Index: integer): string;
procedure Append(const Variable, Value: string);
procedure Prepend(const Variable, Value: string);
procedure Clear;
function AsString: string;
constructor Create;
destructor Destroy; override;
procedure RemoveDoubles(OnGetSameString: TOnGetSameString);
procedure ConsistencyCheck;
procedure WriteDebugReport;
function CalcMemSize(WithNamesAndValues: boolean = true; Original: TExpressionEvaluator = nil): PtrUInt;
property ChangeStamp: integer read FChangeStamp;
procedure IncreaseChangeStamp; inline;
end;
procedure FreeEvalOperand(var V: TEvalOperand);
procedure ClearEvalOperand(out V: TEvalOperand); inline;
function EvalOperandIsTrue(const V: TEvalOperand): boolean; inline;
function EvalOperandToInt64(const V: TEvalOperand): int64;
function CompareEvalOperand(const Operand: TEvalOperand; Value: PChar): integer;
function CompareNames(Name1: PChar; Name1Len: PtrInt;
Name2: PChar; Name2Len: PtrInt): integer;
function CompareNames(const Name1, Name2: string): integer; inline;
implementation
var
IsWordChar, IsIdentifierChar, IsNumberBeginChar, IsNumberChar:
array[#0..#255] of boolean;
procedure InternalInit;
var c:char;
begin
for c:=#0 to #255 do begin
IsWordChar[c]:=(c in ['a'..'z','A'..'Z','_']);
IsNumberBeginChar[c]:=(c in ['0'..'9','$','%']);
IsNumberChar[c]:=(c in ['0'..'9','.','E','e']);
IsIdentifierChar[c]:=(c in ['a'..'z','A'..'Z','_','0'..'9']);
end;
end;
procedure FreeEvalOperand(var V: TEvalOperand);
begin
if V.Free then begin
FreeMem(V.Value);
V.Free:=false;
V.Value:=nil;
V.Len:=0;
end;
end;
procedure ClearEvalOperand(out V: TEvalOperand); inline;
begin
V.Free:=false;
V.Value:=nil;
V.Len:=0;
end;
function EvalOperandIsTrue(const V: TEvalOperand): boolean; inline;
begin
Result:=not ((V.Len=1) and (V.Value^='0'));
end;
function EvalOperandToInt64(const V: TEvalOperand): int64;
var
p: PChar;
l: PtrInt;
Negated: Boolean;
c: Char;
begin
Result:=0;
p:=V.Value;
l:=V.Len;
if l=0 then exit;
if p^='-' then begin
Negated:=true;
inc(p);
dec(l);
end else
Negated:=false;
if p^='$' then begin
// hex number
if l<15 then begin
while l>0 do begin
c:=p^;
case c of
'0'..'9': Result:=Result*16+ord(p^)-ord('0');
'a'..'f': Result:=Result*16+ord(p^)-ord('a')+10;
'A'..'Z': Result:=Result*16+ord(p^)-ord('A')+10;
else
break;
end;
inc(p);
dec(l);
end;
end else begin
try
while l>0 do begin
c:=p^;
case c of
'0'..'9': Result:=Result*16+ord(p^)-ord('0');
'a'..'f': Result:=Result*16+ord(p^)-ord('a')+10;
'A'..'Z': Result:=Result*16+ord(p^)-ord('A')+10;
else
break;
end;
inc(p);
dec(l);
end;
except
end;
end;
end else begin
// decimal number
if l<15 then begin
while l>0 do begin
c:=p^;
if c in ['0'..'9'] then
Result:=Result*10+ord(c)-ord('0')
else
break;
inc(p);
dec(l);
end;
end else begin
try
while l>0 do begin
c:=p^;
if c in ['0'..'9'] then
Result:=Result*10+ord(c)-ord('0')
else
break;
inc(p);
dec(l);
end;
except
end;
end;
end;
if Negated then Result:=-Result;
end;
procedure SetOperandValueStringConst(var V: TEvalOperand;
StartPos, EndPos: PChar);
var
l: PtrInt;
p: PChar;
DstPos: PChar;
begin
l:=0;
p:=StartPos;
if p^<>'''' then begin
if V.Free then FreeEvalOperand(V);
V.Len:=0;
V.Value:=nil;
exit;
end;
inc(p);
while p<EndPos do begin
if p^='''' then begin
inc(p);
if (p^<>'''') or (p=EndPos) then break;
end;
inc(p);
inc(l);
end;
if l<5 then begin
// short string
if V.Free then FreeEvalOperand(V);
V.Value:=@V.Data[0];
end else begin
// big string
if V.Free then
ReAllocMem(V.Value,l)
else begin
Getmem(V.Value,l);
V.Free:=true;
end;
end;
V.Len:=l;
// copy content
p:=StartPos+1;
DstPos:=V.Value;
while p<EndPos do begin
if p^='''' then begin
inc(p);
if (p^<>'''') or (p=EndPos) then break;
end;
DstPos^:=p^;
inc(p);
inc(DstPos);
end;
end;
procedure SetOperandValueChar(var V: TEvalOperand; const c: Char);
begin
if V.Free then FreeEvalOperand(V);
V.Data[0]:=c;
V.Value:=@V.Data[0];
V.Len:=1;
end;
procedure SetOperandValueConst(var V: TEvalOperand; const p: PChar);
begin
if V.Free then FreeEvalOperand(V);
V.Len:=strlen(p);
V.Value:=p;
end;
procedure SetOperandValueInt64(var V: TEvalOperand; i : int64);
const
HexChrs: array[0..15] of char = '0123456789ABCDEF';
var
j: Integer;
k: Integer;
i2: Int64;
begin
if (i>=-999) and (i<=9999) then begin
// small number => save in data
if V.Free then FreeEvalOperand(V);
V.Value:=@V.Data[0];
V.Len:=0;
if i<0 then begin
// sign
V.Data[0]:='-';
inc(V.Len);
i:=-i;
end;
if i<10 then
j:=1
else if i<100 then
j:=2
else if i<1000 then
j:=3
else
j:=4;
inc(V.Len,j);
k:=V.Len-1;
repeat
V.Data[k]:=HexChrs[i mod 10];
dec(j);
if j=0 then break;
i:=i div 10;
dec(k);
until false;
end else begin
// big number => save as hex number
// calculate needed mem
i2:=i;
j:=1; // $
if i2<0 then begin
i2:=-i2;
inc(j);
end;
while i2>0 do begin
i2:=i2 shr 4;
inc(j);
end;
V.Len:=j;
// allocate mem
if V.Free then begin
ReAllocMem(V.Value,j);
end else begin
V.Free:=true;
Getmem(V.Value,j);
end;
// write number
if i<0 then i:=-i;
while i>0 do begin
i:=i shr 4;
dec(j);
V.Value[j]:=HexChrs[i and $f];
end;
// write $
dec(j);
V.Value[j]:='$';
// write minus sign
if j=0 then
V.Value[j]:='-';
end;
end;
function CompareEvalOperand(const Operand: TEvalOperand; Value: PChar): integer;
var
p: PChar;
l: PtrInt;
begin
if (Operand.Value<>nil) and (Operand.Len>0) then begin
if Value<>nil then begin
p:=Operand.Value;
l:=Operand.Len;
while (p^=Value^) and (l>0) do begin
if Value^=#0 then begin
// 'aaa'#0'b' 'aaa'
exit(0);
end;
inc(p);
inc(Value);
dec(l);
end;
if l>0 then begin
if p^<Value^ then begin
// 'aaa' 'aab'
Result:=1;
end else begin
// 'aab' 'aaa' or 'aaa' 'aa'
Result:=-1;
end;
end else begin
if Value=#0 then begin
// 'aaa' 'aaa'
Result:=0;
end else begin
// 'aa' 'aaa'
Result:=1;
end;
end;
end else begin
// 'aaa' nil
Result:=-1;
end;
end else begin
if Value<>nil then begin
// nil 'aaa'
Result:=1;
end else begin
// nil nil
Result:=0;
end;
end;
end;
function OperandsAreEqual(const Op1, Op2: TEvalOperand): boolean;
var
i: Integer;
begin
Result:=false;
if Op1.Len<>Op2.Len then exit;
i:=Op1.Len-1;
while i>=0 do begin
if Op1.Value[i]<>Op2.Value[i] then exit;
dec(i);
end;
Result:=true;
end;
function GetIdentifierLen(Identifier: PChar): integer;
var
p: PChar;
begin
Result:=0;
p:=Identifier;
if p=nil then exit;
if not IsIdentStartChar[p^] then exit;
inc(p);
while IsIdentChar[p^] do inc(p);
Result:=p-Identifier;
end;
function CompareIdentifiers(Identifier1, Identifier2: PChar): integer;
begin
while (UpChars[Identifier1[0]]=UpChars[Identifier2[0]]) do begin
if (IsIdentChar[Identifier1[0]]) then begin
inc(Identifier1);
inc(Identifier2);
end else begin
Result:=0; // for example 'aaA;' 'aAa;'
exit;
end;
end;
if (IsIdentChar[Identifier1[0]]) then begin
if (IsIdentChar[Identifier2[0]]) then begin
if UpChars[Identifier1[0]]>UpChars[Identifier2[0]] then
Result:=-1 // for example 'aab' 'aaa'
else
Result:=1; // for example 'aaa' 'aab'
end else begin
Result:=-1; // for example 'aaa' 'aa;'
end;
end else begin
if (IsIdentChar[Identifier2[0]]) then
Result:=1 // for example 'aa;' 'aaa'
else
Result:=0; // for example 'aa;' 'aa,'
end;
end;
function CompareNames(Name1: PChar; Name1Len: PtrInt;
Name2: PChar; Name2Len: PtrInt): integer;
begin
while (Name1Len>0) and (Name2Len>0) do begin
if UpChars[Name1^]=UpChars[Name2^] then begin
inc(Name1);
dec(Name1Len);
inc(Name2);
dec(Name2Len);
end else begin
if UpChars[Name1^]<UpChars[Name2^] then
Result:=1
else
Result:=-1;
exit;
end;
end;
if Name1Len>Name2Len then
Result:=-1
else if Name1Len<Name2Len then
Result:=1
else
Result:=0;
end;
function CompareNames(const Name1, Name2: string): integer; inline;
begin
Result:=CompareNames(PChar(Name1),length(Name1),PChar(Name2),length(Name2));
end;
{ TBooleanVariables }
procedure TExpressionEvaluator.Clear;
var i: integer;
begin
if FCount=0 then exit;
for i:=0 to FCount-1 do begin
FNames[i]:='';
FValues[i]:='';
end;
FCount:=0;
if FNames<>nil then begin
FreeMem(FNames);
FNames:=nil;
end;
if FValues<>nil then begin
FreeMem(FValues);
FValues:=nil;
end;
FCapacity:=0;
IncreaseChangeStamp;
end;
function TExpressionEvaluator.CompareValues(const v1, v2: string): integer;
// -1 : v1<v2
// 0 : v1=v2
// 1 : v1>v2
var len1,len2,a:integer;
c1: Char;
c2: Char;
ValPos1: Integer;
ValPos2: Integer;
begin
len1:=length(v1);
len2:=length(v2);
ValPos1:=1;
ValPos2:=1;
if (len1>1) and (v1[ValPos1]='''') then begin
inc(ValPos1);
dec(Len1,2);
end;
if (len2>1) and (v2[ValPos2]='''') then begin
inc(ValPos2);
dec(Len2,2);
end;
if len1<len2 then Result:=-1
else if len1>len2 then Result:=1
else begin
for a:=1 to len1 do begin
c1:=v1[ValPos1];
c2:=v2[ValPos2];
if c1<c2 then begin
Result:=-1; exit;
end;
if c1>c2 then begin
Result:=1; exit;
end;
inc(ValPos1);
inc(ValPos2);
end;
Result:=0;
end;
end;
function TExpressionEvaluator.CompAtom(
const UpperCaseTag: string): boolean;
// compare uppercase tag with case insensitive atom
var a,len:integer;
begin
if (OldAtomEnd>OldMax+1) then begin
Result:=false; exit;
end;
len:=OldAtomEnd-OldAtomStart;
if length(UpperCaseTag)<>len then begin
Result:=false; exit;
end;
for a:=1 to len do begin
if (UpChars[OldExpr[OldAtomStart+a-1]]<>UpperCaseTag[a]) then begin
Result:=false; exit;
end;
end;
Result:=true;
end;
constructor TExpressionEvaluator.Create;
begin
inherited Create;
FValues:=nil;
FNames:=nil;
FCount:=0;
end;
destructor TExpressionEvaluator.Destroy;
begin
Clear;
inherited Destroy;
end;
procedure TExpressionEvaluator.RemoveDoubles(OnGetSameString: TOnGetSameString);
var
i: Integer;
V: String;
begin
for i:=0 to FCount-1 do begin
OnGetSameString(FNames[i]);
V:=FValues[i];
if V<>'' then
OnGetSameString(V);
end;
end;
function TExpressionEvaluator.EvalOld(const Expression: string): string;
// 1 = true
// 0 = syntax error
// -1 = false
var s:string;
begin
OldExpr:=Expression;
OldMax:=length(OldExpr);
OldCurPos:=1;
OldAtomStart:=-1; OldAtomEnd:=-1; OldPriorAtomStart:=-1;
FErrorPos:=-1;
s:=EvalAtPos;
if FErrorPos>=0 then begin
// error
Result:=''; exit;
end;
Result:=s;
end;
function TExpressionEvaluator.Items(Index: integer): string;
begin
Result:=FNames[Index]+'='+FValues[Index];
end;
function TExpressionEvaluator.Names(Index: integer): string;
begin
Result:=FNames[Index];
end;
function TExpressionEvaluator.Values(Index: integer): string;
begin
Result:=FValues[Index];
end;
procedure TExpressionEvaluator.Append(const Variable, Value: string);
begin
Variables[Variable]:=Variables[Variable]+Value;
end;
procedure TExpressionEvaluator.Prepend(const Variable, Value: string);
begin
Variables[Variable]:=Value+Variables[Variable];
end;
function TExpressionEvaluator.EvalAtPos: string;
var r: string; // current result
c,o1,o2: char;
OldPos: integer;
AtomCount: Integer;
HasBracket: Boolean;
begin
Result:='';
AtomCount:=0;
repeat
if (not OldReadNextAtom) then exit;
inc(AtomCount);
c:=OldExpr[OldAtomStart];
if IsWordChar[c] then begin
// identifier or keyword
if (CompAtom('AND')) then begin
if (Result='') then FErrorPos:=OldCurPos
else if (Result<>'0') then begin
// true AND ...
Result:=EvalAtPos();
if FErrorPos>=0 then exit;
if (Result='') then FErrorPos:=OldCurPos;
end;
exit;
end else if (CompAtom('OR')) then begin
if (Result='0') then begin
// false OR ...
Result:=EvalAtPos();
if FErrorPos>=0 then exit;
if (Result='') then FErrorPos:=OldCurPos;
end else if (AtomCount<=1) then FErrorPos:=OldCurPos;
exit;
end else if (CompAtom('XOR')) then begin
if (Result='') then begin
FErrorPos:=OldCurPos; exit;
end;
r:=Result;
// true/false XOR ...
Result:=EvalAtPos();
if FErrorPos>=0 then exit;
if (Result='') then begin
FErrorPos:=OldCurPos; exit;
end;
if (r='0') then begin
if (Result='0') then Result:='0' else Result:='1';
end else begin
if (Result='0') then Result:='1' else Result:='0';
end;
exit;
end else if (CompAtom('NOT')) then begin
Result:=EvalAtPos();
if FErrorPos>=0 then exit;
// Note: for Delphi compatibility: "IF not UndefinedVariable" is valid
if (Result='0') then Result:='1'
else Result:='0';
exit;
end else if (CompAtom('DEFINED')) then begin
// read DEFINED(identifier) or defined identifier
if (Result<>'') or (not OldReadNextAtom) then begin
FErrorPos:=OldCurPos;
exit;
end;
HasBracket:=CompAtom('(');
if HasBracket and (not OldReadNextAtom) then begin
FErrorPos:=OldCurPos;
exit;
end;
if IsDefined(copy(OldExpr,OldAtomStart,OldAtomEnd-OldAtomStart)) then
Result:='1'
else
Result:='0';
if HasBracket then begin
if (not OldReadNextAtom) or (not CompAtom(')')) then begin
FErrorPos:=OldCurPos;
exit;
end;
end;
end else if (CompAtom('DECLARED')) then begin
// read DECLARED(identifier)
if (Result<>'') or (not OldReadNextAtom) or (CompAtom('(')=false)
or (not OldReadNextAtom) then begin
FErrorPos:=OldCurPos;
exit;
end;
if CompAtom('UNICODESTRING') then begin
if IsDefined('FPC_HAS_UNICODESTRING') then
Result:='1'
else
Result:='0';
end else begin
Result:='0';// this can only be answered by a real compiler
end;
if (not OldReadNextAtom) or (not CompAtom(')')) then begin
FErrorPos:=OldCurPos;
exit;
end;
end else if (CompAtom('UNDEFINED')) then begin
// read UNDEFINED(identifier) or undefined identifier
if (Result<>'') or (not OldReadNextAtom) then begin
FErrorPos:=OldCurPos;
exit;
end;
HasBracket:=CompAtom('(');
if HasBracket and (not OldReadNextAtom) then begin
FErrorPos:=OldCurPos;
exit;
end;
Result:=Variables[copy(OldExpr,OldAtomStart,OldAtomEnd-OldAtomStart)];
if Result<>'' then
Result:='0'
else
Result:='1';
if HasBracket then begin
if (not OldReadNextAtom) or (not CompAtom(')')) then begin
FErrorPos:=OldCurPos;
exit;
end;
end;
end else begin
// Identifier
if (Result<>'') then begin
FErrorPos:=OldCurPos;
exit;
end else
Result:=Variables[copy(OldExpr,OldAtomStart,OldAtomEnd-OldAtomStart)];
end;
end else if IsNumberBeginChar[c] then begin
// number
if (Result<>'') then begin
FErrorPos:=OldCurPos; exit;
end else Result:=copy(OldExpr,OldAtomStart,OldAtomEnd-OldAtomStart);
end else if c='''' then begin
Result:=copy(OldExpr,OldAtomStart+1,OldAtomEnd-OldAtomStart-2);
end else begin
// operator
case c of
')':exit;
'(':begin
OldPos:=OldAtomStart;
// eval in brackets
Result:=EvalAtPos();
if FErrorPos>=0 then exit;
// go behind brackets
OldCurPos:=OldPos;
if (not OldReadTilEndBracket) then exit;
inc(OldCurPos);
end;
'=','>','<':begin
o1:=c;
if OldAtomEnd=OldAtomStart+1 then begin
r:=EvalAtPos();
if FErrorPos>=0 then exit;
case o1 of
'=':if CompareValues(Result,r)=0 then Result:='1' else Result:='0';
'>':if CompareValues(Result,r)=1 then Result:='1' else Result:='0';
'<':if CompareValues(Result,r)=-1 then Result:='1' else Result:='0';
end;
end else begin
o2:=OldExpr[OldAtomStart+1];
r:=EvalAtPos();
if FErrorPos>=0 then exit;
if o1='<' then begin
if o2='>' then begin
if CompareValues(Result,r)<>0 then Result:='1' else Result:='0';
end else if o2='=' then begin
if CompareValues(Result,r)<=0 then Result:='1' else Result:='0';
end else FErrorPos:=OldAtomStart;
end else if o1='>' then begin
if o2='=' then begin
if CompareValues(Result,r)>=0 then Result:='1' else Result:='0';
end else FErrorPos:=OldAtomStart;
end else FErrorPos:=OldAtomStart;
end;
exit;
end;
'!':
begin
Result:=EvalAtPos();
if FErrorPos>=0 then exit;
if (Result='0') then Result:='1'
else if (Result='') then FErrorPos:=OldCurPos
else Result:='0';
exit;
end;
else
begin
FErrorPos:=OldCurPos;
end;
end;
end;
until (FErrorPos>=0);
end;
procedure TExpressionEvaluator.Expand;
var
NewSize: integer;
begin
FCapacity:=(FCapacity shl 1)+10;
NewSize:=SizeOf(AnsiString)*FCapacity;
ReAllocMem(FValues,NewSize);
ReAllocMem(FNames,NewSize);
end;
function TExpressionEvaluator.IndexOfName(VarName: PChar; VarLen: integer;
InsertPos: boolean): integer;
var l,r,m, cmp: integer;
begin
if FCount=0 then begin
if InsertPos then
Result:=0
else
Result:=-1;
exit;
end;
l:=0;
r:=FCount-1;
m:=0;
cmp:=0;
while l<=r do begin
m:=(l+r) shr 1;
cmp:=CompareNames(VarName,VarLen,PChar(FNames[m]),length(FNames[m]));
if cmp>0 then
l:=m+1
else if cmp<0 then
r:=m-1
else begin
Result:=m;
exit;
end;
end;
if InsertPos then begin
if cmp>0 then inc(m);
Result:=m;
end else begin
Result:=-1;
end;
end;
function TExpressionEvaluator.IndexOfIdentifier(Identifier: PChar;
InsertPos: boolean): integer;
var l,r,m, cmp: integer;
IdentLen: Integer;
CurName: String;
begin
if FCount=0 then begin
if InsertPos then
Result:=0
else
Result:=-1;
exit;
end;
l:=0;
r:=FCount-1;
m:=0;
cmp:=0;
IdentLen:=GetIdentifierLen(Identifier);
while l<=r do begin
m:=(l+r) shr 1;
CurName:=FNames[m];
cmp:=CompareNames(Identifier,IdentLen,PChar(CurName),length(CurName));
if cmp>0 then
l:=m+1
else if cmp<0 then
r:=m-1
else begin
Result:=m;
exit;
end;
end;
if InsertPos then begin
if cmp>0 then inc(m);
Result:=m;
end else begin
Result:=-1;
end;
end;
function TExpressionEvaluator.GetVariables(const Name: string): string;
var i: integer;
begin
i:=IndexOfName(PChar(Name),length(Name),false);
if (i>=0) then
Result:=FValues[i]
else
Result:='';
end;
function TExpressionEvaluator.IsDefined(const Name: string): boolean;
begin
Result:=IndexOfName(PChar(Name),length(Name),false)>=0;
end;
function TExpressionEvaluator.IsIdentifierDefined(Identifier: PChar): boolean;
begin
Result:=IndexOfIdentifier(Identifier,false)>=0;
end;
function TExpressionEvaluator.OldReadNextAtom: boolean;
var c,o1,o2:char;
begin
OldPriorAtomStart:=OldAtomStart;
while (OldCurPos<=OldMax) do begin
c:=OldExpr[OldCurPos];
if (c<=' ') then inc(OldCurPos)
else if IsWordChar[c] then begin
// Identifier
OldAtomStart:=OldCurPos;
repeat
inc(OldCurPos);
until (OldCurPos>OldMax) or (not IsIdentifierChar[OldExpr[OldCurPos]]);
OldAtomEnd:=OldCurPos;
Result:=true;
exit;
end else if IsNumberBeginChar[c] then begin
// Number
OldAtomStart:=OldCurPos;
repeat
inc(OldCurPos);
until (OldCurPos>OldMax) or (IsNumberChar[OldExpr[OldCurPos]]=false);
OldAtomEnd:=OldCurPos;
Result:=true;
exit;
end else if c='''' then begin
// string
OldAtomStart:=OldCurPos;
repeat
inc(OldCurPos);
if OldExpr[OldCurPos]='''' then begin
inc(OldCurPos);
OldAtomEnd:=OldCurPos;
Result:=true;
exit;
end;
if OldCurPos>OldMax then begin
OldAtomEnd:=OldCurPos;
Result:=false;
exit;
end;
until (OldCurPos>OldMax);
end else begin
// Symbol
OldAtomStart:=OldCurPos;
inc(OldCurPos);
if (OldCurPos<=OldMax) then begin
o1:=c;
o2:=OldExpr[OldCurPos];
if ((o2='=') and ((o1='<') or (o1='>')))
or ((o1='<') and (o2='>'))
then inc(OldCurPos);
end;
OldAtomEnd:=OldCurPos;
Result:=true;
exit;
end;
end;
Result:=false;
end;
function TExpressionEvaluator.OldReadTilEndBracket: boolean;
// true = end bracket found
// false = not found
var lvl:integer;
begin
lvl:=0;
while (OldCurPos<=OldMax) do begin
if (OldExpr[OldCurPos]='(') then
inc(lvl)
else if (OldExpr[OldCurPos]=')') then begin
dec(lvl);
if (lvl=0) then begin
Result:=true; exit;
end else if (lvl<0) then begin
FErrorPos:=OldCurPos;
Result:=true; exit;
end;
end;
inc(OldCurPos);
end;
Result:=false;
end;
procedure TExpressionEvaluator.Assign(
SourceExpressionEvaluator: TExpressionEvaluator);
var i, Size: integer;
begin
Clear;
if SourceExpressionEvaluator<>nil then begin
FCount:=SourceExpressionEvaluator.Count;
Size:=SizeOf(AnsiString) * FCount;
if Size>0 then begin
GetMem(FNames,Size);
FillByte(Pointer(FNames)^,Size,0);
GetMem(FValues,Size);
FillByte(Pointer(FValues)^,Size,0);
FCapacity:=FCount;
for i:=0 to FCount-1 do begin
FNames[i]:=SourceExpressionEvaluator.FNames[i];
FValues[i]:=SourceExpressionEvaluator.FValues[i];
end;
end;
IncreaseChangeStamp;
end;
if Assigned(FOnChange) then FOnChange;
end;
procedure TExpressionEvaluator.SetVariables(const Name: string;
const Value: string);
var i: integer;
Size: Integer;
begin
i:=IndexOfName(PChar(Name),length(Name),true);
if (i>=0) and (i<FCount) and (CompareNames(FNames[i],Name)=0) then begin
// variable already exists -> replace value
if FValues[i]<>Value then begin
FValues[i]:=Value;
IncreaseChangeStamp;
end;
end else begin
// new variable
if FCount=FCapacity then Expand;
if i<0 then i:=0;
if i<FCount then begin
Size:=SizeOf(AnsiString)*(FCount-i);
System.Move(PPointer(FNames)[i],PPointer(FNames)[i+1],Size);
System.Move(PPointer(FValues)[i],PPointer(FValues)[i+1],Size);
end;
PPointer(FNames)[i]:=nil;
PPointer(FValues)[i]:=nil;
FNames[i]:=UpperCaseStr(Name);
FValues[i]:=Value;
inc(FCount);
IncreaseChangeStamp;
end;
end;
procedure TExpressionEvaluator.Undefine(const Name: string);
var i: integer;
Size: Integer;
begin
i:=IndexOfName(PChar(Name),length(Name),false);
if (i>=0) then begin
FNames[i]:='';
FValues[i]:='';
dec(FCount);
if FCount>i then begin
Size:=SizeOf(AnsiString)*(FCount-i);
System.Move(PPointer(FNames)[i+1],PPointer(FNames)[i],Size);
System.Move(PPointer(FValues)[i+1],PPointer(FValues)[i],Size);
end;
end;
end;
function TExpressionEvaluator.Equals(
AnExpressionEvaluator: TExpressionEvaluator): boolean;
var i: integer;
begin
if (AnExpressionEvaluator=nil) or (AnExpressionEvaluator.Count<>FCount) then
begin
Result:=false;
exit;
end;
for i:=0 to FCount-1 do begin
if (FNames[i]<>AnExpressionEvaluator.FNames[i])
or (FValues[i]<>AnExpressionEvaluator.FValues[i]) then begin
Result:=false;
exit;
end;
end;
Result:=true;
end;
procedure TExpressionEvaluator.AssignTo(SL: TStringList);
var i: integer;
begin
if SL=nil then exit;
SL.Clear;
for i:=0 to FCount-1 do
SL.Add(FNames[i]+'='+FValues[i]);
end;
function TExpressionEvaluator.Eval(const Expression: string;
AllowExternalMacro: boolean): string;
{ 0 = false
else true }
var
Operand: TEvalOperand;
begin
if Expression='' then exit('0');
if not EvalPChar(PChar(Expression),length(Expression),Operand,AllowExternalMacro) then
Result:=''
else begin
SetLength(Result,Operand.Len);
if Result<>'' then
System.Move(Operand.Value^,Result[1],length(Result));
end;
FreeEvalOperand(Operand);
end;
function TExpressionEvaluator.EvalPChar(Expression: PChar; ExprLen: PtrInt; out
Operand: TEvalOperand; AllowExternalMacro: boolean): boolean;
{ 0 = false
else true
brackets ()
constants: false, true
unary operators: not, defined, undefined
binary operators: + - * / < <= = <> => > div mod and or xor shl shr
functions: defined(), undefined(), declared(), sizeof()=1, option(),
high(), low()
}
type
TOperandAndOperator = record
Operand: TEvalOperand;
theOperator: PChar;
OperatorLvl: integer;
end;
TExprStack = array[0..3] of TOperandAndOperator;
var
ExprStack: TExprStack;
StackPtr: integer; // -1 = empty
ExprEnd: PChar;
p, AtomStart: PChar;
procedure FreeStack;
begin
while StackPtr>=0 do begin
FreeEvalOperand(ExprStack[StackPtr].Operand);
dec(StackPtr);
end;
end;
function GetAtom: string;
var
l: PtrInt;
begin
l:=p-AtomStart;
if l=0 then exit('');
SetLength(Result,l);
System.Move(AtomStart^,Result[1],l);
end;
procedure ReadNextAtom;
var
Float: Boolean;
Exponent: Boolean;
begin
// skip space
while p^ in [' ',#9,#10,#13] do inc(p);
if p>=ExprEnd then begin
p:=ExprEnd;
AtomStart:=p;
exit;
end;
AtomStart:=p;
case UpChars[p^] of
'A'..'Z','_':
begin
while IsIdentChar[p^] do inc(p);
if p>ExprEnd then p:=ExprEnd;
end;
'0'..'9':
begin
inc(p);
Float:=false;
Exponent:=false;
repeat
case p^ of
'0'..'9': inc(p);
'.':
if Float then
break
else begin
Float:=true;
inc(p);
end;
'e','E':
if Exponent or (not Float) then
break
else begin
Exponent:=true;
inc(p);
end;
else
break;
end;
until p>=ExprEnd;
end;
'$':
begin
inc(p);
while IsHexNumberChar[p^] do inc(p);
end;
'>':
begin
inc(p);
case p^ of
'=','>': inc(p); // >= >>
end;
end;
'<':
begin
inc(p);
case p^ of
'<','>','=': inc(p); // <> <= <<
end;
end;
'''':
begin
inc(p);
while (p<=ExprEnd) do begin
if p^='''' then begin
inc(p);
if p^<>'''' then break;
inc(p);
end else begin
inc(p);
end;
end;
end;
else
inc(p);
end;
{$IFDEF VerboseExprEval}
DebugLn(['ReadNextAtom ',GetAtom]);
{$ENDIF}
end;
procedure Error(NewErrorPos: PChar; const NewErrorMsg: string);
begin
if NewErrorPos<>nil then
FErrorPos:=NewErrorPos-Expression
else
FErrorPos:=0;
ErrorMsg:=NewErrorMsg;
{$IFDEF VerboseExprEval}
DebugLn(['Error ',ErrorMsg,' at ',ErrorPosition]);
{$ENDIF}
end;
procedure Error(NewErrorPos: PChar; E: Exception);
begin
Error(NewErrorPos,E.Message);
end;
procedure ExpressionMissing(NewErrorPos: PChar);
begin
Error(NewErrorPos,'expression missing');
end;
procedure IdentifierMissing(NewErrorPos: PChar);
begin
Error(NewErrorPos,'identifier missing');
end;
procedure OperatorMissing(NewErrorPos: PChar);
begin
Error(NewErrorPos,'operator missing');
end;
procedure CharMissing(NewErrorPos: PChar; c: char);
begin
Error(NewErrorPos,c+' missing');
end;
procedure BracketMissing(NewErrorPos: PChar);
begin
Error(NewErrorPos,'closing bracket without opening bracket');
end;
procedure StrExpectedAtPos(NewErrorPos, ExpectedStr: PChar);
var
s: string;
f: string;
begin
s:=ExpectedStr;
if ExprEnd>NewErrorPos then begin
SetLength(f{%H-},ExprEnd-NewErrorPos);
System.Move(NewErrorPos^,f[1],ExprEnd-NewErrorPos);
Error(NewErrorPos,'expected '+s+', but found '+f);
end else begin
Error(NewErrorPos,'expected '+s);
end;
end;
function ReadTilEndBracket: boolean;
// start on bracket open
// ends on bracket close
var
BracketLvl: Integer;
BracketOpen: PChar;
begin
BracketOpen:=AtomStart;
BracketLvl:=0;
while AtomStart<ExprEnd do begin
case AtomStart^ of
'(': inc(BracketLvl);
')':
begin
dec(BracketLvl);
if BracketLvl=0 then exit(true);
end;
end;
ReadNextAtom;
end;
BracketMissing(BracketOpen);
Result:=false;
end;
function ParseDefinedParams(var Operand: TEvalOperand): boolean;
// p is behind defined or undefined keyword
// Operand: '1' or '-1'
var
NameStart: PChar;
begin
Result:=false;
ReadNextAtom;
if AtomStart>=ExprEnd then begin
IdentifierMissing(AtomStart);
exit;
end;
if IsIdentifierChar[AtomStart^] then begin
if IsIdentifierDefined(AtomStart) then begin
SetOperandValueChar(Operand,'1');
end else begin
SetOperandValueConst(Operand,'0');
end;
end else if AtomStart^='(' then begin
ReadNextAtom;
if p=AtomStart then begin
StrExpectedAtPos(AtomStart,'macro name');
exit;
end;
if AtomStart^=')' then begin
SetOperandValueConst(Operand,'0');
exit(true);
end;
NameStart:=AtomStart;
if (AtomStart^=ExternalMacroStart) and AllowExternalMacro then begin
inc(AtomStart);
p:=AtomStart;
end;
if not IsIdentStartChar[AtomStart^] then begin
StrExpectedAtPos(AtomStart,'macro name');
exit;
end;
while IsIdentifierChar[p^] do inc(p);
if IndexOfName(NameStart,p-NameStart,false)>=0 then begin
SetOperandValueConst(Operand,'1');
end else begin
SetOperandValueConst(Operand,'0');
end;
ReadNextAtom;
if AtomStart^<>')' then begin
StrExpectedAtPos(AtomStart,')');
exit;
end;
end else begin
StrExpectedAtPos(AtomStart,'macro name');
exit;
end;
Result:=true;
end;
function ParseOptionParams(var Operand: TEvalOperand): boolean;
// p is behind option keyword
// Operand: '1' or '-1'
begin
Result:=false;
ReadNextAtom;
if AtomStart>=ExprEnd then begin
CharMissing(ExprEnd,'(');
exit;
end;
if AtomStart^<>'(' then begin
StrExpectedAtPos(AtomStart,'(');
exit;
end;
ReadNextAtom;
if not IsIdentifierChar[AtomStart^] then begin
StrExpectedAtPos(AtomStart,'option name');
exit;
end;
SetOperandValueChar(Operand,'1'); // ToDo: check the right flag
ReadNextAtom;
if AtomStart>=ExprEnd then begin
CharMissing(ExprEnd,')');
exit;
end;
if AtomStart^<>')' then begin
StrExpectedAtPos(AtomStart,')');
exit;
end;
Result:=true;
end;
function ParseSizeOfParams(var Operand: TEvalOperand): boolean;
// p is behind option keyword
var
Identifier: String;
Value: int64;
begin
Result:=false;
ReadNextAtom;
if AtomStart>=ExprEnd then begin
CharMissing(ExprEnd,'(');
exit;
end;
if AtomStart^<>'(' then begin
StrExpectedAtPos(AtomStart,'(');
exit;
end;
ReadNextAtom;
if not IsIdentifierChar[AtomStart^] then begin
StrExpectedAtPos(AtomStart,'identifier');
exit;
end;
Identifier:=GetAtom;
ReadNextAtom;
while AtomStart^='.' do begin
Identifier:=Identifier+'.';
ReadNextAtom;
if not IsIdentifierChar[AtomStart^] then begin
StrExpectedAtPos(AtomStart,'identifier');
exit;
end;
Identifier:=Identifier+GetAtom;
ReadNextAtom;
end;
if AtomStart>=ExprEnd then begin
CharMissing(ExprEnd,')');
exit;
end;
if AtomStart^<>')' then begin
StrExpectedAtPos(AtomStart,')');
exit;
end;
case lowercase(Identifier) of
'boolean',
'bytebool',
'byte',
'shortint': Value:=1;
'wordbool',
'word',
'smallint': Value:=2;
'cardinal',
'longword',
'longbool': Value:=4;
'int64',
'qword',
'qwordbool',
'comp': Value:=8;
'pointer',
'ptrint',
'ptruint',
'string',
'ansistring',
'unicodestring',
'rawbytestring',
'widestring':
if IsDefined('CPU16') then
Value:=2
else if IsDefined('CPU32') then
Value:=4
else
Value:=8;
'ansichar': Value:=1;
'widechar','unicodechar': Value:=2;
'char':
if IsDefined('FPC_UNICODESTRINGS') then
Value:=2
else
Value:=1;
'single': Value:=4;
'double': Value:=8;
'extended':
if IsDefined('CPU32') then
Value:=10
else
Value:=8;
else
// default: return default pointer size
if IsDefined('CPU16') then
Value:=2
else if IsDefined('CPU32') then
Value:=4
else
Value:=8;
end;
SetOperandValueInt64(Operand,Value);
Result:=true;
end;
function ReadOperand: boolean;
{ Examples:
Variable
not Variable
not not undefined Variable
defined(Variable)
!Variable
unicodestring
123
$45
'Abc'
(expression)
}
var
i: LongInt;
BracketStart: PChar;
begin
Result:=false;
if AtomStart>=ExprEnd then exit;
{$IFDEF VerboseExprEval}
DebugLn(['ReadOperand ',GetAtom]);
{$ENDIF}
case UpChars[AtomStart^] of
'N':
if CompareIdentifiers(AtomStart,'NOT')=0 then begin
// not
ReadNextAtom;
if not ReadOperand() then exit;
if (Operand.Len=1) and (Operand.Value^='0') then begin
SetOperandValueChar(Operand,'1');
end else begin
SetOperandValueChar(Operand,'0');
end;
exit(true);
end;
'D':
if CompareIdentifiers(AtomStart,'DEFINED')=0 then begin
// "defined V" or "defined(V)"
if not ParseDefinedParams(Operand) then exit;
exit(true);
end
else if CompareIdentifiers(AtomStart,'DECLARED')=0 then begin
// should check if a pascal identifier is already declared
// can not do this here => return always true
if not ParseDefinedParams(Operand) then exit;
SetOperandValueChar(Operand,'1');
exit(true);
end;
'H':
if CompareIdentifiers(AtomStart,'HIGH')=0 then begin
ReadNextAtom;
if AtomStart^<>'(' then StrExpectedAtPos(AtomStart,'(');
if not ReadTilEndBracket then exit;
SetOperandValueChar(Operand,'0');
exit(true);
end;
'L':
if CompareIdentifiers(AtomStart,'LOW')=0 then begin
ReadNextAtom;
if AtomStart^<>'(' then StrExpectedAtPos(AtomStart,'(');
if not ReadTilEndBracket then exit;
SetOperandValueChar(Operand,'0');
exit(true);
end;
'O':
if CompareIdentifiers(AtomStart,'OPTION')=0 then begin
ReadNextAtom;
if not ParseOptionParams(Operand) then exit;
exit(true);
end else
if CompareIdentifiers(AtomStart,'ORD')=0 then begin
// ORD not fully supported yet, return '0' (like HIGH and LOW)
ReadNextAtom;
if AtomStart^<>'(' then StrExpectedAtPos(AtomStart,'(');
if not ReadTilEndBracket then exit;
SetOperandValueChar(Operand,'0');
exit(true);
end;
'S':
if CompareIdentifiers(AtomStart,'SIZEOF')=0 then begin
if not ParseSizeOfParams(Operand) then exit;
exit(true);
end;
'U':
if CompareIdentifiers(AtomStart,'UNDEFINED')=0 then begin
// "undefined V" or "undefined(V)"
if not ParseDefinedParams(Operand) then exit;
if (Operand.Len=1) and (Operand.Value^='0') then begin
SetOperandValueChar(Operand,'1');
end else begin
SetOperandValueChar(Operand,'0');
end;
exit(true);
end
else if CompareIdentifiers(AtomStart,'UNICODESTRING')=0 then begin
// unicodestring
if IsIdentifierDefined('FPC_HAS_UNICODESTRING') then begin
SetOperandValueChar(Operand,'1');
end else begin
SetOperandValueChar(Operand,'0');
end;
exit(true);
end;
'!':
if p-AtomStart=1 then begin
// not
ReadNextAtom;
if not ReadOperand() then exit;
if (Operand.Len=1) and (Operand.Value^='0') then begin
SetOperandValueChar(Operand,'1');
end else begin
SetOperandValueChar(Operand,'0');
end;
exit(true);
end;
'0'..'9','$':
begin
// number
if Operand.Free then FreeEvalOperand(Operand);
Operand.Value:=AtomStart;
Operand.Len:=p-AtomStart;
exit(true);
end;
'''':
begin
SetOperandValueStringConst(Operand,AtomStart,p);
exit(true);
end;
'(':
begin
BracketStart:=AtomStart;
ReadNextAtom;
if AtomStart>=ExprEnd then exit;
{$IFDEF VerboseExprEval}
DebugLn(['ReadOperand BRACKET OPEN']);
{$ENDIF}
if not EvalPChar(AtomStart,ExprLen-(AtomStart-Expression),Operand) then
exit;
{$IFDEF VerboseExprEval}
DebugLn(['ReadOperand BRACKET CLOSED => skip bracket']);
{$ENDIF}
AtomStart:=BracketStart;
p:=AtomStart+1;
if not ReadTilEndBracket then exit;
exit(true);
end;
end;
if IsIdentStartChar[AtomStart^] then begin
// identifier => return current value
i:=IndexOfIdentifier(AtomStart,false);
if i>=0 then begin
if Operand.Free then FreeEvalOperand(Operand);
Operand.Value:=PChar(FValues[i]);
Operand.Len:=length(FValues[i]);
end;
exit(true);
end;
// invalid operand
IdentifierMissing(AtomStart);
end;
function ExecuteStack(LowerOrEqualOperatorLvl: integer): boolean;
var
Op: PChar;
Number1: Int64;
Number2: Int64;
NumberResult: Int64;
StackOperand: PEvalOperand;
begin
Result:=true;
while (StackPtr>=0)
and (ExprStack[StackPtr].OperatorLvl<=LowerOrEqualOperatorLvl) do begin
try
// compute stack item
Op:=ExprStack[StackPtr].theOperator;
StackOperand:=@ExprStack[StackPtr].Operand;
{$IFDEF VerboseExprEval}
DebugLn(['ExecuteStack Operator^=',ExprStack[StackPtr].theOperator^]);
{$ENDIF}
case UpChars[Op^] of
'*': // multiply
begin
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
NumberResult:=Number1*Number2;
SetOperandValueInt64(Operand,NumberResult);
end;
'+': // Add
begin
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
NumberResult:=Number1+Number2;
SetOperandValueInt64(Operand,NumberResult);
end;
'-': // subtract
begin
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
NumberResult:=Number1-Number2;
SetOperandValueInt64(Operand,NumberResult);
end;
'=':
if OperandsAreEqual(StackOperand^,Operand) then begin
SetOperandValueChar(Operand,'1');
end else begin
SetOperandValueChar(Operand,'0');
end;
'<':
case Op[1] of
'>': // <>
if OperandsAreEqual(StackOperand^,Operand) then begin
SetOperandValueChar(Operand,'0');
end else begin
SetOperandValueChar(Operand,'1');
end;
'=':
begin
// <=
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
if Number1<=Number2 then
SetOperandValueChar(Operand,'1')
else
SetOperandValueChar(Operand,'0');
end;
'<':
begin
// <<
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
NumberResult:=Number1 shl Number2;
SetOperandValueInt64(Operand,NumberResult);
end;
else
// <
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
if Number1<Number2 then
SetOperandValueChar(Operand,'1')
else
SetOperandValueChar(Operand,'0');
end;
'>':
case Op[1] of
'=':
begin
// >=
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
if Number1>=Number2 then
SetOperandValueChar(Operand,'1')
else
SetOperandValueChar(Operand,'0');
end;
'>':
begin
// >>
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
NumberResult:=Number1 shr Number2;
SetOperandValueInt64(Operand,NumberResult);
end;
else
// >
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
if Number1>Number2 then
SetOperandValueChar(Operand,'1')
else
SetOperandValueChar(Operand,'0');
end;
'A': // AND
begin
if EvalOperandIsTrue(StackOperand^) and EvalOperandIsTrue(Operand) then
SetOperandValueChar(Operand,'1')
else
SetOperandValueChar(Operand,'0');
end;
'I': // IN
begin
SetOperandValueChar(Operand,'1'); // todo: evaluate in
end;
'D': // DIV
begin
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
NumberResult:=Number1 div Number2;
SetOperandValueInt64(Operand,NumberResult);
end;
'M': // MOD
begin
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
NumberResult:=Number1 mod Number2;
SetOperandValueInt64(Operand,NumberResult);
end;
'S':
case UpChars[Op[1]] of
'H': // SH
case UpChars[Op[2]] of
'L': // SHL
begin
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
NumberResult:=Number1 shl Number2;
SetOperandValueInt64(Operand,NumberResult);
end;
'R': // SHR
begin
Number1:=EvalOperandToInt64(StackOperand^);
Number2:=EvalOperandToInt64(Operand);
NumberResult:=Number1 shr Number2;
SetOperandValueInt64(Operand,NumberResult);
end;
end;
end;
'O': // OR
begin
if EvalOperandIsTrue(StackOperand^) or EvalOperandIsTrue(Operand) then
SetOperandValueChar(Operand,'1')
else
SetOperandValueChar(Operand,'0');
end;
'X': // XOR
begin
if EvalOperandIsTrue(StackOperand^) xor EvalOperandIsTrue(Operand) then
SetOperandValueChar(Operand,'1')
else
SetOperandValueChar(Operand,'0');
end;
end;
except
on E: Exception do begin
Result:=false;
Error(AtomStart,E);
end;
end;
if not Result then exit;
FreeEvalOperand(ExprStack[StackPtr].Operand);
dec(StackPtr);
end;
end;
var
OperatorLvl: Integer;
begin
p:=Expression;
Result:=false;
ClearEvalOperand(Operand);
if p=nil then begin
ExpressionMissing(p);
exit;
end;
ExprEnd:=p+ExprLen;
ReadNextAtom;
if AtomStart>=ExprEnd then begin
ExpressionMissing(AtomStart);
exit;
end;
StackPtr:=-1;
FErrorPos:=-1;
fErrorMsg:='';
try
while AtomStart<ExprEnd do begin
// read operand
if not ReadOperand then
break;
// read operator
ReadNextAtom;
if AtomStart>=ExprEnd then break;
// level 0: NOT () DEFINED UNDEFINED DECLARED: handled by ReadOperand
// level 1: * / DIV MOD AND SHL SHR << >>
// level 2: + - OR XOR
// level 3: = < > <> >= <=
OperatorLvl:=0;
case UpChars[AtomStart^] of
')': break;
'*','/': if p-AtomStart=1 then OperatorLvl:=1;
'+','-': if p-AtomStart=1 then OperatorLvl:=2;
'=': if p-AtomStart=1 then OperatorLvl:=3;
'<': if (p-AtomStart=1)
or (AtomStart[1] in ['=','>']) then
OperatorLvl:=3
else if AtomStart[1]='<' then
OperatorLvl:=1;
'>': if (p-AtomStart=1)
or (AtomStart[1]='=') then
OperatorLvl:=3
else if AtomStart[1]='>' then
OperatorLvl:=1;
'A':
if CompareIdentifiers(AtomStart,'AND')=0 then begin
OperatorLvl:=1;
if not EvalOperandIsTrue(Operand) then begin
SetOperandValueChar(Operand,'0');
break;
end;
end;
'I': if CompareIdentifiers(AtomStart,'IN')=0 then OperatorLvl:=1;
'D': if CompareIdentifiers(AtomStart,'DIV')=0 then OperatorLvl:=1;
'M': if CompareIdentifiers(AtomStart,'MOD')=0 then OperatorLvl:=1;
'S':
case UpChars[AtomStart[1]] of
'H': // SH
case UpChars[AtomStart[2]] of
'L': if p-AtomStart=3 then OperatorLvl:=1; // SHL
'R': if p-AtomStart=3 then OperatorLvl:=1; // SHR
end;
end;
'O':
case UpChars[AtomStart[1]] of
'R':
if p-AtomStart=2 then begin
OperatorLvl:=2;
if EvalOperandIsTrue(Operand) then begin
SetOperandValueChar(Operand,'1');
break;
end;
end;
end;
'X': if CompareIdentifiers(AtomStart,'XOR')=0 then OperatorLvl:=2;
end;
if OperatorLvl=0 then begin
OperatorMissing(AtomStart);
break;
end;
if not ExecuteStack(OperatorLvl) then break;
// push onto stack
inc(StackPtr);
ExprStack[StackPtr].Operand:=Operand;
ExprStack[StackPtr].OperatorLvl:=OperatorLvl;
ExprStack[StackPtr].theOperator:=AtomStart;
ClearEvalOperand(Operand);
ReadNextAtom;
end;
if FErrorPos<0 then begin
Result:=ExecuteStack(4);
end;
finally
// clean up
FreeStack;
end;
end;
function TExpressionEvaluator.EvalBoolean(Expression: PChar; ExprLen: PtrInt;
AllowExternalMacro: boolean): boolean;
var
Operand: TEvalOperand;
begin
Result:=EvalPChar(Expression,ExprLen,Operand,AllowExternalMacro)
and EvalOperandIsTrue(Operand);
FreeEvalOperand(Operand);
end;
function TExpressionEvaluator.AsString: string;
var TxtLen, i, p: integer;
s: String;
begin
TxtLen:=0;
for i:=0 to FCount-1 do begin
inc(TxtLen,length(FNames[i])+2);
s:=FValues[i];
if s<>'' then
inc(TxtLen,length(s)+1);
end;
Setlength(Result{%H-},TxtLen);
p:=1;
for i:=0 to FCount-1 do begin
Move(FNames[i][1],Result[p],length(FNames[i]));
inc(p,length(FNames[i]));
s:=FValues[i];
if length(s)>0 then begin
Result[p]:='=';
inc(p);
Move(FValues[i][1],Result[p],length(FValues[i]));
inc(p,length(FValues[i]));
end;
Result[p]:=#13;
inc(p);
Result[p]:=#10;
inc(p);
end;
end;
procedure TExpressionEvaluator.ConsistencyCheck;
// 0 = ok
var i: integer;
begin
if FCapacity<0 then
RaiseCatchableException('');
if FCapacity<FCount then
RaiseCatchableException('');
if FCount<0 then
RaiseCatchableException('');
if (FCapacity=0) and (FNames<>nil) then
RaiseCatchableException('');
if (FNames=nil) xor (FValues=nil) then
RaiseCatchableException('');
for i:=0 to FCount-1 do begin
if not IsUpperCaseStr(FNames[i]) then
RaiseCatchableException('');
if (i>0) and (FNames[i-1]=FNames[i]) then
RaiseCatchableException('');
if (i>0) and (CompareNames(FNames[i-1],FNames[i])>0) then
RaiseCatchableException('');
end;
end;
procedure TExpressionEvaluator.WriteDebugReport;
var
i: Integer;
begin
DebugLn('[TExpressionEvaluator.WriteDebugReport] ');
ConsistencyCheck;
for i:=0 to Count-1 do
debugln(' ',Items(i));
end;
function TExpressionEvaluator.CalcMemSize(WithNamesAndValues: boolean;
Original: TExpressionEvaluator): PtrUInt;
var
i: Integer;
j: LongInt;
begin
Result:=PtrUInt(InstanceSize)
+MemSizeString(OldExpr)
+SizeOf(Pointer)*PtrUInt(FCount)*2;
if WithNamesAndValues then begin
for i:=0 to FCount-1 do begin
if Original<>nil then begin
j:=Original.IndexOfName(PChar(FNames[i]),length(FNames[i]),false);
if j>=0 then begin
if Pointer(FNames[i])=Pointer(Original.FNames[j]) then continue;
end;
end;
inc(Result,MemSizeString(FNames[i]));
inc(Result,MemSizeString(FValues[i]));
end;
end;
end;
procedure TExpressionEvaluator.IncreaseChangeStamp;
begin
if FChangeStamp<High(Integer) then
inc(FChangeStamp)
else
FChangeStamp:=Low(Integer);
end;
initialization
InternalInit;
end.