--File ParserInput.Mesa
--
November 8, 1980 2:46 PM


DIRECTORY


IODefs: FROM "IODefs" USING[CR, NUL],

SegmentDefs: FROM "SegmentDefs" USING [Read, FileNameError],

StreamDefs: FROM "StreamDefs" USING [NewByteStream, DiskHandle],

ParserErrorDefs: FROM "ParserErrorDefs" USING [Report],

OutputDefs: FROM "OutputDefs" USING [SendMessage],

StringDefs: FROM "StringDefs" USING [AppendString, AppendChar, AppendNumber],

ParserInputDefs: FROM "ParserInputDefs";

ParserInput: PROGRAM
IMPORTS StreamDefs, SegmentDefs, ParserErrorDefs, OutputDefs, StringDefs
EXPORTS ParserInputDefs =
BEGIN

inputFile: StreamDefs.DiskHandle;
nextChar: CHARACTER;
MaxCharsPerFileName: CARDINAL = 80;
lineCount: CARDINAL;
resetLine,started: BOOLEAN;
latestLine: STRING ← [200];

filesOpen: CARDINAL;
MaxFilesOpen: CARDINAL = 10;
top: [0..3*(MaxFilesOpen-1)];
Stack: ARRAY [1..3*(MaxFilesOpen-1)] OF UNSPECIFIED;-- stack to save context

InitInput: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
BEGIN
filesOpen ← 0;
resetLine ← FALSE;
started ← FALSE;
top ← 0;
latestLine.length ← 0;
RETURN[TRUE];
END;

FinishInput: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
BEGIN

IF filesOpen > 1 THEN
BEGIN
ParserErrorDefs.Report["Input Terminated with Nested Files Open", Advisory];
WHILE filesOpen > 1 DO
inputFile.destroy[inputFile];
RestoreFile[];
filesOpen ← filesOpen - 1;
ENDLOOP;
END;
inputFile.destroy[inputFile];
RETURN[TRUE];

END;

GetChar: PUBLIC PROCEDURE RETURNS [CHARACTER] =
BEGIN
c: CHARACTER ← nextChar;
ch: CHARACTER;

IF filesOpen = 0 THEN
BEGIN
nextChar ← ParserInputDefs.EOF;
ParserErrorDefs.Report["No file open for input", Fatal];
RETURN[nextChar];
END
ELSE started ← TRUE;
IF resetLine THEN BEGIN resetLine ← FALSE; latestLine.length ← 0; END;

UNTIL inputFile.endof[inputFile] DO
IF (ch ← inputFile.get[inputFile]) # IODefs.NUL THEN
BEGIN
nextChar ← ch;
EXIT;
END;
REPEAT
FINISHED =>
IF filesOpen = 1 THEN nextChar ← ParserInputDefs.EOF
ELSE
BEGIN
inputFile.destroy[inputFile];
RestoreFile[];
filesOpen ← filesOpen - 1;
END;
ENDLOOP;

BuildLine[c];
IF c = IODefs.CR THEN
BEGIN lineCount ← lineCount + 1; resetLine ← TRUE; END;
RETURN[c];

END;

InFromFile: PUBLIC PROCEDURE [fileName: STRING] RETURNS [BOOLEAN] =
BEGIN
badFile: BOOLEAN ← FALSE;
eMessage: STRING ← [100];

eMessage.length ← 0;

IF filesOpen >= MaxFilesOpen THEN
BEGIN
ParserErrorDefs.Report["Too Many Levels Of File Nesting", Fatal];
RETURN[FALSE];
END;

IF filesOpen > 0 THEN
BEGIN
SaveFile[];
END;

inputFile ← StreamDefs.NewByteStream[fileName,SegmentDefs.Read
! SegmentDefs.FileNameError =>
BEGIN
StringDefs.AppendString[eMessage,"Can’t Find "];
StringDefs.AppendString[eMessage,name];
ParserErrorDefs.Report[eMessage, Fatal];
badFile ← TRUE;
CONTINUE;
END];

IF badFile THEN-- given file not found
BEGIN
IF filesOpen > 0 THEN RestoreFile[];
RETURN [FALSE];
END;

lineCount ← 1;
filesOpen ← filesOpen + 1;
resetLine ← FALSE;
latestLine.length ← 0;-- notice we lose context

IF filesOpen = 1 THEN-- special case first file
nextChar ← IF ~inputFile.endof[inputFile] THEN
inputFile.get[inputFile]
ELSE ParserInputDefs.EOF
ELSE

IF ~inputFile.endof[inputFile] THEN-- if file is non-empty then announce its name
BEGIN
message: STRING ← [100];
message.length ← 0;
THROUGH [2..filesOpen] DO
StringDefs.AppendChar[message, ’];
ENDLOOP;
StringDefs.AppendString[message, fileName];
OutputDefs.SendMessage[Other,message];
nextChar ← inputFile.get[inputFile];
END
ELSE
BEGIN-- otherwise back to previous file
RestoreFile[];
filesOpen ← filesOpen - 1;
END;

BuildLine[nextChar];
RETURN[TRUE];
END;

Peek: PUBLIC PROCEDURE RETURNS [CHARACTER] =
-- will mess up if someone uses Peek to make a decision just before a InFromFile is executed
BEGIN RETURN[nextChar]; END;

Identify: PUBLIC PROCEDURE =
BEGIN OPEN StringDefs;
message: STRING ← [250];

IF started THEN
BEGIN
THROUGH [2..filesOpen] DO
AppendChar[message, ’];-- stick a tab on
ENDLOOP;
AppendString[message, "Line "];
AppendNumber[message, lineCount, 10];
AppendString[message, ":"];
IF latestLine.length > 0 THEN AppendString[message, latestLine];
OutputDefs.SendMessage[Other, message];
END
ELSE RETURN;
END;

Flush: PUBLIC PROCEDURE [BreakChar: CHARACTER] RETURNS [CHARACTER] =
BEGIN
c: CHARACTER ← ParserInputDefs.EOF;
IF EndOfFile[] THEN RETURN[ParserInputDefs.EOF];
WHILE (c←GetChar[]) # ParserInputDefs.EOF AND c # BreakChar DO NULL; ENDLOOP;
RETURN[c];
END;

EndOfFile: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
BEGIN RETURN[nextChar = ParserInputDefs.EOF]; END;

--
Private Procedures

Push: PROCEDURE [data: UNSPECIFIED] =
BEGIN
top ← top+1;
Stack[top] ← data;
RETURN;
END;

Pop: PROCEDURE RETURNS [UNSPECIFIED] =
BEGIN
ans: UNSPECIFIED ← Stack[top];
top ← top-1;
RETURN[ans];
END;

BuildLine: PROCEDURE [c: CHARACTER] =
BEGIN
IF c # IODefs.CR AND latestLine.length <= latestLine.maxlength THEN
BEGIN
latestLine[latestLine.length] ← c;
latestLine.length ← latestLine.length + 1;
END;
END;

SaveFile: PROCEDURE = INLINE
BEGIN
Push[lineCount];
Push[inputFile];
Push[nextChar];
END;

RestoreFile: PROCEDURE = INLINE
BEGIN
nextChar ← Pop[];
inputFile ← Pop[];
lineCount ← Pop[];
END;

END.