{
    Expression-Compiler V1.0
    (C) 1990 by Antivivisektion@t-online.de (Update: 3.8.1998)
    Only for non-commercial use.

    DISCLAIMER
    ----------
    WE DO NOT WARANTEE ANYTHING CONCERNING ANY OF THE SOURCES OR FILES
    WHICH MAKE UP THIS MATH PACKAGE. WE ACCEPT NO RESPONSIBILITY FOR
    ANY LOSS OR DAMAGE OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, LOSSES
    OF A PHYSICAL, MENTAL, SOCIAL, FINANCIAL, MARITAL, OR OF WHATEVER
    NATURE, RESULTING FROM THE USE, OR THE PURPORTED USE, OF THIS MATH
    PACKAGE OR ANY OF THE FILES IN THE PACKAGE, FOR ANY PURPOSE WHATSOEVER.
    WE DO NOT EVEN WARANTEE THAT THE FILES WILL NOT KILL YOU.

    YOU USE THIS MATH PACKAGE ENTIRELY AT YOUR OWN RISK, AND YOU SUPPLY
    IT TO YOUR CUSTOMERS, FRIENDS, FAMILY, ACQUAINTANCES, OR ENEMIES,
    ENTIRELY AT YOUR OWN RISK.

    IF THESE TERMS ARE NOT ACCEPTABLE TO YOU, THEN PLEASE DELETE ALL
    THE FILES FROM YOUR DISKS IMMEDIATELY AND PERMANENTLY.

    In this disclaimer, "WE" refers to: Antivivisektion@t-online.de

    (Disclaimer written by The-African-Chief )
}

{$A+,B-,D+,E-,F-,I-,L+,N+,O-,R-,S-,V-,M 4048,0,$A0000}
{$DEFINE UNIT}

{$IFDEF UNIT}
Unit Math;
Interface
{$ELSE}
Program Math;
{$ENDIF}
Const
  EndID = '=';
  MaxFunc = 20;
  SymbolStackSize = 100;
  FloatStackSize = 80;
  DataIndexMin = 'A';
  DataIndexMax = 'Z';

{$IFOPT N+}
Type
  Float = {Single} Extended;
Const
  MaxFloat = 3.4E38;
{$ELSE}
Type
  Float = Real;
Const
  MaxFloat = 1.7E38;
{$ENDIF}

Type
  FuncNameTyp = String[7];
  FuncResult = Function (f: Float): Float;
  SymbolTyp = (_Add,_Sub,_Mul,_Div,_Pot,_Neg,_Func,_Var,_Val,_lP,_rP,_End);
  SymbolRec = Record
    Case Symbol: SymbolTyp Of
       _Add: (SymChar: Char);
      _Func: (FuncIndex: Word);
       _Val: (Value: Float);
  End;
  SymbolStackIndex = 1..SymbolStackSize;
  FloatStackIndex = 0..FloatStackSize;
  DataIndex = DataIndexMin..DataIndexMax;

  DataObj = Object
    Data: Array[DataIndex] Of Float;
    SymbolStackPtr: Word;
    SymbolStack: Array[1..SymbolStackSize] Of SymbolRec;
    Constructor Init;
    Procedure PushSymbol (Var Symbol: SymbolRec);
    Procedure Error (Const Msg1,Msg2: String);
    Destructor Done;
  End;

  ParseObj = Object(DataObj)
    ParseIndex: Byte;
    Parse: String;
    ThisChar: Char;
    ThisSymbol: SymbolRec;
    LegalFunc: Word;
    FuncTable: Array[1..MaxFunc] Of Record
      FuncName: FuncNameTyp;
      Evaluate: FuncResult;
    End;
    Procedure AddFunc (Const pName: FuncNameTyp; pResult: FuncResult);
    Constructor Init;
    Procedure NextChar;
    Procedure NextSymbol;
    Procedure Error (Msg1,Msg2: String);
    Destructor Done;
  End;

  Expression = Object(ParseObj)
    MathError: Boolean;
    Constructor Init;
    Procedure EnterTerm;
    Procedure FormatTerm;
    Function FormFloat (f: Float): String;
    Procedure Compile;
    Procedure Term;
    Procedure Summand;
    Procedure Factor;
    Procedure Base;
    Function Evaluate: Float;
    Function EvaluateTerm ({Const} TheTerm: String): Float;
    Procedure PrintResult;
    Procedure Optimize;
    Procedure ShowStack;
    Destructor Done;
  End;

{$IFDEF UNIT}
Implementation
{$ENDIF}

Constructor DataObj.Init;
Var Index: DataIndex;
Begin
  For Index := DataIndexMin To DataIndexMax Do Data[Index] := 0.0;
  SymbolStackPtr := 0;
End;

Procedure DataObj.Error (Const Msg1,Msg2: String);
Begin
  WriteLn ('Parsing error ',Msg1,Msg2);
  Halt;
End;

Procedure ParseObj.Error (Msg1,Msg2: String);
Begin
  WriteLn (Parse);
  If (Copy(Msg1,1,7) <> 'Illegal')
     Then Write ('^':ParseIndex-2)
     Else Write ('^':ParseIndex-1-Length(Msg2));
  WriteLn ('-- syntax error: ',Msg1,Msg2);
  Halt;
End;

Procedure DataObj.PushSymbol (Var Symbol: SymbolRec);
Begin
  Inc(SymbolStackPtr);
  SymbolStack[SymbolStackPtr] := Symbol;
End;

Destructor DataObj.Done;
Begin
  SymbolStackPtr := 0;
End;

Constructor ParseObj.Init;
Begin
  ParseIndex := 1;
  Parse := '';
End;

Procedure ParseObj.NextChar;
Begin
  ThisChar := Parse[ParseIndex];
  Inc(ParseIndex);
End;

Procedure ParseObj.NextSymbol;
Var StrHelp: String[63]; Index: Word;
Begin
  With ThisSymbol Do
  Begin
    Case ThisChar Of
      '+': Begin
             Symbol := _Add;
             SymChar := '+';
             NextChar;
           End;
      '-': Begin
             Symbol := _Sub;
             SymChar := '-';
             NextChar;
           End;
      '*': Begin
             Symbol := _Mul;
             SymChar := '*';
             NextChar;
           End;
      '/': Begin
             Symbol := _Div;
             SymChar := '/';
             NextChar;
           End;
      '^': Begin
             Symbol := _Pot;
             SymChar := '^';
             NextChar;
           End;
      '(': Begin
             Symbol := _lP;
             SymChar := '(';
             NextChar;
           End;
      ')': Begin
             Symbol := _rP;
             SymChar := ')';
             NextChar;
           End;
    EndID: Begin
             Symbol := _End;
             SymChar := EndID;
           End;
 'A'..'Z': Begin
             StrHelp := ThisChar;
             NextChar;
             While (ThisChar >= 'A') And (ThisChar <= 'Z') Do
             Begin
               StrHelp := StrHelp+ThisChar;
               NextChar;
             End;
             If (Length(StrHelp) = 1)
               Then
                 Begin
                   Symbol := _Var;
                   SymChar := StrHelp[1];
                 End
               Else
                 Begin
                   Index := 1;
                   While (Index <= LegalFunc) And
                     (StrHelp <> FuncTable[Index].FuncName) Do
                     Inc(Index);

                   If (Index > LegalFunc)
                      Then Error ('Illegal function call: ',StrHelp);
                   FuncIndex := Index;
                   Symbol := _Func;
                 End;
           End;
  '.',',','0'..'9': Begin
             If (ThisChar = ',') Then ThisChar := '.';
             StrHelp := ThisChar;
             NextChar;
             If (ThisChar = ',') Then ThisChar := '.';
             While (ThisChar = '.') Or
               ((ThisChar >= '0') And (ThisChar <= '9')) Do
             Begin
               StrHelp := StrHelp+ThisChar;
               NextChar;
               If (ThisChar = ',') Then ThisChar := '.';
             End;
             Symbol := _Val;
             Val (StrHelp,Value,Index);
             If (Index > 0)
                Then Error('Illegal float: ',StrHelp);
           End;
    End; { Case }
  End; { With }
End;

Destructor ParseObj.Done;
Begin
End;

{$F+}
Function _ABS (f: Float): Float;
Begin
  _ABS := ABS(f);
End;

Function _SIN (f: Float): Float;
Begin
  _SIN := SIN(f);
End;

Function _COS (f: Float): Float;
Begin
  _COS := COS(f);
End;

Function _TAN (f: Float): Float;
Var x: Float;
Begin
  x := COS(f);
  If (x <> 0)
     Then _TAN := SIN(f) / x
     Else _TAN := MaxFloat;
End;

Function _COT (f: Float): Float;
Var x: Float;
Begin
  x := SIN(f);
  If (x <> 0)
     Then _COT := COS(f) / x
     Else _COT := MaxFloat;
End;

Function _EXP (f: Float): Float;
Begin
  _EXP := EXP(f);
End;

Function _LN (f: Float): Float;
Begin
  If (f <= 0)
     Then Begin _LN := 0; WriteLn('RUNTIME ERROR: ILLEGAL LN() VALUE'); End
     Else _LN := LN(f);
End;

Function _LOG (f: Float): Float;
Const LN10 = 2.30258512496948242;
Begin
  If (f <= 0)
     Then Begin _LOG := 0; WriteLn('RUNTIME ERROR: ILLEGAL LOG() VALUE'); End
     Else _LOG := _LN(f) / LN(10);
End;

Function _LD (f: Float): Float;
Const LN2 = 0.693147182464599609;
Begin
  If (f <= 0)
     Then Begin _LD := 0; WriteLn('RUNTIME ERROR: ILLEGAL LD() VALUE'); End
     Else _LD := _LN(f) / LN(2);
End;

Function _SQRT (f: Float): Float;
Begin
  _SQRT := SQRT(f);
End;

Function _SQR (f: Float): Float;
Begin
  _SQR := SQR(f);
End;

Function _SINH (f: Float): Float;
Begin
  _SINH := 0.5 * (Exp(f)-Exp(-f));
End;

Function _COSH (f: Float): Float;
Begin
  _COSH := 0.5 * (Exp(f)+Exp(-f));
End;

Function _TANH (f: Float): Float;
Begin
  _TANH := _SINH(f) / _COSH(f);
End;

Function _COTH (f: Float): Float;
Begin
  _COTH := _COSH(f) / _SINH(f);
End;

Function _ARSINH (f: Float): Float;
Begin
  _ARSINH := _Ln(f+_Sqrt(_Sqr(f)+1));
End;

Function _ARCOSH (f: Float): Float;
Begin
  If (f >= 1)
     Then _ARCOSH := _Ln(f+_Sqrt(_Sqr(f)-1))
     Else _ARCOSH := MaxFloat;
End;

Function _ARTANH (f: Float): Float;
Begin
  If (Abs(f) < 1)
     Then _ARTANH := _Ln(_Sqrt((1+f)/(1-f)))
     Else _ARTANH := MaxFloat;
End;

Function _ARCOTH (f: Float): Float;
Begin
  If (Abs(f) > 1)
     Then _ARCOTH := _ARTANH(1/f)
     Else _ARCOTH := MaxFloat;
End;
{$F-}

Constructor Expression.Init;
Begin
  DataObj.Init;
  ParseObj.Init;
  LegalFunc := 0;
{ FillChar (FuncTable,SizeOf(FuncTable),0);
} AddFunc ('SIN',_SIN);
  AddFunc ('COS',_COS);
  AddFunc ('TAN',_TAN);
  AddFunc ('EXP',_EXP);
  AddFunc ('LN', _LN);
  AddFunc ('LD', _LD);
  AddFunc ('LOG',_LOG);
  AddFunc ('ABS',_ABS);
  AddFunc ('COT',_COT);
  AddFunc ('SQRT',_SQRT);
  AddFunc ('SQR',_SQR);
  AddFunc ('SINH',_SINH);
  AddFunc ('COSH',_COSH);
  AddFunc ('TANH',_SINH);
  AddFunc ('COTH',_SINH);
  AddFunc ('ARSINH',_ARSINH);
  AddFunc ('ARCOSH',_ARCOSH);
  AddFunc ('ARTANH',_ARTANH);
  AddFunc ('ARCOTH',_ARCOTH);
End;

Procedure ParseObj.AddFunc (Const pName: FuncNameTyp; pResult: FuncResult);
Begin
  If (LegalFunc >= MaxFunc)
     Then Error ('Function table overflow','');
  Inc(LegalFunc);
  With FuncTable[LegalFunc] Do
  Begin
    FuncName := pName;
    @Evaluate := @pResult;
  End;
End;

Destructor Expression.Done;
Begin
  ParseObj.Done;
  DataObj.Done;
End;

Function Expression.Evaluate: Float;
Var
  Index: SymbolStackIndex;
  FloatStackPtr: FloatStackIndex;
  FloatStack: Array[1..FloatStackSize] Of Float;

  Function Pop: Float;
  Begin
    Pop := FloatStack[FloatStackPtr];
    Dec(FloatStackPtr);
  End;

  Procedure Push(Wert: Float);
  Begin
    Inc(FloatStackPtr);
    FloatStack[FloatStackPtr] := Wert;
  End;

  Function RaiseOnZero(f: Float): Float;
  Begin
    If (f = 0)
       Then Begin RaiseOnZero := 1E-20; WriteLn('RUNTIME ERROR: DIV BY ZERO'); RunError(200); End
       Else RaiseOnZero := f;
  End;

Begin
  FloatStackPtr := 0;
  For Index := 1 To SymbolStackPtr Do
  Begin
    With SymbolStack[Index] Do
    Begin
      Case Symbol Of
         _Add: Push (Pop+Pop);
         _Sub: Push (-Pop+Pop);
         _Mul: Push (Pop*Pop);
         _Div: Push ((1/RaiseOnZero(Pop))*Pop);
         _Pot: Push (_Exp(Pop*Ln(Pop)));
         _Neg: Push (-Pop);
         _Var: Push (Data[SymChar]);
         _Val: Push (Value);
        _Func: Push (FuncTable[FuncIndex].Evaluate (Pop));
      End;
    End;
  End;
  Evaluate := Pop;
End;

Function Expression.FormFloat (f: Float): String;
Var s: String;
Begin
  Str(f:0:$FF,s);
  While (Length(s) > 0) And (s[Length(s)] = '0') Do Dec(s[0]);
  If (Length(s) > 0) And (s[Length(s)] = '.') Then Dec(s[0]);
  FormFloat := s;
End;

Procedure Expression.ShowStack;
Var Index: SymbolStackIndex;
Begin
  For Index := 1 To SymbolStackPtr Do
  Begin
    With SymbolStack[Index] Do
    Begin
      Case Symbol Of
        _Func: Write (FuncTable[FuncIndex].FuncName);
         _Val: Write (FormFloat(Value));
          Else Write (SymChar);
      End;
    End;
    Write(' ');
  End;
End;

Procedure Expression.Compile;
Begin
  NextChar;
  NextSymbol;
  Term;
  If (ThisSymbol.Symbol <> _End)
     Then Error('Unexpected end of term','');
End;

Procedure Expression.Term;
Var AddHelp: SymbolRec;
Begin
  If (ThisSymbol.Symbol = _Add) Or (ThisSymbol.Symbol = _Sub)
     Then
       Begin
         AddHelp := ThisSymbol;
         NextSymbol;
         Summand;
         If (AddHelp.Symbol = _Sub)
            Then
              Begin
                AddHelp.Symbol := _Neg;
                AddHelp.SymChar := 'ยช';
                PushSymbol (AddHelp);
              End;
       End
     Else Summand;
  While (ThisSymbol.Symbol = _Add) Or (ThisSymbol.Symbol = _Sub) Do
  Begin
    AddHelp := ThisSymbol;
    NextSymbol;
    Summand;
    PushSymbol (AddHelp);
  End;
End;

Procedure Expression.Summand;
Var MulHelp: SymbolRec;
Begin
  Factor;
  While (ThisSymbol.Symbol = _Mul) Or (ThisSymbol.Symbol = _Div) Do
  Begin
    MulHelp := ThisSymbol;
    NextSymbol;
    Factor;
    PushSymbol (MulHelp);
  End;
End;

Procedure Expression.Factor;
Var ExpHelp: SymbolRec;
Begin
  Base;
  ExpHelp := ThisSymbol;
  While (ThisSymbol.Symbol = _Pot) Do
  Begin
    NextSymbol;
    Base;
    PushSymbol (ExpHelp);
  End;
End;

Procedure Expression.Base;
Var FuncHelp: SymbolRec;
Begin
  Case ThisSymbol.Symbol Of
    _Var,_Val: Begin
                 PushSymbol (ThisSymbol);
                 NextSymbol;
               End;
          _lP: Begin
                 NextSymbol;
                 Term;
                 If (ThisSymbol.Symbol = _rP)
                   Then NextSymbol
                   Else Error (') expected','');
               End;
        _Func: Begin
                 FuncHelp := ThisSymbol;
                 NextSymbol;
                 If (ThisSymbol.Symbol = _lP)
                    Then NextSymbol
                    Else {Error('( expected','')};
                 Term;
                 If (ThisSymbol.Symbol = _rP)
                    Then NextSymbol
                    Else If (ThisSymbol.Symbol <> _End) { AutoClose () }
                            Then Error(') expected','');
                 PushSymbol(FuncHelp)
               End;
          Else Error('Base expected','');
  End;
End;

Procedure Expression.EnterTerm;
Begin
  Init;
  Write ('Enter expression: ');
  ReadLn (Parse);
  FormatTerm;
End;

Function Expression.EvaluateTerm ({Const} TheTerm: String): Float;
Begin
  Init;
  Parse := TheTerm;
  FormatTerm;
  Compile;
  EvaluateTerm := Evaluate;
End;

Procedure Expression.FormatTerm;
Var Index1,Index2: Word;
Begin
  Inc(Parse[0]);
  Parse[Length(Parse)] := '=';
  Index1 := 0;
  For Index2 := 1 To Length(Parse) Do
    If (Parse[Index2] <> ' ')
       Then
         Begin
           Inc(Index1);
           Parse[Index1] := UpCase(Parse[Index2]);
         End;
  Parse[0] := Char(Index1);
End;

Procedure Expression.PrintResult;
Begin
  WriteLn (FormFloat(Evaluate));
End;

Procedure Expression.Optimize;
Begin
  { TO-DO! }
End;

{$IFNDEF UNIT}

Var F: Expression;
Begin
  With F Do
  Repeat
    EnterTerm;
    Compile;
    Write(Parse);
    PrintResult;
    ShowStack;
    WriteLn;
  Until FALSE;
{$ENDIF}
End.


         Date: Mon, 03 Aug 1998 22:37:45 +0200
         From: "Antivivisektion e.V." 
 Organization: http://Antivivisektion.base.org
   Newsgroups: comp.lang.pascal.borland, de.lang.pascal.misc
      Subject: Re: Input an equation (provides source)
   References: <6pvcn0$lf6$1@news2.saix.net>

AM la Grange wrote:
>
> I would like to readln an equation, for example: 5*9-2/3,
> or whatever, including reserved pascal words for example:
> "sqr","sqrt","cos","sin", etc.
>
> Then I would like to use it in my program in calculating the answer.

Please compile my file MATH.PAS I wrote several years ago:

[Expression-Compiler (OOP/TP55-TP70)]


You may use it like in my example below:

|  Uses Math;
|  Var E: Expression; Term: String; Result: Float;
|  Begin
|    Write('Enter expression: ');
|    ReadLn(Term);
|    Result := E.EvaluateTerm(Term);
|    Write('Result: ',Result:0:20);
|  End.

> If there is a(n) (easy) way of doing this, please let me know...

It's really that easy ;-)

You may also use one-char variables to evaluate complex expressions:

|  Uses Math;
|  Var E: Expression; X: Word;
|  Begin
|    E.Init;
|    E.Parse := '2*X+SIN(X)*TAN(X/8)';
|    E.FormatTerm;
|    E.Compile;
|    For X := 1 To 100 Do
|    Begin
|      E.Data['X'] := X;
|      E.PrintResult; { same as WriteLn(E.FormFloat(E.Evaluate)); }
|    End;
|  End.

The expression may be as complex as you wish,
with 25 build-in var's A-Z (like in basic).

It's even possible to add your own functions at runtime, e.g. RANDOM:

|  Uses Math;
|  Var E: Expression; X: Word;
|
|  {$F+} Function MyFunc (x: Float): Float; {$F-}
|  Begin
|    MyFunc := Random(Round(x));
|  End;
|
|  Begin
|    E.Init; { Insert ADDFUNC after this line }
|    E.AddFunc('RANDOM',MyFunc);
|    { Now you can use RANDOM(...) in your expressions, example: }
|    E.Parse := 'RANDOM(100)';
|    E.FormatTerm;
|    E.Compile;
|    E.PrintResult;
|  End.

The error-handling (simple HALT) has to be worked out...

--
              ---===Coders Against Vivisection===---

A.E.Neumann fuer die Antivivisektion e.V., PO-Box 201, D-53569 Unkel
mailto:Antivivisektion@t-online.de http://Antivivisektion.base.org/

[Animal research is wasteful and misleading]


    Source: geocities.com/SiliconValley/2926/tpsrc

               ( geocities.com/SiliconValley/2926)                   ( geocities.com/SiliconValley)