(*
    Title: 	FormatIO - Definitions
    LastEdit:	Thu May 24 14:26:33 1984
    Author: 	Mick Jordan; hacked by LDS
                Acorn Computers VLSIC Design Aids Group

    Formatted I/O package for basic types
*)

DEFINITION MODULE FormatIO;

FROM SYSTEM      IMPORT WORD, ADDRESS;
FROM SYSTEMTYPES IMPORT STRING;
FROM Streams     IMPORT STREAM;

EXPORT QUALIFIED NUMBERBASE, ReadCHAR, ReadINTEGER, ReadCARDINAL, ReadBOOLEAN,
                 ReadREAL, ReadSTRING, ReadChars,
		 INTEGERToChars, NumberToChars, BOOLEANToChars, REALToChars,
                 WriteCHAR, WriteSTRING, WriteChars, WriteINTEGER, WriteREAL,
                 WriteCARDINAL, WriteBOOLEAN,
                 NewLine, NewPage,
                 SetEscapeOn,
                 H, S, R, WriteF1, WriteF2, WriteF3, WriteF4;

(*  Since MODULA-2 does not provide a general escape sequence
    facility in string expressions, this package provides a
    run-time interpretation of escape sequences, allowing certain
    useful characters to be denoted.  The following escapes
    are recognised:-

        *C        carriage-return
        *P        Form-feed
        *N        newline
        *T        tab
        *S        space
        **        *
        *Xnn      hexadecimal code 'nn'
        *Z        *Z, where Z is any character other than one of the above.

    These escapes are recognised in all arguments passed as ARRAY OF CHAR
    or STRING to any of the procedures in the package, unless the feature
    is turned off by SetEscapeOn(FALSE).
*)

PROCEDURE SetEscapeOn(seton: BOOLEAN);
(*  interpret escapes iff `seton' = TRUE.
    The default setting is `seton' = TRUE.
*)

(*  the following procedures provide input and output 
    of basic language types, plus the STRING type
    exported by SYSTEMTYPES.
    
    On output, the characters are placed on the stream
    given as first argument by Streams.Put.
    On input, characters are taken off the stream
    given as first argument, with Streams.Get.

    The procedures ReadINTEGER, ReadCARDINAL,
    ReadBOOLEAN, ReadREAL, ReadNumber skip all leading
    whitespace (spaces, tabs, newlines, form-feeds).
    and terminate reading when any character outside the range
    defined by the radix is encountered.

    Signed numbers are only accepted by ReadINTEGER and ReadREAL.    
    
    All input routines put the terminating character back on the stream.
*)

PROCEDURE WriteCHAR(s: STREAM; ch: CHAR);
PROCEDURE ReadCHAR(s: STREAM): CHAR;

PROCEDURE INTEGERToChars(i : INTEGER;  VAR s : ARRAY OF CHAR);
PROCEDURE WriteINTEGER(s : STREAM;  i : INTEGER);
PROCEDURE ReadINTEGER(s: STREAM): INTEGER;

PROCEDURE WriteCARDINAL(s : STREAM;  c : CARDINAL);
PROCEDURE ReadCARDINAL(s: STREAM): CARDINAL;

PROCEDURE BOOLEANToChars(bool: BOOLEAN;  VAR s : ARRAY OF CHAR);
PROCEDURE WriteBOOLEAN(s : STREAM;  bool : BOOLEAN);
PROCEDURE ReadBOOLEAN(s: STREAM): BOOLEAN;
(*  expects the text TRUE or FALSE and returns result accordingly *)
  
PROCEDURE REALToChars(real: REAL; decplaces: CARDINAL; VAR s : ARRAY OF CHAR);
(*  Output 'real' as a signed real number in the format i.f,  where *)
(* 'decplaces' is the number of digits for f.                       *)
PROCEDURE WriteREAL(s : STREAM;  real : REAL;  decplaces : CARDINAL);
PROCEDURE ReadREAL(s: STREAM): REAL;
(*  Reads a real number in the format  i.fEs                          *)
(*  where i and s can be signed, and everything after 'i' is optional.*)
(*  E.g.  45.67E-8    -1.2    4                                       *)

PROCEDURE WriteSTRING(s: STREAM; string: STRING);
PROCEDURE ReadSTRING(s: STREAM; VAR string : STRING; 
                            termchars: ARRAY OF CHAR);
(*  Reads a string until one of the characters in 'termchars' *)
(*  is encountered, or EndOfStream is reached.                *)
(*  The terminating character is put back on the stream.      *)

PROCEDURE WriteChars(s: STREAM; chars: ARRAY OF CHAR);
PROCEDURE ReadChars(s: STREAM; VAR string: ARRAY OF CHAR; 
                                termchars: ARRAY OF CHAR);
(*  Reads a string until one of the characters in 'termchars' is *)
(*  encountered, or EndOfStream is reached or HIGH(string) is    *)
(*  reached. The terminating character is put back on the stream.*)

(*  Procedures to cope with numbers (CARDINALs) in different radices *)

TYPE NUMBERBASE = [2 .. 37];			(* 0-9, A-Z *)

PROCEDURE NumberToChars(base : NUMBERBASE;  number : CARDINAL;
                                         VAR s : ARRAY OF CHAR);
PROCEDURE ReadNumber(s: STREAM; base: NUMBERBASE): CARDINAL;
(*  Reads a number to the given base. *)

(*  The following procedures allow a number of arguments
    to be interpreted dynamically according to a format
    string.  Within the format string the character '%' is
    treated specially and the next argument is output by
    on of the above procedures depending on the character(s)
    following the '%', as follows:-

               $:     just skip the argument
               Cn:     WriteCHAR(arg);
               Un:     WriteCARDINAL(arg);
               In:     WriteINTEGER(arg);
               Bn:     WriteBOOL(arg);
               Sn:     WriteSTRING(arg);
               Rn.m:   WriteREAL(arg, m)
               Rn:     WriteREAL(arg, 0);
               On:     WriteNumber(8, arg);
               Xn:     WriteNumber(16, arg);

    n is optional and means output the object
    right justified in a field width of `n' characters,
    or the minimum necessary.
    n, m may be 1 or 2 digit decimal numbers.

    Note that since all the arguments must map on to type WORD,
    it is not possible to pass actual arguments of type
    ARRAY OF CHAR as arguments to %S formats.  Rather the
    result of applying procedure H (defined below) to the 
    argument, must be passed instead. Since this demands 
    a VAR parameter, it is not possible to pass literal
    strings as %S arguments, except by preassignment to variables.

    H essentially squirrels away details on the character array,
    and returns a unique key to this information.
    There is thus no restriction on the order in which
    H is called and the order of the matchine %S fields
    in the format string.

    Some packages work with pointers to string arrays all
    the time (e.g. STRING), and it is possible to match these to %S fields
    by using the S function, instead of H, PROVIDED
    that the actual string array terminates with a 0C character.

    Furthermore REAL values cannot be substituted for WORD
    parameters on sixteen bit machines, therefore the result of
    applying a procedure called R must be passed instead of the argument.  

    If all this seems complicated, dont' panic, in practice it
    is quite straightforward.
*)

TYPE FORMATKEY; (* opaque type for H,S,R function returns *)

PROCEDURE H(VAR string: ARRAY OF CHAR): FORMATKEY;
(*  See above: e.g. 
    VAR string: ARRAY [0 .. 40] OF CHAR;
    string := "Hello Folks";
    WriteF1("String is %S*N", H(string));
*)

PROCEDURE S(s : STRING): FORMATKEY;
(*  See above: e.g. 
    FROM String IMPORT STRING, New;
    VAR ps: STRING;
    New(ps, "Hello Folks"); (* 0C padding guaranteed *)
    WriteF1("String is %S*N", S(ps));
*)

PROCEDURE R(r: REAL): FORMATKEY;
(*  See above: e.g.
    VAR pi: REAL;
    pi := 3.14156;  WriteF1("PI = %E2.5*N", R(pi));
*)
    
(*  Variants of WriteFn for 1 to 4 arguments
*)
PROCEDURE WriteF1(s: STREAM; format: ARRAY OF CHAR; arg1: WORD);
PROCEDURE WriteF2(s: STREAM; format: ARRAY OF CHAR; arg1: WORD; arg2: WORD);
PROCEDURE WriteF3(s: STREAM; format: ARRAY OF CHAR; arg1: WORD; arg2: WORD; 
                  arg3: WORD);
PROCEDURE WriteF4(s: STREAM; format: ARRAY OF CHAR; arg1: WORD; arg2: WORD;
                  arg3: WORD; arg4: WORD);

PROCEDURE NewLine(s: STREAM);	(* WriteChars(s, "*N")      *)
PROCEDURE NewPage(s: STREAM);	(* WriteChars(s, "*P")      *)

END FormatIO .
