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 TEXT _ NEW[TEXT[100]]; filesOpen: CARDINAL; MaxFilesOpen: CARDINAL = 10; top: [0..MaxFilesOpen]; StackType: TYPE = ARRAY [1..MaxFilesOpen] OF RECORD [ lineCount: INT _ 0, inputChar: CHARACTER _ ' , file: IO.STREAM _ NIL ]; 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: BOOLEAN _ FALSE; 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.STREAM _ IO.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] = BEGIN RETURN[nextChar]; END; Identify: PUBLIC PROCEDURE = BEGIN message: IO.STREAM _ IO.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; 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 BEGIN SendMessage[FatalInternal, "Aborting, please wait"]; ERROR ParserAbort; END; END; ErrorSummary: PUBLIC PROCEDURE RETURNS [ARRAY ParserErrorDefs.ErrorType OF CARDINAL] = BEGIN RETURN[errorCounts]; END; END. File ParserInput.Mesa November 8, 1980 2:46 PM Last Edited by: McCreight, June 19, 1986 12:06:10 pm PDT ... stack to save context will mess up if someone uses Peek to make a decision just before a InFromFile is executed Private Procedures horrors, abort Ê ?˜Jšœ™Jšœ™Jšœ8™8J˜J˜šÏk ˜ Jšœœœ>˜K—J˜šœ œ˜Jšœœœ ˜Jšœ#˜*Jš˜J˜Jšœ œœ˜Jšœ œ˜Jšœœ˜#Jšœ œ˜Jšœœ˜Jš œ œœœœ˜&J˜Jšœ œ˜Jšœœ˜J˜š œ œœœœ˜5Jšœ œ˜Jšœ œ˜Jšœœœ˜Jšœ˜Jšœ™—Jšœœ œ ˜&J˜š Ïn œœ œœœ˜/Jš˜J˜Jšœ œ˜Jšœ œ˜J˜J˜Jšœœ˜ Jšœ˜J˜—š ž œœ œœœ˜1Jš˜J˜šœ˜Jš˜J˜<šœ˜J˜J˜J˜Jšœ˜—Jšœ˜—J˜Jšœœ˜ J˜Jšœ˜J˜—š žœœ œœ œ˜/Jš˜Jšœ œ ˜Jšœ œ˜J˜šœ˜Jš˜Jšœœ˜J˜(Jšœ ˜Jš˜—Jšœ œ˜Jš œ œœ œœ˜FJ˜š˜š œœœœ œ˜JJš˜J˜Jšœ˜Jšœ˜——Jš˜šœ˜Jšœœ˜4š˜Jš˜J˜J˜J˜Jšœ˜——Jšœ˜J˜J˜ šœ œ˜Jšœ(œœ˜7—Jšœ˜ J˜Jšœ˜J˜—š ž œœ œœœœ˜FJš˜Jšœ œœ˜J˜šœ˜!Jš˜J˜1Jšœœ˜Jšœ˜J˜—šœ˜Jš˜J˜ Jšœ˜J˜—šœ œ˜)šœœ ˜ Jš˜Jšœœœ˜