IO.mesa
Top-level stream and I/O interface for Cedar
Last edited by: Teitelman on October 27, 1982 3:05 pm
Note: this interface is heavily structured using Tioga Nodes. Thus, you can use the level clipping to obtain a table of contents, and to successively refined levels of detail in the areas you are especially interested.
DIRECTORY
Ascii USING [CR, SP, TAB, LF, BS, ControlA, ControlX, FF, NUL, ESC, DEL, BEL],
Atom USING [PropList],
Rope USING [ROPE],
RTBasic USING [TV, Type],
System USING [GreenwichMeanTime],
Time USING [Current]
;
IO: CEDAR DEFINITIONS
IMPORTS Time
= BEGIN
Types
STREAM: TYPE = REF STREAMRecord;
STREAMRecord: TYPE = RECORD[
streamProcs: REF StreamProcs, -- see below
streamData: REF ANY, -- data private to streamProcs
propList: Atom.PropList ← NIL, -- permits associating arbitrary information with stream. see StoreData, LookupData, and RemoveData below.  
backingStream: STREAMNIL -- for use with layered streams.
];
Handle: TYPE = STREAM; -- for backward compatibility
ROPE: TYPE = Rope.ROPE;
TV: TYPE = RTBasic.TV;
Type: TYPE = RTBasic.Type;
Base: TYPE = [2..36];
GreenwichMeanTime: TYPE = System.GreenwichMeanTime;
Creating Streams
CreateViewerStreams: PROC [name: ROPE] RETURNS [in: STREAM, out: STREAM];
creates a new viewer with indicated name, and returns two streams "connected" to that viewer, one for input and one for output. The input stream is an edited stream. Note that ViewerIO.CreateViewerStreams...
enables creating streams that are connected to an existing viewer, or specifying that the input stream is not an edited stream, e.g. so that the client can then specify his own deliverWhen procedure when creating an edited stream on top of the input stream.
CreateFileStream To create a stream for reading/writing on a file use FileIO.Open.
CreateInputStreamFromRope, RIS: PROC [rope: ROPE, oldStream: STREAMNIL] RETURNS [stream: STREAM];
stream gets chars from user's rope. If oldStream is non-NIL, it is reused rather than allocating space for a new stream.
CreateOutputStreamToRope, ROS: PROC RETURNS [stream: STREAM];
stream puts chars into a rope which can be retrieved via GetOutputStreamRope, described below. Fine point...
in order to perform this operation efficiently, a REF TEXT is obtained from a pool of scratch REF TEXTs. As a result, it is a good idea to close the stream when it is no longer going to be used in order to return the scratch structure to a common pool. However, if you do not, e.g. if an error occurs, all that will happen is that the structure will be garbage collected when it is no longer referenced, and new scratch structure will be allocated to take its place in the pool.
CreateInputStreamFromText: PROC [text: REF READONLY TEXT, oldStream: STREAMNIL] RETURNS [stream: STREAM];
stream gets chars from user's REF TEXT. If oldStream is non-NIL, it is reused rather than allocating space for a new stream.
CreateOutputStreamToText: PROC [text: REF TEXT, oldStream: STREAMNIL] RETURNS [stream: STREAM];
stream puts characters into text. If oldH is non-NIL, it is reused rather than allocating space for a new stream.
CreateEditedStream: PROCEDURE [in: STREAM, echoTo: STREAM, deliverWhen: DeliverWhenProc ← IsACR] RETURNS [stream: STREAM];
creates a layered inputstream on top of in which buffers input characters and allows the user to perform character editing with ^A, ^W, ^Q. Typing a DEL will cause CreateEditedStream to raise IO.Signal[Rubout]. Default is to buffer to carriage return. Input characters are echoed to and erased from echoTo. ChangeDeliverWhen can be used to change buffering condition.
DeliverWhenProc: TYPE = PROC[char: CHAR, stream: STREAM] RETURNS[BOOL];
IsACR: DeliverWhenProc; -- returns TRUE for CR.
ChangeDeliverWhen: PROC [self: STREAM, proc: DeliverWhenProc] RETURNS [oldProc: DeliverWhenProc];
currently implemented by Edited Stream and Viewer Edited Stream (See ViewerIO).
CreateDribbleStream: PROCEDURE [stream: STREAM, dribbleTo: STREAM, flushEveryNChars: INT ← -1] RETURNS [s: STREAM];
creates a layered stream, s, on top of stream, such that characters read from/printed to s will be read from/printed to stream, and also output to dribbleTo. If flushEveryNChars is > 0, will automatically call Flush on dribbleTo every nChars.
CreateBufferedOutputStream: PROC [stream: STREAM, deliverWhen: DeliverWhenProc ← IsACR, bufferSize: NAT ← 256] RETURNS [s: STREAM];
creates an output stream on top of stream such that characters are buffered until (a) the bufferSize is exceeded, or (b) deliverWhen[char] is TRUE, or (c) an explicit Flush is performed, at which point the characters are output to stream using PutBlock.
CreateFilterCommentsStream: PROCEDURE [stream: IO.STREAM] RETURNS [s: STREAM];
creates a input stream on top of stream such that any comments are filtered out, i.e. the characters comprising the comments are not returned by GetChar[s]. A comment consists of that sequence of characters beginning with -- and ending with -- or a CR. In the latter case, the CR is not considered to be part of the comment.
AppendStreams: PROC [in: STREAM, from: STREAM, appendToSource: BOOLTRUE];
modifies `in' so that an attempt to read characters from `in' will cause the characters to be obtained from `from' until `from' runs dry, at which point `in' is returned to its previous state. Note that it is permissible to nest, i.e. concatenate, more than one stream in this fashion.
If appendToSource = TRUE then modification is done to the root stream, i.e. descends through non-NIL backingstreams. For example, if in is an edited stream, then this will cause characters to be inserted into buffer, from which they can be backspaced over, exactly as though they had been typed, rather than delivered to the client immediately. If in is a dribbleStream, then the characters will be dribbled etc.
noWhereStream: STREAM;
output stream that simply discards its characters.
Unformatted stream procedures: GetChar, PutChar, Close, etc.
Note: Not all of the following are implemented for all streams, e.g. GetLength is not implemented (does not make sense) for a keyboard stream. If an operation is invoked that is not implemented, the Error[NotImplementedForThisStream] is raised. However, many operations are defaulted even when they are not implemented. See discussion below of exactly which operations are implemented for which streams.)
Input Procedures: GetChar, EndOf, CharsAvail, GetBlock, Backup, PeekChar, SetEcho, ...
GetChar: PROC[self: STREAM] RETURNS[CHAR]
= INLINE {RETURN[self.streamProcs.getChar[self]]};
EndOf: PROC[self: STREAM] RETURNS [BOOL]
= INLINE {RETURN[self.streamProcs.endOf[self]]};
true if reading from this stream would raise an endofstream error.
CharsAvail: PROC[self: STREAM] RETURNS [BOOL]
= INLINE {RETURN[self.streamProcs.charsAvail[self]]};
true if self.GetChar[] would return a character without waiting.
Note: ...
For most streams, EndOf is equivalent to ~CharsAvail, and in fact, if CharsAvail is not specified, a default procedure is supplied which will returns as its value NOT EndOf. However, note that EndOf is always FALSE for a keyboard stream, i.e. it is always ok to try to read from a keyboard stream, even when CharsAvail is FALSE, i.e. there are no more characters available now.
GetBlock: PROC[self: STREAM, block: REF TEXT, startIndex: NAT ← 0, stopIndexPlusOne: NATLAST[NAT]] RETURNS[nBytesRead: NAT]
= INLINE {RETURN[self.streamProcs.getBlock[self, block, startIndex, stopIndexPlusOne]]};
value is number of characters actually returned. Equivalent to, but more efficient than, n calls to getChar...
for those streams that provide an implementation for getBlock, e.g. file streams. Otherwise, the getBlock is exactly the same as, i.e. is accomplished by, performing n calls to GetChar. See discussion of default procedures below.
UnsafeGetBlock: UNSAFE PROC[self: STREAM, block: UnsafeBlock] RETURNS[nBytesRead: INT]
= UNCHECKED INLINE {RETURN[self.streamProcs.unsafeGetBlock[self, block]]};
value is number of characters actually returned. The type UnsafeBlock...
is defined below in the section "Private and Semi-private types."
Backup: PROC [self: STREAM, char: CHAR];
Used when input went too far...
Note that Backup is an operation on the input stream. It modifies self so that the next getChar[self] will return the same character and leave self in the state it was in before the Backup. The implementor is only required to implement Backup for those situations where char is in fact the character that was last read by getChar. (The character argument is required in order to provide a generic implementation for the operation, i.e. when the particular stream does not implement Backup itself).
PeekChar: PROC[self: STREAM] RETURNS [char: CHAR];
does a GetChar followed by a Backup, i.e. effectively looks at the next character without actually reading it.
SetEcho: PROC[self: STREAM, echoTo: STREAM] RETURNS [oldEcho: STREAM];
causes every character read from stream to be echoed to echoTo if non-NIL.
output procedures: PutChar, PutBlock, Flush, EraseChar, GetOutputStreamRope, ...
PutChar: PROC[self: STREAM, char: CHAR]
= INLINE {self.streamProcs.putChar[self, char]};
PutBlock: PROC[self: STREAM, block: REF READONLY TEXT, startIndex: NAT ← 0, stopIndexPlusOne: NATLAST[NAT]]
= INLINE {self.streamProcs.putBlock[self, block, startIndex, stopIndexPlusOne]};
UnsafePutBlock: PROC[self: STREAM, block: UnsafeBlock]
= INLINE {self.streamProcs.unsafePutBlock[self, block]};
Flush: PROC [self: STREAM]
= INLINE {self.streamProcs.flush[self]};
causes characters that have been output to stream, but not yet sent, e.g. because of buffering, to be sent
EraseChar: PROC[self: STREAM, char: CHAR];
erases char from output stream, e.g. erases the bits from the display...
Note that EraseChar, is an operation on the output stream. Whatever the corresponding input stream might have done about the character that is being erased is an entirely separate operation EraseChar just specifies what is to be done to the output stream.
CurrentPosition: PROC[self: STREAM] RETURNS[position: INT];
number of characters output since last CR.
NewLine: PROC [self: STREAM]
= INLINE {IF self.CurrentPosition[] # 0 THEN self.PutChar['\n]};
outputs a CR unless already at beginning of the line.
GetOutputStreamRope: PROC [self: STREAM] RETURNS [ROPE];
used in conjuction with the CreateOutputStreamToRope to get the resulting rope
Miscellaneous Procedures: Reset, Close, UserAbort, GetIndex, SetIndex, GetLength, SetLength
Reset: PROC[self: STREAM]
= INLINE {self.streamProcs.reset[self]};
restores the stream to a clean state, e.g. may flush or restore internal buffers. typically invoked following an error,
Close: PROC[self: STREAM, abort: BOOLFALSE]
= INLINE {self.streamProcs.close[self, abort]};
UserAbort: PROC [stream: STREAM] RETURNS [abort: BOOLEAN];
If unimplemented for corresponding stream or backing stream, returns FALSE. TRUE means user indicated desire to abort operation. It is up to program to take the appropriate steps. Currently implemented for ViewerStreams (checks Typescript.UserAbort), which is set by typing control-Del to the viewer, or if the viewer is connected a userexec, can also be set by clicking STOP).
SetUserAbort: PROC [stream: STREAM];
sets the user abort, i.e. provides a programmable way to abort (the operation currently running in) an exec. Generates an error if not implemented for the corresponding stream. Currently only implemented for ViewerStreams.
ResetUserAbort: PROC [stream: STREAM] ;
turn the abort bit off, if implemented. Otherwise, nop.
GetIndex: PROC[self: STREAM] RETURNS [index: INT]
= INLINE {RETURN[self.streamProcs.getIndex[self]]};
SetIndex: PROC[self: STREAM, index: INT]
= INLINE {self.streamProcs.setIndex[self, index]};
GetLength: PROC[self: STREAM] RETURNS [length: INT];
SetLength: PROC[self: STREAM, length: INT];
Formatted stream procedures: Put, PutF, PutList, PutRope, PutTV, PutType, ...
Put: PROC [stream: STREAM, v1, v2, v3: Value ← [null[]]];
The basic output routine, handles any type via Values, defined later. Provides convenient default behaviour for simple output, i.e. outputs integers in decimal, cardinals in octal, time using both date and time, etc. Example: s.Put[int[x+y], rope[" is greater than "], int[x]] would produce '8 is greater than 5', for x = 5 and y = 3. For more control over the output...
use PutF, described below, which allows specification of field Width, base, etc.
Put only takes three value arguments...
so that the procedure call can be "fast" (i.e. take 11 or fewer words on the stack). PutList can be used for calls specifying more arguments at the expense of the allocation of space for the LIST, or the user can write several calls to Put.
PutF: PROC [stream: STREAM, format: ROPENIL, v1, v2, v3, v4, v5: Value ← [null[]]];
produces output according to a collection of values and a format control string which can include embedded format codes. For example: s.PutF["%d is greater than %d", int[x+y], int[x]] is equivalent to the above call on Put. For more discussion, see section "PF Package".
format = NIL is equivalent to "%g%g%g..." which means to print each value the same as would be printed by Put.
Note that PutF is a slow procedure call, i.e. its arguments require more than 11 words on the stack.
PutF may raise signals. RESUME will always work and will either do
nothing or print "#####".
PutList, PutL: PROC [stream: STREAM, list: LIST OF Value];
for outputting more than three quantities via a single call to Put.
PutFList, PutFL: PROC [stream: STREAM, format: ROPENIL, list: LIST OF Value];
PutFToRope, PutFR: PROC [format: ROPENIL, v1, v2, v3, v4, v5: Value ← [null[]]] RETURNS [r: ROPE]
= INLINE
{stream: STREAM ← CreateOutputStreamToRope[]; stream.PutF[format, v1, v2, v3, v4, v5]; r ← stream.GetOutputStreamRope[]; stream.Close[];
};
PutText: PROC [self: STREAM, t: REF READONLY TEXT]
= INLINE {self.PutBlock[t]};
much more efficient than self.Put[text[t]];
PutRope: PROC [self: STREAM, r: ROPE];
much more efficient than self.Put[rope[r]];
PutTV: PROCEDURE[stream: STREAM, tv: TV, depth: INT ← 4, width: INT ← 32, verbose: BOOLFALSE];
depth and width control the cutoff for printing of the tv, verbose specifies whether to print additional information, e.g. the octal value for global frames, etc. stream.Put[tv[x]] is equivalent to stream.PutTV[x], and is the proper way to print tvs for most applications..
PutType: PROC[stream: STREAM, type: Type, depth: INT ← 4, width: INT ← 32, verbose: BOOLEANFALSE];
If verbose is FALSE, just the name of the type is printed. If verbose is TRUE, the undertype is also printed for named types. For example, if x is of type BreakProc, then stream.PutType[x, FALSE] prints: IO.BreakProc, whereas stream.PutType[x, TRUE] prints as: IO.BreakProc = PROC [c: CHAR] RETURNS [IO.BreakAction].
In addition, the range type is printed for undertypes that are refs or pointers. For example, if x is of type STREAM, stream.PutType[x, TRUE] prints as: IO.STREAM = REF IO.STREAMRecord; (and on the next line) IO.STREAMRecord: TYPE = RECORD [...];
stream.Put[type[x]] is equivalent to stream.PutType[type]
PutSignal: PROC [stream: IO.STREAM, signalTV, argsTV: TVNIL];
Prints the indicated signal on stream. If signalTV = NIL, prints the "current" signal, i.e. for use inside of a catch phrase.
Procedures for constructing Values: atom, bool, card, char, int, real, refAny, rope, tv, ...
saves writing two extra brackets. the type Value is defined in section 4.
atom: PROC [v: ATOM] RETURNS [Value]
= INLINE {RETURN[[atom[v]]]};
bool: PROC [v: BOOL] RETURNS [Value]
= INLINE {RETURN[[boolean[v]]]};
card: PROC [v: LONG CARDINAL] RETURNS [Value]
= INLINE {RETURN[[cardinal[v]]]};
char: PROC [v: CHAR] RETURNS [Value]
= INLINE {RETURN[[character[v]]]};
int: PROC [v: INT] RETURNS [Value]
= INLINE {RETURN[[integer[v]]]};
real: PROC [v: REAL] RETURNS [Value]
= INLINE {RETURN[[real[v]]]};
refAny: PROC [v: REF READONLY ANY] RETURNS [Value]
= INLINE {RETURN[[refAny[v]]]};
rope: PROC [v: ROPE] RETURNS [Value]
= INLINE {RETURN[[rope[v]]]};
string: PROC [v: LONG STRING] RETURNS [Value]
= INLINE {RETURN[[string[v]]]};
text: PROC [v: REF READONLY TEXT] RETURNS [Value]
= INLINE {RETURN[[text[v]]]};
time: PROC [v: GreenwichMeanTime ← Time.Current[]] RETURNS [Value]
= INLINE {RETURN[[time[v]]]}; -- i.e. time[] is the current time
tv: PROC [v: RTBasic.TV] RETURNS [Value]
= INLINE {RETURN[[tv[v]]]};
type: PROC [t: RTBasic.Type] RETURNS [Value]
= INLINE {RETURN[[type[t]]]};
Basic input routines for each value type: GetCard, GetInt, GetReal, GetRefAny, ...
Example: i: INT ← stream.GetInt[]; These procs can also be used on a rope r via the RIS routine: GetInt[RIS[r]], etc.
GetAtom: PROC [stream: STREAM] RETURNS [ATOM];
-- calls GetRefAny and narrows result.
GetBool: PROC[stream: STREAM] RETURNS[BOOLEAN];
returns TRUE if next token (see GetToken) is Y, YES, T, or TRUE (case doesn't matter), false if N, NO, F, or FALSE, otherwise raises Error[SyntaxError].
GetCard: PROC [stream: STREAM, base: Base ← 10] RETURNS [LONG CARDINAL];
GetInt: PROC [stream: STREAM, base: Base ← 10] RETURNS [INT];
GetReal: PROC [stream: STREAM] RETURNS [REAL];
Note: GetCard, GetInt, GetReal ...
all effectively call Convert.Parse. For example, GetInt[RS[" -3.2"]] will return -3, GetReal[RS["2"]] will return 2.0, etc.

If the result returned by Convert.Parse is not of the specified type, GetCard, GetInt, GetReal raise Error[SyntaxError]. At some point in the future, GetInt etc. will be converted to use the compilers scanner, at which time it will be possible to notice and handle correctly the fact that - in "1-2" terminates the token, but that neither - in "-3-E27" terminates the token. For now, note that GetInt will do the correct thing for "-2" but not for "1-2", i.e. client must supply "1 -2" instead.
GetRefAny: PROC [stream: STREAM] RETURNS [REF ANY];
GetRefAnyLine: PROC [stream: STREAM] RETURNS[LIST OF REF ANY];
calls GetRefAny, constructing a list of the values returned, until an object is immediately followed by a CR...
e.g. if user types FOO FIE FUM{cr} returns ($FOO, $FIE, $FUM). If user types FOO FIE FUM{sp}{cr} continues reading on next line. Useful for line oriented command interpreters. (Note that a CR typed inside of a list simply has the same effect as a space, i.e. terminates the previous identifier, but not the read operation).
GetTime: PROC [stream: STREAM] RETURNS [GreenwichMeanTime];
-- NOT IMPLEMENTED YET
Parsing the input stream: GetRope, GetToken, GetLine, SkipOver, ...
GetToken: PROC [stream: STREAM, break: BreakProc ← TokenProc] RETURNS[ROPE];
skips over characters for which break returns StopAndTossChar or StopAndPutBackChar, then reads until it gets a character for which break returns StopAndIncludeChar, StopAndTossChar, or StopAndPutBackChar. However, StopAndIncludeChar is treated the same as StopAndPutBackChar if any KeepGoing characters had been encountered, i.e. GetToken applied to " Foo [" will first return "Foo" and then return "["
GetMesaToken: PROC [stream: STREAM] RETURNS[ROPE];
uses standard mesa scanner to obtain next token.
GetRope: PROC [stream: STREAM, break: BreakProc ← IDBreak, skipWhile: BreakProc ← IDBreak] RETURNS [ROPE];
Performs SkipOver[h, skipWhile], and then reads characters until break says to Stop or the end of the stream is reached, e.g. if break = skipWhile = IDBreak, skips over leading SP, CR, etc., then reads until it encounters an SP, CR, etc.
GetText: PROC [stream: STREAM, break: BreakProc ← IDBreak, skipWhile: BreakProc ← IDBreak, text: REF TEXTNIL] RETURNS [REF READONLY TEXT];
Same interpretetation of break and skipWhile as for GetRope. If text is supplied, and is big enough, characters will be stored into it, and it will be returned as the value, otherwise returns a new REF READONLY TEXT.
GetLine: PROC [stream: STREAM] RETURNS [ROPE]
= INLINE {RETURN[GetRope[stream, CRBreak, NIL]]};
reads to CR and returns characters read as a rope.
SkipOver: PROC [stream: STREAM, skipWhile: BreakProc];
skips over characters until it finds one for which skipWhile says to KeepGoing, or until it reaches the end of the stream, e.g. SkipOver[h, IDBreak] will skip until the start of the next id. If skipWhile = NIL, no characters are skipped.
Break Procs
BreakAction: TYPE = {KeepGoing, StopAndIncludeChar, StopAndPutBackChar, StopAndTossChar};
BreakProc: TYPE = PROC [c: CHAR] RETURNS [BreakAction];
some useful breakprocs
CRBreak: BreakProc;
returns StopAndTossChar for CR, KeepGoing for all others
WhiteSpace: BreakProc;
Returns StopAndPutBackChar on SP, CR, TAB, KeepGoing for all others
IDBreak: BreakProc;
returns StopAndPutBackChar on SP, CR, ESC, LF, TAB, comma, colon, semicolon, KeepGoing for all others
TokenProc: BreakProc;
returns StopAndIncludeChar for [, ], (, ), {, }, ;, /, StopAndPutBackChar for SP, CR, TAB, and comma, KeepGoing for all others.
Stream Property Lists
The stream's property list can be accessed and used by the client for storing and retrieving data associated with a particular key, the same as with an atom's property list. The procedures StoreData, LookupData, and RemoveData correspond to PutProp, GetProp, and RemProp.
StoreData: PROC [self: STREAM, key: ATOM, data: REF ANY];
adds an entry to property list, or replaces existing one.
AddData: PROC [self: STREAM, key: ATOM, data: REF ANY] ;
like StoreData, except adds it to front of property list, even if something already there, i.e. so can use property list as a push down list.
LookupData: PROC [self: STREAM, key: ATOM] RETURNS [REF ANY];
returns first occurrence of key
RemoveData: PROC [self: STREAM, key: ATOM];
removes first occurrence of key from property list
Defining new kinds of streams
CreateProcsStream: PROC[streamProcs: REF StreamProcs, streamData: REF ANY, backingStream: STREAMNIL] RETURNS[stream: STREAM];
the general mechanism for implementing streams.
streamProcs is intended to be the result of a call to CreateRefStreamProcs (described below). CreateRefStreamProcs is responsible for inserting the appropriate default procedures for those operations not explicitly implemented by the stream. See discussion of default operations below.
backingStream is used when building a layered stream: those operations not defined in streamProcs will be automatically inherited from (passed down to) the backingStream.
If no backingStream is specified, the minimum required to implement a stream to be used for input is getChar and endOf. The minimum required to implement a stream to be used for output is putChar. Everything else can be defaulted...
In other words, if these operations are implemented, the only way of encountering the signal NotImplementedForThisStream is if the client explicitly calls GetIndex, SetIndex, GetLength, SetLength, GetunsafeBlock, or PutUnsafeblock when they are not implemented, or if the client attempts to obtain input from a stream intended to be used only for for output or vice versa. All other procedures will be handled via the defaults described below.
CreateRefStreamProcs: PROC[
getChar: PROC[self: STREAM] RETURNS[CHAR] ← NIL,
endOf: PROC[self: STREAM] RETURNS[BOOL] ← NIL,
charsAvail: PROC[self: STREAM] RETURNS[BOOL] ← NIL,
getBlock: PROC[self: STREAM, block: REF TEXT, startIndex: NAT, stopIndexPlusOne: NAT] RETURNS[nBytesRead: NAT] ← NIL,
unsafeGetBlock: UNSAFE PROC[self: STREAM, block: UnsafeBlock] RETURNS[nBytesRead: INT] ← NIL,
putChar: PROC[self: STREAM, char: CHAR] ← NIL,
putBlock: PROC[self: STREAM, block: REF READONLY TEXT, startIndex: NAT, stopIndexPlusOne: NAT] ← NIL,
unsafePutBlock: PROC[self: STREAM, block: UnsafeBlock] ← NIL,
flush: PROC[self: STREAM] ← NIL,
reset: PROC[self: STREAM] ← NIL,
close: PROC[self: STREAM, abort: BOOLFALSE] ← NIL,
getIndex: PROC[self: STREAM] RETURNS [INT] ← NIL,
setIndex: PROC[self: STREAM, index: INT] ← NIL,
getLength: PROC[self: STREAM] RETURNS [length: INT] ← NIL,
setLength: PROC[self: STREAM, length: INT] ← NIL,
backup: PROC[self: STREAM, char: CHAR] ← NIL,
userAbort: PROC[self: STREAM] RETURNS[abort: BOOL] ← NIL,
setUserAbort: PROC[self: STREAM] ← NIL,
resetUserAbort: PROC[self: STREAM] ← NIL,
setEcho: PROC[self: STREAM, echoTo: STREAM] RETURNS [oldEcho: STREAM] ← NIL,
eraseChar: PROC[self: STREAM, char: CHAR] ← NIL,
currentPosition: PROC[self: STREAM] RETURNS[position: INT] ← NIL,
name: Rope.ROPENIL -- used for printing the stream itself as a ref any.
]
RETURNS [REF StreamProcs];
creates a REF streamprocs, supplying appropriate defaults for the NIL procedures Intended to be performed only once for each different stream class and the result saved, i.e. all instances of this stream can share the same streamProcs.
What it means to default a stream operation
The default procedure is to first check if there is a backingStream, and if so, call the corresponding operation in the backingStream. If no backingStream is specified, the default procedure is as follows:
CharsAvail: check whether endOf was specified, and if so return ~endOf[handle], otherwise raise signal NotImplementedForThisStream as described below
EndOf: check whether charsAvail was specified, and if so return ~charsAvail[handle], otherwise raise signal NotImplementedForThisStream as described below
GetBlock: implement via n Calls on GetChar
PutBlock: implement via n calls to PutChar
Flush, Reset: Nops, do not signal
Close: if abort = NIL, call Flush, otherwise, call Reset. do not signal
Backup: replace, i.e. ambush, the stream's original StreamProcs by a different set of procedures in which getChar, getBlock, endOf, charsAvail, and reset have different definitions when the stream is in the "backed up" state. The remaining procedures call through to the original procedures. When the character has been consumed, the streams original procedures are restored. Note that this causes the stream to run more slowly only when in the backed up state.
SetEcho: similar to Backup, i.e. ambushes the getChar and getBlock procedures, causing them to also output to the indicated handle. This causes all procedure calls in the stream to run more slowly due to the extra level of procedure call and lookup;
CurrentPosition: ambushes the streams putChar and putBlock procedures, and counts the characters as they go by. Since this does not occur until the first call to CurrentPosition, characters that were output before this call will not be counted, i.e. the first call to CurrentPosition will always return 0, and subsequent calls until the first CR is seen will return the number of characters output since the first call;
EraseChar: output \ followed by the character. Defaulting EraseChar does not affect the performance of the stream on other operations.
For all other cases, the default procedure is to raise SIGNAL Signal[NotImplementedForThisStream] (i.e. client can resume) , if the corresponding operation does NOT return a value (i.e. is executed for effect only, such as putChar), or raise ERROR Error[NotImplementedForThisStream] if the corresponding operation requires a return value, such as getChar.
In general, the decision about whether or not to implement a particular operation for a stream, must be made on the basis of performance and frequency of use versus convenience For example, if the stream implements backup itself, it can include the necessary code in its own getChar, getBlock, reset, etc., so that these procedures will continue to run fast, even when the streeam is backed up. However, it is more work for the implementor. For example, viewer streams implement SetEcho, but InputRopeStream does not. Similarly, an edited stream implements backup but viewer stream does not, on the grounds that most calls to a viewer stream that involve parsing and backing up will instead be going to an an edited stream.
AmbushProcsStream: PRIVATE PROCEDURE[self: STREAM, streamProcs: REF StreamProcs, streamData: REF ANY, reusing: STREAMNIL] ;
used to build a layered stream on top of another stream, for those cases where the desired effect is to have any program that has hold of self to see the new behaviour. Implemented by smashing self to point to the new procs and data, with a backing stream that contains the same data, procs, etc. as self originally did. if reusing is non-NIL, it is reused and no allocations are performed. Typically reusing is saved on the property list of a stream which is going to be ambushed and then unambushed repeatedly.
UnAmbushProcsStream: PRIVATE PROCEDURE[self: STREAM];
inverse of AmbushProcsStream, i.e. elevates the backing stream to self. Nop if no backing stream.
Defining new operations on streams
CreateRefStreamProcs takes as arguments 22 predefined operations on a stream. Suppose we want to define new operations which might not appear in this interface which are to be (optionally) implemented by a various kinds of streams. The procedure Implements allows us to do this. It is used to provide a generic implementation for AppendStreams for all streams that do not implement this operation (currently no stream does), to implement ChangeDeliverWhen for both edited streams and viewer edited streams (see ViewerIO), and also the mechanism used to supply the default behaviour for Backup and CurrentPosition described above. For examples of its use, see IOImpl.
Implements: PRIVATE PROC [self: STREAM, operation, via: PROC ANY RETURNS ANY];
used to supply the implementation of an operation other than those specified as arguments to CreateRefStreamProcs. 'operation' is the generic procedure, and Via is the implementing procedure for this stream class. Via is checked (using the RT interfaces) to make sure it is of the correct type. (The names of the arguments as well as the return values must agree.) If it is not, the signal IO.Error[ProcedureHasWrongType] is raised. If the type is correct, Via is saved on the property list of the streamProcs, so that the type checking need be done only once for each stream class. Subsequent calls to Implements for the same stream class are essentially nops.
When the `operation' is invoked, `via' will be called with the same arguments as the generic procedure was called with.
For example, if CreateRefStreamProcs did not take setLength as an argument, this operation could be implemented for a particular stream class by performing: stream.Implements[SetLength, MySetLength], where MySetLength is of type PROC[self: STREAM, length: INT].
UncheckedImplements: -- Even more -- PRIVATE PROC [self: IO.STREAM, operation, via: PROC ANY RETURNS ANY, data: REF ANYNIL, procRef: REF ANY, key: ATOM];
used by implements. Calling directly and specifying procRef and key enables avoiding use of RT interfaces
LookupProc: PRIVATE PROC [self: STREAM, operation: PROC ANY RETURNS ANY] RETURNS[proc: REF ANY];
PF (PrintFormatted) Package
Concept
PutF (and PutFR and PutFList) produce output according to a collection of Values and a format control string which can including embedded format codes. The Values that are the arguments to PutF are associated with the format codes in the order that they appear in the format string. For example:
h.PutF["This is %g in a 5 position field: |%5d|", rope["an integer"], int[17]];
would produce the result:
"This is an integer in a 5 position field: | 17|".
If a format code is encountered which is not recognized, the signal IO.Signal[UnknownFormat] is raised. If there are not enough format codes to match the values supplied as arguments, or not enough values then ???
PreDefined Format Codes
The Value refAny is accepted by all formats; if dereferencing yields a value of an acceptable type, it is printed directly. Otherwise, if %g or %h are used, IO.PrintRefAny is called. If some other code was used, the Signal IO.Signal[UnprintableValue] or IO.Signal[TypeMismatch] is raised. If this signal is RESUMEd, some '# characters are printed.
In the following descriptions, "number" means a Value that is either card, char, int, real, or time. CHARACTERS passed to a number routine print as their Ascii code.
"string" means a Value that is either a string, rope, text, bool, or a number. BOOLEANs passed to a "string" routine print as TRUE or FALSE and CHARACTERs print as characters.
%e: accepts numbers. Like Fortran E format, prints in "scientific notation" (mantissa and exponent).
%f: accepts numbers. Like Fortran F format, prints in "fixed point" format.
%g: accepts all values. Prints whatever you give it sensibly. When in doubt, use %g.
%h: like %g, except that control characters (code < 40B) print as "^<char + '@>.
%l: accepts ROPEs. Changes looks. Implemented for ViewerStreams only. The value is interpreted as a sequence of looks, e.g. h.PutF["Make this %lbold%l for emphasis", rope["b"], rope["B]] will print "Make this bold for emphasis". (Note that B means remove the bold look because it is shift-b.) The character space has the same interpretation as for tioga, i.e. removes all looks.
%r: accepts numbers, interprets as time interval in seconds, and prints as HH:MM:SS
%t: accepts numbers and Time[], prints in usual date format with time zone (e.g. " PDT")
The following formats apply to all numbers (and to BOOLEANs with the conversion TRUE = 1 and FALSE = 0). (To print a POINTER, LONG POINTER, REF, etc. value as a number, PUN it into a number of the correct length before converting to a Value.)
%b: print in octal.
%d: print in decimal.
%x: print in hex.
Field Width Specifications
Information about printing a particular value can be inserted between the "%" and the code letter. Is this true for all control codes??? Field width, left or right justification, and zero filling can be controlled. The default is to print the value in its entirety, with no spacing on either side. If a number is present between the "%" and code (e.g. "%6d"), it is interpreted as a minimum field width, useful for printing columns, etc.
Extra spaces are inserted before the number (right adjusted) to fill out to the indicated number of spaces, unless a "-" precedes the number (e.g. "%-6d") to indicated filling with spaces on the right (left-adjusted). If "-" is not present, a "0" may be inserted (e.g. "%06d") to indicate right-adjustment and filling with zeroes rather than spaces. In any case the field width is a minimum, not a maximum. Should the size of the output exceed the field width, it is printed in its entirety.
A field width may have the form xx.yy, viz: "%12.4g". These specifications are interpreted by the floating point number printing routines. Other types of conversions ignore the "fraction".
Defining new format codes
Users may specify their own procedures to interpret codes following the "%" via SetPFCodeProc. For example, h.SetPFCodeProc['z, ZProc] will cause subsequent references to "%z" to call ZProc. Predefined codes (e.g. d, e, f) may be redefined. Only alphabetic (['a..'z]) code characters are legal, case does no matter.
SetPFCodeProc: PROC [stream: STREAM, char: CHAR, codeProc: PFCodeProc];
when %char is encountered, codeProc is called with format = ???, and val = the Value that corresponded to this control code.
PFCodeProc: TYPE = PROC [stream: STREAM, val: Value, format: Format, char: CHAR];
Format: TYPE = RECORD [form: ROPE, first: INT];
Another way of defining new commands is via SetPFStarCodeProc. PFStarCodeProcs differ from PFCodeProcs in that they do not take arguments, i.e. none of the Values that comprise the arguments to the output procedure are associated with this PFStarCode. PFStarCodes are indicated in the format string by preceding the code with a * instead of a %, and then following it immediately with the code letter (alphabetic - non-alphabetic characters following * print as themselves, e.g. "**" prints as *),
Undefined StarCodes also raise IO.Signal[NotImplementedForThisStream].
PFStarCodeProc: TYPE = PROC [stream: STREAM, char: CHAR];
SetPFStarCodeProc: PROC [stream: STREAM, char: CHAR, codeProc: PFStarCodeProc];
PrintProcs
AttachRefPrintProc: PROC [refType: RTBasic.Type, refPrintProc: RefPrintProc];
Used to define a procedure to be called for printing an object of the specified ref type. The procedure will be called with the corresponding ref and a stream. If the class of refType is not ref or list, raises IO.Signal[NotARefType].
RefPrintProc: TYPE = PROC [ref: REF READONLY ANY, stream: STREAM];
AttachTVPrintProc: PROC [type: RTBasic.Type, tvPrintProc: TVPrintProc, canHandleRemote: BOOLFALSE];
More general print proc mechanism for use with any type. The procedure will be called with a tv for the corresponding object and a stream. IF canHandleRemote is TRUE, the printproc will also be invoked on remote tvs, otherwise not.
TVPrintProc: TYPE = PROC [tv: RTBasic.TV, stream: STREAM];
Signals and Errors
EndOfStream: ERROR [stream: STREAM];
attempt to do a getChar at end of stream, or attempt to do a setindex beyond end of stream. Separate error for convenience.
Error: ERROR [ec: ErrorCode, stream: STREAM];
ErrorCode: TYPE = {
NotImplementedForThisStream,
a signal, if the corresponding operation does not return a value, e.g. SetEcho, SetLength, etc., and not resumable, i.e. an ERROR, if the operation does return a value, e.g. GetLength,
IllegalBackup,
attempt to do a second backup without having done an intermediate getChar, or attempt to backup a character other than the one just read.
SyntaxError,
raised by GetBool, GetInt, GetCard, GetReal, GetRefany. Also raised for \n not followed by two digits, i.e. not of form \nnn, when reading a ref text
StreamClosed,
raised by file stream operations after Close (except Flush, Reset, Close.)
FileTooLong,
may be raised by SetLength
BadIndex
negative length or index to SetLength/SetIndex, or
negative or too-large index to UnsafeGetBlock/UnsafePutBlock
};
Signal: SIGNAL [ec: SignalCode, stream: STREAM];
SignalCode: TYPE = {
NotImplementedForThisStream,
a signal, if the corresponding operation does not return a value, e.g. SetEcho, SetLength, etc., and not resumable, i.e. an ERROR, if the operation does return a value, e.g. GetLength,
ProcedureHasWrongType,
Raised by Implements when via is not of the type of operation. If resumed, effect is simply not to implement the operation,
NotARefType,
Raised by AttachRefPrintProc when refType is not of class ref or list. If resumed, effect is simply not to implement the print proc.
EmptyBuffer,
raised by EditedStream when attempt to do an eraseChar and nothing is there.
UnmatchedLeftParen, UnmatchedStringDelim,
raised by GetRefAny if endofstream reached while reading a list or string. If resumed, the effect is to supply the missing character, e.g. doing GetRefAny on the rope "(A B C" will return the list (A B C) if the client resumes the signal UnmatchedLeftParen.
Rubout,
raised by EditedStream when a DEL is seen.
UnprintableValue, TypeMismatch, UnknownFormat
raised by PF.
};
BufferOverFlow: SIGNAL[text: REF TEXT] RETURNS[REF TEXT];
used by text streams, should return a bigger one, with characters copied over, or else can return same text with length reset after having done something with chartacters.
Private and semi-private Types
ValueType: TYPE = {
null, atom, boolean, character, cardinal, integer, proc, real, refAny, rope,
string, text, time, tv, type};
Value: TYPE = RECORD [SELECT type: ValueType FROM
null => NULL,
atom => [value: ATOM],
boolean => [value: BOOL],
character => [value: CHAR],
cardinal => [value: LONG CARDINAL],
integer => [value: INT],
real => [value: REAL],
refAny => [value: REF READONLY ANY],
rope => [value: ROPE],
string => [value: LONG STRING],
text => [value: REF READONLY TEXT],
time => [value: GreenwichMeanTime],
tv => [value: RTBasic.TV],
type => [value: RTBasic.Type],
ENDCASE];
UnsafeBlock: TYPE = RECORD [
base: LONG POINTER, startIndex, stopIndexPlusOne: INT --bytes--];
used in conjunction with UnsafeGetBlock, UnsafePutBlock described above.
Note: slots in the StreamProcs record are allocated to those procedures that are either so basic to the operation of a stream that nearly every stream will implement them, or else for which performance is an issue, such as getBlock, getIndex, etc. Other generic operations such as Backup, PeekChar, CheckForAbort, SetEcho, EraseChar, Position, are implemented by storing the corresponding procedure on the streams property list, and looking it up (each time) the generic operation is called. If the stream (or its backing stream) does not supply an implementation, a default procedure is supplied which will accomplish the operation. {16}
StreamProcs: TYPE = PRIVATE RECORD[
getChar: PROC[self: STREAM] RETURNS[CHAR] ← NIL,
endOf: PROC[self: STREAM] RETURNS[BOOL] ← NIL,
charsAvail: PROC[self: STREAM] RETURNS[BOOL] ← NIL,
getBlock: PROC[self: STREAM, block: REF TEXT, startIndex: NAT, stopIndexPlusOne: NAT] RETURNS[nBytesRead: NAT] ← NIL,
unsafeGetBlock: UNSAFE PROC[self: STREAM, block: UnsafeBlock] RETURNS[nBytesRead: INT] ← NIL,
putChar: PROC[self: STREAM, char: CHAR] ← NIL,
putBlock: PROC[self: STREAM, block: REF READONLY TEXT, startIndex: NAT, stopIndexPlusOne: NAT] ← NIL,
unsafePutBlock: PROC[self: STREAM, block: UnsafeBlock] ← NIL,
flush: PROC[self: STREAM] ← NIL,
reset: PROC[self: STREAM] ← NIL,
close: PROC[self: STREAM, abort: BOOLFALSE] ← NIL,
getIndex: PROC[self: STREAM] RETURNS [INT] ← NIL,
setIndex: PROC[self: STREAM, index: INT] ← NIL,
otherStreamProcs: LIST OF StreamProperty ← NIL, -- used for specifying the implementation of optional, generic procedures. See Implements below.
name: Rope.ROPE
];
StreamProperty: TYPE = REF StreamPropertyRecord;
StreamPropertyRecord: TYPE = PRIVATE RECORD[operation, via: PROC ANY RETURNS ANY, proc: REF ANY, key: ATOM];
Character constants. Included for convenience.
CR: CHAR = Ascii.CR;
SP: CHAR = Ascii.SP;
TAB: CHAR = Ascii.TAB;
LF: CHAR = Ascii.LF;
BS: CHAR = Ascii.BS;
ControlA: CHAR = Ascii.ControlA;
ControlX: CHAR = Ascii.ControlX;
FF: CHAR = Ascii.FF;
NUL: CHAR = Ascii.NUL;
ESC: CHAR = Ascii.ESC;
DEL: CHAR = Ascii.DEL;
BEL: CHAR = Ascii.BEL;
Miscellaneous
BackSlashChar: PROC [char: CHAR, stream: STREAMNIL] RETURNS [CHAR];
interpreters \ conventions, e.g. maps \n to CR, \t to TAB, etc. Raises SyntaxError if \ not followed by acceptable character. IF char is in ['0..9], then stream must be supplied, or else syntaxerror. If stream is supplied, two more characters are read. If these are digits, returns corresponding character, otherwise, raises SyntaxError.
Zone: PRIVATE ZONE; -- prefixed zone used for storing stream data, stream procs, etc.
END.