--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.