IMP Core Environment Standard


                 Section 5: Stream I/O System Overview

   The primary input/output (I/O) mechanism provided to the IMP
programmer is the stream I/O system, which allows the programmer to
access objects which may be seen as an ordered collection of byte values
(commonly characters).  This section attempts to give an overview of the
stream I/O system prior to its formal definition in the two following
sections.

   The explanation of the stream I/O system given here may seem more
complex than the previous informal definitions which have been
understood by IMP programmers in the past.  The reason for this is
twofold.  Firstly, the stream I/O system described here is somewhat more
complex than the original IMP-77 system in that 'nested' opening of
streams is permitted.  This implies that the original concept of a
stream as a direct connection to an object is no longer sufficient.
Secondly, the Imp Core Environment Standard must be able to define the
precise semantics of any combination of OPEN, CLOSE and other
operations.  The detailed description of the function of the stream I/O
system given here is intended to make it possible to resolve any
questions about an implementation's conformance to this standard without
relying on any informal understanding of the mechanism of stream
manipulations which may be biased by exposure to particular classes of
operating system.

   Note that, although the description below is given in terms of named
abstractions like object, route and stream, this does not imply that an
implementor is required by this standard to implement such entities.  An
implementor is left free to implement the semantics of the system
described here in any way which is convenient as long as that system
appears to the IMP programmer as described below.


5.1 Objects and Accessors

   The term object will be used throughout this definition to indicate
an ordered collection of byte values.  Some possible objects are: the
null object (which contains an empty sequence of bytes), an IMP string
value or an arbitrary sequence of bytes in memory.  One interesting
possibility for an object would be one which had no physical counterpart
at all, such as a process generating a sequence of values such as
"1, 2, 3, 4".  More common classes of object accessed through the stream
I/O system are those of operating system files and interactive terminal
devices.

   Any object which is being used by a program is said to be accessed.
An object might be accessed more than once simultaneously; each such
access is thought of as being owned by an entity which we will refer to
here as an accessor.  The accessor for a particular object access might
hold information such as the object's name, the current character
position within the object and more esoteric information such as the
appropriate operating system control blocks for the object if it is an
operating system file.

   Thus, the accessor "wraps up" the bare object so that it can be
usefully manipulated by adding notions such as the current position in
the object and the class of access to the object.  Most accessors are
either generators or acceptors of sequences of byte values.  For
example, an accessor used to read from a data file would be a generator
returning each character value from the file in turn.  Conversely, an
example of an acceptor would be an accessor being used to write to a
similar data file; each byte value presented to the accessor would be
appended to the data in the file.


5.2 Routes

   We have seen that at any particular time in the execution of a
program, there will be in existence a number of accessors, some of which
might be acceptors and some generators.  We have not yet seen how these
accessors are made visible to the programmer.

   The IMP stream I/O system allows reference to accessors only through
entities known as routes.  Each accessor is connected to a number of
routes, any of which may be used indistinguishably to refer to the
accessor.  The accessor is said to have these routes open to it: when
the last route open to an accessor is closed, the accessor is also
discarded.  This all implies that the original creation of an accessor
(i.e. the accessing of an object) also results in the creation of the
first route to that accessor.  In many cases this initial route will be
the only route ever opened to the accessor.


5.3 Streams and Selection

   So far, a system has been described in which the I/O environment of a
program at any time consists of a possibly large number of routes which
connect to accessors which access objects.  The IMP programmer
manipulates this system through an interface which we will now describe.

   At any given time, input operations performed by a program are
performed via an object known as the currently selected input stream and
similarly any output operations are performed via the currently selected
output stream.  These two entities are examples of streams, which are
simply last-in/first-out collections of routes.  The most recently
entered route in any stream is distinguished as the current route for
that stream.  Any I/O operations performed on a stream are performed on
the accessor connected to the stream's current route.  This accessor is
referred to as the accessor associated with the stream.

   Only a small number of streams exists in the stream I/O system; this
number is defined by each particular implementation.  These streams are
divided equally into two groups, input and output, with one of each
distinguished as the currently selected stream of that group.  Each
stream (input or output) is referenced by the IMP programmer using its
stream number, which is a small non-negative integer.  This number is
used to change the currently selected stream (a SELECT operation) or to
change the definition of a stream by adding a new route to it (an OPEN
operation).  As has been mentioned, all other operations refer to the
currently selected streams.


   There is always a currently selected input stream and a currently
selected output stream, even before the program has performed any SELECT
operations.  Prior to any select being performed by the program, input
stream 1 is the currently selected input stream and output stream 1 is
the currently selected output stream.


5.4 Initial State of Stream System

   Every IMP program which performs any useful work requires access to
the environment outside itself, to read input data, output results and
so forth.  Rather than require that every program explicitly perform an
OPEN operation before performing any input or output, the two lowest
numbered input and output streams (numbers 0 and 1) are defined to be
opened on the programs behalf as described below.  The higher numbered
streams (2..MAXSTREAM) are simply opened to the null object, with the
effect that any output written to output streams 2..MAXSTREAM is
discarded and all input streams 2..MAXSTREAM are always empty.

   The low-numbered streams are defined to be opened on the closest
equivalent in the host operating system to the following:

         0 in   "command input"
         0 out  "error output"
         1 in   "standard input"
         1 out  "standard output"

This arrangement provides a simple program with all that it needs to
operate: a stream of input data to process, a stream on which to place
the results, one to indicate error conditions and finally one on which
to accept commands or options.  More complex programs usually disregard
the standard input and standard output streams in favour of explicit
OPEN operations.

   Although most operating systems provide a set of facilities which map
reasonably well onto the above scheme, very few provide the whole set of
four standard streams and as a result most implementations must
compromise with, for example, command input and standard input being
mapped onto the same operating system facility.  Each implementation
must define (DEF0010; default stream mapping) the way in which the IMP
default streams map onto the underlying operating system's facilities.

   One well-known example of an operating system which the above scheme
maps very well onto is UNIX, where every program is provided with three
operating system streams "stdin", "stdout" and "stderr" corresponding to
"standard input", "standard output" and "error output" in the above
list.  An IMP program which processes input stream 1 giving results on
output stream 1 and errors on output stream 0 can be used under UNIX as
a "filter" program as the three operating system streams can be
redirected at each invocation of the program.  For example, consider the
following simple IMP program "uc.imp":

         begin
            integer Sym
            while not End Of Input cycle
               Read Symbol(Sym)
               Sym = Sym-'a'+'A' if 'a' <= Sym <= 'z'
               Print Symbol(Sym)
            repeat
         end

This program reads from its standard input stream, converts each
character in turn into upper case and puts the result onto its standard
output stream.  The program terminates when there is no more data to
convert.  On a UNIX system, this program could be invoked as part of a
"pipeline" of commands as follows:

         % cat fred | uc | lpr

Here, the output of the command "cat fred" becomes the standard input
stream for our example program, whose output is in turn fed into the
"lpr" command.

                      [still lots more to follow]