mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 15:49:32 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			866 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			866 lines
		
	
	
		
			22 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()
 | 
						|
}
 | 
						|
unit ExprEval;
 | 
						|
 | 
						|
{$ifdef FPC}{$mode objfpc}{$endif}{$H+}
 | 
						|
 | 
						|
{$I codetools.inc}
 | 
						|
 | 
						|
interface
 | 
						|
 | 
						|
uses
 | 
						|
  {$IFDEF MEM_CHECK}
 | 
						|
  MemCheck,
 | 
						|
  {$ENDIF}
 | 
						|
  Classes, SysUtils, KeyWordFuncLists, FileProcs;
 | 
						|
 | 
						|
const
 | 
						|
  ExternalMacroStart = '#';
 | 
						|
 | 
						|
type
 | 
						|
  TOnValuesChanged = procedure of object;
 | 
						|
  TOnGetSameString = procedure(var s: string) of object;
 | 
						|
  ArrayOfAnsiString = ^AnsiString;
 | 
						|
  
 | 
						|
 | 
						|
  { TExpressionEvaluator }
 | 
						|
 | 
						|
  TExpressionEvaluator = class
 | 
						|
  private
 | 
						|
    FChangeStamp: integer;
 | 
						|
    FNames, FValues: ArrayOfAnsiString; // always sorted in FNames and FNames uppercase
 | 
						|
    FCount: integer;
 | 
						|
    FCapacity: integer;
 | 
						|
    Expr: string;
 | 
						|
    CurPos, Max, AtomStart, AtomEnd, PriorAtomStart, ErrorPos: integer;
 | 
						|
    FOnChange: TOnValuesChanged;
 | 
						|
    function ReadTilEndBracket:boolean;
 | 
						|
    function CompAtom(const UpperCaseTag:string): boolean;
 | 
						|
    function ReadNextAtom: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(const VarName: string; 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;
 | 
						|
    function Equals(AnExpressionEvaluator: TExpressionEvaluator): boolean;
 | 
						|
    procedure Assign(SourceExpressionEvaluator: TExpressionEvaluator);
 | 
						|
    procedure AssignTo(SL: TStringList);
 | 
						|
    function Eval(const Expression: string):string;
 | 
						|
    property ErrorPosition:integer read ErrorPos;
 | 
						|
    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;
 | 
						|
 | 
						|
  { TExpressionSolver
 | 
						|
    Checks if expression is always true 1, always false 0, or something }
 | 
						|
 | 
						|
  TExpressionSolver = class
 | 
						|
  public
 | 
						|
    //Defines: TStringToStringTree;
 | 
						|
    //Undefines: TStringToStringTree;
 | 
						|
    ErrorMsg: string; // last error message
 | 
						|
    ErrorPos: integer;// last error position
 | 
						|
    constructor Create;
 | 
						|
    destructor Destroy; override;
 | 
						|
    function Solve(const Expr: string; out ExprResult: string): boolean;
 | 
						|
    function Solve(const Src: string; StartPos, EndPos: integer;
 | 
						|
                  out ExprResult: string): boolean;
 | 
						|
  end;
 | 
						|
 | 
						|
 | 
						|
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;
 | 
						|
 | 
						|
{ 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 (AtomEnd>Max+1) then begin
 | 
						|
    Result:=false;  exit;
 | 
						|
  end;
 | 
						|
  len:=AtomEnd-AtomStart;
 | 
						|
  if length(UpperCaseTag)<>len then begin
 | 
						|
    Result:=false;  exit;
 | 
						|
  end;
 | 
						|
  for a:=1 to len do begin
 | 
						|
    if (UpChars[Expr[AtomStart+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.Eval(const Expression: string): string;
 | 
						|
//  1 = true
 | 
						|
//  0 = syntax error
 | 
						|
// -1 = false
 | 
						|
var s:string;
 | 
						|
begin
 | 
						|
  Expr:=Expression;
 | 
						|
  Max:=length(expr);
 | 
						|
  CurPos:=1;
 | 
						|
  AtomStart:=-1;  AtomEnd:=-1;  PriorAtomStart:=-1;
 | 
						|
  ErrorPos:=-1;
 | 
						|
  s:=EvalAtPos;
 | 
						|
  if ErrorPos>=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 ReadNextAtom) then exit;
 | 
						|
    inc(AtomCount);
 | 
						|
    c:=Expr[AtomStart];
 | 
						|
    if IsWordChar[c] then begin
 | 
						|
      // identifier or keyword
 | 
						|
      if (CompAtom('AND')) then begin
 | 
						|
        if (Result='') then ErrorPos:=CurPos
 | 
						|
        else if (Result<>'0') then begin
 | 
						|
          // true AND ...
 | 
						|
          Result:=EvalAtPos();
 | 
						|
          if ErrorPos>=0 then exit;
 | 
						|
          if (Result='') then ErrorPos:=CurPos;
 | 
						|
        end;
 | 
						|
        exit;
 | 
						|
      end else if (CompAtom('OR')) then begin
 | 
						|
        if (Result='0') then begin
 | 
						|
          // false OR ...
 | 
						|
          Result:=EvalAtPos();
 | 
						|
          if ErrorPos>=0 then exit;
 | 
						|
          if (Result='') then ErrorPos:=CurPos;
 | 
						|
        end else if (AtomCount<=1) then ErrorPos:=CurPos;
 | 
						|
        exit;
 | 
						|
      end else if (CompAtom('XOR')) then begin
 | 
						|
        if (Result='') then begin
 | 
						|
          ErrorPos:=CurPos;  exit;
 | 
						|
        end;
 | 
						|
        r:=Result;
 | 
						|
        // true/false XOR ...
 | 
						|
        Result:=EvalAtPos();
 | 
						|
        if ErrorPos>=0 then exit;
 | 
						|
        if (Result='') then begin
 | 
						|
          ErrorPos:=CurPos;  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 ErrorPos>=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 ReadNextAtom) then begin
 | 
						|
          ErrorPos:=CurPos;
 | 
						|
          exit;
 | 
						|
        end;
 | 
						|
        HasBracket:=CompAtom('(');
 | 
						|
        if HasBracket and (not ReadNextAtom) then begin
 | 
						|
          ErrorPos:=CurPos;
 | 
						|
          exit;
 | 
						|
        end;
 | 
						|
        if IsDefined(copy(Expr,AtomStart,AtomEnd-AtomStart)) then
 | 
						|
          Result:='1'
 | 
						|
        else
 | 
						|
          Result:='0';
 | 
						|
        if HasBracket then begin
 | 
						|
          if (not ReadNextAtom) or (not CompAtom(')')) then begin
 | 
						|
            ErrorPos:=CurPos;
 | 
						|
            exit;
 | 
						|
          end;
 | 
						|
        end;
 | 
						|
      end else if (CompAtom('DECLARED')) then begin
 | 
						|
        // read DECLARED(identifier)
 | 
						|
        if (Result<>'') or (not ReadNextAtom) or (CompAtom('(')=false)
 | 
						|
        or (not ReadNextAtom) then begin
 | 
						|
          ErrorPos:=CurPos;
 | 
						|
          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 ReadNextAtom) or (not CompAtom(')')) then begin
 | 
						|
          ErrorPos:=CurPos;
 | 
						|
          exit;
 | 
						|
        end;
 | 
						|
      end else if (CompAtom('UNDEFINED')) then begin
 | 
						|
        // read UNDEFINED(identifier) or undefined identifier
 | 
						|
        if (Result<>'') or (not ReadNextAtom) then begin
 | 
						|
          ErrorPos:=CurPos;
 | 
						|
          exit;
 | 
						|
        end;
 | 
						|
        HasBracket:=CompAtom('(');
 | 
						|
        if HasBracket and (not ReadNextAtom) then begin
 | 
						|
          ErrorPos:=CurPos;
 | 
						|
          exit;
 | 
						|
        end;
 | 
						|
        Result:=Variables[copy(Expr,AtomStart,AtomEnd-AtomStart)];
 | 
						|
        if Result<>'' then
 | 
						|
          Result:='0'
 | 
						|
        else
 | 
						|
          Result:='1';
 | 
						|
        if HasBracket then begin
 | 
						|
          if (not ReadNextAtom) or (not CompAtom(')')) then begin
 | 
						|
            ErrorPos:=CurPos;
 | 
						|
            exit;
 | 
						|
          end;
 | 
						|
        end;
 | 
						|
      end else begin
 | 
						|
        // Identifier
 | 
						|
        if (Result<>'') then begin
 | 
						|
          ErrorPos:=CurPos;
 | 
						|
          exit;
 | 
						|
        end else
 | 
						|
          Result:=Variables[copy(Expr,AtomStart,AtomEnd-AtomStart)];
 | 
						|
      end;
 | 
						|
    end else if IsNumberBeginChar[c] then begin
 | 
						|
      // number
 | 
						|
      if (Result<>'') then begin
 | 
						|
        ErrorPos:=CurPos;  exit;
 | 
						|
      end else Result:=copy(Expr,AtomStart,AtomEnd-AtomStart);
 | 
						|
    end else if c='''' then begin
 | 
						|
      Result:=copy(Expr,AtomStart+1,AtomEnd-AtomStart-2);
 | 
						|
    end else begin
 | 
						|
      // operator
 | 
						|
      case c of
 | 
						|
      ')':exit;
 | 
						|
      '(':begin
 | 
						|
          OldPos:=AtomStart;
 | 
						|
          // eval in brackets
 | 
						|
          Result:=EvalAtPos();
 | 
						|
          if ErrorPos>=0 then exit;
 | 
						|
          // go behind brackets
 | 
						|
          CurPos:=OldPos;
 | 
						|
          if (not ReadTilEndBracket) then exit;
 | 
						|
          inc(CurPos);
 | 
						|
        end;
 | 
						|
      '=','>','<':begin
 | 
						|
          o1:=c;
 | 
						|
          if AtomEnd=AtomStart+1 then begin
 | 
						|
            r:=EvalAtPos();
 | 
						|
            if ErrorPos>=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:=Expr[AtomStart+1];
 | 
						|
            r:=EvalAtPos();
 | 
						|
            if ErrorPos>=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 ErrorPos:=AtomStart;
 | 
						|
            end else if o1='>' then begin
 | 
						|
              if o2='=' then begin
 | 
						|
                if CompareValues(Result,r)>=0 then Result:='1' else Result:='0';
 | 
						|
              end else ErrorPos:=AtomStart;
 | 
						|
            end else ErrorPos:=AtomStart;
 | 
						|
          end;
 | 
						|
          exit;
 | 
						|
        end;
 | 
						|
      '!':
 | 
						|
        begin
 | 
						|
          Result:=EvalAtPos();
 | 
						|
          if ErrorPos>=0 then exit;
 | 
						|
          if (Result='0') then Result:='1'
 | 
						|
          else if (Result='') then ErrorPos:=CurPos
 | 
						|
          else Result:='0';
 | 
						|
          exit;
 | 
						|
        end;
 | 
						|
      else
 | 
						|
        begin
 | 
						|
          ErrorPos:=CurPos;
 | 
						|
        end;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
  until (ErrorPos>=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(
 | 
						|
  const VarName: string; 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;
 | 
						|
  while l<=r do begin
 | 
						|
    m:=(l+r) shr 1;
 | 
						|
    cmp:=CompareText(VarName,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 CompareText(VarName,FNames[m])>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(Name,false);
 | 
						|
  if (i>=0) then
 | 
						|
    Result:=FValues[i]
 | 
						|
  else 
 | 
						|
    Result:='';
 | 
						|
end;
 | 
						|
 | 
						|
function TExpressionEvaluator.IsDefined(const Name: string): boolean;
 | 
						|
var i: integer;
 | 
						|
begin
 | 
						|
  i:=IndexOfName(Name,false);
 | 
						|
  Result:=(i>=0);
 | 
						|
end;
 | 
						|
 | 
						|
function TExpressionEvaluator.ReadNextAtom: boolean;
 | 
						|
var c,o1,o2:char;
 | 
						|
begin
 | 
						|
  PriorAtomStart:=AtomStart;
 | 
						|
  while (CurPos<=Max) do begin
 | 
						|
    c:=Expr[CurPos];
 | 
						|
    if (c<=' ') then inc(CurPos)
 | 
						|
    else if IsWordChar[c] then begin
 | 
						|
      // Identifier
 | 
						|
      AtomStart:=CurPos;
 | 
						|
      repeat
 | 
						|
        inc(CurPos);
 | 
						|
      until (CurPos>Max) or (not IsIdentifierChar[Expr[CurPos]]);
 | 
						|
      AtomEnd:=CurPos;
 | 
						|
      Result:=true;
 | 
						|
      exit;
 | 
						|
    end else if IsNumberBeginChar[c] then begin
 | 
						|
      // Number
 | 
						|
      AtomStart:=CurPos;
 | 
						|
      repeat
 | 
						|
        inc(CurPos);
 | 
						|
      until (CurPos>Max) or (IsNumberChar[Expr[CurPos]]=false);
 | 
						|
      AtomEnd:=CurPos;
 | 
						|
      Result:=true;
 | 
						|
      exit;
 | 
						|
    end else if c='''' then begin
 | 
						|
      // string
 | 
						|
      AtomStart:=CurPos;
 | 
						|
      repeat
 | 
						|
        inc(CurPos);
 | 
						|
        if Expr[CurPos]='''' then begin
 | 
						|
          inc(CurPos);
 | 
						|
          AtomEnd:=CurPos;
 | 
						|
          Result:=true;
 | 
						|
          exit;
 | 
						|
        end;
 | 
						|
        if CurPos>Max then begin
 | 
						|
          AtomEnd:=CurPos;
 | 
						|
          Result:=false;
 | 
						|
          exit;
 | 
						|
        end;
 | 
						|
      until (CurPos>Max);
 | 
						|
    end else begin
 | 
						|
      // Symbol
 | 
						|
      AtomStart:=CurPos;
 | 
						|
      inc(CurPos);
 | 
						|
      if (CurPos<=Max) then begin
 | 
						|
        o1:=c;
 | 
						|
        o2:=Expr[CurPos];
 | 
						|
        if ((o2='=') and ((o1='<') or (o1='>')))
 | 
						|
        or ((o1='<') and (o2='>'))
 | 
						|
        then inc(CurPos);
 | 
						|
      end;
 | 
						|
      AtomEnd:=CurPos;
 | 
						|
      Result:=true;
 | 
						|
      exit;
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
  Result:=false;
 | 
						|
end;
 | 
						|
 | 
						|
function TExpressionEvaluator.ReadTilEndBracket: boolean;
 | 
						|
// true = end bracket found
 | 
						|
// false = not found
 | 
						|
var lvl:integer;
 | 
						|
begin
 | 
						|
  lvl:=0;
 | 
						|
  while (CurPos<=Max) do begin
 | 
						|
    if (Expr[CurPos]='(') then
 | 
						|
      inc(lvl)
 | 
						|
    else if (Expr[CurPos]=')') then begin
 | 
						|
      dec(lvl);
 | 
						|
      if (lvl=0) then begin
 | 
						|
        Result:=true;  exit;
 | 
						|
      end else if (lvl<0) then begin
 | 
						|
        ErrorPos:=CurPos;
 | 
						|
        Result:=true;  exit;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
    inc(CurPos);
 | 
						|
  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;
 | 
						|
    GetMem(FNames,Size);
 | 
						|
    FillChar(Pointer(FNames)^,Size,0);
 | 
						|
    GetMem(FValues,Size);
 | 
						|
    FillChar(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;
 | 
						|
    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(Name,true);
 | 
						|
  if (i>=0) and (i<FCount) and (CompareText(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(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.AsString: string;
 | 
						|
var TxtLen, i, p: integer;
 | 
						|
begin
 | 
						|
  TxtLen:=FCount*3;
 | 
						|
  for i:=0 to FCount-1 do
 | 
						|
    inc(TxtLen,length(FNames[i])+length(FValues[i]));
 | 
						|
  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]));
 | 
						|
    Result[p]:=' ';
 | 
						|
    inc(p);
 | 
						|
    if length(FValues[i])>0 then begin
 | 
						|
      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 (CompareText(FNames[i-1],FNames[i])>0) then
 | 
						|
      RaiseCatchableException('');
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TExpressionEvaluator.WriteDebugReport;
 | 
						|
begin
 | 
						|
  DebugLn('[TExpressionEvaluator.WriteDebugReport] ');
 | 
						|
  ConsistencyCheck;
 | 
						|
end;
 | 
						|
 | 
						|
function TExpressionEvaluator.CalcMemSize(WithNamesAndValues: boolean;
 | 
						|
  Original: TExpressionEvaluator): PtrUInt;
 | 
						|
var
 | 
						|
  i: Integer;
 | 
						|
  j: LongInt;
 | 
						|
begin
 | 
						|
  Result:=PtrUInt(InstanceSize)
 | 
						|
    +MemSizeString(Expr)
 | 
						|
    +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(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;
 | 
						|
 | 
						|
 | 
						|
{ TExpressionSolver }
 | 
						|
 | 
						|
constructor TExpressionSolver.Create;
 | 
						|
begin
 | 
						|
 | 
						|
end;
 | 
						|
 | 
						|
destructor TExpressionSolver.Destroy;
 | 
						|
begin
 | 
						|
  inherited Destroy;
 | 
						|
end;
 | 
						|
 | 
						|
function TExpressionSolver.Solve(const Expr: string; out
 | 
						|
  ExprResult: string): boolean;
 | 
						|
begin
 | 
						|
  Result:=Solve(Expr,1,length(Expr),ExprResult);
 | 
						|
end;
 | 
						|
 | 
						|
function TExpressionSolver.Solve(const Src: string;
 | 
						|
  StartPos, EndPos: integer; out ExprResult: string): boolean;
 | 
						|
{ '' -> ''
 | 
						|
  true = nonzero, false = zero
 | 
						|
  defined(name)
 | 
						|
  sizeof(type)
 | 
						|
  unary operators: not, !
 | 
						|
  binary operators: = <> >= <= > < and or xor shl shr
 | 
						|
  round brackets ()
 | 
						|
}
 | 
						|
var
 | 
						|
  AtomStart: LongInt;
 | 
						|
  SrcPos: LongInt;
 | 
						|
 | 
						|
  function AtomIs(const s: shortstring): boolean;
 | 
						|
  var
 | 
						|
    len: Integer;
 | 
						|
    i: Integer;
 | 
						|
  begin
 | 
						|
    len:=length(s);
 | 
						|
    if (len<>SrcPos-AtomStart) then exit(false);
 | 
						|
    if SrcPos>EndPos then exit(false);
 | 
						|
    for i:=1 to len do
 | 
						|
      if Src[AtomStart+i-1]<>s[i] then exit(false);
 | 
						|
    Result:=true;
 | 
						|
  end;
 | 
						|
 | 
						|
begin
 | 
						|
  if StartPos>=EndPos then begin
 | 
						|
    ExprResult:='';
 | 
						|
    exit(true);
 | 
						|
  end;
 | 
						|
  SrcPos:=StartPos;
 | 
						|
  AtomStart:=SrcPos;
 | 
						|
  //ReadNextCAtom(Source,SrcPos,AtomStart);
 | 
						|
  if AtomIs('!') then begin
 | 
						|
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
initialization
 | 
						|
  InternalInit;
 | 
						|
 | 
						|
end.
 | 
						|
 |