 {
 ****************************************************************************
 CRW32 project
 Copyright (C) by Kuryakin Alexey, Sarov, Russia, 2006, <kouriakine@mail.ru>
 System calculator - interpreter for system calculations.
 That is thread safe and robust wrapper for TExpressionEvaluator.
 Modifications:
 20060303 - Creation
 20060325 - Add Fifo
 20160927 - SystemCalculatorFifoPutText
 20170228 - SystemCalculatorFifoLimit,SystemCalculatorFifoFactor
 ****************************************************************************
 }

unit _SYSCAL; { SYStem CALculator }

{$I _sysdef}

interface

uses
 sysutils, _alloc, _fpu, _str, _fio, _ee, _fifo;

type
 TSystemCalculator = class(TMasterObject)
 private
  myEe   : TExpressionEvaluator;
  myFifo : TFifo;
  function GetFifo:TFifo;
 public
  constructor Create;
  destructor  Destroy; override;
  function    Eval(const Expression:LongString):Double;
  function    Evaluate(const Expression : LongString;
                         var Answer     : Double;
                         var Comment    : LongString) : Integer;
  function    SetAction(const aName:LongString; aAction:TeeAction; const aNote:LongString):Boolean;
  function    SetConst(const aName:LongString; aValue:Double):Boolean;
  function    SetFunc(const aName:LongString; aArgs:Integer; aFunc:TeeFunction; const aNote:LongString):Boolean;
  procedure   SaveVars(const IniFile,Section:ShortString);
  procedure   RestoreVars(const IniFile,Section:ShortString);
  property    Fifo : TFifo read GetFifo;
 end;

procedure Kill(var TheObject:TSystemCalculator); overload;

function SystemCalculator:TSystemCalculator;

function SystemCalculatorFifoPutText(const Msg:LongString):Integer;

const
 SystemCalculatorFifoSize   = 1024*64;
 SystemCalculatorFifoLimit  = 1024*1024*64;
 SystemCalculatorFifoFactor = 2;

implementation

 {
 *****************
 TSystemCalculator
 *****************
 }
constructor TSystemCalculator.Create;
begin
 inherited Create;
 myEe:=NewExpressionEvaluator;
 myEe.Master:=myEe;
 myFifo:=NewFifo(SystemCalculatorFifoSize);
 myFifo.Master:=myFifo;
 myFifo.GrowLimit:=SystemCalculatorFifoLimit;
 myFifo.GrowFactor:=SystemCalculatorFifoFactor;
end;

destructor TSystemCalculator.Destroy;
begin
 Kill(myEe);
 Kill(myFifo);
 inherited Destroy;
end;

function TSystemCalculator.GetFifo:TFifo;
begin
 if Assigned(Self) then Result:=myFifo else Result:=nil;
end;

function  TSystemCalculator.Eval(const Expression:LongString):Double;
begin
 Result:=_Nan;
 if Assigned(Self) then
 try
  myEe.Lock;
  try
   if myEe.EvaluateLine(PChar(Expression))=ee_Ok then Result:=myEe.Answer;
  finally
   myEe.Unlock;
  end;
 except
  on E:Exception do BugReport(E,Self);
 end;
end;

function  TSystemCalculator.Evaluate(const Expression : LongString;
                                       var Answer     : Double;
                                       var Comment    : LongString) : Integer;
begin
 Answer:=0;
 Comment:='';
 if Assigned(Self) then
 try
  myEe.Lock;
  try
   Result:=myEe.EvaluateLine(PChar(Expression));
   if Result=ee_Ok then begin
    if myEe.MayPrint then Comment:=Format('%g',[myEe.Answer]);
    Answer:=myEe.Answer;
   end else begin
    Comment:=ee_ErrorMessage(myEe.Status)+'.'+CRLF+
             StrPas(myEe.Buffer)+CRLF+
             CharStr(myEe.ErrorPos+1)+'^'+CRLF+
             CharStr(myEe.ErrorPos+1-Length(myEe.ErrorToken))+StrPas(myEe.ErrorToken);
   end;
  finally
   myEe.Unlock;
  end;
 except
  on E:Exception do begin
   BugReport(E,Self);
   Result:=ee_Exception;
   Comment:=ee_ErrorMessage(Result);
  end;
 end else begin
  Result:=ee_NilRef;
  Comment:=ee_ErrorMessage(Result);
 end;
end;

function TSystemCalculator.SetAction(const aName:LongString; aAction:TeeAction; const aNote:LongString):Boolean;
begin
 Result:=false;
 if Assigned(Self) then
 try
  myEe.Lock;
  try
   if Assigned(aAction)
   then Result:=myEe.SetAction(aName,aAction,aNote)
   else Result:=myEe.ActionList.ClearAction(PChar(aName));
  finally
   myEe.Unlock;
  end;
 except
  on E:Exception do BugReport(E,Self);
 end;
end;

function TSystemCalculator.SetConst(const aName:LongString; aValue:Double):Boolean;
begin
 Result:=false;
 if Assigned(Self) then
 try
  myEe.Lock;
  try
   Result:=myEe.SetConst(aName,aValue);
  finally
   myEe.Unlock;
  end;
 except
  on E:Exception do BugReport(E,Self);
 end;
end;

function TSystemCalculator.SetFunc(const aName:LongString; aArgs:Integer; aFunc:TeeFunction; const aNote:LongString):Boolean;
begin
 Result:=false;
 if Assigned(Self) then
 try
  myEe.Lock;
  try
   if Assigned(aFunc)
   then myEe.SetFunc(aName,aArgs,aFunc,aNote)
   else myEe.FuncList.ClearFunc(PChar(aName));
  finally
   myEe.Unlock;
  end;
 except
  on E:Exception do BugReport(E,Self);
 end;
end;

procedure TSystemCalculator.RestoreVars(const IniFile,Section:ShortString);
begin
 if Assigned(Self) then
 try
  myEe.Lock;
  try
   myEe.RestoreVars(IniFile,Section);
  finally
   myEe.UnLock;
  end;
 except
  on E:Exception do BugReport(E,Self);
 end;
end;

procedure TSystemCalculator.SaveVars(const IniFile,Section:ShortString);
begin
 if Assigned(Self) then
 try
  myEe.Lock;
  try
   myEe.SaveVars(IniFile,Section);
  finally
   myEe.UnLock;
  end;
 except
  on E:Exception do BugReport(E,Self);
 end;
end;

procedure Kill(var TheObject:TSystemCalculator); overload;
begin
 try
  FreeAndNil(TheObject);
 except
  on E:Exception do BugReport(E);
 end;
end;

function SystemCalculatorFifoPutText(const Msg:LongString):Integer;
begin
 if SystemCalculator.Fifo.PutText(Msg)
 then Result:=Length(Msg)
 else Result:=0;
end;

 {
 *******************************************************************************
 SystemCalculator
 *******************************************************************************
 }
const
 mySystemCalculator : TSystemCalculator = nil;

function SystemCalculator:TSystemCalculator;
begin
 if not Assigned(mySystemCalculator) then begin
  mySystemCalculator:=TSystemCalculator.Create;
  mySystemCalculator.Master:=mySystemCalculator;
  SystemSendToMainConsoleFunction:=SystemCalculatorFifoPutText;
 end;
 Result:=mySystemCalculator;
end;

initialization

 //SystemCalculator.Ok;

finalization

 Kill(mySystemCalculator);

end.
