program Parser;

{adapted to Turbo Pascal by Glenn Brooke 5/6/86 from a program
by Herbert Shcildt.

this program reads an expression and returns the result.  It can
handle up to 26 one letter (A-Z) variables and real numbers.
Supports +,-,*,/,and powers.  Not bad!  Speed isn't too bad, either.

This kind of a program is really best as a function in your own program,
so that the user can enter an expression, and the program can compute
the result.  For example, a function plotting program can simply ask
for a function like 2*X + (3.14/X^4)/1.23, and plot the curve from
-5 to +5.  Quite powerful!

}
type
   str80 = string[80];
   Ttype = (Delimiter, Variable, Number);
var
  token, prog : str80;
  TokType : Ttype;
  code, t : integer;
  result : real;
  vars : array[0..25] of real;      {26 variables}

function IsAlpha(ch : char) : boolean;
{true if ch is letter in alphabe}
begin
    IsAlpha := (Upcase(ch) in ['A'..'Z'])
end;

function IsWhite(ch : char) : boolean;
{true if newline, space or tab}
begin
    IsWhite := (ch= ' ') or (ch=chr(9)) or (ch=chr(13));
end;

function IsDelim(ch : char) : boolean;
begin
    if pos(ch,' +-/*%^=()$')<>0 then IsDelim := true
    else IsDelim := false
end;

function Isdigit(ch : char) : boolean;
begin
    Isdigit := ch in ['0'..'9']
end;

procedure GetToken;
var temp : str80;
begin
    token := '';
    while (IsWhite(prog[t])) do t := succ(t);
    if prog[t]='$' then token := '$';
    if pos(prog[t],'+-*/%^=()')<>0 then
       begin
       TokType := Delimiter;
       token := prog[t];    {is an operator}
       t := succ(t);
       end
    else if IsAlpha(prog[t]) then
         begin
         while (not IsDelim(prog[t])) do
               begin
               token := token + prog[t];    {build token}
               t := succ(t)
               end;
         TokType := Variable;
         end
    else if IsDigit(prog[t]) then
         begin
         while (not IsDelim(prog[t])) do
               begin
               token := token + prog[t];   {build number}
               t := succ(t);
               Toktype := number;
               end;
         end;
end; {GetToken}

procedure PutBack;  {put back unused token}
begin
    t := t - length(token)
end;

procedure Serror(i : integer);  {print error msg}
begin
    case i of
         1 : writeln('Syntax error');
         2 : writeln('Unbalanced parentheses');
         3 : writeln('No expression Present')
    end;
end;

function Pwr(a,b : real) : real;
{raise a to the b power}
var t : integer;
   temp : real;
begin
    if a= 0 then pwr := 1
    else
        begin
        temp := a;
        for t := trunc(b) downto 2 do a := a * temp;
        Pwr := a
        end
end;

function FindVar(s : str80) : real;
var t : integer;
begin
    FindVar := vars[ord(upcase(s[1]))-ord('A')]
end;

procedure Arith(op : char; var result, operand : real);
begin
    case op of
         '+' : result := result + operand;
         '-' : result := result - operand;
         '*' : result := result * operand;
         '/' : result := result / operand;
         '^' : result := Pwr(result,operand);
   end
end;

{***********  Expression Parser w/ variables and assignment  ********}
procedure Level2(var result : real); forward;
procedure Level1(var result : real); forward;
procedure Level3(var result : real); forward;
procedure Level4(var result : real); forward;
procedure Level5(var result : real); forward;
procedure Level6(var result : real); forward;
procedure Primitive(var result : real); forward;


procedure GetExp(var result: real);
begin
    GetToken;
    if length(token) <> 0 then Level1(result) else Serror(3)
end;

procedure Level1;
var hold : real;
   temp : Ttype;
   slot : integer;
   TempToken : str80;
begin
    if Toktype = Variable then
       begin
       {save old token}
       temptoken := token;
       temp := toktype;
       slot := ord(upcase(token[1]))-ord('A');
       GetToken;  {see if there is an = for assignment}
       if token[1] <> '=' then  {restore}
          begin
          Putback;
          token := temptoken;
          toktype := temp;
          level2(result)
          end
       else {is assignment}
            begin
            Gettoken;
            Level2(result);
            vars[slot] := result;
            end;
       end
   else Level2(result)
end; {Level1}


procedure Level2;
var op : char;
   hold : real;
begin
    Level3(result);
    op := token[1];
    while ((op='+') or (op='-')) do
          begin
          Gettoken;
          Level3(hold);
          arith(op, result, hold);
          op := token[1]
          end;
end; {Level2}

procedure Level3;
var op : char;
   hold : real;

begin
    Level4(result);
    op := token[1];
    while ((op='*') or (op='/')) do
          begin
          Gettoken;
          level4(hold);
          arith(op, result, hold);
          op := token[1]
          end;
end; {Level3}

procedure Level4;
var hold : real;
begin
    Level5(result);
    if token[1] = '^' then
       begin
       GetToken;
       Level4(hold);
       arith('^',result, hold);    {exponent}
       end
end;

procedure Level5;
var op : char;
begin
    op := ' ';
    if ((tokType=Delimiter) and ((token[1]='+') or (token[1]= '-'))) then
       begin  {unary plus or minus}
              op := token[1];
              Gettoken
       end;
    Level6(result);
    if op='-' then result := -result
end; {level5}

procedure Level6;
begin
    if (token[1]='(') and (Toktype=Delimiter) then
       begin {parenthesized expression}
       GetToken;
       Level2(result);
       if token[1]<>')' then Serror(2);  {unbalanced}
       GetToken;
       end
    else Primitive(result);
end; {Level6}


procedure Primitive;
begin
    if TokType=Number then val(token, result, code)
    else if TokType=Variable then result := FindVar(token)
    else serror(1);
    GetToken
end; {Primitive}




{**************************  Main Test body  ******************}
begin
    for t := 0 to 25 do vars[t] := 0;  {initialize variables}
    repeat
          t := 1;
          write('  Enter an expression  (quit to stop) : ');
          readln(prog);
          prog := prog + '$';
          GetExp(result);
          writeln(result);
    until prog = 'quit$';
end.