mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-24 06:00:15 +02:00
2156 lines
56 KiB
ObjectPascal
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.
|
|
|