As BASIC has numerous diverse realizations, the question arises, which of its many incarnations shall we use as ‘standard’ to implement a calculator? I believe that to be QuickBASIC. It was very popular on MS DOS in the pre-Windows times, and sufficiently advanced to have influenced strongly most if not nearly all BASIC dialects in use today – therefore QuickBASIC is rather representative of the BASICs in general. The nominal standard, ANSI/ISO BASIC, is much less popular, and eventually ineffective as a standard.
The following program runs under QuickBASIC and compatible compilers/interpreters. With small changes it can be adapted to a number of other dialects.
The main, initial, part of the program runs an indefinite loop reading a string
s$ (as a way of implicit data typing, the names of string-valued variables and functions must end with a $-sign) and passing it to the sub-routine
evalrpn for evaluation. Before calling
evalrpn, all possible occurrences of tab charactes get replaced by spaces, and then all leading and trailing spaces are removed from
s$, so that we can tell if the string is empty and avoid processing it.
Evalrpn returns a flag value
y – true when the RPN expression evaluation is successful. In that case it also returns the value
r of the expression. The main program checks
y and prints either
r or an error message.
Evalrpn starts by adding a space to the string it receives, for the sake of uniformity of extracting tokens from it. Then it creates a numeric array
stk of large enough size – with respect to the string
s$ – to serve as a stack of intermediate values in the course of evaluating the RPN expression. (One can notice that, at the very beginning of the program, we have set a base 1 for array subscripts – the default 0 turns to be a bit less convenient.)
Nst counts the actual number of stored values in
The expression is evaluated by repeatedly extracting a token from
s$ and acting on it accordingly. Extraction is delegated to the
gettoken sub-routine, which returns
tk$ and the shortened
tk$ appears to be one of the four arithmetic operators and
stk contains the two arguments needed to apply the operator, the corresponding computation is carried out. (Here and elsewhere
: is used to join two or more program sentences in a single line.) Otherwise, we call
getnumber to check if
tk$ is a number. Succeeding in either of the two cases results in storing the obtained value
x on the stack. The process is repeated until either
s$ becomes empty or an error occurs due to an invalid token or an insufficient number of operator arguments in the stack. Once the loop ends, an additional check is done to ensure that there is only one value left on the stack. Since
x, the parameter of
evalrpn, is always a copy of the topmost value on the stack,
evalrpn's caller does receive the value that it needs.
getnumber, we first ‘detach’ a possible arithmetic sign from the front of the string, keeping a mark of what the sign was in
sg. Then the string is scanned character by character to ensure that it contains a valid representation of a number: the flag
yes is set when at least one digit, at most one decimal dot and no other characters are found. If
yes holds true, the built-in function
val is employed for actually extracting the numeric value from the string.
The built-in function
mid$, used throughout the program, is a means to refer to a substring or a single character in a given string. As may be observed, this also allows changing the respective part. Another function,
instr, locates where, if at all, a string is included as a substring within another.
As written, the program keeps reading input lines forever (unless, of course, forcefully interrupted by the user). This is because there is no way of detecting the end of the standard input stream. Some implementations of BASIC solve this issue in their own ways.
option base 1 do line input s$ for i=1 to len(s$) if mid$(s$,i,1)=chr$(9) then mid$(s$,i,1) = " " next i s$ = ltrim$(rtrim$(s$)) if len(s$)>0 then call evalrpn(s$,r,y) if y then print r else print "error" end if loop sub evalrpn(so$,x,yes) s$ = so$+" " dim stk(len(s$)\2) : nst = 0 do call gettoken(s$,tk$) yes = len(tk$)=1 and instr("+-*/",tk$)>0 and nst>=2 if yes then x = stk(nst-1) : y = stk(nst) : nst = nst-2 select case tk$ case "+" : x = x+y case "-" : x = x-y case "*" : x = x*y case "/" : x = x/y end select else call getnumber(tk$,x,yes) end if if yes then nst = nst+1 stk(nst) = x end if loop until not yes or s$="" yes = yes and nst=1 end sub sub gettoken(s$,tk$) i = instr(s$," ") tk$ = mid$(s$,1,i-1) for i=i+1 to len(s$) if mid$(s$,i,1)<>" " then exit for next i s$ = mid$(s$,i) end sub sub getnumber(so$,x,yes) c$ = mid$(so$,1,1) sg = 1 : s$ = so$ if c$="+" or c$="-" then if c$="-" then sg = -1 s$ = mid$(so$,2) end if d = 0 : t = 0 for i=1 to len(s$) k = instr(".0123456789",mid$(s$,i,1)) if k=0 then exit for if k>1 then d = d+1 else t = t+1 next i yes = k>0 and d>0 and t<=1 if yes then x = sg*val(s$) end sub end