🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.pascal.borland
Article #586

Re: Calculator

#586
From: Osmo Ronkanen
Date: Sun, 21 Sep 1997 00:00
319 lines
9013 bytes
In article <3422EC5A.5B3E@club-internet.fr>,
Timomai  <timomai@club-internet.fr> wrote:
>Dr Zeed wrote:
>> 
>> Hello!
>> 
>> I just started with Pascal.. now I got a little problem! I want to do a
>> Calculator.. I want the calculator to look like this:
>> 
>> 3+4+5+9 [enter]
>> Answer: 21
>> 
>> I want to be able to use +,-,*,/ and write many numbers on the same row. I
>> hope someone can help me, maybe you know a good idea...
>> 
>> Dr_Zeed@hotmail.com
>Hi,
>
>	That is a very hard problem in Pascal to solve. You have 2 solutions :
>   1 you take a such function from a book or from a website
>   2 you write it
>
>        If you decide to write it, you should be a good programmer in
>Pascal. (you have to know pointers...) So, if you want to have your
>function now, it is better to download it. But it can be a good exercise
>to write it. If you want, i can explain you better how to write such
>function. For instance, i wrote, 2 years ago, a such function which
>recognizes more thatn + \ * and -, and which can derivate the function
>given by the string..

Well there is little need for pointers. Sure one can form an expression
tree and calculate it but the same routine that builds the tree can
instead return the value directly. Expression trees are probably useful
when one needs to calculate same value repeatedly.

Here is part of an earlier message by me in slightly modified form:

Basic is an interpreted language, so the EVAL can just use the
interpreter to do the job. Pascal on the other hand is compiled and
there is no way to use any interpreter. For example the compiled Pascal
program has no knowledge of variable names. One has to write the
procedures oneself.

A good way to do is to write a recursive parser. First begin with the
syntax of the expression, like:

expression = term {+ | - term }
term = factor {* | / factor }
factor = number | '(' expression ')'

Then the rest is just coding. Here is an example. To use it just set
variables, like

v['X']:=1;
v['Y']:=5;
v['Z']:=7.3;

Or you could use

vl['x']:=1; etc.
 ^

Then call the routine

res:=value('2*x+(5*y)/z')

Use Errorposition>0  (or errocode>0) to detect errors.
(Errorcode=1 in case of syntax errors, in case of floating point and
stack errors it is as specified in TP manual. If the line is 255
characters long then errocode:=255)

The routine does not support raising to power or functions. If you want
to add them, first change the syntax in comments and then the code.

If you need longint version then just change real to longint (uyou can
even put type real=longint in the _implemntation_ part and change
"/" to "DIV" (in code, not inside quotes)


Unit Eval;

Interface


{$IFOPT N+}
  Type Real=Double; {or extended, or single. Make sure that it is the }
                    {type as the variables you use }
{$ENDIF}

Function Value(const str:string):real;

var v:array['A'..'Z'] of real;              {  Variables used in evaluation }
    vl:array['a'..'z'] of real absolute v;
                          
{ You can change this to a simple array of values. In this case the
  types do not need to be exactly same but one has to change the values
  directly to the array }

var errorcode:byte;
    errorposition:integer;

Implementation

{ Set jump and longjump routines for error handling. Setjump returns
  always value TRUE. When Longjump is called, the program returns from the
  previous setjump call, but so that the value returned is FALSE. (Note
  the numeric equivalents of those are just the opposite of those used
  in C)

  for Turbo pascals before 6.0 use external assembler routines

}


Var JRec:record
            sp_:word;
            bp_:word;
            case Boolean of
                 false: (ip_,cs_:word);
                 true:  (RetAddr:Pointer);
         End;


Function SetJump:Boolean; far; Assembler;
          Asm
          Pop JRec.ip_
          Pop JRec.cs_
          Mov JRec.Bp_,bp
          Mov JRec.Sp_,sp
          Mov al,1
          Jmp JRec.RetAddr
          End;

Procedure LongJump; far; Assembler;
          Asm
          Mov bp,Jrec.bp_
          Mov sp,Jrec.sp_
          xor ax,ax
          Jmp Jrec.RetAddr
          end;


{
  Error handling routines to get back from runtime error: 
  division by zero, overflow. Jumping out from Exitprocedure is not
  something from Borland manual, but it seems to work
}


var ExitSave:pointer;

Procedure ExitP; Far;
Begin
  ExitProc:=ExitSave;
  If (ErrorAddr<>Nil) then begin
     errorcode:=Exitcode;
     LongJump;
  End;
End;



Procedure SetErrorRoutine;
Begin
  If ExitProc<>@ExitP Then Begin
    ExitSave:=ExitProc;
    ExitProc:=@ExitP;
  End;
End;



Procedure RemoveErrorRoutine;
Begin
  ExitProc:=ExitSave;
End;

{ Function to convert a sting to upper case. Used in MOD. }

function UpcaseStr(st:string): string;
var i:integer;
begin
  for i:=1 to length(st) do st[i]:=upcase(st[i]);
  UpcaseStr:=st;
End;

var st:string;
    index:integer;

Procedure SkipBlank;
begin
  while st[index]=' ' do inc(index);
End;

(* Syntax:

       expression = term {'+'|'-' term}
       term       = factor {'*'|'/' factor }
       factor     = {'+'| '-'}  ( number | '(' expression ')' | variable )

       number     = a real value
       variable   = A..Z


*)


Function term:real; forward;
Function factor:real; forward;

Function Expression:real;
var x:real;
begin
  x:=term;
  repeat
    SkipBlank;
    case st[index] of
         '+': begin inc(index); x:=x+term; End;
         '-': begin inc(index); x:=x-term; End;
         else begin expression:=x; exit; end;
    End;
  until false;
End;


Function term:real;
var x:real;
begin
  x:=factor;
  repeat
    SkipBlank;
    case upcase(st[index]) of
         '*': begin inc(index); x:=x*factor; end;
         '/': begin inc(index); x:=x/factor; end;
        else  begin term:=x; exit; end;
    End;
 until false;
End;


Procedure ValCopy(const st:string; first, count:integer; var x:real;
                  var error:integer);
begin
  val(copy(st,first,count),x,error);
End;


Function factor:real;
var x:real;
    err,err2:integer;
    s:-1..1;
begin
  if sptr<700 then begin
     errorcode:=202;
     longjump;
  End;
{  writeln(sptr);}
  SkipBlank;
  s:=1;
  while (st[index]='-') or (st[index]='+') do begin
      if st[index]='-' then s:=-s;
      inc(index);
      skipblank;
  End;
  case st[index] of
       '0'..'9': begin
                     valcopy(st,index,255,x,err);
                     if err=0 then longjump;         { Just in case }
                     valcopy(st,index,err-1,x,err2);
                     inc(index,err-1);
                     if err2>0 then longjump;
                   End;
         '(':      begin
                     inc(index);
                     x:=expression;
                     SkipBlank;
                     if st[index]<>')' then longjump;
                     inc(index);
                   End;
        else if upcase(st[index]) in ['A'..'Z'] then begin
                     x:=v[upcase(st[index])];
                     inc(index);
             end else longjump;
  End;
  factor:=x*s;
End;


Function Value(const str:string):real;
Begin
  if length(str)=255 then begin 
                             errorposition:=255; 
                             errorcode:=255;
                             Value:=0;
                             exit; 
                           end;
  st:=str+#0;
  errorcode:=0;
  errorposition:=0;
  if SetJump then begin
    SetErrorRoutine;
    index:=1;
    value:=expression;
    if index<=length(str) then longjump;
  End
  else begin
         value:=0;
         errorposition:=index;
         if errorcode=0 then errorcode:=1;
       End;
  RemoveErrorRoutine;
End;

begin
  fillchar(v,sizeof(v),#0);
end.

 
Technical Details
Message-ID: <602oeu$f8g@kruuna.Helsinki.FI>
Path: arc1-ca.pugleaf.net!unknown!not-for-mail
References: <01bcc51b$3c593780$a65ac6c2@anders> <3422EC5A.5B3E@club-internet.fr>

Show Full Headers
Complete Article Headers
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: 10261c,173f658d4bc382ca
X-Google-Attributes: gid10261c,public
X-Google-Thread: fea06,173f658d4bc382ca
X-Google-Attributes: gidfea06,public
X-Google-Thread: 111659,173f658d4bc382ca
X-Google-Attributes: gid111659,public
X-Google-Thread: 102601,173f658d4bc382ca
X-Google-Attributes: gid102601,public
X-Google-Thread: 10263e,173f658d4bc382ca
X-Google-Attributes: gid10263e,public
From: ronkanen@cc.helsinki.fi (Osmo Ronkanen)
Subject: Re: Calculator
Date: 1997/09/21
Message-ID: <602oeu$f8g@kruuna.Helsinki.FI>
X-Deja-AN: 274208135
References: <01bcc51b$3c593780$a65ac6c2@anders> <3422EC5A.5B3E@club-internet.fr>
X-Complaints-To: usenet@oravannahka.Helsinki.FI
X-Trace: oravannahka.Helsinki.FI 874833183 6667 (None) 128.214.205.14
Organization: University of Helsinki
Newsgroups: comp.lang.pascal.borland,comp.lang.pascal.misc,ctdl.lang.pascal,maus.lang.tpascal,sk.comp.pascal