-- BasicProgram.mesa
-- edited by Brotz and Hilton, July 20, 1982 3:15 PM

DIRECTORY
Ascii,
BasicImpDefs,
FrameDefs,
IODefs,
Real,
Storage,
StreamDefs,
String,
vmD: FROM "VirtualMgrDefs";

BasicProgram: PROGRAM

IMPORTS BasicImpDefs, FrameDefs, IODefs, Real, Storage, String, vmD
EXPORTS BasicImpDefs =

BEGIN OPEN BasicImpDefs;

programLineDescHead: PUBLIC ProgramLineDescPtr ← NIL;
cM: PUBLIC vmD.ComposedMessagePtr;
codeCm: PUBLIC vmD.ComposedMessagePtr;
lineCodeCm: PUBLIC vmD.ComposedMessagePtr;


AllocateAndInsert: PUBLIC
PROCEDURE [programLineNumber, textStartIndex: CARDINAL] =
-- Inserts (or replaces) a program line descriptor at the appropriate place in
-- the line descriptor chain according to programLineNumber. Calls
-- InsertMessage to insert the program text in cM.
BEGIN
cur: ProgramLineDescPtr;
prev: ProgramLineDescPtr ← NIL;
new: ProgramLineDescPtr ← Storage.Node[SIZE[ProgramLineDesc]];
new↑ ← [ , programLineNumber, 0, 0, 0, 0];
FOR cur ← programLineDescHead, cur.next DO
IF cur = NIL OR cur.lineNumber >= new.lineNumber THEN
BEGIN --insert the new line between prev and cur.
IF prev = NIL THEN programLineDescHead ← new
ELSE {prev.next ← new; new.start ← prev.end; new.codeStart ← prev.codeEnd};
IF cur = NIL OR cur.lineNumber > new.lineNumber THEN
BEGIN -- insert a new line. --
new.next ← cur;
InsertMessage[textStartIndex, new, NIL];
END
ELSE BEGIN -- replace an existing line.
new.next ← cur.next;
vmD.DeleteRangeInMessage[[cur.start, cur.end, cM]];
vmD.DeleteRangeInMessage[[cur.codeStart, cur.codeEnd, codeCm]];
InsertMessage[textStartIndex, new, cur];
Storage.Free[cur];
IF new.start = new.end THEN
BEGIN
IF prev = NIL THEN programLineDescHead ← new.next
ELSE prev.next ← new.next;
Storage.Free[new];
END;
END;
EXIT;
END;
prev ← cur;
ENDLOOP;
END; -- of AllocateAndInsert --


FindLineNumber: PUBLIC PROCEDURE [lineNumber: CARDINAL]
RETURNS[tempPc: ProgramLineDescPtr] =
BEGIN
tempPc ← programLineDescHead;
UNTIL tempPc = NIL OR tempPc.lineNumber = lineNumber DO
tempPc ← tempPc.next;
ENDLOOP;
IF tempPc = NIL THEN RunTimeError["Target line number does not exist."L];
RETURN[tempPc];
END; -- of FindLineNumber --


InsertMessage: PROCEDURE [textStartIndex: CARDINAL, new, old: ProgramLineDescPtr] =
-- Inserts the characters from inputLine (global) starting at
-- inputLine[textStartIndex] through the last character in inputLine into
-- cM. Indices for succeeding lines are updated to account for the new
-- characters added (and possibly deletedChars deleted.)
BEGIN
cur: ProgramLineDescPtr;
textLength: CARDINAL ← inputLine.length - textStartIndex;
codeLength: CARDINAL ← vmD.GetMessageSize[lineCodeCm];
deletedChars: CARDINAL ← IF old = NIL THEN 0 ELSE old.end - old.start;
deletedCode: CARDINAL ← IF old = NIL THEN 0 ELSE old.codeEnd - old.codeStart;
vmD.StartMessageInsertion[cM, new.start];
vmD.InsertSubstringInMessage[cM, inputLine, textStartIndex, textLength
! vmD.MessageOverflow => GO TO overflow];
vmD.StopMessageInsertion[cM];
IF textLength = 0 THEN codeLength ← 0 -- don’t insert code for text line that will go away.--
ELSE vmD.InsertRangeInMessage[new.codeStart, codeCm, [0, codeLength, lineCodeCm]];
new.end ← new.start + textLength;
new.codeEnd ← new.codeStart + codeLength;
FOR cur ← new.next, cur.next UNTIL cur = NIL DO
--correct succeeding start and end indices
cur.start ← cur.start + textLength - deletedChars;
cur.end ← cur.end + textLength - deletedChars;
cur.codeStart ← cur.codeStart + codeLength - deletedCode;
cur.codeEnd ← cur.codeEnd + codeLength - deletedCode;
ENDLOOP;
EXITS
overflow =>
BEGIN
vmD.StopMessageInsertion[cM];
ParseError["Message overflow: your program is too long. Life is hard."L];
END;
END; -- of InsertMessage --


NextStatement: PUBLIC PROCEDURE =
BEGIN
savedIndex: CARDINAL;
UNTIL endOfLine OR charTablePtr[token[0]] = statementSeparator DO
b: BOOLEAN ← GetToken[];
ENDLOOP;
IF token[0] = statementSeparatorChar THEN
BEGIN
savedIndex ← inputLineIndex;
IF GetToken[] THEN ParseStatement[savedIndex]
ELSE ParseError["Statement missing after colon."L];
END;
END; -- of NextStatement --


OutputAutoLineNumber: PUBLIC PROCEDURE =
BEGIN
numberString: STRING ← [7];
keyBoard: StreamDefs.StreamHandle ← IODefs.GetInputStream[];
keyBoard.putback[keyBoard, Ascii.SP];
numberString.length ← 0;
autoStart ← autoStart + autoIncrement;
String.AppendDecimal[numberString, autoStart];
FOR i: CARDINAL DECREASING IN [0..numberString.length]
UNTIL i = 0 DO
keyBoard.putback[keyBoard, numberString[i - 1]];
ENDLOOP;
autoStart ← String.StringToDecimal[numberString];
END; -- of OutputAutoLineNumber --


ParseError: PUBLIC PROCEDURE [s: STRING] =
BEGIN
IODefs.WriteLine["Parse error:"L];
IODefs.WriteLine[s];
ERROR ParseErrorSignal;
END; -- of ParseError --


ParseErrorSignal: PUBLIC ERROR = CODE;


ProgramCleanUp: PUBLIC PROCEDURE =
BEGIN
previousPtr: ProgramLineDescPtr;
currentPtr: ProgramLineDescPtr ← programLineDescHead;
UNTIL currentPtr = NIL DO
previousPtr ← currentPtr;
currentPtr ← currentPtr.next;
Storage.Free[previousPtr];
ENDLOOP;
vmD.FreeVirtualMessageObject[cM];
vmD.FreeVirtualMessageObject[codeCm];
vmD.FreeVirtualMessageObject[lineCodeCm];
THROUGH [1..2] DO FrameDefs.UnlockCode[Real.InitReals]; ENDLOOP;
END; -- of ProgramCleanUp --


RunTimeError: PUBLIC PROCEDURE [s: STRING] =
BEGIN
IODefs.WriteString["Run time error in line "L];
IF currentProgLine # NIL THEN IODefs.WriteDecimal[currentProgLine.lineNumber];
IODefs.WriteLine["."L];
IODefs.WriteLine[s];
ClearStack[];
ERROR RunTimeErrorSignal;
END; -- of RunTimeError --


RunTimeErrorSignal: PUBLIC ERROR = CODE;


TokenIsLineNumber: PUBLIC PROCEDURE
RETURNS[b: BOOLEAN, programLineNumber: CARDINAL] =
-- Determines whether or not the first token of each string is a number.
-- If so, it is a program line.
BEGIN
i : CARDINAL ← 0;
IF token[i] IN [’0..’9] THEN
RETURN [b : TRUE, programLineNumber : String.StringToDecimal[token]]
ELSE RETURN [b : FALSE, programLineNumber : 0];
END; -- of TokenIsLineNumber --


-- Initialization


cM ← vmD.AllocateComposedMessageObject[];
vmD.InitComposedMessage[cM, ""L];
codeCm ← vmD.AllocateComposedMessageObject[];
vmD.InitComposedMessage[codeCm, ""L];
lineCodeCm ← vmD.AllocateComposedMessageObject[];
vmD.InitComposedMessage[lineCodeCm, ""L];


END. -- of BasicProgram --