mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 15:32:00 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			2041 lines
		
	
	
		
			53 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			2041 lines
		
	
	
		
			53 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
 | 
						|
        ,'BOOLEVAL'       // B
 | 
						|
        ,'ASSERTIONS'     // C
 | 
						|
        ,'DEBUGINFO'      // D
 | 
						|
        ,''               // E
 | 
						|
        ,''               // F
 | 
						|
        ,''               // G
 | 
						|
        ,'LONGSTRINGS'    // H
 | 
						|
        ,'IOCHECKS'       // I
 | 
						|
        ,''               // J
 | 
						|
        ,''               // K
 | 
						|
        ,'LOCALSYMBOLS'   // L
 | 
						|
        ,'TYPEINFO'       // M
 | 
						|
        ,''               // N
 | 
						|
        ,''               // O
 | 
						|
        ,'OPENSTRINGS'    // P
 | 
						|
        ,'OVERFLOWCHECKS' // Q
 | 
						|
        ,'RANGECHECKS'    // R
 | 
						|
        ,''               // S
 | 
						|
        ,'TYPEADDRESS'    // T
 | 
						|
        ,''               // U
 | 
						|
        ,'VARSTRINGCHECKS'// V
 | 
						|
        ,'STACKFRAMES'    // W
 | 
						|
        ,'EXTENDEDSYNTAX' // X
 | 
						|
        ,'REFERENCEINFO'  // Y
 | 
						|
        ,''               // 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;
 | 
						|
begin
 | 
						|
  for i:=0 to FCount-1 do begin
 | 
						|
    OnGetSameString(FNames[i]);
 | 
						|
    OnGetSameString(FValues[i]);
 | 
						|
  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;
 | 
						|
  begin
 | 
						|
    Setlength(Result,p-AtomStart);
 | 
						|
    if Result<>'' then
 | 
						|
      System.Move(AtomStart^,Result[1],length(Result));
 | 
						|
  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,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 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;
 | 
						|
    'S':
 | 
						|
      if CompareIdentifiers(AtomStart,'SIZEOF')=0 then begin
 | 
						|
        ReadNextAtom;
 | 
						|
        if AtomStart^<>'(' then StrExpectedAtPos(AtomStart,'(');
 | 
						|
        if not ReadTilEndBracket then exit;
 | 
						|
        SetOperandValueChar(Operand,'1');
 | 
						|
        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;
 | 
						|
        '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;
 | 
						|
      '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,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.
 | 
						|
 |