File ParserInput.Mesa
November 8, 1980 2:46 PM
Last Edited by: McCreight, June 19, 1986 12:06:10 pm PDT
DIRECTORY
Ascii, FS, IO, OutputDefs, ParserErrorDefs, ParserInputDefs, RefText, Rope;
ParserInput: CEDAR PROGRAM
IMPORTS FS, IO, OutputDefs
EXPORTS ParserInputDefs, ParserErrorDefs =
BEGIN
inputFile: IO.STREAM;
nextChar: CHARACTER;
MaxCharsPerFileName: CARDINAL = 80;
lineCount: INT;
resetLine,started: BOOLEAN;
latestLine: REF TEXTNEW[TEXT[100]];
filesOpen: CARDINAL;
MaxFilesOpen: CARDINAL = 10;
top: [0..MaxFilesOpen];
StackType: TYPE = ARRAY [1..MaxFilesOpen] OF RECORD [
lineCount: INT ← 0,
inputChar: CHARACTER ← ' ,
file: IO.STREAMNIL
];
... stack to save context
stack: REF StackType ← NEW[StackType];
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
Report["Input Terminated with Nested Files Open", Advisory];
WHILE filesOpen > 1 DO
inputFile.Close[];
RestoreFile[];
filesOpen ← filesOpen - 1;
ENDLOOP;
END;
inputFile.Close[];
RETURN[TRUE];
END;
GetChar: PUBLIC PROCEDURE RETURNS [CHARACTER] =
BEGIN
c: CHARACTER ← nextChar;
ch: CHARACTER;
IF filesOpen = 0 THEN
BEGIN
nextChar ← ParserInputDefs.EOF;
Report["No file open for input", Fatal];
RETURN[nextChar];
END
ELSE started ← TRUE;
IF resetLine THEN BEGIN resetLine ← FALSE; latestLine.length ← 0; END;
DO
IF (ch ← inputFile.GetChar[! IO.EndOfStream => GOTO EOF]) # Ascii.NUL THEN
BEGIN
nextChar ← ch;
EXIT;
END;
REPEAT
EOF =>
IF filesOpen = 1 THEN nextChar ← ParserInputDefs.EOF
ELSE
BEGIN
inputFile.Close[];
RestoreFile[];
filesOpen ← filesOpen - 1;
END;
ENDLOOP;
BuildLine[c];
IF c = Ascii.CR THEN
BEGIN lineCount ← lineCount + 1; resetLine ← TRUE; END;
RETURN[c];
END;
InFromFile: PUBLIC PROCEDURE [fileName: Rope.ROPE] RETURNS [BOOLEAN] =
BEGIN
badFile: BOOLEANFALSE;
IF filesOpen >= MaxFilesOpen THEN
BEGIN
Report["Too Many Levels Of File Nesting", Fatal];
RETURN[FALSE];
END;
IF filesOpen > 0 THEN
BEGIN
SaveFile[];
END;
inputFile ← FS.StreamOpen[fileName, $read
! FS.Error =>
BEGIN
Report[IO.PutFR["Can't Find %g", IO.rope[fileName]], 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[] THEN
inputFile.GetChar[]
ELSE ParserInputDefs.EOF
ELSE
IF ~inputFile.EndOf[] THEN  -- if file is non-empty then announce its name
BEGIN
message: IO.STREAMIO.ROS[];
THROUGH [2..filesOpen] DO message.PutChar[' ] ENDLOOP;
message.PutRope[fileName];
OutputDefs.SendMessage[Other, message.RopeFromROS[]];
nextChar ← inputFile.GetChar[];
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
message: IO.STREAMIO.ROS[];
IF started THEN
BEGIN
THROUGH [2..filesOpen] DO
message.PutChar[' ];  -- stick a tab on
ENDLOOP;
message.PutF["Line %g, position %g: ", IO.int[lineCount], IO.int[inputFile.GetIndex[]]];
IF latestLine.length > 0 THEN
BEGIN
message.PutText[latestLine];
IF latestLine.length = latestLine.maxLength THEN message.PutRope[" ..."];
END;
OutputDefs.SendMessage[Other, message.RopeFromROS[]];
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
BuildLine: PROCEDURE [c: CHARACTER] =
BEGIN
IF c # Ascii.CR AND latestLine.length < latestLine.maxLength THEN
BEGIN
latestLine[latestLine.length] ← c;
latestLine.length ← latestLine.length + 1;
END;
END;
SaveFile: PROCEDURE =
BEGIN
top ← top+1;
stack[top] ← [lineCount, nextChar, inputFile];
END;
RestoreFile: PROCEDURE =
BEGIN
[lineCount, nextChar, inputFile] ← stack[top];
top ← top-1;
END;
errorCounts: ARRAY ParserErrorDefs.ErrorType OF CARDINAL;
ParserAbort: PUBLIC ERROR = CODE;
InitError: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
BEGIN
errorCounts ← ALL[0];
RETURN[TRUE];
END;
FinishError: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
BEGIN
RETURN[TRUE];
END;
Report: PUBLIC PROCEDURE [message: Rope.ROPE, error: ParserErrorDefs.ErrorType] =
BEGIN OPEN OutputDefs;
IF error = Advisory THEN OutputDefs.SendMessage[Advisory, "Warning: ", FALSE];
OutputDefs.SendMessage[error, message];
Identify[];
errorCounts[error] ← errorCounts[error] + 1;
IF error = FatalInternal THEN
horrors, abort
BEGIN
SendMessage[FatalInternal, "Aborting, please wait"];
ERROR ParserAbort;
END;
END;
ErrorSummary: PUBLIC PROCEDURE RETURNS [ARRAY ParserErrorDefs.ErrorType OF CARDINAL] =
BEGIN
RETURN[errorCounts];
END;
END.