(*
    Title: 	WriteF - Definitions
    LastEdit:	"Sat Nov 10 14:48:17 1984"
    Author: 	Mick Jordan
		Acorn Research Centre

    Formatted Output package for standard types
*)

DEFINITION MODULE WriteF;
FROM SYSTEM IMPORT WORD;
FROM SystemTypes IMPORT String;
FROM Streams IMPORT Stream;

EXPORT QUALIFIED
    WriteF0, WriteF1, WriteF2, WriteF3, WriteF4, H, S, R, FormatKey;

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

        \C        carriage-return
        \P        page-throw (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 format strings.
*)

(*  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:     TextIO.WriteCHAR(arg);
               Un:     TextIO.WriteCARDINAL(arg);
               In:     TextIO.WriteINTEGER(arg);
               Bn:     TextIO.WriteBOOL(arg);
               Sn:     TextIO.WriteString(ValueFromKey(arg));
                       TextIO.WriteChars(ValueFromKey(arg));
               Rn.m:   TextIO.WriteREAL(ValueFromKey(arg), m)
               Rn:     TextIO.WriteREAL(ValueFromKey(arg), 0);
               Tn.m:   TextIO.WriteBITSET(arg, m);
               Tn:     TextIO.WriteBITSET(arg, SYSTEM.BITSPERWORD)
               On:     TextIO.WriteBasedCARDINAL(arg, 8);
               Xn:     TextIO.WriteBasedCARDINAL(arg, 16);

    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.
    All padding is done with blanks.

    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 pre-assignment 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 matching %S fields
    in the format string.

    Some programs prefer to work with pointers to string arrays all
    the time (i.e. String), and it is possible to match these to %S fields
    by using the S function, instead of H.

    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.
   There is a fixed number of keys available.
   Repeatedly calling the H, S or R without the related
   WriteF calls, will ultimately generate the NoMoreKeys
   exception
*)

(* EXCEPTION NoMoreKeys *)

PROCEDURE H(VAR string: ARRAY OF CHAR): FormatKey
          (* RAISES {NoMoreKeys} *);
(*  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
          (* RAISES {NoMoreKeys} *);
(*  See above: e.g. 
    FROM String IMPORT String, CopyCS;
    VAR s: String;
    s := CopyCS("Hello Folks"); (* 0C padding guaranteed *)
    WriteF1(sysOut, "String is %S*N", S(ps));
*)

PROCEDURE R(r: REAL): FormatKey
          (* RAISES {NoMoreKeys} *);
(*  See above: e.g.
    VAR pi: REAL;
    pi := 3.14156;
    WriteF1(sysOut, "PI = %R2.5*N", R(pi));
*)
    
(*  Variants of WriteFn for 0 <= n <= 4 *)

PROCEDURE WriteF0(s: Stream; format: ARRAY OF CHAR);
(*  simply to get character escape sequences
*)

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);

(*  If you need more than four arguments, split the call into 
    a sufficient number of calls of the 1-4 variants.
*)

END WriteF .
