Download an Apple II disk image which includes Calc (.tgz).
Download the same disk image as a ShrinkIt disk archive (.sdk).
Calc: A scientific calculator in Pascal.
Calc is a command-line, scientific calculator written in Kyan Pascal for the Apple II computer. The program is supplied as a ProDOS system file, and the source code, shown below, is included on the disk.
Calc: Features.
The program performs arithmetic on numbers using either decimal (e.g. 123.456) or scientific (e.g. 1.23456e2) notation. Numbers may be in the range ±9.9999999999e±99. The usual arithmetic operation are supported: +, -, *, /, ^, and !. The operations have the expected precedence, but you can use parentheses to change the order. The built-in functions are below. The program responds to several commands, shown below, may be abbreviated to a single letter.
Calc: Design.
The program was originally designed to evaluate integer expressions for a roff-style formatting program. It was later adapted to evaluating floating-point expressions for numerical integration software. This latest version of the program implements the following grammar:
<expr> ::= <term> + | - <term> <term> ::= <factor> * | / <factor><factor> ::= <value> ^ <factor> | <value> ! <value> ::= [ + | - ] ( <expr> ) | <result> | <real><result> ::= <name> ( <expr> ) <name> ::= <letter> { <letter> | <digit> } <real> ::= <number> [ . ] [ <number> ] [ e <scale> ]<number> ::= <digit> { <digit> } <scale> ::= [ + | - ] <number> <digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9The result is to make addition and subtraction have equally low precedence, while multiplication and division are higher. Exponentiation and factorial are yet higher, and parenthetic expressions (including function calls) are highest.
Calc: Code.
The program implements the four levels of precedence using four nested functions: Expression, Term, Factor and Value. Evaluation proceeds from left to right for all but exponentiation. Instead, Factor is called recursively, causing an expression like 2^3^4 to be interpreted as 2^(3^4).
{28-Apr-05; J. Matthews; GPL; no warranty}program Calc;const maxString = 80; bang = '!'; dot = '.'; minus = '-'; parOpen = '('; parClose = ')'; plus = '+'; power = '^'; slash = '/'; space = ' '; star = '*';type String = array [1..maxString] of char;var s : String; i, len, width, dec : integer; t, nul, tab : char; sn, done : boolean;procedure LowerCase (var s : String; len : integer);var i : integer;begin for i := 1 to len do if s[i] in ['A'..'Z'] then s[i] := chr(ord(s[i]) + 32)end; {LowerCase}function IsDigit (c : char) : boolean;begin IsDigit := c in ['0'..'9', dot]end; {IsDigit}function ParseInt (var s : String; var i : integer) : integer;var n, sign : integer;begin while (s[i] = space) or (s[i] = tab) do i := i + 1; if (s[i] = minus) then sign := -1 else sign := 1; if (s[i] = plus) or (s[i] = minus) then i := i + 1; n := 0; while s[i] in ['0'..'9'] do begin n := 10 * n + ord(s[i]) - ord('0'); i := i + 1 end; ParseInt := sign * nend; {ParseInt}function ParseReal (var s : String; var i : integer) : real;var v : real; j, decimal, exponent : integer;begin while s[i] in [space, tab] do i := i + 1; v := 0.0; decimal := 0; exponent := 0; while (isdigit(s[i])) do begin {parse decimal number} if s[i] = dot then decimal := i else v := 10 * v + (ord(s[i]) - ord('0')); i := i + 1; end; if decimal > 0 then for j := 1 to (i - decimal - 1) do v := v / 10; if s[i] = 'e' then begin {handle scientific notation} i := i + 1; exponent := ParseInt(s, i); if exponent > 0 then for j := 1 to exponent do v := v * 10 else if exponent < 0 then for j := 1 to abs(exponent) do v := v / 10 end; ParseReal := vend; {ParseReal}function DoDivision (x, y : real) : real;begin if y <> 0 then DoDivision := x / y else begin Writeln('Please don''t divide by zero!'); DoDivision := 0 endend; {DoDivision}function DoFactorial (x : real) : real;var v : real; i, j : integer;begin v := 1; j := abs(trunc(x)); if (j = 0) or (j = 1) then DoFactorial := 1 else if j > 69 then Writeln('Arithemtic overflow.') else for i := j downto 2 do v := v * i; if x < 0 then DoFactorial := -v else DoFactorial := vend; {DoLn}function DoLn (x : real) : real;begin if x > 0.0 then DoLn := ln(x) else begin Writeln('Natural log argument must be positive.'); DoLn := 0.0 endend; {DoLn}function DoPower (b, e : real) : real;var p : integer; u, v : real;begin v := 1; u := b; p := trunc(e); if ((e - p) < 0.00001) and (p > 0) then begin {handle positive integral exponents} while p > 0 do begin while not odd(p) do begin p := p div 2; u := sqr(u) end; p := p - 1; v := u * v end; DoPower := v end {else use natural logarithm} else DoPower := exp(e * DoLn(b)) end; {Power}function DoSqrt (x : real) : real;begin if x > 0.0 then DoSqrt := sqrt(x) else begin Writeln('Square root argument must be positive.'); DoSqrt := 0.0 endend; {DoSqrt}procedure SkipToken (var s : String; var i : integer);begin while s[i] in ['a'..'z'] do i := i + 1;end; {SkipToken}function SkipSpace (var s : String; var i : integer) : char;begin while (s[i] in [space, tab]) do i := i + 1; SkipSpace := s[i]end;function Expression (var s : string; var i : integer) : real;var v : real; t : char; j : integer; function Term (var s : String; var i : integer) : real; var v : real; t : char; function Factor (var s : String; var i : integer) : real; var v : real; t : char; function Value (var s : String; var i : integer) : real; var v : real; t : char; begin v := 0.0; t := SkipSpace(s, i); if t = parOpen then begin {nested expression} i := i + 1; v := Expression(s, i); if (SkipSpace(s, i) = parClose) then i := i + 1 else Writeln('Missing parenthesis in expression.') end else if t = plus then begin {unary plus} i := i + 1; v := Value(s, i) end else if t = minus then begin {unary minus} i := i + 1; v := -Value(s, i) end else if IsDigit(t) then begin {real number} v := ParseReal(s, i) end else if t in ['a','c','e','l','p','s','t'] then begin {function} j := i; SkipToken(s, i); if s[j] = 'a' then if s[j+1] = 'b' then v := abs(Value(s, i)) else if s[j+1] = 't' then v := arctan(Value(s, i)); if s[j] = 'c' then v := cos(Value(s, i)); if s[j] = 'e' then if s[j+1] = 'x' then v := exp(Value(s, i)) else v := exp(1); {e} if s[j] = 'l' then v := DoLn(Value(s, i)); if s[j] = 'p' then v := 3.14159265358979; if s[j] = 's' then if s[j+1] = 'i' then v := sin(Value(s, i)) else if s[j+1] = 'q' then v := DoSqrt(Value(s, i)); if s[j] = 't' then begin v := Value(s, i); v := sin(v)/cos(v) end end else Writeln('Syntax error.'); Value := v end; {Value} begin v := Value(s, i); t := SkipSpace(s, i); while t in [bang, power] do begin i := i + 1; case t of bang: v := DoFactorial(v); power: v := DoPower(v, Factor(s, i)) end; t := SkipSpace(s, i) end; Factor := v end; {Factor} begin v := Factor(s, i); t := SkipSpace(s, i); while t in [star, slash, power] do begin i := i + 1; case t of star: v := v * Factor(s, i); slash: v := DoDivision(v, Factor(s, i)); power: v := DoPower(v, Factor(s, i)) end; t := SkipSpace(s, i) end; Term := v end; {Term}begin v := Term(s, i); t := SkipSpace(s, i); while t in [plus, minus] do begin i := i + 1; case t of plus: v := v + Term(s, i); minus: v := v - Term(s, i) end; t := SkipSpace(s, i) end; Expression := vend; {Expression}function Length (var s : String) : integer;var i : integer;begin i := maxString; while (s[i] = space) and (i <> 1) do i := i - 1; if (s[i] = space) and (i = 1) then i := 0; Length := i;end; {Length}procedure SetDecimal (var s : String; var i, dec : integer);begin SkipToken(s, i); dec := trunc(ParseReal(s, i)); if dec > 15 then dec := 15; if dec < 0 then dec := 0; Writeln('Decimal precision set to ', dec);end; {SetDecimal}procedure SetWidth (var s : String; var i, width : integer);begin SkipToken(s, i); width := trunc(ParseReal(s, i)); if width > 80 then width := 80; if width < 0 then width := 0; Writeln('Decimal width set to ', width);end; {SetWidth}procedure SetNotation (var sn : boolean);begin sn := not(sn); if sn then Writeln('Scientific notation on.') else Writeln('Scientific notation off.')end; {SetNotation}procedure Format (var s : String; var i : integer);begin if sn then Writeln(Expression(s, i):width) else Writeln(Expression(s, i):width:dec)end; {Format}function FileCheck : boolean;beginFileCheck := false;#a jsr _mli db $C4 ;Get_File_Info dw finfo bne fexit ldy #5 ;result offset lda #1 ;true sta (_sp),yfexit equ *#end; {FileCheck}#afname str "calc.txt"finfo db 10 dw fname ds 15#procedure Execute;var s : String; i, len : integer; f : file of char;begin if FileCheck then begin reset(f, 'calc.txt'); while not(eof(f)) do begin i := 1; repeat s[i] := f^; i := i + 1; get(f) until f^ = chr(13); get(f); s[i] := nul; for i := 1 to i - 1 do Write(s[i]); Write(' = '); i := 1; t := SkipSpace(s, i); Format(s, i) end end else Writeln('File "calc.txt" not found.')end; {Execute}procedure Help;begin Writeln; Writeln('+--------------------+'); Writeln('| Welcome to Calc v1 |'); Writeln('+--------------------+'); Writeln; Writeln('A scientific calculator by J. Matthews'); Writeln; Writeln('Operators (increasing precedence):'); Writeln(' +, -, *, /, !, ^, unary +/-, ()'); Writeln; Writeln('Functions & constants:'); Writeln(' sqrt(), ln(), exp(), atan()'); Writeln(' cos(), sin(), tan(), e, pi'); Writeln; Writeln('Commands (may be abbreviated):'); Writeln(' width #: set output field width to #'); Writeln(' decimal #: set the precision to #'); Writeln(' notation: toggle scientific notation'); Writeln(' xecute: execute the file "calc.txt"'); Writeln(' help: print this help screen'); Writeln(' quit: exit the program'); Writelnend; {Help}begin#a ; clear screen stx _t jsr $FC58 ldx _t# nul := chr(0); tab := chr(9); width := 12; dec:= 6; sn := false; done := false; Help; repeat Write('> '); Readln(s); len := Length(s); s[len + 1] := nul; {terminate string} if len > 0 then begin LowerCase(s, len); i := 1; t := SkipSpace(s, i); if t = 'd' then SetDecimal(s, i, dec) else if t = 'h' then Help else if t = 'n' then SetNotation(sn) else if t = 'q' then done := true else if t = 'w' then SetWidth(s, i, width) else if t = 'x' then Execute else Format(s, i) end until doneend. {Calc}Copyright 1986,2005 John B. Matthews
Distribution permitted under the terms of the GNU Public License.
Last updated 5-Jan-2019.