(* PushBackInput.mod provides a method for pushing back and consuming input. Copyright (C) 2001-2025 Free Software Foundation, Inc. Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>. This file is part of GNU Modula-2. GNU Modula-2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Modula-2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Under Section 7 of GPL version 3, you are granted additional permissions described in the GCC Runtime Library Exception, version 3.1, as published by the Free Software Foundation. You should have received a copy of the GNU General Public License and a copy of the GCC Runtime Library Exception along with this program; see the files COPYING3 and COPYING.RUNTIME respectively. If not, see <http://www.gnu.org/licenses/>. *) IMPLEMENTATION MODULE PushBackInput ; FROM FIO IMPORT ReadChar, IsNoError, EOF, OpenToRead, WriteChar, StdErr ; FROM DynamicStrings IMPORT string, Length, char ; FROM ASCII IMPORT nul, cr, lf ; FROM Debug IMPORT Halt ; FROM StrLib IMPORT StrCopy, StrLen ; FROM NumberIO IMPORT WriteCard ; FROM StrIO IMPORT WriteString, WriteLn ; FROM StdIO IMPORT Write, PushOutput, PopOutput ; FROM libc IMPORT exit ; IMPORT FIO ; CONST MaxPushBackStack = 8192 ; MaxFileName = 4096 ; VAR FileName : ARRAY [0..MaxFileName] OF CHAR ; CharStack : ARRAY [0..MaxPushBackStack] OF CHAR ; ExitStatus: CARDINAL ; Column, StackPtr, LineNo : CARDINAL ; Debugging : BOOLEAN ; (* GetCh - gets a character from either the push back stack or from file, f. *) PROCEDURE GetCh (f: File) : CHAR ; VAR ch: CHAR ; BEGIN IF StackPtr>0 THEN DEC(StackPtr) ; IF Debugging THEN Write(CharStack[StackPtr]) END ; RETURN( CharStack[StackPtr] ) ELSE IF EOF(f) OR (NOT IsNoError(f)) THEN ch := nul ELSE REPEAT ch := ReadChar(f) UNTIL (ch#cr) OR EOF(f) OR (NOT IsNoError(f)) ; IF ch=lf THEN Column := 0 ; INC(LineNo) ELSE INC(Column) END END ; IF Debugging THEN Write(ch) END ; RETURN( ch ) END END GetCh ; (* PutStr - pushes a dynamic string onto the push back stack. The string, s, is not deallocated. *) PROCEDURE PutStr (s: String) ; VAR i: CARDINAL ; BEGIN i := Length (s) ; WHILE i > 0 DO DEC (i) ; IF PutCh (char (s, i)) # char (s, i) THEN Halt('assert failed', __FILE__, __FUNCTION__, __LINE__) END END END PutStr ; (* PutString - pushes a string onto the push back stack. *) PROCEDURE PutString (a: ARRAY OF CHAR) ; VAR l: CARDINAL ; BEGIN l := StrLen (a) ; WHILE l > 0 DO DEC (l) ; IF PutCh (a[l]) # a[l] THEN Halt ('assert failed', __FILE__, __FUNCTION__, __LINE__) END END END PutString ; (* PutCh - pushes a character onto the push back stack, it also returns the character which has been pushed. *) PROCEDURE PutCh (ch: CHAR) : CHAR ; BEGIN IF StackPtr<MaxPushBackStack THEN CharStack[StackPtr] := ch ; INC(StackPtr) ELSE Halt('max push back stack exceeded, increase MaxPushBackStack', __FILE__, __FUNCTION__, __LINE__) END ; RETURN( ch ) END PutCh ; (* Open - opens a file for reading. *) PROCEDURE Open (a: ARRAY OF CHAR) : File ; BEGIN Init ; StrCopy(a, FileName) ; RETURN( OpenToRead(a) ) END Open ; (* Close - closes the opened file. *) PROCEDURE Close (f: File) ; BEGIN FIO.Close(f) END Close ; (* ErrChar - writes a char, ch, to stderr. *) PROCEDURE ErrChar (ch: CHAR) ; BEGIN WriteChar(StdErr, ch) END ErrChar ; (* Error - emits an error message with the appropriate file, line combination. *) PROCEDURE Error (a: ARRAY OF CHAR) ; BEGIN PushOutput(ErrChar) ; WriteString(FileName) ; Write(':') ; WriteCard(LineNo, 0) ; Write(':') ; WriteString(a) ; WriteLn ; PopOutput ; FIO.Close(StdErr) ; exit(1) END Error ; (* WarnError - emits an error message with the appropriate file, line combination. It does not terminate but when the program finishes an exit status of 1 will be issued. *) PROCEDURE WarnError (a: ARRAY OF CHAR) ; BEGIN PushOutput(ErrChar) ; WriteString(FileName) ; Write(':') ; WriteCard(LineNo, 0) ; Write(':') ; WriteString(a) ; WriteLn ; PopOutput ; ExitStatus := 1 END WarnError ; (* WarnString - emits an error message with the appropriate file, line combination. It does not terminate but when the program finishes an exit status of 1 will be issued. *) PROCEDURE WarnString (s: String) ; VAR p : POINTER TO CHAR ; BEGIN p := string(s) ; WriteString(FileName) ; Write(':') ; WriteCard(LineNo, 0) ; Write(':') ; REPEAT IF p#NIL THEN IF p^=lf THEN WriteLn ; WriteString(FileName) ; Write(':') ; WriteCard(LineNo, 0) ; Write(':') ELSE Write(p^) END ; INC(p) END ; UNTIL (p=NIL) OR (p^=nul) ; ExitStatus := 1 END WarnString ; (* GetExitStatus - returns the exit status which will be 1 if any warnings were issued. *) PROCEDURE GetExitStatus () : CARDINAL ; BEGIN RETURN( ExitStatus ) END GetExitStatus ; (* SetDebug - sets the debug flag on or off. *) PROCEDURE SetDebug (d: BOOLEAN) ; BEGIN Debugging := d END SetDebug ; (* GetColumnPosition - returns the column position of the current character. *) PROCEDURE GetColumnPosition () : CARDINAL ; BEGIN IF StackPtr>Column THEN RETURN( 0 ) ELSE RETURN( Column-StackPtr ) END END GetColumnPosition ; (* GetCurrentLine - returns the current line number. *) PROCEDURE GetCurrentLine () : CARDINAL ; BEGIN RETURN( LineNo ) END GetCurrentLine ; (* Init - initialize global variables. *) PROCEDURE Init ; BEGIN ExitStatus := 0 ; StackPtr := 0 ; LineNo := 1 ; Column := 0 END Init ; BEGIN SetDebug(FALSE) ; Init END PushBackInput.