IO.mesa
Top-level stream and I/O interface for Cedar
Last edited by: Teitelman on April 20, 1983 3:10 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
The following procedures enable the user to create input and/or output streams to various entities such as viewers, files, ropes, as well as streams that are layered on top of another stream, the backing stream, so as to modify or augment the behaviour of the backing stream, such as edited streams, dribble streams, filtered comment streams, etc. All of these streams are defined in terms of a general mechanism for implementing streams, CreateProcsStream, which is described below under the section Defining New 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.
CreateMessageWindowStream To create a stream for writing to the MessageWindow, use ViewerIO.CreateMessageWindowStream.
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.
CreateDribbleStream: PROCEDURE [stream: STREAM, dribbleTo: STREAM, flushEveryNChars: INT ← -1] RETURNS [s: STREAM];
creates a layered stream, s, on top of stream (i.e. stream will be the backingStream), 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 [out: STREAM, deliverWhen: DeliverWhenProc ← IsACR, howOften: INT ← 256] RETURNS [s: STREAM];
creates an output stream on top of out such that characters are buffered until (a) (howOften >= 0 and) howOften characters have been output since last flush, or (b) (deliverWhen # NIL and) deliverWhen[char] is TRUE, or (c) an explicit Flush is performed, at which point the characters are output to stream using PutBlock. Characters are buffered in a reftext so no allocations are performed.
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.
noInputStream: IO.STREAM;
input stream for which CharsAvail is FALSE, EndOf is TRUE, but any attempt to get characters raises IO.StreamClosed.
Edited Streams
An edited stream is a stream which obtains characters from a backing stream, and buffers these characters until a specified predicate returns TRUE, saying that it is time to make these characters available to any procedures attempting to obtain input from this stream. Before the characters are delivered, the user can perform various editing operations on them, such as delete the last character, delete the last word, etc. In the case of a viewer edited stream, the user can edit the characters in the Tioga manner, i.e. select, insert, delete, exchange, etc.
Note that if the user types DEL, the signal Rubout will be raised, so clients should be prepared to catch this signal.
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. Input characters are echoed to and erased from echoTo. ChangeDeliverWhen can be used to change buffering condition.
CreateEditedViewerStream: PROCEDURE [in: STREAM, echoTo: STREAM, deliverWhen: DeliverWhenProc ← IsACR] RETURNS [h: STREAM];
defined in ViewerIO.
DeliverWhenProc: TYPE = PROC[char: CHAR, stream: STREAM] RETURNS[BOOL];
IsACR: DeliverWhenProc; -- returns TRUE for CR.
ChangeDeliverWhen: PROC [self: STREAM, proc: DeliverWhenProc] RETURNS [oldProc: DeliverWhenProc];
Used to change the buffering condition.
GetBufferContents: PROC [self: STREAM] RETURNS[buffer: ROPE];
returns contents of the buffer without disturbing. For use in conjunction with DeliverWhenProcs which might need to see what has been typed in order to decide whether to terminate, e.g. parenthesis balancing, etc.
Rubout: SIGNAL;
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 there are characters available for immediate consumption, i.e. a call to GetChar will not wait.
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.
WaitUntilCharsAvail: PROC [stream: IO.STREAM];
returns when CharsAvail would be TRUE.
For most streams, this is implemented by simply pausing, waking up, and checking CharsAvail, but for viewer streams, this is accomplished in a way that interacts better with the tioga/viewer locking mechanism.
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.
SpaceTo: PROC [self: STREAM, n: INT, nextLine: BOOLEANTRUE];
spaces to position n. If current position already beyond n and nextLine is TRUE, then new line and n spaces.
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, which should culminate in raising IO.UserAborted. Currently implemented for ViewerStreams, TRUE if user types 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 and cardinals in decimal, 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]]]};
Parsing the input stream: GetCedarToken, GetToken, GetInt, GetReal, GetRefAny, ...
Parsing the input stream as a sequence of characters: GetSequence
CharProc: TYPE = PROC [char: CHAR, stream: STREAM] RETURNS [quit: BOOLFALSE, include: BOOLTRUE];
if quit = TRUE, then the last character is also included if include = TRUE, otherwise the last character is backed up, i.e. will be seen by the next caller.
GetSequence: PROC [stream: STREAM, charProc: CharProc] RETURNS[ROPE];
reads characters from stream until EOF or quit is TRUE, including those characters for which include is TRUE. For example, the definition of LineAtATime is RETURN[quit: char = CR, include: char # CR]. Thus s.GetSequence[LineAtATime] will read to the next CR and return the characters read as a rope (but not read the CR).
GetLine: PROC [stream: STREAM] RETURNS[ROPE];
reads characters until next CR, and returns all characters read up to but not including the CR (EOF also acts as a CR). The CR is read and discarded.
As an example of the use of GetSequence, here is the definition for GetLine:
{line: ROPE = GetSequence[stream, LineAtATime];
[] ← GetChar[stream ! IO.EndOfStream => CONTINUE];
RETURN[line];
};
LineAtATime: CharProc;
definition is {RETURN[char = CR, char # CR]}
EveryThing: CharProc;
definition is {RETURN[NOT CharsAvail[stream], TRUE]} i.e. in.GetSequence[EveryThing] returns the contents of in.
Parsing the input stream as a sequence of Cedar Tokens: GetCedarToken
GetCedarToken: PROC [stream: STREAM] RETURNS[ROPE];
uses CedarScanner to scan the input stream for the next mesa token, which is returned as a rope, e.g. for the stream IO.RIS["[$foo, a.b, 3.2]", successive calls on GetCedarToken would return the nine tokens "[" "$foo" "," "a" "." "b" "," "3.2" and "]". GetCedarToken automatically filters out all comments. For convenience in use with the interpreter, & is treated as a regular character, rather than causing a syntax error, i.e. "&23" will parse as a single token.
GetAtom, GetBool, GetCard, GetInt, GetReal, and GetRope described below provide ways of parsing the input stream into objects of the corresponding type. If the client knows what type of object is next expected, he can use the input routine for that type, e.g. i: INT ← stream.GetInt[], rather than calling GetCedarToken and then converting the resulting rope to the corresponding type. GetRefAny provides a compromise between these two positions by returning an object that is a REF to the corresponding type, e.g. REF INT, REF BOOL, etc. The client can then discriminate on the type of the REF.
FromTokenProc: TYPE = PROC[closure: CedarScanner.GetClosure, token: CedarScanner.Token];
GetCedarScannerToken: PRIVATE PROC [stream: STREAM, fromTokenProc: FromTokenProc];
reads one token and then calls FromTokenProc on corresponding closure.
For example, GetCedarToken is defined as
{fromTokenProc: FromTokenProc = {value ← CedarScanner.ContentsFromToken[closure, token]};
GetCedarScannerToken[stream, fromTokenProc];
};
GetCedarScannerToken enables reading a bunch of tokens without having to create ropes for each one.
Basic input routines for each value type: GetCard, GetInt, GetReal, ...
Example: i: INT ← stream.GetInt[];
Note: all of the following procedures use the CedarScanner to obtain the next token. If the token is of the appropriate 'kind', the corresponding value is returned, otherwise, raise SyntaxError with an appropriate message.
SyntaxError: ERROR[stream: IO.STREAM, msg: ROPENIL];
GetAtom: PROC [stream: STREAM] RETURNS [ATOM];
raises SyntaxError if next token is not an atom.
GetBool: PROC[stream: STREAM] RETURNS[BOOLEAN];
returns TRUE if next token is TRUE, FALSE if FALSE, otherwise SyntaxError.
Note: if you are planning on using GetBool to ask for confirmation:
UserExec.ExecConfirm and UserExec.ViewerConfirm provide convenient and standard ways of asking for confirmation. These procedures also post menu buttons and handle the case where the user has typed ahead before the need for confirmation arose.
GetCard: PROC [stream: STREAM] RETURNS [LONG CARDINAL];
GetInt: PROC [stream: STREAM] RETURNS [INT];
To parse the input stream using a different input radix, the best way to proceed is to obtain a rope for the corresponding token, e.g. via GetToken, and then call Convert.Parse. For example, Convert.Parse[text: [rope[r]], template: [unsigned[base: 16]]].value is an expression which takes a rope r, and interprets it as a hexidecimal number. For more details, see the Convert interface.
GetReal: PROC [stream: STREAM] RETURNS [REAL];
GetRope: PROC [stream: STREAM] RETURNS [ROPE];
raises SyntaxError if next token is not a rope.
GetId: PROC [stream: STREAM] RETURNS [ROPE];
raises SyntaxError if next token is not an identifier.
Reading Ref Anys: GetRefAny, GetRefAnyLine
GetRefAny: PROC [stream: STREAM] RETURNS [REF ANY];
Use cedar scanner to parse the input stream, then return the resulting token as a REF to the appropriate type, e.g. REF INT, REF REAL, REF BOOL, ATOM, ROPE, or LIST OF REF ANY, etc. ^ in front of an object is ignored in order that read and print be inverses, e.g. ^3 will produce NEW[INT ← 3]. Identifiers are read in as atoms, e.g. foo will produce $foo. The input syntax for lists is ( followed by a sequence of tokens terminated by ), e.g. (a b c) will read in as LIST[$A, $B, $C]. Commas are permitted (and ignored) between elements of a list.
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).
Parsing the input stream as a sequence of arbitrary tokens: GetToken
GetSequence parses the input stream into a sequence of characters, e.g. the next line. The application then typically has to parse this sequence into smaller units, called tokens. GetCedarToken is one way to do this. However, occasionally the user may not need the full power of the cedar scanner, or may wish to employ some other algorithm. The charProc argument of GetToken described below provides for a limited form of parsing by dividing all characters into three classes: {sepr, break, other}.
A sepr character is a character that separates tokens. Sepr characters never appear in tokens. For example, for most applications, SP would be a sepr.
A break character is a character that is a token all by itself. For example, for some applications, ] might be a break character.
other characters are everything else.
BreakProc: TYPE = PROC [char: CHAR] RETURNS [CharClass];
CharClass: TYPE = {break, sepr, other};
GetToken: PROC [stream: STREAM, breakProc: BreakProc ← TokenProc] RETURNS[ROPE];
skips over characters which breakProc says are seprs, and then reads until it encounters either a break or sepr. If break is the first thing encountered, return a token consisting of the single break character, otherwise return the sequence of other characters read before the first break or sepr. The terminating character, be it sepr or break, will be seen by the next caller, i.e. is not read.
Thus each time you call GetToken, you get back a rope consisting of either one or more other characters, or else a rope consisting of just a single character that is a break character.
For example, suppose your application involved parsing an input stream into a sequence of identifiers separated by commas, where you want to make sure that there is a comma between each identifier, but don't care whether there are any spaces before or after the comma. Here is what you might write:
YourProc: BreakProc = {RETURN[IF char = ', THEN break ELSE IF char = SP THEN sepr ELSE other]};
Successive calls on GetToken using this BreakProc would alternately return identifiers and ",".
Since it is a good example of the use of GetSequence, here is the definition of GetToken:
GetToken: PROC[stream: STREAM, breakProc: BreakProc] = {
anySeen: BOOL ← FALSE;
proc: CharProc = {
SELECT breakProc[char] FROM
break => {include ← NOT anySeen; quit ← TRUE};
sepr => {include ← FALSE; quit ← anySeen};
other => {include ← TRUE; quit ← FALSE; anySeen ← TRUE};
ENDCASE => ERROR;
};
RETURN[GetSequence[stream, charProc]];
};
The following procedures may be useful in conjunction with GetToken.
WhiteSpace: BreakProc;
Returns sepr on SP, CR, LF, TAB, other for all others
IDProc: BreakProc;
Returns sepr for SP, CR, ESC, LF, TAB, comma, colon, and semicolon,
and other for all others, i.e. s.GetToken[IdProc] is approximately the same as the old s.GetRope[].
TokenProc: BreakProc;
Returns sepr for SP, CR, ESC, LF, TAB, comma, colon, and semicolon,
break for [, ], (, ), {, }, '", '+, '-, '*, /, '@, '←,
and other for all others, i.e. s.GetToken[TokenProc] is approximately the same as s.GetCedarToken[] (but real numbers, quoted literals, quoted text, etc. are not handled the same).
SkipOver: PROC [stream: STREAM, skipWhile: BreakProc ← WhiteSpace];
skips over all seprs, or until it reaches the end of the stream. If skipWhile = NIL, no characters are skipped.
AskUser
AskUser provides for a standard way of interacting with the user to obtain one of a small, specified set of responses, such as Yes or No, or Yes, No, All, or Quit. When called with a Commander.Handle containing a UserExec.ExecHandle, AskUser is implemented via UserExec.AskUser, which means that the user can respond either by typing or via the corresponding menu buttons which will be posted, and the problem of type ahead is handled automatically. When called from "Leaner Meaner Cedar", the interaction is only via the keyboard, i.e. the streams in the Commander Handle, and the problem of typeahead is not addressed.
AskUser: PROC [msg: Rope.ROPE, in, out: STREAM, defaultKey: ATOMNIL, keyList: LIST OF ATOMNIL, timeout: INT ← -1] RETURNS [value: ATOM];
keyList = NIL => LIST[$Yes, $No].
The character the user types must match the first character of one of the atoms in the keylist. If so, the entire print name is echoed and AskUser returns. Otherwise, the question is repeated.
A CR matches the first atom in the KeyList, so order is important.
DEL is treated exactly like 'N.
Timeout is in milliseconds, -1 means don't timeout.
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
A stream is defined by specifying a collection of procedures which implement the various operations that a stream must support, such as reading or printing a character, erasing a character, closing the stream, changing its echoing, etc. Not all of the procedures need to be specified. For example, all procedures relating to output need not be defined for an input stream. Similarly, some operations that are not defined can be carried out in terms of others that are, e.g. if the GetBlock procedure is not specified, a call to GetBlock will be automatically transformed into n calls to GetChar, etc. Finally, if the stream is layered on top of another stream, it will automatically inherit the latter streams definitions for those procedures that are not explicitly specified (with some exceptions as explained below). For a more complete discussion of what it means to default a stream procdure, see the discussion below following the definition of CreateProcsStream.
The following is an example of defining a new kind of stream.
An example: defining a stream to the Message Window
The application is to define a stream which can be used for output to the Viewer Message Window. Since the MessageWindow interface allows one to display ropes, the idea is to dump the characters into a rope, and then display the rope in the message window when a Flush is executed. We can write characters into a rope using a Rope Output Stream, described above. Furthermore, if we make the Rope Output Stream be the backing stream for our Message Window Stream, we do not even have to define PutChar, but can simply allow for that operation to be executed by a call through to the Rope Output Stream's PutChar. All we have to do is to define what Flush and Reset mean:
MessageWindowStreamProcs: REF StreamProcs ← NIL; -- the block of procedures for the stream. Initialized the first time one of these streams is created.
CreateMessageWindowStream: PROCEDURE RETURNS [STREAM] = {
IF MessageWindowStreamProcs = NIL THEN MessageWindowStreamProcs ← CreateRefStreamProcs[
flush: MessageWindowStreamFlush,
reset: MessageWindowStreamReset,
name: "Message Window"
]; -- initialize the block of procedures
RETURN[IO.CreateProcsStream[streamProcs: MessageWindowStreamProcs, backingStream: IO.ROS[], streamData: NIL]];
}; -- of CreateMessageWindowStream
MessageWindowStreamFlush: PROC[self: STREAM] = {
r: ROPE ← self.backingStream.GetOutputStreamRope[]; -- get the characters that have been printed.
i: INT ← 0;
self.backingStream.Reset[]; -- reset the rope output stream.
WHILE (i ← Rope.Find[s1: r, s2: "\n", pos1: i]) # -1 DO
r ← Rope.Replace[base: r, start: i, len: 1, with: " "]; -- change crs to spaces (since message window is only one line high!)
ENDLOOP;
MessageWindow.Append[message: r, clearFirst: TRUE]; -- display the rope
};
MessageWindowStreamReset: PROC[self: STREAM] = {
self.backingStream.Reset[]; -- reset the rope output stream.
MessageWindow.Clear[]; -- clear the message window.
};
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.
Note: 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
Except where specified below, e.g. GetBlock, Backup, etc., 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 on this stream. The backingStream's implementation of GetBlock is never used. (Reason: this stream may do something special with getChar, rather than call thru to the backingStream. It is important therefore that each character go through this stream's getChar. Note that if this stream does not specify GetChar, then the backing stream's getChar will be called.)
PutBlock: implement via n calls to PutChar on this stream. The backingStream's implementation of PutBlock is never used.
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. The backingStream is never consulted, i.e. the stream is always backed up at this level.
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 stream operations
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 for ViewerStreams, is a NOP for all other streams so that the client does not have to know whether he is printing to a viewer or a file or whatever. 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 (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
PrintProcs allow the client to specify an alternative way of printing an object of a specified type. Usually this is employed to summarize the object in some useful way. For example, a viewer is an object of type REF ViewerClasses.ViewerRec where ViewerRec is a RECORD consisting of nineteen fields! Printing this object without a PrintProc would thus produce copious amounts of information, most of which is probably not of interest to the user who simply wants to see which viewer it is. Instead, we can define a printproc which prints a viewer showing just its class and name as follows:
ViewerPrintProc: IO.RefPrintProc = {
h: REF READONLY ViewerClasses.ViewerRec ← NARROW[ref];
stream.PutF["{Viewer - class: %g, name: %g}", atom[h.class.flavor], rope[IF Rope.Length[h.name] # 0 THEN h.name ELSE "(no name)"]];
};
We register this printProc via:
AttachRefPrintProc[refType: CODE[ViewerClasses.Viewer], refPrintProc: ViewerPrintProc];
Following this, the viewer for the initial UserExec would print as:
{Viewer - class: Typescript, name: Work Area A: Executive}
AttachRefPrintProc: PROC [refType: RTBasic.Type, refPrintProc: RefPrintProc];
Used to register 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, depth: INT ← 4, width: INT ← 32, verbose: BOOLFALSE];
Occasionally it is useful to define a printproc for a non-ref TYPE. In this case, the client can define a PrintProc which is a given a tv describing the corresponding object. Here is an example of a PrintProc which will print an object of type System.GreenwichMeanTime as a time, rather than a LONG CARDINAL (which is the type of System.GreenwichMeanTime).
TimePrintProc: TVPrintProc = {
t: System.GreenwichMeanTime;
t ← LOOPHOLE[AMBridge.TVToLC[tv]]; -- get the "time" out of the tv.
stream.Put[time[t]];
};
AttachTVPrintProc[type: CODE[System.GreenwichMeanTime], tvPrintProc: TimePrintProc, canHandleRemote: FALSE];
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, depth: INT ← 4, width: INT ← 32, verbose: BOOLFALSE];
Signals and Errors
UserAborted: ERROR [abortee: REF ANYNIL, msg: ROPENIL];
the standard error to be raised by an application to indicate that the user has aborted. Replaces UserExec.UserAborted.
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.
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.
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.
UnprintableValue, TypeMismatch, UnknownFormat
raised by PF.
};
Private and semi-private Types
ValueType: TYPE = {
null, atom, boolean, character, cardinal, integer, 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.
Converting from 3.5
The following are some suggestions about how to convert a program using IO.GetRope or IO.GetToken to the new scheme of things. These suggestions should cover about 95% of the cases.
Let us consider GetToken first. A old-style call of the form s.GetToken[], which defaults the second argument, can stay the same. But you might want to consider using GetCedarToken instead, especially if what you are doing is reading mesa: GetCedarToken will do more Cedar-like things for real numbers, quoted literals, quoted text, and the like.
GetRope is only a little more complicated. An old-style call of the form s.GetRope[], which defaults the second and third arguments, can be replaced by the new call s.GetToken[IDProc]. Note that the new call s.GetToken[] would also probably do what you want, the only difference being in the treatment of break characters: for example, the old s.GetRope[] would read @foo as a single token, while the new s.GetToken[] would read it as two separate tokens. Similarly, /indigo/cedar/top/io.df would be read as 8 tokens by the new s.GetToken[], but as only one token by the old s.GetRope[] and the new s.GetToken[IDProc]. All three of these have the same effect if your desired tokens are always separated by white space.
Beware! If you use GetRope, you cannot depend on compile error messages alone to tell you where changes have to be made. The new GetRope means something quite different from the old GetRope, but any call to the old GetRope that defaults the second and third arguments will still look OK to the compiler as a call on the new GetRope. Clients of the old GetRope were just parsing some random input into things separated by white space. A client of the new GetRope should be in the process of parsing the input stream as a sequence of mesa tokens. Such a client calls the new GetRope when she expects a rope literal to be the next thing in the input stream, and the call to GetRope either returns the corresponding rope or raises an error. Thus, all calls to the old GetRope must be changed to GetToken as described in the previous paragraph, and you must search your program to find them all.
If you have defined your own IO.BreakProcs for use with GetToken, the following recipe should work: change all of the StopAndPutBackChar and StopAndTossChar to sepr, StopAndIncludeChar to break, and KeepGoing to other. If you have defined your own IO.BreakProcs for use with GetRope, you should change KeepGoing to other and everything else to sepr, and then use GetToken in place of GetRope. However, you might want to rewrite in terms of GetSequence instead.