9.4 Sets and the I/O Library

It was remarked in passing that the I/O library flags read, write, raw, text, old, and echo are all in fact sets. It is instructive to see how this is done. Part of the module ChanConsts first defines the enumerated type ChanFlags and the set type FlagSet as follows:

TYPE
  ChanFlags =      (* Request flags possibly given when a channel is opened *)
  ( readFlag,      (* input operations are requested/available *)
    writeFlag,     (* output operations are requested/available 
    oldFlag,       (* a file may/must/did exist before the channel is opened *)
    textFlag,      (* text operations are requested/available *)
    rawFlag,       (* raw operations are requested/available *)
    interactiveFlag;(* interactive use is requested/applies *)
    echoFlag       (* echoing by interactive device on removal of characters from input stream requested/applies *)
  );
 
  FlagSet = SET OF ChanFlags;

Observe the appropriate prettyprinting when the meaning of each value in the enumerated type is documented. Because values of type FlagSet are to be passed to a variety of I/O operations, and because each flag often has to be referred to as a singleton, ChanConsts then defines the following convenience singleton sets:

CONST
  read = FlagSet{readFlag};
  write = FlagSet{writeFlag};
  old = FlagSet{oldFlag};
  text = FlagSet{textFlag};
  raw = FlagSet{rawFlag};
  interactive = FlagSet{interactiveFlag};
  echo = FlagSet{echoFlag};

Of these, the only one not already used in the last chapter is interactive. It is available for vendors to establish device drivers employing the concept of an interactive terminal in some implementation defined fashion, but is not actually employed by any device driver in the standard library itself.

For further convenience, device driver definition modules such as StreamFile import the constants and then redefine them as their own:

DEFINITION MODULE StreamFile;

IMPORT IOChan, ChanConsts;
 
TYPE
  ChanId = IOChan.ChanId;
  FlagSet = ChanConsts.FlagSet;
  OpenResults = ChanConsts.OpenResults;
 
  (* Accepted singleton values of FlagSet *)
 
CONST
  read = FlagSet{ChanConsts.readFlag};
  (* input operations are requested/available *)
  write = FlagSet{ChanConsts.writeFlag};
  (* output operations are requested/available *)
  old = FlagSet{ChanConsts.oldFlag};
  (* a file may/must/did exist before the channel is opened *)
  text = FlagSet{ChanConsts.textFlag};
  (* text operations are requested/available *)
  raw = FlagSet{ChanConsts.rawFlag};
  (* raw operations are requested/available *)

In this manner, only the flags that are useful for that particular device driver are given a specific meaning by for it and are made available by it. When StreamFile then defines items using the type FlagSet (that is, its own version of the type), such as:

PROCEDURE Open (VAR cid: ChanId;
                      name: ARRAY OF CHAR;
                      flags: FlagSet; VAR res: OpenResults);

it becomes possible to call these in the convenient fashion already used in the last chapter, and employing the set union operator to indicate more than one flag:

  Open (theChan, theName, read+old, res);

Observe that StreamFile, in common with other device drivers, also imports and redefines as its own the type ChanId. Thus, a person writing software employing such a device driver need not import directly from the lower level where reside ChanConsts and IOChan.


Contents