{*********************************************************}
{                                                         }
{     TParser 10.1 for Borland Delphi                     }
{                                                         }
{       A component for parsing and evaluating            }
{       mathematical expressions specified at runtime     }
{                                                         }
{         Renate Schaaf (schaaf@math.usu.edu), 1993       }
{         Alin Flaider (aflaidar@datalog.ro), 1996        }
{         Stefan Hoffmeister                              }
{              Stefan.Hoffmeister@Uni-Passau.de, 1997     }
{                                                         }
{                                                         }
{  See  PARSER10.TXT  for documentation                   }
{                                                         }
{*********************************************************}
unit Parser10;

{$IFDEF Win32}
  {$H+,S-} { long strings, no stack-checking}
{$ENDIF}

{.$DEFINE DEBUG} { by default make it lean and efficient }
{$IFNDEF DEBUG}
  {$D-} {$L-} {$Q-} {$R-} {$S-}
{$ENDIF}

{$I+} { I/O checking ON }

interface

uses
  SysUtils,
  Classes;

type
  { a couple of unfortunately necessary global declarations }
  ParserFloat = double;  { please do NOT use "real", only single, double, extended}
  PParserFloat = ^ParserFloat;

  TToken=( variab, constant,
           minus,
           sum, diff, prod, divis, modulo, IntDiv,
           integerpower, realpower,
           square, third, fourth,
           FuncOneVar, FuncTwoVar);

  POperation = ^TOperation;
  { functions that are added to the engine MUST have this declaration }
  { make sure that the procedure is declared far !!! }
  TMathProcedure = procedure(AnOperation: POperation);
  TOperation = record
                 { MUST use pointers (!), because argument and destination are linked... }
                 Arg1, Arg2 : PParserFloat;
                 Dest : PParserFloat;

                 NextOperation : POperation;

                 Operation: TMathProcedure;
                 Token : TToken;
               end;

  EMathParserError = class(Exception); { create a new exception class and... }

  { ... some descendants }
  ESyntaxError = class(EMathParserError);
  EExpressionHasBlanks = class(EMathParserError);
  EExpressionTooComplex = class(EMathParserError);
  ETooManyNestings = class(EMathParserError);
  EMissMatchingBracket = class(EMathParserError);
  EBadName = class(EMathParserError);
  EParserInternalError = class(EMathParserError); { hopefully we will never see this one }


  { we COULD use Forms and the TExceptionEvent therein,
    but that would give us all the VCL overhead.
    Consequentially we just redeclare an appropriate event }
  TParserExceptionEvent = procedure (Sender: TObject; E: Exception) of object;



  TCustomParser = class(TComponent)
  private
    { some pre-allocated space for variables }
    FA,
    FB,
    FC,
    FD,
    FE,
    FX,
    FY,
    FT: ParserFloat;
  private
    FExpression : string;
    FPascalNumberformat: boolean;
    FParserError : boolean;

    FVariables: TStringList;

    FStartOperationList: POperation;

    FOnParserError : TParserExceptionEvent;

    function CalcValue: extended;
    procedure SetExpression(const AnExpression: string);
    procedure SetVar(const VarName: string; const Value: extended);
  protected
    { lists of available functions, see .Create for example use }
    FunctionOne : TStringList;     { functions with ONE argument, e.g. exp() }
    FunctionTwo : TStringList;     { functions with TWO arguments, e.g. max(,) }

    { predefined variables - could be left out }
    property A: ParserFloat read FA write FA;
    property B: ParserFloat read FB write FB;
    property C: ParserFloat read FC write FC;
    property D: ParserFloat read FD write FD;
    property E: ParserFloat read FE write FE;
    property T: ParserFloat read FT write FT;
    property X: ParserFloat read FX write FX;
    property Y: ParserFloat read FY write FY;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    function ParseExpression(const AnExpression: string): boolean;
    procedure FreeExpression;

    { The PParserFloat returned points to the place in memory where the
      variable actually sits; to speed up assignment you can DIRECTLY
      assign data to the memory area. }
    function SetVariable(VarName: string; const Value: extended): PParserFloat;
    function GetVariable(const VarName: string): extended;

    procedure AddFunctionOneParam(const AFunctionName: string; const Func: TMathProcedure);
    procedure AddFunctionTwoParam(const AFunctionName: string; const Func: TMathProcedure);

    procedure ClearVariables;
    procedure ClearVariable(const AVarName: string);
    function  VariableExists(const AVarName: string): boolean;

    procedure ClearFunctions;
    procedure ClearFunction(const AFunctionName: string);

    property ParserError: boolean read FParserError;
    property LinkedOperationList: POperation read FStartOperationList;

    property Variable[const VarName: string]: extended read GetVariable write SetVar;
  published
    property Value: extended read CalcValue stored false;

    { setting Expression automatically parses it
      Warning: exceptions MAY be raised, if OnParserError is NOT assigned,
               otherwise the event will be triggered in case of an error }
    property Expression: string read FExpression write SetExpression;
    property PascalNumberformat: boolean read FPascalNumberformat write FPascalNumberformat default true;
    property OnParserError: TParserExceptionEvent read FOnParserError write FOnParserError;
  end;




  TParser = class(TCustomParser)
  public
    { overrides to add the properties below as variables
      and adds all the functions }
    constructor Create(AOwner: TComponent); override;

    { returns the string with the blanks inside removed }
    class function RemoveBlanks(const s: string): string;
  published
    { predefined variables - could be left out }
    property A;
    property B;
    property C;
    property D;
    property E;
    property T;
    property X;
    property Y;
 end;


implementation

{$DEFINE UseMath}
{ Note: if you do not have the MATH unit simply remove the conditional define
        the component will continue to work, just a bit slower }

uses
{$IFDEF UseMath}
  Math,
{$ENDIF}
  P10Build;

(*
{$IFDEF VER80}
  {$R *.D16}
{$ELSE}
  {$IFDEF VER90}
    {$R *.D32}
  {$ENDIF}
{$ENDIF}
*)

{****************************************************************}
{                                                                }
{   Following are "built-in" calculation procedures              }
{                                                                }
{****************************************************************}
{
Naming convention for functions:

  Name of built-in function, prepended with an underscore.
  Example:

    ln --> _ln

Passed arguments / results:

  If the function takes any arguments - i.e. if it has been added to
  either the FunctionOne or the FunctionTwo list:

  - First  argument --> arg1^
  - Second argument --> arg2^

  The result of the operation must ALWAYS be put into

     dest^


 Note: These are POINTERS to floats.
}



{****************************************************************}
{                                                                }
{   These are mandatory procedures - never remove them           }
{                                                                }
{****************************************************************}

{ do nothing - this only happens if the "term" is just a number
  or a variable; otherwise this procedure will never be called }
procedure _nothing(AnOp: POperation); far;
begin
end;

procedure _Add(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := arg1^ + arg2^;
end;

procedure _Subtract(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := arg1^ - arg2^;
end;

procedure _Multiply(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := arg1^ * arg2^;
end;

procedure _RealDivide(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := arg1^ / arg2^;
end;

procedure _Modulo(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := trunc(arg1^) mod trunc(arg2^);
end;

procedure _IntDiv(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := trunc(arg1^) div trunc(arg2^);
end;

procedure _Negate(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := -arg1^;
end;

procedure _IntPower(AnOp: POperation); far;
{$IFNDEF UseMath}
var
  n, i: longint;
{$ENDIF}
begin

{$IFNDEF UseMath}
  with AnOp^ do
  begin
    n := trunc(abs(arg2^))-1;

    case n of
      -1: dest^ := 1;
       0: dest^ := arg1^;
    else
      dest^ := arg1^;
      for i := 1 to n do
        dest^ := dest^ * arg1^;
    end;

    if arg2^ < 0 then
      dest^ := 1 / dest^;

  end;
{$ELSE}
  with AnOp^ do
    dest^ := IntPower(arg1^, trunc(arg2^));
{$ENDIF}
end;

procedure _square(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := sqr(arg1^);
end;

procedure _third(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := arg1^ * arg1^ * arg1^;
end;

procedure _forth(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := sqr(sqr(arg1^));
end;

procedure _power(AnOp: POperation); far;
begin
  with AnOp^ do
  begin
{$IFNDEF UseMath}
    if arg1^ = 0 then
      dest^ := 0
    else
      dest^ := exp(arg2^*ln(arg1^));
{$ELSE}
    dest^ := Power(arg1^, arg2^);
{$ENDIF}
  end;
end;


{****************************************************************}
{                                                                }
{   These are OPTIONAL procedures - you may remove them, though  }
{   it is preferable to not register them for use                }
{                                                                }
{****************************************************************}
procedure _sin(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := sin(arg1^);
end;

procedure _cos(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := cos(arg1^);
end;

procedure _arctan(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := arctan(arg1^);
end;

procedure _arg(AnOp: POperation); far;
begin
  with AnOp^ do
    if arg1^ < 0 then
      dest^ := arctan(arg2^/arg1^)+Pi
    else
      if arg1^>0 then
        dest^ := arctan(arg2^/arg1^)
      else
        if arg2^ > 0 then
          dest^ := 0.5 * Pi
        else
          dest^ := -0.5 * Pi;
end;

procedure _sinh(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := (exp(arg1^)-exp(-arg1^))*0.5;
end;

procedure _cosh(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := (exp(arg1^)+exp(-arg1^))*0.5;
end;

procedure _cotan(AnOp: POperation); far;
begin
  with AnOp^ do
  {$IFNDEF UseMath}
    dest^ := cos(arg1^) / sin(arg1^);
  {$ELSE}
    dest^ := cotan(arg1^);
  {$ENDIF}
end;

procedure _tan(AnOp: POperation); far;
begin
  with AnOp^ do
  {$IFNDEF UseMath}
    dest^ := sin(arg1^) / cos(arg1^);
  {$ELSE}
    dest^ := tan(arg1^);
  {$ENDIF}
end;

procedure _exp(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := exp(arg1^);
end;

procedure _ln(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := ln(arg1^);
end;

procedure _log10(AnOp: POperation); far;
const
  _1_ln10 =  0.4342944819033;
begin
  with AnOp^ do
{$IFDEF UseMath}
    dest^ := log10(arg1^);
{$ELSE}
    dest^ := ln(arg1^) * _1_ln10;
{$ENDIF}
end;

procedure _log2(AnOp: POperation); far;
const
  _1_ln2 = 1.4426950409;
begin
  with AnOp^ do
{$IFDEF UseMath}
    dest^ := log2(arg1^);
{$ELSE}
    dest^ := ln(arg1^) * _1_ln2;
{$ENDIF}
end;

procedure _logN(AnOp: POperation); far;
begin
  with AnOp^ do
{$IFDEF UseMath}
    dest^ := logN(arg1^, arg2^);
{$ELSE}
    dest^ := ln(arg1^) / ln(arg2^);
{$ENDIF}
end;

procedure _sqrt(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := sqrt(arg1^);
end;


procedure _abs(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := abs(arg1^);
end;

procedure _min(AnOp: POperation); far;
begin
  with AnOp^ do
    if arg1^ < arg2^ then
      dest^ := arg1^
    else
      dest^ := arg2^;
end;

procedure _max(AnOp: POperation); far;
begin
  with AnOp^ do
    if arg1^ < arg2^ then
      dest^ := arg2^
    else
      dest^ := arg1^;
end;

procedure _heaviside(AnOp: POperation); far;
begin
  with AnOp^ do
    if arg1^ < 0 then
      dest^ := 0
    else
      dest^ := 1;
end;

procedure _sign(AnOp: POperation); far;
begin
  with AnOp^ do
    if arg1^ < 0 then
      dest^ := -1
    else
      if arg1^ > 0 then
        dest^ := 1.0
      else
        dest^ := 0.0;
end;

procedure _zero(AnOp: POperation); far;
begin
  with AnOp^ do
    if arg1^ = 0.0 then
      dest^ := 0.0
    else
      dest^ := 1.0;
end;

procedure _trunc(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := int(arg1^)
end;

procedure _ceil(AnOp: POperation); far;
begin
  with AnOp^ do
    if frac(arg1^) > 0 then
      dest^ := int(arg1^ + 1)
    else
      dest^ := int(arg1^);
end;

procedure _floor(AnOp: POperation); far;
begin
  with AnOp^ do
    if frac(arg1^) < 0 then
      dest^ := int(arg1^ - 1)
    else
      dest^ := int(arg1^);
end;

procedure _rnd(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := Random * int(arg1^);
end;

procedure _random(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := Random;
end;

procedure _radius(AnOp: POperation); far;
begin
  with AnOp^ do
    dest^ := sqrt(sqr(arg1^)+sqr(arg2^));
end;

procedure _phase(AnOp: POperation); far;
var
  a: ParserFloat;
begin
  with AnOp^ do
  begin
    a := arg1^ / (2/pi);
    dest^ := (2*pi) * (a-round(a));
  end;
end;

{****************************************************************}
{                                                                }
{   TCustomParser                                                }
{                                                                }
{    A base class which does not publish the variable properties }
{    and adds no functions by default                            }
{                                                                }
{****************************************************************}
function TCustomParser.ParseExpression(const AnExpression: string):boolean;
var
  OperationLoop: POperation;
begin
  FreeExpression;
  FExpression := AnExpression;

  if AnExpression <> '' then
  begin
    Result := false;

    try
      ParseFunction( AnExpression,

                     FVariables,

                     FunctionOne,
                     FunctionTwo,

                     FPascalNumberformat,

                     FStartOperationList,
                     Result);

      FParserError := Result;

    except
      on E: Exception do
      begin
        FParserError := true;

        if Assigned(FOnParserError) then
        begin
          FOnParserError(Self, E);
          exit;
        end
        else
          raise;
      end;
    end;

    Result := not Result;

    OperationLoop := FStartOperationList;
    while OperationLoop <> nil do
    begin
      with OperationLoop^ do
      begin
        case Token of

          variab,
          constant:      Operation := _nothing;

          minus:         Operation := _negate;

          sum:           Operation := _add;
          diff:          Operation := _subtract;
          prod:          Operation := _multiply;
          divis:         Operation := _RealDivide;
          
          modulo:        Operation := _Modulo;
          intdiv:        Operation := _IntDiv;

          integerpower:  Operation := _IntPower;
          realpower:     Operation := _Power;

          square:        Operation := _square;
          third:         Operation := _third;
          fourth:        Operation := _forth;

          FuncOneVar, FuncTwoVar:    { job has been done in build already !};
        end; {case}

        OperationLoop := NextOperation;
      end; {with OperationLoop^}

    end; {while OperationLoop<>nil}
  end;
end;

constructor TCustomParser.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FPascalNumberformat := true;

  FVariables := TStringList.Create;
  with FVariables do
  begin
    Sorted := true;
    Duplicates := dupIgnore;
  end;

  FunctionOne := TStringList.Create;
  with FunctionOne do
  begin
    Sorted := true;
    Duplicates := dupError;
  end;

  FunctionTwo := TStringList.Create;
  with FunctionTwo do
  begin
    Sorted := true;
    Duplicates := dupError;
  end;

end;

destructor TCustomParser.Destroy;
begin
  FreeExpression;

  ClearVariables;
  FVariables.Free;

  FunctionOne.Free;
  FunctionTwo.Free;

  inherited Destroy;
end;




procedure TCustomParser.SetVar(const VarName: string; const Value: extended);
begin
  SetVariable(VarName, Value);
end;

function TCustomParser.SetVariable(VarName: string; const Value: extended): PParserFloat;
var
  i: integer;
begin
  { always convert to uppercase }
  VarName := UpperCase(VarName);

  with FVariables do
    if Find(VarName, i) then
    begin
      Result := PParserFloat(Objects[i]);
      Result^ := Value;
    end
    else
    begin
      if Length(Varname) = 1 then
        case VarName[1] of
          'A': Result := @FA;
          'B': Result := @FB;
          'C': Result := @FC;
          'D': Result := @FD;
          'E': Result := @FE;
          'T': Result := @FT;
          'X': Result := @FX;
          'Y': Result := @FY;
        else { case }
          { is the variable name a valid identifier? }
          if not IsValidIdent(VarName) then
            raise EBadName.Create(VarName);

          { unravelled loop for improved (string!) performance! }

          { check whether the variable contains any of the operators (DIV and MOD)
            this would confuse the parser... }
          if pos('+', VarName) <> 0 then
              raise EBadName.Create(VarName);

          if pos('-', VarName) <> 0 then
              raise EBadName.Create(VarName);

          if pos('*', VarName) <> 0 then
              raise EBadName.Create(VarName);

          if pos('/', VarName) <> 0 then
              raise EBadName.Create(VarName);

          if pos('^', VarName) <> 0 then
              raise EBadName.Create(VarName);

          if pos('DIV', VarName) <> 0 then
              raise EBadName.Create(VarName);

          if pos('MOD', VarName) <> 0 then
              raise EBadName.Create(VarName);

          new(Result);
        end { case }
      else
      begin
        { is the variable name a valid identifier? }
        if not IsValidIdent(VarName) then
          raise EBadName.Create(VarName);

        new(Result);
      end;

      Result^ := Value;

      AddObject(VarName, TObject(Result));
    end
end;

function TCustomParser.GetVariable(const VarName: string): extended;
var
  i: integer;
begin
  with FVariables do
    if Find(UpperCase(VarName), i) then
      Result := PParserFloat(Objects[i])^
    else
      Result := 0.0;
end;

procedure TCustomParser.AddFunctionOneParam(const AFunctionName: string; const Func: TMathProcedure);
begin
  if IsValidIdent(AFunctionName) then
    FunctionOne.AddObject(UpperCase(AFunctionName), TObject(@Func))
  else
    raise EBadName.Create(AFunctionName);
end;

procedure TCustomParser.AddFunctionTwoParam(const AFunctionName: string; const Func: TMathProcedure);
begin
  if IsValidIdent(AFunctionName) then
    FunctionTwo.AddObject(UpperCase(AFunctionName), TObject(@Func))
  else
    raise EBadName.Create(AFunctionName);
end;

procedure TCustomParser.ClearVariables;
var
  i: integer;
  APPFloat: PParserFloat;
  AString: string; { disregard stack consumption }
begin
  with FVariables do
  begin
    i := Count;
    while i > 0 do
    begin
      dec(i);
      AString := Strings[i];
      if (Length(AString) <> 1) or
         (not (AString[1] in ['A'..'E', 'T', 'X', 'Y'])) then
      begin
        APPFloat := PParserFloat(Objects[i]);
        if APPFloat <> nil then
          dispose( APPFloat ); { dispose only user-defined variables }
      end;
    end;

    Clear;
  end;

  with FVariables do
  begin
    i := Count;
    while i > 0 do
    begin
      dec(i);
      AString := Strings[i];
      if (Length(AString) <> 1) or
         (not (AString[1] in ['A'..'E', 'T', 'X', 'Y'])) then
      begin
        APPFloat := PParserFloat(Objects[i]);
        if APPFloat <> nil then
          dispose( APPFloat ); { dispose only user-defined variables }
      end;
    end;

    Clear;
  end;

  SetExpression(''); { invalidate expression }
end;

procedure TCustomParser.ClearVariable(const AVarName: string);
var
  index: integer;
begin
  with FVariables do
  begin
    if Find(AVarName, index) then
    begin
      if (Length(AVarName) <> 1) and
         (not (AVarName[1] in ['A'..'E', 'T', 'X', 'Y'])) then
        dispose( PParserFloat(Objects[index]) ); { dispose only user-defined variables }

      Delete(index);
    end;
  end;

  SetExpression(''); { invalidate expression }
end;

function TCustomParser.VariableExists(const AVarName: string): boolean;
var
  index: integer;
begin
  Result := FVariables.Find(UpperCase(AVarName), index);
end;

procedure TCustomParser.ClearFunctions;
begin
  FunctionOne.Clear;
  FunctionTwo.Clear;

  SetExpression(''); { invalidate expression }
end;

procedure TCustomParser.ClearFunction(const AFunctionName: string);
var
  index: integer;
begin
  with FunctionOne do
  begin
    if Find(AFunctionName, index) then
    begin
      Delete(index);
      SetExpression(''); { invalidate expression }
      exit;
    end;
  end;

  with FunctionTwo do
  begin
    if Find(AFunctionName, index) then
    begin
      Delete(index);
      SetExpression(''); { invalidate expression }
    end;
  end;
end;


procedure TCustomParser.FreeExpression;
var
  LastOP,
  NextOP: POperation;
begin
  LastOP := FStartOperationList;

  while LastOP <> nil do
  begin
    NextOP := LastOP^.NextOperation;

    while NextOP <> nil do
      with NextOP^ do
      begin
        if (Arg1 = lastop^.Arg1) or (Arg1 = lastop^.Arg2) or (Arg1 = lastop^.Dest) then
          Arg1 := nil;

        if (Arg2 = lastop^.Arg1) or (Arg2 = lastop^.Arg2) or (Arg2 = lastop^.Dest) then
          Arg2 := nil;

        if (Dest = lastop^.Arg1) or (Dest = lastop^.Arg2) or (Dest = lastop^.Dest) then
          Dest := nil;

        NextOP := NextOperation;
      end;

    with LastOP^, FVariables do
    begin
      if IndexOfObject( TObject(Arg1)) >= 0 then Arg1 := nil;
      if IndexOfObject( TObject(Arg2)) >= 0 then Arg2 := nil;
      if IndexOfObject( TObject(Dest)) >= 0 then Dest := nil;

      if (Dest <> nil) and (Dest <> Arg2) and (Dest <> Arg1) then
        dispose(Dest);

      if (Arg2 <> nil) and (Arg2 <> Arg1) then
        dispose(Arg2);

      if (Arg1 <> nil) then
        dispose(Arg1);
    end;

    NextOP := LastOP^.NextOperation;
    dispose(LastOP);
    LastOP := NextOP;
  end;

  FStartOperationList := nil;
end;

procedure TCustomParser.SetExpression(const AnExpression: string);
begin
  ParseExpression(AnExpression); { this implies FExpression := AnExpression }
end;


function TCustomParser.CalcValue: extended;
var
  LastOP: POperation;
begin
  if FStartOperationList <> nil then
  begin
    LastOP := FStartOperationList;

    while LastOP^.NextOperation <> nil do
    begin
      with LastOP^ do
      begin
        Operation(LastOP);
        LastOP := NextOperation;
      end;
    end;
    LastOP^.Operation(LastOP);

    Result := LastOP^.Dest^;
  end
  else
    Result := 0;
end;


{****************************************************************}
{                                                                }
{   TCustomParser                                                }
{                                                                }
{****************************************************************}
constructor TParser.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  with FVariables do
  begin
    AddObject( 'A', TObject(@FA));
    AddObject( 'B', TObject(@FB));
    AddObject( 'C', TObject(@FC));
    AddObject( 'D', TObject(@FD));
    AddObject( 'E', TObject(@FE));
    AddObject( 'X', TObject(@FX));
    AddObject( 'Y', TObject(@FY));
    AddObject( 'T', TObject(@FT));
  end;

  with FunctionOne do
  begin
{$DEFINE SpeedCompare} { compare speed against older versions with less functions }

    AddObject('TAN', TObject(@_tan));
    AddObject('SIN', TObject(@_sin));
    AddObject('COS', TObject(@_cos));
    AddObject('SINH', TObject(@_sinh));
    AddObject('COSH', TObject(@_cosh));
    AddObject('ARCTAN', TObject(@_arctan));
{$IFNDEF SpeedCompare}
    AddObject('COTAN', TObject(@_cotan));
    AddObject('ARG', TObject(@_arg));
{$ENDIF}

    AddObject('EXP', TObject(@_exp));
    AddObject('LN', TObject(@_ln));
{$IFNDEF SpeedCompare}
    AddObject('LOG10', TObject(@_log10));
    AddObject('LOG2', TObject(@_log2));

    AddObject('SQR', TObject(@_square));
{$ENDIF}
    AddObject('SQRT', TObject(@_sqrt));

    AddObject('ABS', TObject(@_abs));
{$IFNDEF SpeedCompare}
    AddObject('TRUNC', TObject(@_trunc));
    AddObject('INT', TObject(@_trunc)); { NOTE: INT = TRUNC ! }
    AddObject('CEIL', TObject(@_ceil));
    AddObject('FLOOR', TObject(@_floor));
{$ENDIF}

    AddObject('HEAV', TObject(@_heaviside));
    AddObject('SIGN', TObject(@_sign));
    AddObject('ZERO', TObject(@_zero));
    AddObject('PH', TObject(@_phase));
    AddObject('RND', TObject(@_rnd));
{$IFNDEF SpeedCompare}
    AddObject('RANDOM', TObject(@_random));
{$ENDIF}
  end;

  with FunctionTwo do
  begin

    AddObject('MAX', TObject(@_max));
    AddObject('MIN', TObject(@_min));

{$IFNDEF SpeedCompare}
    AddObject('POWER', TObject(@_Power));
    AddObject('INTPOWER', TObject(@_IntPower));

    AddObject('LOGN', TObject(@_logN)); *)
{$ENDIF}
  end;
end;


class function TParser.RemoveBlanks(const s: string): string;
{deletes all blanks in s}
var
  i : integer;
begin
  Result := s;

  i := pos(' ', Result);
  while i > 0 do
  begin
    delete(Result, i, 1);
    i := pos(' ', Result);
  end;
end;

end.
