dos_compilers/Borland Turbo Pascal v3/CALC.INC
2024-07-03 16:09:46 -07:00

1582 lines
52 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{*************************************************************************}
{ MicroCalc Module 000 }
{ Last Modified : 7-22-85 }
{ }
{ Purpose : Miscellaneous utilities and commands. }
{ }
{*************************************************************************}
procedure Help; forward; { procedure Help is forward declared in the int- }
{ erest of keeping all miscellaneous routines }
{ grouped in one module. }
{ procedure Center centers the string passed as the paramater on the }
{ screen. NOTE : This routine assumes an 80 column screen. }
procedure Center(S : AnyString);
var
I : Integer;
begin
for I := 1 to ((80-Length(S)) div 2) do
Write(' '); {Fill beginning of line with spaces based on the length(S)}
writeln(S);
end; { end Sub-procedure Center }
{ procedure Welcome says Welcome and activates the help procedure if the }
{ user presses any key other than Carriage Return. }
procedure Welcome;
begin { procedure Welcome }
ClrScr;
GotoXY(1,9);
Center('Welcome to MicroCalc. A Turbo Pascal demonstration program');
Center('Press any key for help or <RETURN> to start');
GotoXY(40,12);
Read(Kbd,Ch);
{ If Ch entered does not equal a carriage return then call Help routine.}
if Ch <> ^M then Help;
end; { end procedure Welcome }
{ procedure Msg outputs the string, passed in as a paramater, to the }
{ screen. }
procedure Msg(S : AnyString);
begin
GotoXY(1,24);
ClrEol;
Write(S);
ClrEol;
end; { End procedure Msg }
{ The procedure Flash causes the string input as a paramater to flash by }
{ alternately writeing it to the screen in LowVideo and NormVideo. }
{ NOTES :
1. procedure Flash allows you to specify the column in which
the string will be written. A possible extension of the
routine could be to allow the user to specify the row also.
2. By setting the blink paramater to true or false the string
will flash or not flash. If Blink is true the string will
Flash until a key is pressed.
3. Upon exit of the procedure, the string will be written in
LowVideo regardless of the value of Blink }
procedure Flash(X : Integer; S : AnyString; Blink : boolean);
begin
NormVideo;
GotoXY(X,23);
Write(S);
if Blink then { Check value of Blink, if true then execute the repeat }
begin { until loop. }
repeat
GotoXY(X,23);
Blink := not Blink; { By modifying the value of Blink after }
if Blink then { each iteration of the loop, we can use }
NormVideo { Blink's value as a test for the appro- }
else { priate Video attribute value. }
LowVideo;
Write(S);
Delay(175);
until KeyPressed;
end;
LowVideo;
end; { End procedure Flash }
{ procedure IBMCh determines the extended scan code read in and returns }
{ a one character interpolation of the characters read in. }
procedure IBMCh(var Ch: Char);
begin
case Ch of
'H': Ch := ^E;
'P': Ch := ^X;
'M': Ch := ^D;
'K': Ch := ^S;
'S': Ch := #127;
'R': Ch := ^V;
'G', 'I', 'O',
'Q': Ch := #00;
end;
end; { End procedure IBMCh }
procedure Auto; { Toggles automatic calculation mode on and off }
begin
AutoCalc:=not AutoCalc;
if AutoCalc then Flash(60,'AutoCalc: ON ',false)
else Flash(60,'AutoCalc: OFF',false);
end;
{*************************************************************************}
{ MicroCalc Module 001 }
{ Last Modified 7-22-85 }
{ }
{ Purpose : initialize Spread sheet, redraw screen, and clear current }
{ spread sheet }
{ }
{*************************************************************************}
{ procedure Grid Displays the entire spread sheet grid and command line. }
procedure Grid;
var
I : Integer;
Count : Char;
begin
NormVideo;
For Count := 'A' to FXMax do { Display Column Names (A..FXMax) }
begin
GotoXY(XPos[Count],1);
Write(Count);
end;
GotoXY(1,2);
for I := 1 to FYMax do { Display Row numbers (1..FYMax) }
writeln(I:2);
LowVideo;
if AutoCalc then { Display Status line }
Flash(60,'AutoCalc: ON' ,false)
else
Flash(60,'AutoCalc: OFF',false);
Flash(33,' Type / for Commands',false);
end; { End procedure Grid }
{ procedure Init initializes the values of each of the cells in the grid. }
{ NOTE : Initializing variables in TURBO PASCAL is very important. }
procedure Init;
var
I : ColumnName;
J : Integer;
LastName : string[2];
begin
for I := 'A' to FXMAX do { Traverse grid Column by Column - Row }
begin { by Row, initializing each cell }
for J := 1 to FYMAX do
begin
with Sheet[I,J] do { Access record representing this cell }
begin
CellStatus := [Txt]; { Default cell attribute }
Contents := ''; { Value of default cell attribute }
Value := 0; { Default value of numeric field }
DEC := 2; { Default number of decimals }
FW := 10; { Default field width }
end;
end;
end;
AutoCalc := True; { AutoCalc defaults to True or ON }
FX:='A'; FY:=1; { Field A1 is default current field }
end; { End procedure Init }
{ procedure Clear clears the current worksheet. }
procedure Clear;
begin
NormVideo;
GotoXY(1,24); ClrEol;
Write('Clear this worksheet? (Y/N) ');
repeat
Read(Kbd,Ch)
until Upcase(Ch) in ['Y','N'];
Write(Upcase(Ch));
if UpCase(Ch)='Y' then
begin
ClrScr;
Init;
Grid;
end;
end; { End procedure Clear }
{*************************************************************************}
{ MicroCalc Module 002 }
{ Last Modified 7-22-85 }
{ }
{ Purpose : Cursor movement whithin the spread sheet }
{ }
{*************************************************************************}
{ procedure DisplayType outputs the current cell it's type and if the }
{ cell's type is a formula the formula is also output. }
procedure DisplayType;
begin
with Sheet[FX,FY] do
begin
GotoXY(1,23);
Write(FX,FY:2,' ');
if Formula in CellStatus then { Determine Type of Cell }
write('Formula ')
else
if Constant in CellStatus then
Write('Numeric ')
else
if Txt in CellStatus then
Write('Text ');
GotoXY(1,24); ClrEol;
if Formula in CellStatus
then Write(Contents);
end;
end; { End procedure DisplayType }
{*************************************************************************}
{ The following procedures move between the Cells on the calc sheet. Each }
{ Cell has an associated record containing its X,Y coordinates and data. }
{ See the type definition for "Cell". }
{*************************************************************************}
{ procedure GotoCell goes to the cell indicated by the procedures }
{ paramaters and outputs it's current value. }
procedure GotoCell(GX: ColumnName; GY: Integer);
begin
with Sheet[GX,GY] do { Get specified record (i.e. cell) }
begin
NormVideo;
GotoXY(XPos[GX],GY+1); { Go to leftmost column of specified cell }
Write(' '); { Clear the cell }
GotoXY(XPos[GX],GY+1);
if Txt in CellStatus then
Write(Contents)
else { cell does not contain text }
begin
if DEC >= 0 then { check if number of decimal spaces is }
Write(Value:FW:DEC) { specified else output in scientific }
else { notation. }
Write(Value:FW);
end;
DisplayType;
GotoXY(XPos[GX],GY+1);
end;
LowVideo;
end;
{ procedure LeaveCell simply writes the current cell's contents in }
{ LowVideo. }
procedure LeaveCell(FX : ColumnName; FY : Integer);
begin
with Sheet[FX,FY] do { Get record representing current cell }
begin
GotoXY(XPos[FX],FY + 1);
LowVideo;
if Txt in CellStatus then
Write(Contents)
else
begin
if DEC >= 0 then
Write(Value:FW:DEC)
else
Write(Value:FW);
end;
end;
end; { End procedure LeaveCell }
procedure Update;
var
UFX : ColumnName;
UFY : Integer;
begin
ClrScr;
Grid;
for UFX := 'A' to FXMax do
for UFY := 1 to FYMax do
if Sheet[UFX,UFY].Contents <> '' then
LeaveCell(UFX,UFY);
GotoCell(FX,FY);
end;
{ procedure MoveDown updates the current cell by calling procedure }
{ LeaveCell. It then moves the cursor down on position. Note : The repeat }
{ loop in this routine does the actuall moving of the cursor. A valid cell}
{ must be found before the cursor can be moved. The cell is valid if it's }
{ CellStatus is not in [OverWritten,Locked] . }
procedure MoveDown;
var
Start : Integer;
begin
LeaveCell(FX,FY);
Start := FY;
repeat
FY := FY + 1;
if FY > FYMax then
FY := 1;
until (Sheet[FX,FY].CellStatus * [OverWritten,Locked] = []) or (FY = Start);
if FY <> Start then
GotoCell(FX,FY);
end; { End procedure MoveDown }
{ procedure MoveUp updates the current cell, then moves the cursor up one }
{ position. The note for the last procedure applies here also. }
procedure MoveUp;
var
Start : Integer;
begin
LeaveCell(FX,FY);
Start := FY;
repeat
FY := FY - 1;
if FY < 1 then
FY := FYMax;
until (Sheet[FX,FY].CellStatus * [OverWritten,Locked] = []) or (FY = Start);
if FY <> Start then
GotoCell(FX,FY);
end; { End procedure MoveUp }
{ procedure MoveRight acts the same as the previous two procedures with }
{ the exception of the direction it moves the cursor. }
procedure MoveRight;
var
Start : ColumnName;
begin
LeaveCell(FX,FY);
Start := FX;
repeat
FX := Succ(FX);
if FX > FXMax then
begin
FX := 'A';
FY := FY + 1;
if FY > FYMax then
FY := 1;
end;
until (Sheet[FX,FY].CellStatus * [OverWritten,Locked] = []) or (FX = Start);
if FX <> Start then
GotoCell(FX,FY);
end; { End procedure MoveRight }
{ procedure MoveLeft acts the same as the previous three procedures with }
{ the exception of the direction it moves the cursor. }
procedure MoveLeft;
var
Start : ColumnName;
begin
LeaveCell(FX,FY);
Start := FX;
repeat
FX := Pred(FX);
if FX < 'A' then
begin
FX := FXMax;
FY := FY - 1;
if FY < 1 then
FY := FYMax;
end;
until (Sheet[FX,FY].CellStatus * [OverWritten,Locked] = []) or (FX = Start);
if FX <> Start then
GotoCell(FX,FY);
end; { End procedure MoveLeft }
{*************************************************************************}
{ MicroCalc Module 003 }
{ Last Modified : 7-22-85 }
{ }
{ PURPOSE: Load, Save and Print a spread sheet. }
{ Display on-line manual. }
{*************************************************************************}
{ function Exist returns a boolean value indicating the existence of
the file passed in as a paramater. }
function Exist(FileN : AnyString): boolean;
var
F : file;
begin
{$I-}
assign(F,FileN);
reset(F);
{$I+}
if IOResult <> 0 then
Exist := false
else
begin
Exist := true;
close(F);
end;
end; { End function Exist }
{ Procedure GetFileName reads a filename input by the user. The
filename's extension is passed in as the second paramater of the
procedure.
Note : As the procedure stands, the user can not use path names. }
Procedure GetFileName(var Line : AnyString; FileType : String3);
var
i : Integer;
begin
Line := '';
Read(Line);
clreol;
if Length(Line) > 8 then
begin
clreol;
Line[0] := chr(8);
Msg('Filename Truncated to : ' + Line);
end;
for i := 1 to Length(Line) do
begin
Line[i] := UpCase(Line[i]);
if not (Line[i] in ['A'..'Z','0'..'9'
(*,'!','#'..')','-',',','/','@','\','_','`','{','}'*)
]) then
Msg('Invalid Filename');
end;
if Line <> '' then
Line := Line + '.' + FileType;
end; { End Procedure GetFileName }
{ Procedure Save saves the current spreadsheet to a file.
Note : this routine attaches the extension 'MCS' to the filename. }
Procedure Save;
var
I : ColumnName;
J : Integer;
begin
NormVideo;
Msg('Save: Enter filename ');
GetFileName(Filename,'MCS');
if FileName <> '' then
begin
Assign(MCFile,FileName);
Rewrite(MCFile);
for I := 'A' to FXmax do
for J := 1 to FYmax do
write(MCfile,Sheet[I,J]);
Grid;
Close(MCFile);
LowVideo;
GotoCell(FX,FY);
end;
end; { End Procedure Save }
{ Procedure Load loads a spreadsheet from a file.
Note : the procedure gives the user two chances to enter the
correct filename. }
Procedure Load;
begin
NormVideo;
Msg('Load: Enter filename ');
GetFileName(Filename,'MCS'); { Get filename }
if (Filename <> '') then { Check if filename is legal }
if (not exist(FileName)) then { if filename not legal then }
repeat { prompt the user again. }
Msg('File not Found: Enter another filename ');
GetFileName(Filename,'MCS');
until exist(FileName) or (FileName = '');
if FileName <> '' then
begin
ClrScr;
Msg('Please Wait. Loading definition...');
Assign(MCFile,FileName);
Reset(MCFile);
for FX := 'A' to FXmax do { Read records from file }
for FY := 1 to FYmax do
read(MCFile,Sheet[FX,FY]);
FX := 'A';
FY := 1;
LowVideo;
UpDate;
end;
GotoCell(FX,FY);
end; { End Procedure Load }
{ Procedure Print prints the current spreadsheet to a file.
Note : a possible improvement might be to allow the user to specify
not only the left margin of the paper, but also the width
of the paper. Also allowing output to go directly to the
printer. }
Procedure Print;
var
I : ColumnName;
J, Count,
LeftMargin : Integer;
P : string[20];
MCFile : Text;
begin
NormVideo;
Msg('Print: Enter filename "P" for Printer> ');
GetFileName(Filename,'LST');
Msg('Left margin > ');
Read(LeftMargin);
if FileName = 'P.LST' then
FileName := 'LST';
Msg('Printing to: ' + FileName + '....');
Assign(MCFile,FileName);
Rewrite(MCFile);
For Count := 1 to 5 do
Writeln(MCFile);
for J := 1 to FYmax do
begin
Line := '';
for I := 'A' to FXmax do
begin
with Sheet[I,J] do
begin
while (Length(Line) < XPOS[I] - 4) do
Line := Line + ' ';
if (Constant in CellStatus) or (Formula in CellStatus) then
begin
if not (Locked in CellStatus) then
begin
if DEC > 0 then
Str(Value:FW:DEC,P)
else
Str(Value:FW,P);
Line := Line + P;
end; { End if not (Locked in CellStatus) then }
end { End if or then }
else
Line := Line + Contents;
end; { End With Statement }
end; { End For Loop - have gotten one line }
For Count := 1 to LeftMargin do { Insert left margin }
Write(MCFile,' ');
writeln(MCFile,Line);
end; { End for loop - move to next line }
Grid;
Close(MCFile);
LowVideo;
GotoCell(FX,FY);
end; { End Procedure Print }
{ Procedure Help dislpays the help file CALC.HLP for the user. }
Procedure Help;
type
str80 = string[80];
Pages = array[0..200] of str80;
var
H : text;
Line : string[80];
LineCounter, EndOfFile, ErrorResult, BeginPage,
J : Integer;
Bold : boolean;
Buffer : ^Pages;
Procedure DisplayMessage;
begin
GotoXY(6,24);
NormVideo;
write('Press ');
LowVideo;
Write('`P`');
NormVideo;
Write(' for previous page ');
LowVideo;
Write('`N`');
NormVideo;
Write(' for next,');
LowVideo;
Write('`Esc`');
NormVideo;
Write(' to exit Help file.');
end;
{ Procedure GetPages reads the help file from disk into an array of a }
{ dynamicly allocated array. NOTE : the number of lines read in is kept }
{ in the zero'th index of the array. }
Procedure GetPages;
var
st : string[10];
count : integer;
begin
count := 1;
Assign(H,'CALC.HLP');
Reset(H);
while not Eof(H) do
begin
Readln(H,Buffer^[count]);
count := count + 1;
end;
close(H);
Str(count,st);
Buffer^[0] := st;
end;
begin
LineCounter := 1; { Initialize line counter}
if Exist('CALC.HLP') then
begin
New(Buffer); { Allocate dynamic array }
GetPages; { Read in Help File }
Val(Buffer^[0],EndOfFile,ErrorResult); { EndOfFile := # of Lines}
while LineCounter < EndOfFile do
begin
BeginPage := LineCounter - 1;
if BeginPage < 1 then { If Previous page is called while on }
BeginPage := 1; { first page, Beginpage is off by 1. }
ClrScr;
Bold := false;
LowVideo;
Line := Buffer^[LineCounter]; { Get first line of current page. }
{ This while loop steps through the array looking for the end of }
{ page indicator '.PA', writing out each line as it goes. }
while (LineCounter < EndOfFile) and (Line <> '.PA') do
begin
Write(' ');
For J := 1 to Length(Line) do
begin
if Line[J] = ^B then { Check for bold face type }
begin
Bold := not Bold; { Toggle Bold type }
if Bold then
NormVideo
else
LowVideo;
end
else
write(Line[J]);
end; { End for loop - have written entire line }
Writeln;
LineCounter := LineCounter + 1;
Line := Buffer^[LineCounter]; { Get next line }
end; { end while }
DisplayMessage;
repeat
read(Kbd,Ch);
until Upcase(Ch) in ['P','N',#27];
case UpCase(Ch) of
'P' : begin { Get start of previous page }
repeat
BeginPage := BeginPage - 1;
until (Buffer^[BeginPage] = '.PA') or (BeginPage < 1);
LineCounter := BeginPage + 1;
end;
'N' : LineCounter := LineCounter + 1; { Get start of next page }
#27 : LineCounter := EndOfFile; { force counter to end of file }
end;
end; { end while }
Dispose(Buffer); { Deallocate dynamic array }
UpDate; { Return to Spread Sheet }
end { End if then statement }
else { Help file did not exist }
begin
Msg('To get help the file CALC.HLP must be on your disk. Press <RETURN>');
repeat Read(kbd,Ch) until Ch=^M;
GotoCell(FX,FY);
end;
end; { End procedure Help }
{*************************************************************************}
{ MicroCalc Module 004 }
{ Last Modified 7-22-85 }
{ }
{ PURPOSE: Evaluate formulas. }
{ Recalculate spread sheet. }
{ }
{ NOTE: This module contains recursive procedures }
{*************************************************************************}
{$A-}
{ procedure NextCh returns the next Character in the formula of the cell }
{ currently being evaluated. }
procedure NextCh(var Position : Integer;
var NextChar : Char;
Formula : AnyString);
begin
repeat
Position := Position + 1;
if Position <= Length(Formula) then
NextChar := Formula[Position]
else
NextChar := eofline;
until NextChar <> ' ';
end; { End procedure NextCh }
{ procedure Evaluate evaluates a string passed to it, the string }
{ represents an expression or formula. }
procedure Evaluate(var IsFormula : Boolean;
var Formula : AnyString;
var Value : Real;
var ErrorPosition : Integer);
var
Position : Integer; { Current position in formula }
EvalCh : Char; { Current Character being scanned }
EXY : string[3]; { Intermediate string for conversion }
function Expression(var ExpressionCh : Char):Real;forward;
{ Sub-function Fact returns the factorial value of the integer passed in}
{ as it's paramater. }
function Fact(I : Real): Real;
{WARNING: This program will not compile using TURBO-87.COM unless you
have an 8087 math coprocessor installed in your computer. Use TURBO.
COM to compile}
begin
if ((I > 0.0) and (I < 34.0)) then { check if value is in legal range.}
Fact := I * Fact(I - 1) { Fact(34) is out of range. }
else
Fact := 1.0;
end; { End sub-function Fact }
{ function Factor is the meat of procedure Evaluate. Within this pro- }
{ -cedure the current subexpression is actualy evaluated. Using nested }
{ If-Then-Else statements, the function determines if the sub-expression}
{ is a number, the sum of a subrange of cells (i.e A1>A5), or a }
{ function(i.e.ABS(X) ). }
function Factor(var FactorCh : Char): Real;
type
{ Declare a set of the standard functions }
Standardfunction =
(fabs,fsqrt,fsqr,fsin,fcos,farctan,fln,flog,fexp,ffact);
{ Declare array with indices being the Standard function names. }
StandardfunctionList = array[Standardfunction] of string[6];
const
{ Declare Typed Constant array initializing it with function names }
StandardfunctionNames : StandardfunctionList =
('ABS','SQRT','SQR','SIN','COS','ARCTAN','LN','LOG','EXP','FACT');
var
E, EE, L : Integer; { temporary variables }
Found : Boolean; { boolean value - expression found or not }
CellSum, F : Real; { F holds value returned by recursive }
{ calls to Factor }
Sf : Standardfunction;
OldExpFY, { ExpEFX and ExpFY hold the positions of }
ExpFY, { the cells referenced to in any formulas }
SumFY, { SumFX and SumFY hold the sum to be }
Start : Integer; { calculated at the fromula's position }
OldExpFX, ExpFX, SumFX : ColumnName;
begin { begin main block of function Factor }
F := -1;
if FactorCh in Numbers then { Get integer or real number }
begin
Start := Position; { Save first position of number in string }
repeat { Skip to non number character }
NextCh(Position,FactorCh,Formula);
until not (FactorCh in Numbers);
if FactorCh = '.' then { is decimal point }
repeat
NextCh(Position,FactorCh,Formula);
until not (FactorCh in Numbers);
if FactorCh = 'E' then { is Exponent symbol }
begin
NextCh(Position,FactorCh,Formula);
repeat
NextCh(Position,FactorCh,Formula)
until not (FactorCh in Numbers);
end;
{ Get value of number }
Val(Copy(Formula,Start,Position - Start),F,ErrorPosition);
if FactorCh = ')' then
NextCh(Position,FactorCh,Formula);
end { end if FactorCh in Numbers }
else { FactorCh not a number }
if FactorCh = '(' then
begin
NextCh(Position,FactorCh,Formula);
F := Expression(FactorCh);
if FactorCh = ')' then {if matching paren is found then read next }
NextCh(Position,FactorCh,Formula)
else { else there is an error in the expression. }
ErrorPosition := Position;
end { End if FactorCh = '(' }
else { else FactorCh <> Number or '('}
{FactorCh may be a cell reference or function name or a function name }
if FactorCh in ['A'..'G','L','S'] then
begin
ExpFX := FactorCh;
NextCh(Position,FactorCh,Formula);
if FactorCh in Numbers then
begin
F := 0;
EXY := FactorCh;
NextCh(Position,FactorCh,Formula);
if FactorCh in Numbers then
begin
EXY := EXY + FactorCh;
NextCh(Position,FactorCh,Formula);
end;
Val(EXY,ExpFY,ErrorPosition);
IsFormula := true; { the expression is a formula }
{ Check if content of the cell referenced in the formula is a }
{ constant. If so then verify that it has been calculated. If the }
{ constant has been calculated then make a recursive call to the }
{ procedure Evaluate to evaluate the contents of the cell. }
if (Constant in Sheet[ExpFX,ExpFY].CellStatus) and
not (Calculated in Sheet[ExpFX,ExpFY].CellStatus) then
begin
Evaluate(Form,Sheet[ExpFX,ExpFY].contents,f,ErrorPosition);
{ Update CellStatus to indicate that the cells value has been }
{ calculated. }
Sheet[ExpFX,ExpFY].CellStatus :=
Sheet[ExpFX,ExpFY].CellStatus + [Calculated]
end { End if ... and not ... Statement }
{ The cell refered to either didn't contain a constant or the }
{ constants value was already calculated. }
else { If the contents of the cell are not text then they must be }
{ a constant. }
if not (Txt in Sheet[ExpFX,ExpFY].CellStatus) then
F := Sheet[ExpFX,ExpFY].Value;
if FactorCh = '>' then { Check for formula range designator }
begin
OldExpFX := ExpFX;
OldExpFY := ExpFY;
NextCh(Position,FactorCh,Formula);
ExpFX := FactorCh;
NextCh(Position,FactorCh,Formula);
if FactorCh in Numbers then
begin
EXY := FactorCh;
NextCh(Position,FactorCh,Formula);
if FactorCh in Numbers then
begin
EXY := EXY + FactorCh;
NextCh(Position,FactorCh,Formula);
end;
if FactorCh = ')' then
NextCh(Position,FactorCh,Formula);
val(EXY,ExpFY,ErrorPosition);
Cellsum := 0;
{ visit each cell specified in sub-range of formula }
for SumFY := OldExpFY to ExpFY do
begin
for SumFX := OldExpFX to ExpFX do
begin
F := 0;
{ Note that the next few statements are identical to the }
{ statements about fifty lines back. }
if (Constant in Sheet[SumFX,SumFY].CellStatus) and
not (Calculated in Sheet[SumFX,SumFY].CellStatus) then
begin
Evaluate(Form,Sheet[SumFX,SumFY].contents,f,ErrorPosition);
Sheet[SumFX,SumFY].CellStatus :=
Sheet[SumFX,SumFY].CellStatus+[Calculated];
end
else
if not (Txt in Sheet[SumFX,SumFY].CellStatus) then
F := Sheet[SumFX,SumFY].Value;
Cellsum := Cellsum + f;
f := Cellsum;
end; { for SumFX := OldExpFX to ExpFX do }
end; { for SumFY := OldExpFY to ExpFY do }
end; { if FactorCh in Numbers then }
end { if FactorCh = '>' then }
else { FactorCh = ')' }
if FactorCh = ')' then
begin
NextCh(Position,FactorCh,Formula);
found := true;
end;
end { if FactorCh in Numbers then }
else {FactorCh is second letter in function name (i.e. ABS or LN )}
begin
found := false;
for sf := fabs to ffact do { step through all possible func's }
if not found then
begin
l := Length(StandardfunctionNames[sf]);
if copy(Formula,Position - 1,l) = StandardfunctionNames[sf] then
begin
Position := Position + l - 1;
NextCh(Position,FactorCh,Formula);
F := Expression(FactorCh);
case sf of
fabs : f := abs(f);
fsqrt : begin
if f > 0 then
f := sqrt(f) { WARNING: This progam will not
compile with TURBOBCD.COM. (Sqrt,
Sin, and similar operations are
not implemented in the BCD
version.) Use TURBO.COM to
compile. }
else
F := -1;
end;
fsqr : begin
if Abs(f) < Sqrt(exp(38) * ln(1)) then
f := sqr(f)
else
F := -1;
end;
fsin : f := sin(f);
fcos : f := cos(f);
farctan : f := arctan(f);
fln : f := ln(f);
flog : f := ln(f)/ln(10);
fexp : begin
if abs(f) < 89 then { Exp(+/-89) will cause }
f := exp(f) { floating point Overflow. }
else
f := -1;
end;
ffact : f := fact(int(f));
end; { end case }
Found := true;
end; { if then Statement }
end; { if not found Statement }
if not Found then
ErrorPosition := Position;
end; { else statement }
end; { if FactorCh in ['A'..'G'] then }
Factor := F;
end; { End Sub-function Factor}
{ Sub-function Signed Factor first determines the sign of the exp- }
{ -ression. It then calls the function Factor to get the value of the }
{ expression. }
function SignedFactor(var SignedCh : Char):Real;
begin { begin main block of procedure SignedFactor }
if SignedCh = '-' then
begin
NextCh(Position,SignedCh,Formula);
SignedFactor := -Factor(SignedCh);
end
else
SignedFactor := Factor(SignedCh);
end; { End Sub-function SignedFactor }
{ Sub-function Term returns the value of the sub-expression being eval- }
{ -uated. If the expression contains an exponent expression Term eval- }
{ -uates it. }
function Term(var TermCh : Char):Real;
var
T : Real;
begin { begin main block of procedure Term }
T := SignedFactor(TermCh);
while TermCh = '^' do
begin
NextCh(Position,TermCh,Formula);
T := exp(ln(t) * SignedFactor(TermCh));
end;
Term := T;
end; { End Sub-function Term }
{ Sub-function SimpleExpression calls the function term and evaluates }
{ the simple expression returned. }
function SimpleExpression(var SimpleExpCh : Char):Real;
var
SimpExp : Real;
Opr : Char;
begin { begin main block of procedure SimpleExpression }
SimpExp := Term(SimpleExpCh);
while SimpleExpCh in ['*','/'] do
begin
Opr := SimpleExpCh;
NextCh(Position,SimpleExpCh,Formula);
case Opr of
'*' : SimpExp := SimpExp * term(SimpleExpCh);
'/' : SimpExp := SimpExp / term(SimpleExpCh);
end;
end;
SimpleExpression := SimpExp;
end; { End Sub-function SimpleExpression }
{ Sub-function Expression evaluates the expression contained in the }
{ current cell. }
function Expression{(var ExpressionCh : Char):Real;forward};
var
E : Real;
Opr : Char;
begin { begin main block of function Expression }
E := SimpleExpression(ExpressionCh);
while ExpressionCh in ['+','-'] do
begin
Opr := ExpressionCh;
NextCh(Position,ExpressionCh,Formula);
case Opr of
'+' : E := E +
SimpleExpression(ExpressionCh);
'-' : E := E -
SimpleExpression(ExpressionCh);
end;
end;
Expression := E;
end; { End Sub-function Expression }
begin { main body of procedure Evaluate }
if Formula[1] = '.' then
Formula := '0' + Formula;
if Formula[1] = '+' then
delete(Formula,1,1);
IsFormula := false;
Position := 0;
NextCh(Position,EvalCh,Formula);
Value := Expression(EvalCh);
if EvalCh = EofLine then
ErrorPosition := 0
else
ErrorPosition := Position;
end; { End procedure Evaluate }
{ procedure Recalculate steps through the entire spreadsheet recalculating}
{ each cell. }
procedure Recalculate;
var
RFX : ColumnName;
RFY : Integer;
OldValue : Real;
Err : Integer;
begin
LowVideo;
GotoXY(1,24);
ClrEol;
Write('Calculating..');
for RFY := 1 to FYMax do { Reevaluate each cell updating it's }
begin { contents when appropriate. }
for RFX := 'A' to FXMax do
begin
with Sheet[RFX,RFY] do
begin
if (Formula in CellStatus) then
begin
CellStatus := CellStatus + [Calculated];
OldValue := Value;
Evaluate(Form,Contents,Value,Err);
if OldValue <> Value then
begin
GotoXY(XPos[RFX],RFY + 1);
if (DEC >= 0) then
Write(Value:FW:DEC)
else
Write(Value:FW);
end;
end;
end;
end;
end;
GotoCell(FX,FY);
end; { End procedure Recalculate }
{*************************************************************************}
{ MicroCalc Module 005 }
{ Last Modified 7-22-85 }
{ }
{ PURPOSE: Read the contents of a cell and update }
{ associated cells. }
{ }
{*************************************************************************}
{ procedure GetCell gets the contents of a cell from the user. This }
{ routine gets all input entered by the user. procedure GetCell }
{ initializes the temporary variable "S" with the last read Character. }
{ Depending on this character, it then calls GetFormula, GetText, or }
{ EditCell. }
procedure GetCell(FX : ColumnName; FY : Integer);
var
S : AnyString;
NewStatus : SetofAttri;
ErrorPosition : Integer;
EvalResult : Real;
I : ColumnName;
Abort,
IsForm : Boolean;
{ Sub-procedure ClearCells clears the current cell and its associated }
{ cells. An associated cell is a cell overwritten by data from the cur- }
{ -rent cell. The data can be text in which case the cell has the attri-}
{ -bute "OverWritten". If the data is a result from an expression and }
{ the field width is larger than 11 then the associated cell is }
{ "Locked." }
procedure ClearCells(FX : ColumnName; FY : Integer);
var
I : ColumnName;
begin
I := FX;
repeat
with Sheet[I,FY] do
begin
GotoXY(XPos[I],FY + 1);
write(' ');
I := Succ(I);
end;
{ Cell is not OverWritten not Locked }
until ([OverWritten,Locked] * Sheet[I,FY].CellStatus = []);
end; { End Sub-procedure ClearCells }
{ Sub-procedure GetLine is the routine used to get input from the user. }
{ The procedure allows editing of input and checks that the input con- }
{ -tains legal characters. }
procedure GetLine(ColNO, LineNO, { Where to start line }
MAX : Integer; { Max length }
UpperCase : Boolean);{ True if auto Upcase }
var
X : Integer;
InsertOn : boolean;
OkChars : set of Char;
procedure GotoX;
begin
GotoXY(X + ColNo - 1,LineNo);
end;
begin
OkChars := [' '..'}'];
InsertOn := true;
X := 1;
GotoX;
Write(S);
if Length(S) = 1 then
X := 2;
if ErrorPosition <> 0 then { there was an error found }
X := ErrorPosition;
GotoX;
repeat
Read(Kbd,Ch);
if KeyPressed then
begin
Read(kbd,Ch);
IBMCh(Ch);
end;
if UpperCase then
Ch := UpCase(Ch);
case Ch of
^[ : begin
S := chr($FF); { abort editing }
Ch := ^M;
end;
^D : begin { Move cursor right }
X := X + 1;
if (X > length(S) + 1) or (X > MAX) then
X := X - 1;
GotoX;
end;
^G : begin { Delete Char under cursor }
if X <= Length(S) then
begin
Delete(S,X,1);
Write(copy(S,X,Length(S) - X + 1),' ');
GotoX;
end;
end;
^S,^H : begin { Move cursor left }
X := X - 1;
if X < 1 then
X := 1;
GotoX;
end;
^F : begin { Move cursor to end of line }
X := Length(S) + 1;
GotoX;
end;
^A : begin { Move cursor to beginning of line }
X := 1;
GotoX;
end;
#127 : begin { Delete left Char }
X := X - 1;
if (Length(S) > 0) and (X > 0) then
begin
Delete(S,X,1);
Write(copy(S,X,Length(S) - X + 1),' ');
GotoX;
if X < 1 then
X := 1;
end
else
X := 1;
end;
^V : InsertOn := not InsertOn; { Toggle Insert/Overwrite }
else { Not Upcase }
begin
if Ch in OkChars then
begin
if InsertOn then
begin
insert(Ch,S,X);
Write(copy(S,X,Length(S) - X + 1),' ');
end
else
begin
write(Ch);
if X = length(S) then
S := S + Ch
else
S[X] := Ch;
end;
if Length(S) + 1 <= MAX then
X := X + 1
else
OkChars := []; { Line too Long }
GotoX;
end { end if Ch in OkChars }
else
if Length(S) + 1 <= Max then
OkChars:= [' '..'}']; { Line ok again }
end; { end case statement's else }
end; { end case statement }
until CH = ^M;
end; { End Sub-procedure GetLine }
{ Sub-procedure GetText calls the procedure GetLine with the current }
{ cells X,Y position as parameters. This means that text entering takes }
{ place direcly at the cells position on the Sheet. }
procedure GetText;
var
LineLength : Integer;
begin
GotoXY(5,23);
LowVideo;
if FX = 'G' then
LineLength := 10
else
LineLength := 70;
with Sheet[FX,FY] do
GetLine(XPos[FX],FY+1,LineLength,False);
end; { End Sub-procedure GetText }
{ Sub-procedure GetFormula calls the routine GetLine to get a line from }
{ the user. It then calls the routine Evaluate to evaluate the Formula }
{ input by the user. }
procedure GetFormula;
begin
GotoXY(5,23);
LowVideo;
repeat
GetLine(1,24,70,True);
if S <> Chr($FF) then
begin
Evaluate(IsForm,S,EvalResult ,ErrorPosition);
if ErrorPosition <> 0 then
Flash(15,'GetFormula Error at cursor' + ^G,false)
else
Flash(15,'get formula ok',false);
end;
until (ErrorPosition = 0) or (S = Chr($FF));
if IsForm then
NewStatus := NewStatus + [Formula];
end; { End Sub-procedure GetFormula }
{ Sub-procedure EditCell loads a copy of the current cell's contents }
{ into the variable S before calling either GetText or GetFormula. In }
{ this way no changes are actually made to the current cell. }
procedure EditCell;
begin
GotoXY(5,23);
with Sheet[FX,FY] do
begin
S := Contents;
if Txt in CellStatus then
GetText
else
GetFormula;
end;
end; { End procedure EditCell }
{ Sub-procedure UpdateCells is a little more complicated. Basically it }
{ makes sure to tag and untag cells which have been overwritten or }
{ cleared by data from another cell. It also updates the current cell }
{ with the new type and contents which are still in the temporary }
{ variable "S". }
procedure UpdateCells;
var
I : ColumnName;
Flength : Integer;
begin
Sheet[FX,FY].Contents := S;
if Txt in NewStatus then
begin
I := FX;
FLength := Length(S);
repeat { this repeat loop checks the status of adjoining cells to }
{ verify that they have or have not been overwritten. }
I := Succ(I);
with Sheet[I,FY] do
begin
FLength := Flength - 11;
if (Flength > 0) then
begin
CellStatus := [Overwritten,Txt];
Contents := '';
end
else
begin
if OverWritten in CellStatus then
begin
CellStatus := [Txt];
GotoCell(I,FY);
LeaveCell(I,FY);
end; { if OverWritten in CellStatus }
end; { else }
end; { with Sheet[I,FY] do }
until (I = FXMax) or (Sheet[I,FY].Contents <> '');
Sheet[FX,FY].CellStatus := [Txt];
end
else { string changed to formula or constant }
begin
I := FX;
repeat { this repeat loop checks whether or not the cell is }
{ overwritten. }
with Sheet[I,FY] do
begin
if OverWritten in CellStatus then
begin
CellStatus := [Txt];
Contents := '';
end;
I := Succ(I);
end;
until not (OverWritten in Sheet[I,FY].CellStatus);
with Sheet[FX,FY] do
begin
CellStatus := [Constant];
if IsForm then
CellStatus := CellStatus + [Formula];
Value := EvalResult ;
end; { with }
end; { else }
end; { End Sub-procedure UpdateCells }
begin { begin main block of procedure GetCell }
S := Ch;
ErrorPosition := 0;
Abort := false;
NewStatus := [];
EvalResult := -1;
if Ch in ['0'..'9','+','-','.','(',')'] then
begin
NewStatus := [Constant];
if not (Formula in Sheet[FX,FY].CellStatus) then
begin
GotoXY(11,24);
ClrEol;
ClearCells(FX,FY);
GetFormula;
end
else { Formula in Sheet[FX,FY].CellStatus }
begin
Flash(15,'Edit formula Y/N?',true);
repeat
read(Kbd,Ch)
until UpCase(CH) in ['Y','N'];
Flash(15,' ',false);
if UpCase(Ch) = 'Y' then
begin
EditCell;
end
Else
Abort := true;
end;
end
else { Ch not in ['0'..'9','+','-','.','(',')'] }
begin
if Ch = ^[ then
begin
NewStatus := (Sheet[FX,FY].CellStatus) * [Txt,Constant];
EditCell;
end
else { Ch <> '^[' and not in ['0'..'9','+','-','.','(',')'] }
begin
if formula in Sheet[FX,FY].CellStatus then
begin
Flash(15,'Edit formula Y/N?',true);
repeat
read(Kbd,Ch)
until UpCase(CH) in ['Y','N'];
Flash(15,' ',false);
if UpCase(Ch) = 'Y' then
begin
EditCell;
end
Else
Abort := true;
end
else { formula not in Sheet[FX,FY].CellStatus }
begin
NewStatus := [Txt];
ClearCells(FX,FY);
GetText;
end; { End else }
end; { end else }
end; { end else }
if not Abort then { do neccesary bookkeeping }
begin
if S <> Chr($FF) then
UpDateCells;
GotoCell(FX,FY);
if AutoCalc and (Constant in Sheet[FX,FY].CellStatus) then
Recalculate;
if Txt in NewStatus then
begin
GotoXY(3,FY + 1);
Clreol;
For I := 'A' to FXMax do
LeaveCell(I,FY);
end;
end; { end if not abort }
Flash(15,' ',False);
GotoCell(FX,FY);
end; { End procedure GetCell }
{ procedure Format is used to modify the numeric format of a range of }
{ cells in the current column. }
procedure Format;
var
J, FW, DEC,
FromLine, ToLine : Integer;
Lock : Boolean;
{ procedure GetInt reads an integer from the keyboard screening it for }
{ illegal characters. }
procedure GetInt(var I: Integer; Max: Integer);
var
S : String[8];
Result : Integer;
Ch : Char;
begin
S := '';
repeat
repeat Read( Kbd,Ch) until Ch in ['0'..'9','-',^M];
if Ch <> ^M then
begin
Write(Ch);
S := S + Ch;
Val(S,I,Result);
end;
until (I >= Max) or (Ch = ^M);
if I > Max then I := Max;
end; { End Subprocedure GetInt }
begin
NormVideo;
Msg('Format: Enter number of places beyond decimal point (Max 11) : ');
GetInt(DEC,11);
Msg('Enter Cell width (if larger than 10 next column will be locked) : ');
GetInt(FW,20);
Msg('From which line in column '+ FX +' : ');
GetInt(FromLine,FYMax);
Msg('To which line in column '+ FX +' : ');
GetInt(ToLine,FYMax);
if FW > 10 then
Lock := true
else
Lock := False;
for J := FromLine to ToLine do { Format specified cells within }
begin { current column. }
Sheet[FX,J].DEC := DEC;
Sheet[FX,J].FW := FW;
with Sheet[Succ(FX),J] do
begin
if Lock then
begin
CellStatus := CellStatus + [Locked,Txt];
Contents := '';
end
else
CellStatus :=CellStatus - [Locked];
end; { End With Statement }
end; { End For Loop }
NormVideo;
UpDate;
GotoCell(FX,FY);
end; { End procedure Format }
{ Procedure Commands is called from the program's main loop when the user }
{ types a slash '/'. The procedure in turn calls the appropriate procedure}
{ based on the users response to the menu displayed. }
{ }
{ For easy reference the source code module number is shown in a comment }
{ on the right following the procedure call. }
Procedure Commands;
begin
GotoXY(1,24);
NormVideo;
Write('Update, Quit, Load, Save, Recalculate, Print, Format, AutoCalc, Help ');
ClrEol;
Read(KBD,Ch);
Ch := UpCase(Ch);
case Ch of { module # }
'Q': Halt; { Halt execution of program }
'F': Format; { Format a range of cells 005 }
'S': Save; { Save the current spreadsheet to a file 003 }
'L': Load; { Load a spreadsheet from a file 003 }
'H': Help; { Call the Help procedure 003 }
'R': Recalculate; { Recalculate spreadsheet 005 }
'A': Auto; { Toggle AutoCalc On or Off 000 }
'U': Update; { Redraw the screen 001 }
'C': Clear; { Clear screen of spreadsheet 001 }
'P': Print; { Print spreadsheet to a file 003 }
end;
Grid; { 001 }
GotoCell(FX,FY); { 002 }
end; { End Procedure Commands }