A calculator in MiniBasic
10 rem Calculator, by Ed Davis, 2023
20 rem Operators in priority order
30 rem
40 rem ^ power
50 rem unary -, +
60 rem *, /, mod \ (integer division)
70 rem +, -
80 rem =, <>, >, >=, <, <=
90 rem unary not
100 rem and
110 rem or
120 rem
130 rem All operators are left associative, except power, which is right associative
140 rem
150 rem functions
160 rem abs()
170 rem (Easy to add more)
180 rem
190 rem A few simple test cases:
200 rem Expression: 12/+3*4
210 rem 16
220 rem Expression: 4^3^2
230 rem 262144
240 rem Expression: -4^-3^-2
250 rem -.8572439828530728
260 rem Expression: 10 - 5 - 3
270 rem 2
280 rem
290 dim nstack#(30): rem stack for results in expressions
300 dim pstack(30)
310 rem
320 input "Expression: ", inputst$
330 if inputst$ = "" then end
340 gosub 990
350 gosub 500
360 print retval#
370 goto 320
380 rem
390 rem expression processing
400 rem
410 if tok$ <> "(" then print "Paren Expr: Expecting '(', found:"; tok$: return
420 gosub 990: rem skip the "("
430 prec = 0
440 gosub 520: rem get the expression
450 if tok$ <> ")" then print "Paren Expr: Expecting ')', found:"; tok$: return
460 gosub 990: rem skip closing ")"
470 return
480 rem
490 rem expression processing - external entry point
500 nsp = 0: retval# = 0: prec = 0: minprec = 0
510 rem expression processing - internal entry point
520 gosub 880
530 n# = 0
540 minprec = prec
550 rem
560 rem handle numeric operands - numbers and unary operators
570 if tok$ = "-" then gosub 990: prec = 7: gosub 520: n# = -retval#: goto 660
580 if tok$ = "+" then gosub 990: prec = 7: gosub 520: n# = retval#: goto 660
590 if tok$ = "not" then gosub 990: prec = 3: gosub 520: n# = not retval#: goto 660
600 if tok$ = "(" then gosub 410: n# = retval#: goto 660
610 if tok$ = "abs" then gosub 990: gosub 410: n# = abs(retval#): goto 660
620 if toktype$ = "number" then n# = num#: gosub 990: goto 660
630 rem
640 print "syntax error: expecting an operand, found: ", tok$: goto 840
650 rem
660 rem while binary operator and precedence of tok$ >= minprec
670 rem
680 if minprec <= 1 and tok$ = "or" then gosub 990: prec = 2: gosub 520: n# = n# or retval#: goto 660
690 if minprec <= 2 and tok$ = "and" then gosub 990: prec = 3: gosub 520: n# = n# and retval#: goto 660
700 if minprec <= 4 and tok$ = "=" then gosub 990: prec = 5: gosub 520: n# = n# = retval#: goto 660
710 if minprec <= 4 and tok$ = "<" then gosub 990: prec = 5: gosub 520: n# = n# < retval#: goto 660
720 if minprec <= 4 and tok$ = ">" then gosub 990: prec = 5: gosub 520: n# = n# > retval#: goto 660
730 if minprec <= 4 and tok$ = "<>" then gosub 990: prec = 5: gosub 520: n# = n# <> retval#: goto 660
740 if minprec <= 4 and tok$ = "<=" then gosub 990: prec = 5: gosub 520: n# = n# <= retval#: goto 660
750 if minprec <= 4 and tok$ = ">=" then gosub 990: prec = 5: gosub 520: n# = n# >= retval#: goto 660
760 if minprec <= 5 and tok$ = "+" then gosub 990: prec = 6: gosub 520: n# = n# + retval#: goto 660
770 if minprec <= 5 and tok$ = "-" then gosub 990: prec = 6: gosub 520: n# = n# - retval#: goto 660
780 if minprec <= 6 and tok$ = "*" then gosub 990: prec = 7: gosub 520: n# = n# * retval#: goto 660
790 if minprec <= 6 and tok$ = "/" then gosub 990: prec = 7: gosub 520: n# = n# / retval#: goto 660
800 if minprec <= 6 and tok$ = "\" then gosub 990: prec = 7: gosub 520: n# = n# \ retval#: goto 660
810 if minprec <= 6 and tok$ = "mod" then gosub 990: prec = 7: gosub 520: n# = n# mod retval#: goto 660
820 if minprec <= 8 and tok$ = "^" then gosub 990: prec = 8: gosub 520: n# = n# ^ retval#: goto 660
830 rem
840 retval# = n#: gosub 940
850 return
860 rem
870 rem for expressions: save the current context
880 nsp = nsp + 1
890 nstack#(nsp) = n#
900 pstack(nsp) = minprec
910 return
920 rem
930 rem for expressions: restore the current context
940 n# = nstack#(nsp)
950 minprec = pstack(nsp)
960 nsp = nsp - 1
970 return
980 rem
990 tok$ = "": toktype$ = ""
1000 if left$(inputst$, 1) = " " then inputst$ = mid$(inputst$, 2): goto 1000
1010 if inputst$ = "" then return
1020 rem
1030 tok$ = left$(inputst$, 2)
1040 toktype$ = "punct"
1050 if instr("|<=|>=|<>|", "|" + tok$ + "|") > 0 then inputst$ = mid$(inputst$, 3): return
1060 tok$ = left$(inputst$, 1)
1070 rem
1080 if instr("()*+-/<=>\^", tok$) > 0 then inputst$ = mid$(inputst$, 2): return
1090 if tok$ >= "a" and tok$ <= "z" then gosub 1220: return
1100 if tok$ >= "0" and tok$ <= "9" then gosub 1140: return
1110 print "What?"; tok$
1120 return
1130 rem
1140 tok$ = "": toktype$ = "number"
1150 if (left$(inputst$, 1) >= "0" and left$(inputst$, 1) <= "9") or left$(inputst$, 1) = "." then tok$ = tok$ + left$(inputst$, 1):inputst$ = mid$(inputst$, 2):goto 1150
1160 rem tok$ = tok$ + left$(inputst$, 1)
1170 rem inputst$ = mid$(inputst$, 2)
1180 rem wend
1190 num# = val(tok$)
1200 return
1210 rem
1220 tok$ = "": toktype$ = "ident"
1230 if left$(inputst$, 1) >= "a" and left$(inputst$, 1) <= "z" then tok$ = tok$ + left$(inputst$, 1):inputst$ = mid$(inputst$, 2):goto 1230
1240 rem tok$ = tok$ + left$(inputst$, 1)
1250 rem inputst$ = mid$(inputst$, 2)
1260 rem wend
1270 return
1280 rem