Contributor: WARREN PORTER            

{
From: WARREN PORTER
Subj: eval
Program to evaluate expressions using a stack. }

const
  Maxstack = 100;

type

  stack = record
        top : 0..Maxstack;
        Item : array[1..Maxstack] of char
        end;

  RealStack = record
        top: 0..Maxstack;
        Item : array[1..Maxstack] of real
        end;

  xptype = record
        oper : char;
        opnd : real
        end;

Function Empty(var A:stack):boolean;

Begin
  Empty:= A.top = 0;
End;

Function Pop(var A:stack):char;

Begin
  if A.Top < 1 then
    begin
      writeln('Attempt to pop an empty stack');
      halt(1)
    end;
  Pop:= A.item[A.top];
  A.top:= A.top - 1
End;

Procedure Push(var A:stack; Nchar:char);

Begin
  if A.Top = Maxstack then
    begin
      writeln('Stack already full');
      halt(1)
    end;
  A.top:= A.top + 1;
  A.item[A.top]:=Nchar
End;

     {The following functions are for the real stack only.}

Function REmpty(var D:RealStack):boolean;

Begin
  REmpty:= D.top = 0;
End;

Function RPop(var D:RealStack):real;

Begin
  if D.Top < 1 then
    begin
      writeln('Attempt to pop an empty RealStack');
      halt(1)
    end;
  RPop:= D.item[D.top];
  D.top:= D.top - 1
End;

Procedure RPush(var D:RealStack; Nreal:real);

Begin
  if D.Top = MaxStack then
    begin
      writeln('Stack already full');
      halt(1)
    end;
  D.top:= D.top + 1;
  D.item[D.top]:=Nreal
End;

Function pri(op1, op2:char):boolean;

var
  tpri: boolean;
Begin
  if op2 = ')' then
    tpri:= true                            else
  if (op1 = '$') and (op2 <> '$') and (op2 <> '(')  then
    tpri:= true                            else
  if (op1 in ['*','/']) and (op2 in ['+','-']) then
    tpri:= true
  else
    tpri:= false;
  pri:= tpri{;
  write('Eval op 1= ',op1, ' op2 = ',op2);
  if tpri= false then
     writeln(' false')
  else
     writeln(' true')}
End;

Function ConvReal(a:real;NumDec:integer):real;

var
   i, tenpower: integer;

Begin
   tenpower:= 1;
   for i:= 1 to NumDec do
      tenpower:= tenpower * 10;
   ConvReal:= a / tenpower
End;

Function ROper(opnd1, opnd2: real; oper: char):real;
Var temp: real;

Begin
   Case oper of
      '+': temp:= opnd1 + opnd2;
      '-': temp:= opnd1 - opnd2;
      '*': temp:= opnd1 * opnd2;
      '/': temp:= opnd1 / opnd2;
      '$': temp:= exp(ln(opnd1) * opnd2)
   End {Case}     ;
   {Writeln(opnd1:6:3,' ',oper,' ',opnd2:6:3 ,' = ',temp:6:3);}
   ROper := temp
End; {R oper}

{Main procedure starts here}

var
  A: stack;
  Inbuff:string[Maxstack];
  len, i, j, NumDecPnt, lenexp: integer;
  temp, opnd1, opnd2, result : real;
  valid, expdigit, expdec, isneg, openok: boolean;
  operators, digits : set of char;
  HoldTop : char;
  B: array[1..Maxstack] of xptype;
  C: array[1..Maxstack] of xptype;
  D: RealStack;

Begin
  digits:= ['0'..'9'];
  operators:= ['$','*','/','+','-','(',')'];
  Writeln('Enter expression to evaluate or RETURN to stop');
  Writeln('A space should follow a minus sign unless it is used to');
  Writeln('negate the following number.  Real numbers with multi-');
  Writeln('digits and decimal point (if needed) may be entered.');
  Writeln;
  Readln(Inbuff);
  len:=length(Inbuff);

  repeat
    i:= 1;
    A.top:= 0;
    valid:= true;
    repeat
      if Inbuff[i] in ['(','[','{'] then
        push(A,Inbuff[i])
      else
        if Inbuff[i] in [')',']','}'] then
          if empty(A) then
            valid:= false
          else
            if (ord(Inbuff[i]) - ord(Pop(A))) > 2 then
              valid:= false;
      i:= i + 1
    until (i > len) or (not valid);
    if not empty(A) then
      valid:= false;
    if not valid then
      Writeln('The expression is invalid')
    else
      Begin
         {Change all groupings to parenthesis}
         for i:= 1 to len do Begin
           if Inbuff[i] in ['[','{'] then
              Inbuff[i]:= '('  else
           if Inbuff[i] in [']','}'] then
              Inbuff[i]:= ')';
           B[i].oper:= ' ';
           B[i].opnd:= 0;
           C[i].oper:= ' ';
           C[i].opnd:= 0    End;

         { The B array will be the reformatted input string.
           The C array will be the postfix expression. }

         i:= 1; j:= 1; expdigit:= false; expdec:= false; isneg:= false;
         while i <= len do
            Begin
               if (Inbuff[i] = '-') and (Inbuff[i + 1] in digits) then
                  Begin
                     isneg:= true;
                     i:= i + 1
                  End;
               if (Inbuff[i] = '.' ) then  Begin
                  i:= i + 1;
                  expdec:= true            End;
               if Inbuff[i] in digits then
                  Begin
                     if expdec then
                        NumDecPnt:= NumDecPnt + 1;
                     if expdigit then
                        temp:= temp * 10 + ord(inbuff[i]) - ord('0')
                     else                  Begin
                        temp:= ord(inbuff[i]) - ord('0');
                        expdigit:= true    End
                  End
               else
                  if expdigit = true then    Begin
                     if isneg then
                        temp:= temp * -1;
                     B[j].opnd:= ConvReal(temp,NumDecPnt);
                     j:= j + 1;
                     expdigit := false;
                     expdec   := false;
                     NumDecPnt:= 0;
                     isneg:= false           End;

               If Inbuff[i] in operators     then Begin
                  B[j].oper:= Inbuff[i];
                  j:= j + 1                       End;

               if not (Inbuff[i] in digits)    and
                  not (Inbuff[i] in operators) and
                  not (Inbuff[i] = ' ') then                Begin
                  Writeln('Found invalid operator: ',Inbuff[i]);
                  valid:= false                             End;

               i:= i + 1;

            End;  {While loop to parse string.}

            if expdigit = true then    Begin
               if isneg then
                  temp:= temp * -1;
               B[j].opnd:= ConvReal(temp,NumDecPnt);
               j:= j + 1;
               expdigit := false;
               expdec   := false;
               NumDecPnt:= 0;
               isneg:= false           End;

      End; {First if valid loop.  Next one won't run if invalid operator}

    if valid then
      Begin
         lenexp:= j - 1;    {Length of converted expression}
         writeln;
         for i:= 1 to lenexp do
            Begin
               if B[i].oper = ' ' then
                  write(B[i].opnd:2:3)
               else
                  write(B[i].oper);
               write(' ')
            End;

         {Ready to create postfix expression in array C }

         A.top:= 0;
         j:= 0;

         for i:= 1 to lenexp do
            Begin
               {writeln('i = ',i);}
               if B[i].oper = ' ' then       Begin
                  j:= j + 1;
                  C[j].opnd:= B[i].opnd      End
               else
                  Begin
                  openok := true;
                     while (not empty(A) and openok and
                           pri(A.item[A.top],B[i].oper)) do
                        Begin
                           HoldTop:= pop(A);
                           if HoldTop = '(' then
                              openok:= false
                           else
                              Begin
                                 j:= j + 1;
                                 C[j].oper:=HoldTop
                              End
                        End;
                     if B[i].oper <> ')' then
                        push(A,B[i].oper);
                  End; {Else}
            End; {For loop}

            while not empty(A) do
               Begin
                  HoldTop:= pop(A);
                  if HoldTop <> '(' then
                     Begin
                        j:= j + 1;
                        C[j].oper:=HoldTop
                     End
               End;

         lenexp:= j;  {Since parenthesis are not included in postfix.}

         for i:= 1 to lenexp do
            Begin
               if C[i].oper = ' ' then
                  write(C[i].opnd:2:3)
               else
                  write(C[i].oper);
               write(' ')
            End;

         {The following evaluates the expression in the real stack}

         D.top:=0;
         for i:= 1 to lenexp do
            Begin
               if C[i].oper = ' ' then
                  Rpush(D,C[i].opnd)
               else
                  Begin
                     opnd2:= Rpop(D);
                     opnd1:= Rpop(D);
                     result:= ROper(opnd1,opnd2,C[i].oper);
                     Rpush(D,result)
                  End {else}
            End; {for loop}
         result:= Rpop(D);
         if Rempty(D) then
            writeln('    = ',result:2:3)
         else
            writeln('    Could not evaluate',chr(7))
      End;

    Readln(Inbuff);
    len:= length(Inbuff)
  until len = 0
End.