Go to the first, previous, next, last section, table of contents.


I/O Subsystem

Most programs need to perform input (reading data), output (writing data), or both in order to be useful. The OOC library attempts to simplify these Input/Output (I/O) operations by providing several related abstractions that relate to I/O.

The two primary abstractions are channels and riders. The entire I/O Subsystem of the OOC library revolves around these two concepts.

Input/Output Overview

In order to provide uniform access to different sorts of devices (files, program arguments, sockets, and so forth) the I/O subsystem consists of several interrelated class hierarchies of related abstractions. The two primary abstractions are channels and riders.

The intention of these abstractions is to allow similar handling of devices; even, potentially, to the level of such exotic devices as a screen pixelmap, a windowing system, or a speech output device.

The benefit of this unified I/O handling approach allows a programmer to write procedures that operate on any kind of I/O channel. A program writing to stdout could be easily converted to allow writing into a file instead. Or, similarly, it could serve as a remote telnet connection.

All channels can share the same operations for text based I/O (ReadInt, WriteInt, and so forth). Riders (readers and writers) can then be attached to the channel, allowing standard I/O, regardless of the actual device used.

I/O Concepts

There are several conceptual layers to the I/O process that are modeled by various abstractions in the OOC library. Their relationships are shown here:

  data locations - where data resides (raw data).  
      |  (e.g., hard disk, memory block, keyboard port, RS232 links)
      |
      |
  channels - connections to data locations in the form of byte streams.  
      |  (e.g., files - on disk and in memory, pipes, 
      |   TCP/IP connections)
      |
  basic riders - basic operations on bytes.  
      | (e.g., SetPos, ReadByte, ReadBytes, WriteByte, WriteBytes)
      |
      |
  mappers - translations of high level data to and from a byte stream.  
        (e.g., binary reader/writer, text reader/writer)

A data location (or simply location) is a source of input data or destination of output data. It it the physical or logical place where data exists; say a hard disk, or keyboard buffer.

A channel is a connection to a data location. A channel is envisioned as a contiguous sequence, or stream, of bytes. Channels may be sequential as in the case of terminal I/O, a TCP stream, pipes, and so forth; or positionable like Files and ProgramArgs.

Riders are associated with a channel and provide read and write access of a location; they operate directly on a stream of bytes (i.e., a channel). Multiple readers and writers can exist for a single channel.

A mapper is a high-level rider; it operates on a particular format of data, like textual or binary representation of elementary data types. Mappers rely on the primitive operations of basic riders to build more complex operations.

The benefit of differentiating these layers is allowing a way to distinguish between the simple access layer, that doesn't know a thing about the byte stream being read or written, and the interpretation layer that transforms bytes into useful data.

Riders and Mappers

The term rider can be used to describe any operator that provides read or write operations on channels. However, there is a distinction between low-level (basic riders) and high-level operations (mappers).

Basic riders are associated directly with a particular channel type. Notice that the rider, not the channel, has a position property (the place where reading or writing occurs). Several riders can operate on the same channel at the same time. Riders may provide sequential or positionable (i.e., random) access depending on the type of channel.

In general, there are only two types of basic riders: readers and writers.

Mappers are similar to basic riders and, like riders, may be either readers or writers. They translate between a sequence of data items and an uninterpreted sequence of bytes. But mappers may also provide more sophisticated read/write operations; for instance, scanners are mappers that can distinguish between different types of data within a particular format, and then read in that data based on the type. See section Module TextRider, and See section Module BinaryRider, for descriptions of the simplest mappers.

Please note: a basic rider is dependent on the implementation of its channel, (e.g., a file rider must know how to position itself within a file). When a channel type is extended, usually the rider must be extended as well.

Mappers, on the other hand, are independent of a particular channel's implementation; mappers use riders in their implementation. This independence means that every mapper may be used on any compatible rider without the need to implement all combinations of mappers and riders individually.

Locators and Opening Channels

Before reading or writing to a location, a connection must be created by opening a channel on the location. The operations for opening channels are collectively called locators. The primary function of locators is to resolve a data location (as specified by a file name, URL, etc.), and then open a channel to that location.

Locators may be simply a set of functions; for instance:

  PROCEDURE New* (...): ChannelType;

  PROCEDURE Old* (...): ChannelType;

For channels that correspond to a location that can be both read and changed, New() will create a new channel for the given data location, deleting all data previously contained in it. Old() will open a channel to existing data.

For channels representing a unidirectional byte stream (like output to/ input from terminal, or a TCP stream), only a procedure New() is provided. It will create a connection with the designated location.

The formal parameters of these procedures will normally include some kind of reference to the data being opened (e.g., a file name) and, optionally, flags that modify the way the channel is opened (e.g., read-only, write-only, etc). Their use (and therefore, interface) depends on the type of channel to be opened.

In more complex circumstances, actual locator types may be required; in that case, the locator type might provide type-bound procedures Old and New to create a new channel.

When finished reading to or writing from the location, the connection can be terminated by closing the channel ((each channel provides a Close method for this purpose; locators do not supply any close operations). This will free all resources allocated by the system for the channel. Once a channel is closed, no further input or output operations can be performed on it.

Please note: A channel implementation may limit the number of channels that can be open simultaneously. It's common for an OS to only support a limited number of open files or open sockets at the same time. See individual channel types for these limitations (if such limitations exist for that type).

Channels

This section describes the channel types provided by the OOC library. Each module contains both the channel and its associated basic riders. Constant values that are relevant to a particular channel type are also declared within the defining module.

Module Channel

Module Channel provides three abstract classes: Channel, Reader, and Writer.

All types and procedures declared in this module are considered abstract; they are never instanciated or called. Module Channel is of interest, however, because like all abstract classes, its types define the interface elements that are required for any concrete classes, which are derived from them.

Abstract class Channel is the base for all channel types.

Abstract classes Reader and Writer are the required basic rider types that must be declared for each channel. Notice that these define only read and write operations for sequences of bytes (see section Riders and Mappers).

See the various concrete channel classes for more detail and examples of usage (like section Module Files, section Module StdChannels, or section Module ProgramArgs). In particular, the chapter about Files can be read without any prior knowledge about channels.

Abstract Class Channel

Data type: Result = Msg.Msg
This is the result type for indication of error status, and includes the facilities for extracting error messages (see section Messages).

Abstract Class: Channel = POINTER TO ChannelDesc
This is the abstract base channel type. Channel types are used to connect to data locations (see section Input/Output Overview). Channel contains the following fields:
Field: res-: Result
res is the result (i.e., error flag) signalling failure of a call to NewReader, NewWriter, Flush, Close, etc. res is initialized to done when the channel is created. Every operation sets this to done if successful, or otherwise, res.code is set to an error value indicating the cause of the error (use either res.GetLText() or res.GetText() to get a plain text error description). See section Summary of Channel Constants, for a list of applicable error codes.
Field: readable-: BOOLEAN
readable is set to TRUE if, and only if, readers can be attached to this channel with NewReader.
Field: writable-: BOOLEAN
writable is set to TRUE if, and only if, writers can be attached to this channel with NewWriter.
Field: open-: BOOLEAN
open indicates the channel's status; that is, it is set to TRUE on channel creation, and set to FALSE by a call to Close. Closing a channel prevents all further read or write operations on it.
Method: (ch: Channel) Length (): LONGINT
Length returns the number of bytes of data for the channel ch. If ch represents a file, then this value is the file's size. If ch has no fixed length (e.g., because it's interactive), it returns noLength.
Method: (ch: Channel) GetModTime (VAR mtime: Time.TimeStamp)
GetModTime retrieves the modification time of the data location accessed by channel ch. If no such information is available, ch.res.code is set to noModTime; otherwise ch.res is set to done.
Method: (ch: Channel) NewReader (): Reader
This method attaches a new reader to the channel ch. The reader's position is set to the beginning of the channel, and its res field is initialized to done. ch.res is set to done on success and the new reader is returned. Otherwise, it returns NIL and ch.res.code is set to indicate the error cause. Please note: if the channel does not support multiple reading positions, the same reader is always returned.
Method: (ch: Channel) NewWriter (): Writer
This method attaches a new writer to the channel ch. The writer's position is set to the beginning of the channel, and its res field is initialized to done. ch.res is set to done on success and the new writer is returned. Otherwise, it returns NIL and ch.res.code is set to indicate the error cause. Please note: if the channel does not support multiple writing positions, the same writer is always returned.
Method: (ch: Channel) Flush
Flushes all buffers related to this channel. Any pending write operations are passed to the underlying OS and all buffers are marked as invalid. The next read operation will get its data directly from the channel instead of the buffer. If a writing error occurs, the field ch.res.code will be changed to writeError, otherwise ch.res is set to done. Please note: you must check the channel's res flag after an explicit Flush; none of the attached writers will indicate a write error in this case.
Method: (ch: Channel) Close
Flushes all buffers associated with ch, closes the channel, and frees all system resources allocated to it. This invalidates all riders attached to ch; they can't be used further. On success, if all read and write operations (including Flush) have completed successfully, ch.res is set to done. An opened channel can only be closed once, successive calls of Close are undefined. Please note: unlike the Oberon System all opened channels have to be closed explicitly. Otherwise resources allocated to them will remain blocked.
Method: (ch: Channel) ClearError
Sets the result flag ch.res to done.

Abstract Class Reader

Abstract Class: Reader = POINTER TO ReaderDesc
This is the abstract base reader type. Reader types are used to perform read operations on channels (see section Input/Output Overview). Reader contains the following fields:
Field: base-: Channel
base refers to the channel the reader is connected to.
Field: res-: Result
res is a result (error) flag that signals failure of a call to ReadByte, ReadBytes, or SetPos. res is initialized to done when creating a reader or by calling ClearError. The first failed read operation (or SetPos) changes this to indicate the error, all further calls to ReadByte, ReadBytes, or SetPos will be ignored until ClearError resets this flag. This means that the successful completion of an arbitrary complex sequence of read operations can be ensured by asserting that res equals done beforehand and also after the last operation. If res is not equal to done, res.code is set to the applicable error code. Use either of the methods res.GetLText() or res.GetText() to get a plain text error description of this error code. See section Summary of Channel Constants, for a list of applicable error codes.
Field: bytesRead-: LONGINT
bytesRead is set by ReadByte and ReadBytes to indicate the number of bytes that were successfully read.
Field: positionable-: BOOLEAN
positionable is set to TRUE if, and only if, the reader can be moved to another position with SetPos; for channels that can only be read sequentially, like input from the keyboard, this is set to FALSE.
Method: (r: Reader) Pos (): LONGINT
Returns the current reading position associated with the reader r in channel r.base, i.e., the index of the first byte that is read by the next call to ReadByte or ReadBytes. This procedure returns noPosition if the reader has no concept of a reading position (e.g., if it corresponds to input from keyboard), otherwise the result is non-negative.
Method: (r: Reader) Available (): LONGINT
Returns the number of bytes available for the next reading operation. For a file this is the length of the channel r.base minus the current reading position, for an sequential channel (or a channel designed to handle slow transfer rates) this is the number of bytes that can be accessed without additional waiting. The result is -1 if Close() was called for the channel (or the channel has been otherwise disconnected), or no more bytes are available. Please note: the number returned may be an approximation of the number of bytes that could be read at once; it could be lower than the actual value. For some channels or systems, this value may be as low as 1 even if more bytes are waiting to be processed.
Method: (r: Reader) SetPos (newPos: LONGINT)
Sets the reading position to newPos. Using a negative value of newPos, or calling this procedure for a reader that doesn't allow positioning, will set r.res.code to outOfRange. A value larger than the channel's length is legal, but the next read operation will most likely fail with an readAfterEnd error (unless the channel has grown beyond this position in the meantime). Calls to this procedure while r.res # done will be ignored; in particular, a call with r.res.code = readAfterEnd error will not reset res to done.
Method: (r: Reader) ReadByte (VAR x: SYSTEM.BYTE)
Reads a single byte from the channel r.base at the reading position associated with r and places it in x. The reading position is moved forward by one byte on success, and r.res is set to done. Otherwise, r.res.code indicates the error cause. Calling this procedure with the reader r placed at the end (or beyond the end) of the channel will set r.res.code to readAfterEnd. r.bytesRead will be 1 on success and 0 on failure. Calls to this procedure while r.res # done will be ignored.
Method: (r: Reader) ReadBytes (VAR x: ARRAY OF SYSTEM.BYTE; start, n: LONGINT)
Reads n bytes from the channel r.base at the reading position associated with r and places them in x beginning at index start. The reading position is moved forward by n bytes on success, and r.res is set to done. Otherwise, r.res.code indicates the error cause. Calling this procedure with the reader r positioned less than n bytes before the end of the channel will will set r.res.code to readAfterEnd. r.bytesRead will hold the number of bytes that were actually read (being equal to n on success). Calls to this procedure while r.res # done will be ignored. Pre-condition: n and start are non-negative. Also, there is enough space in array x, starting at index start, to hold n bytes.
Method: (r: Reader) ClearError
Sets the result flag r.res to done, re-enabling further read operations on r.

Abstract Class Writer

Abstract Class: Writer = POINTER TO WriterDesc
This is the abstract base writer type. Writer types are used to perform write operations on channels (see section Input/Output Overview). Writer contains the following fields:
Field: base-: Channel
This field refers to the channel the writer is connected to.
Field: res-: Result
res is a result (error) flag that signals failure of a call to WriteByte, WriteBytes, or SetPos. It is initialized to done when creating a writer or by calling ClearError. The first failed writing (or SetPos) operation sets res.code to indicate the error; all further calls to WriteByte, WriteBytes, or SetPos will be ignored until ClearError resets this flag. This means that the successful completion of an arbitrary complex sequence of write operations can be ensured by asserting that res equals done beforehand and also after the last operation. If res is not equal to done, res.code is set to the applicable error code. Use either of the methods res.GetLText() or res.GetText() to get a plain text error description of this error code. See section Summary of Channel Constants, for a list of applicable error codes. Please note: due to buffering, a write error may occur when flushing or closing the underlying channel; you have to check the channel's res field after any Flush() or the final Close() because a writer's res field may not indicate a write error in that case.
Field: bytesWritten-: LONGINT
Set by WriteByte and WriteBytes to indicate the number of bytes that were successfully written.
Field: positionable-: BOOLEAN
TRUE if, and only if, the writer can be moved to another position with SetPos; for channels that can only be written sequentially, like output to a terminal, this is FALSE.
Method: (w: Writer) Pos (): LONGINT
Returns the current writing position associated with the writer w in channel w.base, i.e., the index of the first byte that is written by the next call to WriteByte or WriteBytes. This procedure returns noPosition if the writer has no concept of a writing position (e.g., if it corresponds to output to terminal), otherwise the result is non-negative.
Method: (w: Writer) SetPos (newPos: LONGINT)
Sets the writing position to newPos. A negative value of newPos, or calling this procedure for a writer that doesn't allow positioning, will set w.res.code to outOfRange. A value larger than the channel's length is legal, however, the next write operation zero fills the intervening space. That is, the gap from the previous end of the channel to newPos are filled with 0X bytes. Calls to this procedure while w.res # done are ignored.
Method: (w: Writer) WriteByte (x: SYSTEM.BYTE)
Writes a single byte x to the channel w.base at the writing position associated with w. The writing position is moved forward by one byte on success, and r.res is set to done. Otherwise, w.res.code is set to indicate the error cause. w.bytesWritten will be 1 on success and 0 on failure. Calls to this procedure while w.res # done are ignored.
Method: (w: Writer) WriteBytes (VAR x: ARRAY OF SYSTEM.BYTE; start, n: LONGINT)
Writes n bytes from x, beginning at index start, to the channel w.base at the writing position associated with w. The writing position is moved forward by n bytes on success, and r.res is set to done. Otherwise, w.res.code is set to indicate the error cause. w.bytesWritten will hold the number of bytes that were actually written (being equal to n on success). Calls to this procedure while w.res # done are ignored. Pre-condition: n and start are non-negative. Also, this method requires that accessing n bytes in array x, starting from index start, will not go past the end of the array.
Method: (w: Writer) ClearError
Sets the result flag w.res to done, re-enabling further write operations on w.

Summary of Channel Constants

Constant: noLength
A result value for Channel.Length.

Constant: noPosition
A possible return value for Reader.Pos() or Writer.Pos() meaning that the reader or writer has no concept of a position (e.g., if it corresponds to input from keyboard or output to a terminal).

A specific channel implementation (e.g., see section Module Files) defines its own list of codes, containing aliases for the codes below (where appropriate) plus error codes of its own. These values are compared against the res.code field of the corresponding object (of types Channel, Reader, or Writer).

The methods res.GetLText() or res.GetText() can be used to translate any error code into a human readable message.

The following constant applies to the res field, and may be compared to it. (i.e., ch.res = done or ch.res # done.)

Constant: done
This indicates successful completion of the last operation.

If res is not equal to done, the following values may appear in res.code. These values apply to Channel, Reader, or Writer. Please note: These codes only cover the most typical errors.

Constant: invalidChannel
The channel isn't valid. For example, because it wasn't opened in the first place or was somehow corrupted.

Constant: writeError
A write error occured; usually this error happens with a writer, but for buffered channels this may also occur during a Flush or a Close.

Constant: noRoom
A write operation failed because there isn't any space left on the device. For example, the disk is full or you exeeded your quota; usually this error happens with a writer, but for buffered channels this may also occur during a Flush or a Close.

The following constants apply only to Reader.res.code and Writer.res.code:

Constant: outOfRange
SetPos has been called with a negative argument or it has been called on a rider that doesn't support positioning.

Constant: readAfterEnd
A call to ReadByte or ReadBytes has tried to access a byte beyond the end of the channel. This means that there weren't enough bytes left or the read operation started at (or after) the end.

Constant: channelClosed
The rider's channel has been closed, preventing any further read or write operations. This means there was a call to Channel.Close() (in which case, you probably made a programming error), or the channel has been otherwise disconnected (e.g., the process at the other end of the channel, say a pipe or TCP stream, closed the connection).

Constant: readError
An unspecified read error.

Constant: invalidFormat
Set by a mapper (e.g., TextRiders.Reader) if the byte stream at the current reading position doesn't represent an object of the requested type.

The following constants apply only to Channel.res.code:

Constant: noReadAccess
NewReader was called to create a reader on a channel that doesn't allow read access.

Constant: noWriteAccess
NewWriter was called to create a writer on a channel that doesn't allow write access.

Constant: closeError
An attempt to close the channel failed.

Constant: noModTime
No modification time is available for the given channel.

Constant: noTmpName
Creation of a temporary file failed because the system was unable to assign an unique name to it (closing or registering an existing temporary file beforehand might help in this case).

Constant: freeErrorCode
Free error code number. This is provided so that a specific channel implemenatation can start defining new error codes from this value.

Module Files

Most computer systems provide some way of storing persistent data--- information that exists between one program activation and the next. The most common way of accessing persistent data is through a file system. A file is generally a collection of data that is held on some physical medium like a hard disk or magnetic tape. A file system provides a means to manage files; grouping them logically into entities called directories, and otherwise accessing them through file names. As these are typical, basic computer concepts, this document will assume some familiarity with file systems.

Module Files provides facilities for accessing files using channel and rider abstractions. Files provides three related classes: File, Reader, and Writer. These classes are concrete subclasses of their conterparts in module Channel (see section Module Channel).

Class File is derived from the base channel type and adds additional methods for file specific operations. Files are probably the most frequently used channel implementation and, at the same time, the first channel to be used by a novice user. Therefore the description below incorporates all the relevant parts from the chapter about the abstract base type Channel.

As with all basic riders, Reader and Writer operate on sequences of bytes. Consequently, most of the time, after a file is opened, a mapper would be attached to provide more useful read/write operations (see section Module BinaryRider and section Module TextRider).

Please note: Most Unix systems only allow a fixed number of files (and sockets) to be open simultaneously. If this limit is reached, no new file can be opened or socket be created until an old file/socket is closed. For any POSIX compliant system at least 16 open files are supported, most implementations provide a much larger number.

Class File

Class File allows access to files as contiguous sequences of bytes.

Example:

VAR  f: Files.File;

f := Files.Old ("example.dat", {Files.read, Files.write}, res);
IF (res # Files.done) THEN
    (* Error processing: failed to open "old" file.  *)
END; ...

f.Close; (* Be sure to close the file so that resources are freed. *)

Class: File = POINTER TO FileDesc
This is the concrete subclass of Channel that corresponds to actual files. File inherits the following fields:
Field: res-: INTEGER
res is the result (i.e., error flag) signalling failure of a call to NewReader, NewWriter, Flush, Close, etc. res is initialized to done when the file is created. Every operation sets this to done if successful, or otherwise, res.code is set to an error value to indicate the cause of the error (use either res.GetLText() or res.GetText() to get a plain text error description). See section Summary of File Constants, for a list of applicable error codes.
Field: readable-: BOOLEAN
readable is set to TRUE if, and only if, readers can be attached to this file with NewReader.
Field: writable-: BOOLEAN
writable is set to TRUE if, and only if, writers can be attached to this file with NewWriter.
Field: open-: BOOLEAN
open indicates the file's status; that is, it is set to TRUE on file creation, and set to FALSE by a call to Close. Closing a file prevents all further read or write operations on it.

File inherits the following methods from the abstract class Channel:

Method: (f: File) Length (): LONGINT
Length returns the number of bytes of data for the file f. If f represents a genuine file, this value is the file's size. If f has no fixed length (e.g., because it's a FIFO special file), it returns noLength. Example:
(* For file,
 -rw-rw-r--   1 nikitin      8641 Jun  6 08:14 misc.txt
*)

VAR len: LONGINT;

len := f.Length();
    => len = 8641
Method: (f: File) GetModTime (VAR mtime: Time.TimeStamp)
GetModTime retrieves the modification time of the data location accessed by file f. If no such information is available, f.res.code is set to noModTime; otherwise, f.res is set to done. For more on time stamps See section Module Time. Example:
(* For file,
 -rw-rw-r--   1 nikitin      8641 Jun  6 08:14 misc.txt
*)

VAR fTime: Time.TimeStamp;

f.GetModTime(fTime);
    => fTime.days = 50605
    => fTime.msecs = 44064000
Method: (f: File) NewReader (): Reader
This method attaches a new (basic) reader to the file f (you will most likely never need to call this directly; you'd normally connect a mapper instead). The reader's position is set to the beginning of the file, and its res field is initialized to done. f.res is set to done on success and the new reader is returned. Otherwise, it returns NIL and f.res.code is set to indicate the error cause. Please note: if the file does not support multiple reading positions (e.g., because it's a FIFO special file), the same reader is always returned. Example:
VAR r: Files.Reader;

r := f.NewReader();
IF (f. res # Files.done) THEN
   (* Error processing:  failed to attach a new reader.  *)
END; 
Method: (f: File) NewWriter (): Writer
This method attaches a new writer to the file f (you will most likely never need to call this directly; you'd normally connect a mapper instead). The writer's position is set to the very start of the file, and its res field is initialized to done. f.res is set to done on success and the new writer is returned. Otherwise, it returns NIL and f.res.code is set to indicate the error cause. Please note: if the file does not support multiple writing positions (e.g., because it's a FIFO special file), the same writer is always returned. Example:
VAR w: Files.Writer;

w := f.NewWriter();
IF (f. res # Files.done) THEN
   (* Error processing:  failed to attach a new writer.  *)
END; 
Method: (f: File) Flush
Flushes all buffers related to this file. Any pending write operations are passed to the underlying OS and all buffers are marked as invalid. The next read operation will get its data directly from the channel instead of the buffer. If a writing error occurs, the field f.res.code will be set to writeError, otherwise, f.res is set to done. Please note: you must check the file's res flag after an explicit Flush; none of the attached writers will indicate a write error in this case. Example:
f.Flush;
IF (f.res # Files.done) THEN
   (* Error processing:  write error when flushing buffers. *)
END; 
Method: (f: File) Close
Flushes all buffers associated with f, closes the file, and frees all system resources allocated to it. This invalidates all riders attached to f; they can't be used further. On success, if all read and write operations (including Flush) have completed successfully, f.res is set to done. An opened file can only be closed once, successive calls of Close are undefined. Please note: unlike the Oberon System all opened Files have to be closed explicitly. Otherwise resources allocated to them will remain blocked. Example:
f.Close;
IF (f. res # Files.done) THEN
   (* Error processing:  error occured as file was closed.  *)
END; 
Method: (f: File) ClearError
Sets the result flag f.res to done. Example:
f.ClearError;
   => f.res = done

Besides its inherited methods, File has the following additional method:

Method: (f: File) Register
Registers the file f in the directory structure if it has been created with the Tmp procedure (see section File Locators). Registration happens atomically, i.e., it is guaranteed that any previously existing file is replaced by the newly registered one without any "in between" state. If the operation is interrupted, then either the old file still exists on the file system, or it has been replaced completely by the new one. Calling Tmp and Register successively has the same effect as calling New. Calling this procedure has no effect if the file f has been created with New or has been registered previously. Registration fails with an anonymousFile error if it was created by calling Tmp with an empty file name, and with a channelClosed error if f is closed. Example:
(* open named temporary file *)
f := Files.Tmp ("temp.fil", {Files.write}, res);

f.Close;
f.Register;
   => f.res.code = channelClosed
res.GetText (str);
   => str = "File has been closed"

(* open anonymous temporary file *)
f := Files.Tmp ("", {Files.write}, res); 

f.Register;
   => f.res.code = anonymousFile
res.GetText (str);
   => str = "Can't register anonymous file"

Class Reader

Class Reader provides primitive read operations on Files; that is, reading of bytes from a file. Most programmers would not use this class directly; a mapper class like BinaryRider.Reader or TextRider.Reader would be used instead (see section Module BinaryRider and section Module TextRider)

Class: Reader = POINTER TO ReaderDesc
This is a concrete rider type for reading bytes from files. Reader inherits the following fields from the base reader type:
Field: base-: Channel.Channel
base refers to the file the reader is connected to.
Field: res-: INTEGER
res is a result (error) flag that signals failure of a call to ReadByte, ReadBytes, or SetPos. res is initialized to done when creating a reader or by calling ClearError. The first failed read operation (or SetPos) changes this to indicate the error, all further calls to ReadByte, ReadBytes, or SetPos will be ignored until ClearError resets this flag. This means that the successful completion of an arbitrary complex sequence of read operations can be ensured by asserting that res equals done beforehand and also after the last operation. If res is not equal to done, res.code is set to the applicable error code. Use either of the methods res.GetLText() or res.GetText() to get a plain text error description of this error code. See section Summary of File Constants, for a list of applicable error codes.
Field: bytesRead-: LONGINT
bytesRead is set by ReadByte and ReadBytes to indicate the number of bytes that were successfully read.
Field: positionable-: BOOLEAN
positionable is set to TRUE if, and only if, the reader can be moved to another position with SetPos; for files that can only be read sequentially, this is set to FALSE.

Reader inherits the following methods from the abstract reader class:

Method: (r: Reader) Pos (): LONGINT
Returns the current reading position associated with the reader r in file r.base, i.e., the index of the first byte that is read by the next call to ReadByte or ReadBytes. This procedure returns a non-negative result.
Method: (r: Reader) Available (): LONGINT
Returns the number of bytes available for the next reading operation. For a file this is the length of the file r.base minus the current reading position. The result is -1 if Close() was called for the file (or the file has been otherwise closed), or no more bytes are available.
Method: (r: Reader) SetPos (newPos: LONGINT)
Sets the reading position to newPos. Using a negative value of newPos, or calling this procedure for a reader that doesn't allow positioning, will set r.res.code to outOfRange. A value larger than the file's length is legal, but the following read operation will most likely fail with an readAfterEnd error (unless the file has grown beyond this position in the meantime). Calls to this procedure while r.res # done will be ignored, in particular a call with r.res.code = readAfterEnd error will not reset res to done. Example:
(* For file,
 -r--r--r--   1 nikitin     12265 Jun  9 11:16 test.dat
*)

VAR pos, avail: LONGINT;
    r: Files.Reader;
    f: Files.File;

f := Files.Old("test.dat", {Files.read}, res);
r := f. NewReader();

pos := r.Pos();
   => pos = 0

avail := r.Available();
   => avail = 12265

r.SetPos(6000);

pos := r.Pos();
   => pos = 6000

avail := r.Available();
   => avail = 6265
Method: (r: Reader) ReadByte (VAR x: SYSTEM.BYTE)
Reads a single byte from the file r.base at the reading position associated with r and places it in x. The reading position is moved forward by one byte on success, and r.res is set to done. Otherwise r.res.code indicates the error cause. Calling this procedure with the reader r placed at the end (or beyond the end) of the file will set r.res.code to readAfterEnd. r.bytesRead will be 1 on success and 0 on failure. Calls to this procedure while r.res # done will be ignored. Example:
(* OOC assumes that SIZE(SYSTEM.BYTE) = SIZE(SHORTINT) *)
VAR byte: SHORTINT;
    ch  : CHAR;

r.ReadByte(byte);
r.ReadByte(ch);
Method: (r: Reader) ReadBytes (VAR x: ARRAY OF SYSTEM.BYTE; start, n: LONGINT)
Reads n bytes from the file r.base at the reading position associated with r and places them in x, beginning at index start. The reading position is moved forward by n bytes on success, and r.res is set to done. Otherwise, r.res.code indicates the error cause. Calling this procedure with the reader r positioned less than n bytes before the end of the file will will set r.res.code to readAfterEnd. r.bytesRead will hold the number of bytes that were actually read (being equal to n on success). Calls to this procedure while r.res # done will be ignored. Pre-condition: n and start are non-negative. Also, there is enough space in array x, starting at index start, to hold n bytes. Example:
VAR byteArr: ARRAY 256 OF SHORTINT;

r.ReadBytes(byteArr, 0, 16);
   => reads the next 16 bytes from r.base into byteArr[0..15]

r.ReadBytes(byteArr, 16, 100);
   => reads the next 100 bytes from r.base into 
        byteArr[16..115]
Method: (r: Reader) ClearError
Sets the result flag r.res to done, re-enabling further read operations on r. Example:
r.ClearError
   => r.res = done

Class Writer

Class Writer provides primitive write operations on Files; that is, writing of bytes to a file. Most programmers would not use this class directly; a mapper class like BinaryRider.Writer or TextRider.Writer would be used instead (see section Module BinaryRider and see section Module TextRider)

Class: Writer = POINTER TO WriterDesc
This is a concrete rider type for writing bytes to files. Writer inherits the following fields from the base writer type:
Field: base-: Channel.Channel
This field refers to the file the Writer is connected to.
Field: res-: INTEGER
res is a result (error) flag that signals failure of a call to WriteByte, WriteBytes, or SetPos. It is initialized to done when creating a writer or by calling ClearError. The first failed writing (or SetPos) operation sets res.code to indicate the error; all further calls to WriteByte, WriteBytes, or SetPos will be ignored until ClearError resets this flag. This means that the successful completion of an arbitrary complex sequence of write operations can be ensured by asserting that res equals done beforehand and also after the last operation. If res is not equal to done, res.code is set to the applicable error code. Use either of the methods res.GetLText() or res.GetText() to get a plain text error description of this error code. See section Summary of File Constants, for a list of applicable error codes. Please note: due to buffering, a write error may occur when flushing or closing the underlying file; you have to check the file's res field after any Flush() or the final Close().
Field: bytesWritten-: LONGINT
Set by WriteByte and WriteBytes to indicate the number of bytes that were successfully written.
Field: positionable-: BOOLEAN
TRUE if, and only if, the writer can be moved to another position with SetPos; for files that can only be written sequentially, this is FALSE.

Writer inherits the following methods from the abstract writer class:

Method: (w: Writer) Pos (): LONGINT
Returns the current writing position associated with the writer w in file w.base, i.e., the index of the first byte that is written by the next call to WriteByte or WriteBytes. This procedure returns a non-negative result.
Method: (w: Writer) SetPos (newPos: LONGINT)
Sets the writing position to newPos. A negative value of newPos, or calling this procedure for a writer that doesn't allow positioning, will set w.res.code to outOfRange. A value larger than the file's length is legal, however, the next write operation zero fills the intervening space. That is, the gap from the previous end of the file to newPos are filled with 0X bytes. Calls to this procedure while w.res # done are ignored. Example:
(* For file,
 -r--r--r--   1 nikitin     12265 Jun  9 11:16 test.dat
*)

VAR pos, LONGINT;
    w: Channel.Writer;
    f: Files.File;

f := Files.Old("test.dat", {Files.write}, res);
w := f. NewWriter();

pos := w.Pos();
   => pos = 0

w.SetPos(6000);

pos := w.Pos();
   => pos = 6000
Method: (w: Writer) WriteByte (x: SYSTEM.BYTE)
Writes a single byte x to the file w.base at the writing position associated with w. The writing position is moved forward by one byte on success, and r.res is set to done. Otherwise, w.res.code is set to indicate the error cause. w.bytesWritten will be 1 on success and 0 on failure. Calls to this procedure while w.res # done are ignored. Example:
(* OOC assumes that SIZE(SYSTEM.BYTE) = SIZE(SHORTINT) *)
VAR byte: SHORTINT;

byte = ODH;
w.WriteByte(byte);
w.WriteByte("A");
Method: (w: Writer) WriteBytes (VAR x: ARRAY OF SYSTEM.BYTE; start, n: LONGINT)
Writes n bytes from x, starting at index start in x, to the file w.base at the writing position associated with w. The writing position is moved forward by n bytes on success, and r.res is set to done. Otherwise, w.res.code is set to indicate the error cause. w.bytesWritten will hold the number of bytes that were actually written (being equal to n on success). Calls to this procedure while w.res # done are ignored. Pre-condition: n and start are non-negative. Also, this method requires that accessing n bytes in array x, starting from index start, will not go past the end of the array. Example:
(* OOC assumes that SIZE(SYSTEM.BYTE) = SIZE(CHAR). *)
VAR charArr: ARRAY 256 OF CHAR;

charArr := "abcdefghijklmnopqrstuvwxyz";  
        (* Note charArr[26] = 0X *)

w.WriteBytes(charArr, 0, 16);
   => writes exactly 16 values 
      (i.e., 0X is not automatically written) 
   => abcdefghijklmnop

w.WriteBytes(charArr, 16, 11);
   => writes exactly 11 values 
      (i.e., 0X is written from charArr[26]) 
   => qrstuvwxyz0X
Method: (w: Writer) ClearError
Sets the result flag w.res to done, re-enabling further write operations on w. Example:
w.ClearError
   => w.res = done

Besides its inherited methods, Writer has the following additional methods:

Method: (VAR w: Writer) Truncate (VAR newLength: LONGINT)
Causes the file associated with w to have the specified length. If the file was previously larger than newLength, the extra data is lost. If it was previously shorter, bytes between the old and new lengths are read as null bytes (i.e., 0X bytes). The writer's position is not modified in either case. Please note: On systems that do not support shortening files directly it is implemented as a partial file copy.

File Locators

The following locator procedures are provided for opening files. Possible values for the flags parameter are read, write, tryRead, tryWrite (see section Summary of File Constants).

Function: New (VAR file: ARRAY OF CHAR; VAR flags: SET; VAR res: Result): File
Creates a new file under the name file. On success, the new file object is returned, and res is set to done. Otherwise, it returns NIL and res.code and will indicate the problem.

If res is not equal to done, use either of the methods res.GetLText() or res.GetText() to get a plain text error description of this error code. See section Summary of File Constants, for a list of applicable error codes.

Please note: In terms of the Oberon System, this procedure combines the procedures New and Register.

Function: Old (VAR file: ARRAY OF CHAR; VAR flags: SET; VAR res: Result): File
Opens an existing file. On success the new file object is returned and res is set to done. Otherwise, it returns NIL and res.code will indicate the problem.

If res is not equal to done, use either of the methods res.GetLText() or res.GetText() to get a plain text error description of this error code. See section Summary of File Constants, for a list of applicable error codes.

Function: Tmp (VAR file: ARRAY OF CHAR; VAR flags: SET; VAR res: Result): File
Creates a temporary file that can be registered later on. On success the new file object is returned and res is set to done. Otherwise, it returns NIL and res.code will indicate the problem.

If res is not equal to done, use either of the methods res.GetLText() or res.GetText() to get a plain text error description of this error code. See section Summary of File Constants, for a list of applicable error codes.

Temporary files are created with only the user's write bit set, and the permissions are extended upon registration. The files are deleted if they haven't been registered and are closed, or the program terminates.

An unique temporary file name is created if the given file name is the empty string. Such a file can't be registered later. Note that some systems may have a low limit for the number of temporary file names. The limit is never less than 25. To be safe, you should never have more than 25 anonymous temporary files open simultaneously, or check that the TMP_MAX macro in /usr/include/stdio.h is large enough for your purposes.

With oo2c if file isn't empty, the new name is derived from the old one by appending "^", "^1", "^2", etc. in turn, until a file name is found that doesn't exist already. If such call to Tmp returns nameTooLong, then this refers to the constructed temporary name, not the one in file.

This function corresponds to Oberon System's New.

Other File Operations

It isn't always desirable to have to open a file before performing certain operations on it. You may not be interested in a file's contents; but rather some property of the file itself (for instance, does the named file even exist). As such, module Files provides some free-standing procedures:

Procedure: SetModTime (VAR file: ARRAY OF CHAR; VAR mtime: Time.TimeStamp; VAR res: Result)
Sets the modification time of the given file to mtime. On success res will be set to done. Otherwise, res.code holds an error code that indicates the problem.

Please note: under Unix this procedure will also change the access time to the value of mtime.

Procedure: GetModTime (VAR file: ARRAY OF CHAR; VAR mtime: Time.TimeStamp; VAR res: Result)
Gets the modification time of the given file to mtime. On success res will be set to done. Otherwise, res.code holds an error code that indicates the problem.

Function: Exists (VAR file: ARRAY OF CHAR): BOOLEAN
Returns TRUE if file file exists, FALSE otherwise. This procedure may be changed in future revisions to give more useful information on failure.

Example:

(* Attempting to open a "read-only" file for writing *)

f := Files.Old ("example.dat", {Files.write}, res);
   => res.code = accessDenied
res.GetText (str);
   => str = "Failed to open file with requested access rights"

Summary of File Constants

For constant values that are common to all channel types (see section Summary of Channel Constants), local names have been provided:

Constant: noLength
A result value for File.Length.

Constant: noPosition
A possible return value for Reader.Pos() or Writer.Pos() meaning that the reader or writer has no concept of a position.

The following constant applies to the res field, and may be compared to it. (i.e., ch.res = done or ch.res # done.)

Constant: done
This indicates successful completion of the last operation.

The following values are compared against the res.code field of the corresponding object (of types Channel, Reader, or Writer).

The methods res.GetLText() or res.GetText() can be used to translate any error code into a human readable message.

Constant: invalidChannel
The channel (i.e., file) isn't valid. For example, because it wasn't opened in the first place or was somehow corrupted.

Constant: writeError
A write error occured; usually this error happens with a writer, but for buffered files this may also occur during a Flush or a Close.

Constant: noRoom
A write operation failed because there isn't any space left on the device. For example, the disk is full or you exeeded your quota; usually this error happens with a writer, but for buffered files this may also occur during a Flush or a Close.

The following constants only apply to Reader.res.code and Writer.res.code:

Constant: outOfRange
SetPos has been called with a negative argument or it has been called on a rider that doesn't support positioning.

Constant: readAfterEnd
A call to ReadByte or ReadBytes has tried to access a byte beyond the end of the file. This means that there weren't enough bytes left or the read operation started at (or after) the end.

Constant: channelClosed
The rider's channel (i.e., file) has been closed, preventing any further read or write operations. This means there was a call to File.Close() (in which case, you probably made a programming error), or the channel has been otherwise closed.

Constant: readError
An unspecified read error.

Constant: invalidFormat
Set by a mapper (e.g., TextRiders.Reader) if the byte stream at the current reading position doesn't represent an object of the requested type.

The following constants only apply to File.res.code:

Constant: noReadAccess
NewReader was called to create a reader on a file that doesn't allow read access.

Constant: noWriteAccess
NewWriter was called to create a writer on a file that doesn't allow write access.

Constant: closeError
An attempt to close the file failed.

Constant: noModTime
No modification time is available for the given file.

Constant: noTmpName
Creation of a temporary file failed because the system was unable to assign an unique name to it (closing or registering an existing temporary file beforehand might help in this case).

The following values report problems when opening or modifying a file:

Constant: accessDenied
Access to the file was denied, e.g., because a file's permissions don't permit the requested access method, or because the given URL isn't publically readable.

Constant: isDirectory
The flags argument specified write access, and the file is a directory.

Constant: tooManyFiles
The process or the entire system has too many files open.

Constant: noSuchFile
The named file in a call to Old() does not exist. Or the directory part of a file name passed to New() or Tmp() does not exist.

Constant: directoryFull
The directory or the file system that would contain the new file cannot be extended, either because there is no space left or the directory has a fixed upper limit.

Constant: readOnlyFileSystem
The file resides on a read-only file system and it is attempted to create a new file or to gain write access for an existing one.

Constant: invalidTime
The time passed to procedure SetModTime is not a valid time stamp; either the millisecond part isn't valid, or the time value is too large or too small to be mapped to the time value of the underlying OS.

Constant: notOwner
Only the owner of a file can change its modification time.

Constant: anonymousFile
A file can only be registered if a file name was passed to the initial call to Tmp().

Constant: dirWriteDenied
You need to have write permission for the directory you want to add a new file to.

Constant: fileError
Unspecified error when opening/creating a file; this usually means that this module doesn't know how to interpret the error code delivered by the OS.

Constant: nameTooLong
Either the total length of the file name or of an individual file name component is too large; the operating system can impose such limits (see PATH_MAX and NAME_MAX in /usr/include/limits.h), or the file system itself restricts the format of names on it.

Constant: notDirectory
A file that is referenced as a directory component of the file name exists, but is not a directory.

Constant: linkLoop
Too many symbolic links were resolved while trying to look up the file name; the operating system has an arbitrary limit on the number of symbolic links that may be resolved in looking up a single file name, as a primitive way to detect loops.

The following are possible elements for the flags parameter of New, Old, or Tmp.

Please note: at least one of the following flags has to be set; otherwise you will get an "access denied" error:

Constant: read
If the file cannot be opened for reading access, then it isn't opened at all; in this case the error code is set to noReadAccess.

Constant: write
If the file cannot be opened for writing access, then it isn't opened at all; in this case the error code is set to noWriteAccess.

Constant: tryRead
Try to open this file for reading access; if the file permissions don't permit reading, the file is opened nevertheless, but the file descriptor's attribute readable is set to FALSE.

Constant: tryWrite
Try to open this file for writing access; if the file permissions don't permit writing, the file is opened nevertheless, but the file descriptor's attribute writable is set to FALSE.

Module StdChannels

Module StdChannels defines the standard I/O channels, which are predefined channels for input (typically the keyboard) and output (typically the computer screen).

Standard channels do not have to be opened by a client program because they are already open and ready for use. Their attributes and operations are described by the class Channel.Channel.

The standard channels (stdin, stdout, and stderr) should never be closed. You can close the standard channels (e.g., to detach a program from its terminal), but StdChannels does not provide a way to reopen them. Notice that the modules In, Out, Err, OakIn, and OakOut are all affected by such operations on standard channels. If, for example, you call stdout.Close, then the procedures in module Out will no longer function (unless you use Out.SetWriter to set another channel).

A fourth standard channel, null, is also provided.

Mappers may be attached to any of these channels to provide read and write operations for them. Mappers from module TextRider are most often used.

Also, be aware that modules In, Out, and Err provide simple interfaces to the standard channels (see section Standard I/O). So that, in many cases, you may not have to use module StdChannels directly.

Read-only Variable: stdin
The standard input channel, which is a predefined source of input for the program. The referenced channel is read-only.

Example:

VAR stringVar: ARRAY 256 OF CHAR; 
    rdr:       TextRider.Reader;

rdr := TextRider.ConnectReader(StdChannels.stdin);
rdr.ReadLine(stringVar);

Read-only Variable: stdout
The standard output channel, which is a predefined destination for output from the program. The referenced channel is write-only.

Example:

VAR wrtr: TextRider.Writer;

wrtr := TextRider.ConnectWriter(StdChannels.stdout);
wrtr.WriteString("A string to write"); wrtr.WriteLn;

Read-only Variable: stderr
The standard error channel, which can be used for error messages and diagnostics issued by the program. The referenced channel is write-only.

Example:

VAR wrtr: TextRider.Writer;

wrtr := TextRider.ConnectWriter(StdChannels.stderr);
wrtr.WriteString("An error has occured"); wrtr.WriteLn;

Read-only Variable: null
The null channel, which can be used as a destination for output that is to be discarded. The referenced channel is write-only.

Module ProgramArgs

This module provides access to the command line arguments passed to the program's invocation. They are mapped onto a standard channel args, with each argument transformed into a single line of text. Interpreting the list of arguments is usually done by applying an instance of TextRider.Reader or TextRider.Scanner to the argument channel.

The number of arguments is determined by calling args.ArgNumber(). If the invocation were, for example, foo bar 42, where foo is the name of the program itself, then the channel's contents would look like this:

foo
bar
42

For the above example, args.ArgNumber() would return 2; that is, the program name is not counted by ArgNumber even though it is present in args.

Note that any end-of-line characters within command line arguments are mapped to space (20X) characters. This ensures, that a single argument is always mapped onto a single line of text, even if it has embedded end-of-line characters.

Also, be careful with settings for TextRider.Reader and especially TextRider.Scanner: end-of-line characters are treated as whitespace by many of the read operations, which means, for a program foo, the reader or scanner has no way of distinguishing between

foo 123 bar
for "123 bar"

You would normally consider the first invocation as having two arguments, and the second as having one; which is also how ProgramArgs would interpret them. For foo 123 bar, args would contain

foo
123
bar

whereas, for foo "123 bar", args would contain

foo
123 bar

But a text reader or scanner, if set to treat end-of-line as whitespace, would treat both of these invocations as equivalent.

Please note: In cases where separate arguments need to be considered as a whole, the reader method ReadLine should be used. Unlike other read operations, such asReadInt or ReadIdentifier, leading whitespace is not skipped and, after completion, the reading position is just behind the end-of-line character.

So ReadLine should be used to read, for example, file name arguments because operating systems like Unix typically allow arbitrary characters in file names, including blanks and control codes.

Module ProgramArgs provides local equivalents for the following constants from module Channels: done, outOfRange, readAfterEnd, channelClosed, noWriteAccess, and noModTime.

Class: Channel = POINTER TO ChannelDesc
This class is derived from the abstract base channel class. In addition to its inherited fields and methods (see section Abstract Class Channel), the class provides the following method:
Method: (VAR ch: Channel) ArgNumber (): LONGINT
Returns the number of command line arguments (excluding the program name itself) passed to the program.

Read-only Variable: args
The predefined program arguments channel. The referenced channel is read-only.

As a further example, suppose a program foo required exactly two (positional) command line arguments. The first is an integer value and the second is an identifier. Also, suppose that all of the following invocations are to be considered equivalent:

foo 123 bar
foo +123 bar
foo "  +123" " bar"

Note that, the following module would not consider `foo 123 " bar "' or `foo 123+ bar' to be equivalent to the above invocations.

Example:

VAR r: TextRider.Reader;
    str: ARRAY 256 OF CHAR;
    int: LONGINT;

  r := TextRider.ConnectReader(ProgramArgs.args);
  IF r = NIL THEN 
     (* Error processing: failed to connect to `args' *)
  END;

  IF ProgramArgs.args.ArgNumber() # 2 THEN
     (* Error processing: wrong number of arguments *)
  END;

  (* skip past the line containing the program name `foo' *)
  r.ReadLn;

  r.ReadLInt(int);
  IF r.res # TextRider.done THEN
     (* Error processing: can't read an integer *)
  ELSIF ~r.Eol() THEN
     (* Error processing: this argument has other stuff after
        the integer just read *)
  END;

  r.ReadLn; (* skip to the next line *)

  r.ReadIdentifier(str);
  IF r.res # TextRider.done THEN
     (* Error processing: can't read an identifier *)
  ELSIF ~r.Eol() THEN
     (* Error processing: extra stuff after the identifier *)
  END;

Messages

Module `Msg' provides a framework for messages, which are used as a level of indirection between simple error codes and human readable error messages. Unlike numeric error codes, an instance of Msg carries its own interpretation context. Using this context, plus the error code stored in the message, and possibly additional data, the message can be converted into a description. The additional data can be text fragments, numbers, or other messages, and it can be inserted anywhere into the message's text. There is no need to determine the message text at the place the message is created. A message can be converted to text anywhere in the program.

This module actually combines several concepts: messages, message attributes, message contexts, and message lists. Although this may seem a bit complicated, the actual mechanism is very simple.

A message is an object that can be converted to human readable text and presented to a program's user. Within the OOC Library, messages are used to store errors in the I/O modules. Another example is an XML parser, which uses messages to create an error list when parsing an XML document.

Contexts and attributes are primarily of interest for modules that generate messages. These determine the content of the message, and how it can be translated into readable text. A typical user will mostly be in the position of message consumer, and will be handed filled in message objects. For a user, the typical operation will be to convert a message into descriptive text (see methods Message.GetText() and Message.GetLText()).

Message lists are a convenience feature for modules like parsers, which normally do not abort after a single error message. Usually, they try to continue their work after an error, looking for more problems and possibly emitting more error messages. Using message lists, errors can be collected together (e.g., within a compiler) to be presented to the user in a single batch.

Messages

Class: Msg = POINTER TO MsgDesc
A message's type is uniquely identified by its context and its code. Using these two attributes, a message can be converted to text. The text may contain placeholders, which are filled by the textual representation of attribute values associated with the message.
Field: nextMsg-: Msg
Field: prevMsg-: Msg
These two fields are initialized to NIL, and are used by MsgList.
Field: code-: Code
Field: context-: Context
Field: attribList-: Attribute
This list of attributes is sorted by name. Follow Attribute.nextAttrib to traverse the list.

The following function is a constructor for a message object:

Function: New (context: Context; code: Code): Msg
This function creates and returns a new message object for the given context, using the specified message code. The message's attribute list is empty.

Users of messages will be most interested in the following methods, which are used to retrieve the textual representation of a message:

Method: (msg: Msg) GetLText (VAR text: LString)
This method converts a message into a text string. The basic format of the string is determined by calling msg.context.GetTemplate. Then the attributes are inserted into the template string; the placeholder string `${foo}' is replaced with the textual representation of each attribute (see Context.GetTemplate). Pre-condition: LEN(text)<2^15 Please note: Behaviour is undefined if replacement text of an attribute contains an attribute reference.
Method: (msg: Msg) GetText (VAR text: String)
This method operates just like GetLText, but the message text is truncated to ISO-Latin-1 characters. All characters that are not part of ISO-Latin-1 are mapped to question marks `?'.

Example:

VAR r: TextRider.Reader;
    f: Files.File;
    str: ARRAY 256 OF CHAR;
    res: Files.Result;     (* `Result' is an alias for `Msg.Msg'. *)
    
  f := Files.Old("Sample.txt", {Files.read}, res);
  IF (f = NIL) THEN
     res.GetText(str);
     Err.String(str); Err.Ln;
  ELSE
     r := TextRider.ConnectReader(f); 
     IF (r # NIL) THEN 
        r.ReadLine(str);	    (* Read the lines of a file. *)
        WHILE r.res=Files.done DO
           Out.String(str); Out.Ln; (* And output them to the screen. *)
           r.ReadLine(str);	
        END;
        (*  Check to see if it stopped reading because it reached
         *  end-of-file.  If not, then print the error string.
         *)
        IF (r.res.code#Files.readAfterEnd) THEN 
           r.res.GetText(str);
           Err.String(str); Err.Ln;
        END;    
     END;
  END;

A programmer who is creating a library module can use the following methods to manage the attributes of a message:

Method: (msg: Msg) GetAttribute (name: String): Attribute
This method returns the attribute name of the message object. If no such attribute exists, the value NIL is returned.
Method: (msg: Msg) SetAttribute (attr: Attribute)
This method appends an attribute to the message's attribute list. If an attribute of the same name exists already, it is replaced by the new one. Pre-condition: Length(attr.name^)<=sizeAttrName and attr has not been attached to any other message.
Method: (msg: Msg) SetIntAttrib (name: String; value: LONGINT)
Pre-condition: Length(name)<=sizeAttrName

Example:

VAR
  lineVal, colVal: LONGINT;
  attrib1, attrib2: Msg.Attribute;

msg.SetIntAttrib ("line", lineVal);
msg.SetIntAttrib ("column", colVal);

...

attrib1 := GetAttribute("line");
attrib2 := GetAttribute("column");
Method: (msg: Msg) SetStringAttrib (name: String; value: StringPtr)
Pre-condition: Length(name)<=sizeAttrName
Method: (msg: Msg) SetLStringAttrib (name: String; value: LStringPtr)
Pre-condition: Length(name)<=sizeAttrName
Method: (msg: Msg) SetMsgAttrib (name: String; value: Msg)
Pre-condition: Length(name)<=sizeAttrName

Contexts and Attributes

When writing a library module (or perhaps a set of related library modules), a Context is defined, which may specify message formats and handle generation of messages. Specific Attributes that directly relate to a Context, and its related messages, are defined to go along with that Context.

The basic steps are

The following is an example showing how a Context can be set up. (In this case, for a command line parser). Note that use of Attributes is not required (and not shown in this example), and that this example has only a single error message.

MODULE CmdLine;

IMPORT Msg;

(* Context and template infrastructure *)
CONST
  connectFailed = 1;
  
TYPE
  ErrorContext = POINTER TO ErrorContextDesc;
  ErrorContextDesc = RECORD
    (Msg.ContextDesc)
  END;

VAR
  cmdLineContext: ErrorContext;

PROCEDURE (context: ErrorContext) GetTemplate* (msg: Msg.Msg; 
                                                VAR templ: Msg.LString);
  VAR
    t: ARRAY 128 OF Msg.LChar;
  BEGIN
    CASE msg. code OF
    | connectFailed:
      t := "Failed to connect reader to program arguments"
    END;
    COPY (t, templ)
  END GetTemplate;

PROCEDURE Error (code: Msg.Code): Msg.Msg;
(* Create error message for context `cmdLineContext', using the error
   code `code'.  *)
  VAR
    err: Msg.Msg;
  BEGIN
    err := Msg.New (cmdLineContext, code);
    RETURN err
  END Error;

BEGIN
  (* initialize error context *)
  NEW (cmdLineContext);
  Msg.InitContext (cmdLineContext, "CmdLine")
END CmdLine.

Class: Context = POINTER TO ContextDesc
Instances of this class describe the context under which messages are converted into their textual representation. Together, a message's context and its code identify the message type.
Field: id-: StringPtr
As a debugging aid, this field is usually filled with an string that identifies the module that created this context instance (see procedure InitContext).

The following is an initialization procedure for Contexts:

Procedure: InitContext (context: Context; id: String)
This procedure intializes an instance of Context. The string argument id should describe the message context to the programmer. It should not appear in output generated for a program's user, or at least, it should not be necessary for a user to interpret this string to understand the message. Generally, it is a good idea to use the module name of the context variable for the identifier. If this is not sufficient to identify the variable, add the variable name to the string.
Method: (context: Context) GetTemplate (msg: Msg; VAR templ: LString)
This method returns a template string for the message msg. The template is used as the basis for the human readable string returned by GetText. Typically, the string is derived from the message code, and it contains attribute references. Instead of the reference `${foo}', the procedure GetText (see below) will insert the textual representation of the attribute with the name `foo'. The special reference `${MSG_CONTEXT}' is replaced by the value of context.id, and `${MSG_CODE}' with msg.code. The default implementation returns this string:
MSG_CONTEXT: ${MSG_CONTEXT}
MSG_CODE: ${MSG_CODE}
attribute_name: ${attribute_name}
The last item is repeated for every attribute name. The lines are separated by CharClass.eol. Pre-condition: msg # NIL

Example:

PROCEDURE (context: aContext) GetTemplate* (msg: Msg.Msg; 
                                            VAR templ: Msg.LString);
VAR
   t: ARRAY 128 OF Msg.LChar;
BEGIN
   CASE msg. code OF

   ...  (* set the value of `t' with appropriate message *)

   END;
   COPY (t, templ);
   (* then append the line and column numbers ---
    * note that attribute values are later substituted by
    * `Msg.GetLText' or `Msg.GetText'.
    *)
   LongStrings.Append (" line=${line}, column=${column}", templ);
END GetTemplate;

Attributes

Constant: sizeAttrName
Maximum length of the attribute name for InitAttribute, NewIntAttrib, NewStringAttrib, NewLStringAttrib, or NewMsgAttrib.

Class: Attribute = POINTER TO AttributeDesc
An attribute is a (name, value) tuple, which can be associated with a message. When a message is tranlated into its readable version through the GetText function, the value part of each attribute can be converted to some textual representation, and then inserted into the message's text. Within a message, an attribute is uniquely identified by its name.
Field: nextAttrib-: Attribute
Field: name-: StringPtr
The name of an Attribute is restricted to sizeAttrName characters.

The following is an initialization procedure for Attributes:

Procedure: InitAttribute (attr: Attribute; name: String)
This procedure initializes an attribute object and sets its name.
Method: (attr: Attribute) ReplacementText (VAR text: LString)
This method converts the attribute value into some textual representation. The length of the resulting string must not exceed sizeAttrReplacement characters. Note that GetLText() calls this procedure with a text buffer of `sizeAttrReplacement+1' bytes.

The following are default implementations for some commonly used message attributes and their corresponding constructors and ReplacementText methods:

Class: IntAttribute = POINTER TO IntAttributeDesc
Field: int-: LONGINT

Function: NewIntAttrib (name: String; value: LONGINT): IntAttribute
This function creates and returns a new attribute (IntAttribute) object.

Pre-condition: Length(name)<=sizeAttrName

Method: (attr: IntAttribute) ReplacementText (VAR text: LString)

Class: StringAttribute = POINTER TO StringAttributeDesc
Field: string-: StringPtr

Function: NewStringAttrib (name: String; value: StringPtr): StringAttribute
This function creates and returns a new attribute (StringAttribute) object.

Pre-condition: Length(name)<=sizeAttrName

Method: (attr: StringAttribute) ReplacementText (VAR text: LString)

Class: LStringAttribute = POINTER TO LStringAttributeDesc
Field: string-: LStringPtr

Function: NewLStringAttrib (name: String; value: LStringPtr): LStringAttribute
This function creates and returns a new attribute (LStringAttribute) object.

Pre-condition: Length(name)<=sizeAttrName

Method: (attr: LStringAttribute) ReplacementText (VAR text: LString)

Class: MsgAttribute = POINTER TO MsgAttributeDesc
Field: msg-: Msg

Function: NewMsgAttrib (name: String; value: Msg): MsgAttribute
This function creates and returns a new attribute (MsgAttribute) object.

Pre-condition: Length(name)<=sizeAttrName

Method: (attr: MsgAttribute) ReplacementText (VAR text: LString)

Message Lists

Class: MsgList = POINTER TO MsgListDesc
Field: msgCount-: LONGINT
The number of messages on the list.
Field: msgList-: Msg
The messages of the list can be traversed using the fields Msg.nextMsg and Msg.prevMsg.

The following are for construction and initialization of MsgLists:

Procedure: InitMsgList (l: MsgList)
This procedure initializes a message list object.

Function: NewMsgList (): MsgList
This function creates and returns a new message list object.

The following methods are used to add messages to a message list:

Method: (l: MsgList) Append (msg: Msg)
Appends the message msg to the list l. Pre-condition: msg is not part of another message list.
Method: (l: MsgList) AppendList (source: MsgList)
Appends the messages of list source to l. Afterwards, source is an empty list, and the elements of source can be found at the end of the list l.

Standard Mappers

Mappers are high-level riders, which are used to translate between a sequence of data items and an uninterpreted sequence of bytes (see section Riders and Mappers). Thus, the reader and writer types in BinaryRider and TextRider are considered mappers.

The standard mappers, defined in this section, use the basic riders associated with a particular channel type for reading and writing bytes. You'll notice that there are very few error code constants defined within either of these modules; error codes are dependant on the channel being read, and so you'll have to use the constant values for readers and writers that are declared within each particular channel module.

Because OOC has both CHAR and LONGCHAR character types, mappers for textual data have been set up as a class hierarchy, with base classes in module `Rider' from which all other text mappers derive.

Text Mappers

The text mapper modules (`Rider', `LongRider', `TextRider', and `UnicodeRider') provide facilities for reading and writing values in text format. Text format is delimited, or otherwise formatted, sequences of character values that can be interpreted as words, numbers, symbols, and so forth. This corresponds to the way human beings read text, or perhaps how an Oberon-2 source file is parsed by a compiler. Data in text format are generally refered to simply as text.

Text can usually be interpreted in a limited number of ways. For example, the number 2 can be read as an INTEGER value or as a REAL. It could be an element of a SET, or perhaps even be part of an identifier such as oo2c. The interpretation is based on context and the format of the characters rather than as a fixed number of bytes.

Because the corresponding classes from the text mapper modules provide related facilities, they form a class hierarchy as follows:

            Rider [ABSTRACT]
            /    \
          /        \
        /            \
   TextRider        LongRider [ABSTRACT]
                         |
                         |
                         |
                    UnicodeRider

Module Rider

Module `Rider' encapsulates the base classes for all other text mapper classes. These base classes (Reader, Writer, and Scanner) are abstract classes that define the interface elements required for concrete classes derived from them.

See the concrete text mapper classes for more detail and examples of usage (section Module TextRider and section Module UnicodeRider).

Class Reader (Rider)

Constant: maxLengthEol
The maximum number of characters allowed in Reader.eol.

Abstract Class: Reader = POINTER TO ReaderDesc
This class provides facilities for reading various kinds of text. Note that this type does not inherit properties from any basic reader type; rather it uses the basic reader type associated with the channel it is attached to.

Also note that, after any failed read operation, all further attempts to read will be ignored until the error is cleared using ClearError.

See section Class Reader (TextRider), for examples of usage.

Field: opt-: SET
The current read options setting for the reader.
Field: res-: Msg.Msg
This field indicates the status of the last read operation (e.g., ReadLine, ReadInt, SetPos, etc.). Error codes (for res.code) are highly dependent on the channel being read, and therefore on the basic riders provided by that channel, so you must look at the result codes for a particular channel's reader type (e.g., Files.Reader error codes). See the various channel types for details of these error codes (i.e., section Module Files, section Module StdChannels, or section Module ProgramArgs). If res#done, use either res.GetLText() or res.GetText() to get a plain text error description corresponding to the error code.
Field: base-: Channel.Channel
This field refers to the channel the reader is connected to.

The following fields determine how the reader interprets end-of-line markers. Note that the end-of-line marker may contain the character `0X', which means its length must be stored in a separate field. The eol marker cannot be empty, and all characters must be an ASCII code in the range 00X..1FX.

Field: eol-: ARRAY maxLengthEol OF CHAR
The character sequence that represents an end-of-line marker. Note that this is a character array, not a string (i.e., it may contain the character `0X').
Field: eolLen-: INTEGER
The number of characters in eol. The default value for this is `-1', which means that end-of-line is auto detected (see SetEol below). Otherwise, this value is in the range 1 <= eolLen <= maxLengthEol.

The following methods can be used to check the status of a reader or, in some cases, change its state. Some methods are fully described in the abstract reader section (see section Abstract Class Reader), so only brief descriptions of those are given here.

Method: (r: Reader) Available () : LONGINT
Returns the number of bytes available for the next read operation.
Method: (r: Reader) ClearError
Clears error conditions on the reader r, re-enabling further read operations.
Method: (r: Reader) Eol (): BOOLEAN
This method returns TRUE if the reader is currently positioned at an end-of-line marker (see SetEol below). This will also return TRUE if r.res # done. Otherwise, FALSE is returned.
Method: (r: Reader) Pos (): LONGINT
Returns the current reading position associated with the reader r in channel r.base.
Method: (r: Reader) SetEol (marker: ARRAY OF CHAR; markerLen: INTEGER)
This method sets the end-of-line marker; that is, what character(s) is used to mark the end of a line. If the passed string marker does not fit into the field eol, or if it contains a character >= ` ', then r.res.code is set to invalidFormat. A marker length markerLen=-1 enables auto detection of the end-of-line convention used by the channel. For auto detection to work, the channel is required to use one of the following eol markers:
`LF'
used by Unix
`CR'
used by MacOS
`CR/LF'
used by MS-DOS and Windows
Please note: ReadChar is unaffected by the current eol setting. That is, if the end-of-line marker consists of more than one character (like `CR/LF'), each character is read separately. All other read operations view an end-of-line marker at an atomic entity when the channel is read sequentially. If auto detection is enabled, and the eol convention of the file is `CR/LF', then the first end-of-line marker is not skipped completely when reached by the reader (r.Pos() is at the `LF'). This is transparent to all reading procedures except ReadChar and Pos; the `LF' will be skipped automatically on the next read. This positioning inconsistency only applies for the very first eol encountered. Pre-condition: All of the following apply:
  1. r.res = done, and
  2. (markerLen = -1) OR (1 <= markerLen < LEN (marker)), and
  3. markerLen <= maxLengthEol, and
  4. for all i: marker[i] < 20X
Method: (r: Reader) SetOpts (opts: SET)
This method is used to set the reader options r.opt.
Method: (r: Reader) SetPos (newPos: LONGINT)
Sets the reading position to newPos.

The following methods read a value of the given type from the current position of the reader. Most read operations skip leading whitespace before reading a token; there are only three methods that do not skip whitespace: ReadChar, ReadLn, and ReadLine.

When attempting to read, and if the value is not properly formatted for its type, r.res.code is set to invalidFormat. The reader remains positioned at the character which caused the invalidFormat error, but further reading can not take place until the error is cleared.

If a number, or potential set element, is properly formatted, but has a value that is out of range of the target type, then a valueOutOfRange error occurs. In this case, the reader is positioned after the last character that was read. Again, further reading can not take place until the error is cleared.

A valueOutOfRange error also occurs for methods reading into an ARRAY OF CHAR (i.e., ReadLine, ReadIdentifier, and ReadString) if the character array is not large enough to hold the entire input.

Otherwise, for any operation attempting to read when there are no characters left to be read, a read-after-end error occurs and Reader.res.code is set to readAfterEnd.

In any case, whenever an error occurs, it is safest to assume that no value has been read. That is, the variable being read into is left with an undefined value.

All further calls of these read methods will be ignored if r.res#done. That is, no new characters will be read if an error has occurred previously.

Method: (r: Reader) ReadBool (VAR bool: BOOLEAN)
Reads in an identifier (see ReadIdentifier below), and if it is either of the tokens TRUE or FALSE, it is converted to a BOOLEAN value. If this method encounters any other token, an invalidFormat error occurs and the value of bool is undefined.
Method: (r: Reader) ReadChar (VAR ch: CHAR)
Reads in a single character value and places it in ch.
Method: (r: Reader) ReadHex (VAR lint: LONGINT)
Reads in characters in the form of an unsigned hexadecimal number and converts them to a LONGINT value. The first character must be a decimal digit (i.e., `0..9') and subsequent characters must be valid hexadecimal digits (i.e., `0..9' or `A..F'). If the first non-whitespace character is not a digit, then an invalidFormat error occurs. If the input is properly formatted as an unsigned hex number, but the value is out of range for a LONGINT, then a valueOutOfRange error occurs. Upon encountering an error, the value of lint is undefined. Please note: Because LONGINT values are signed, hex numbers in the range `80000000H..FFFFFFFFH' are interpreted as negative LONGINT values.
Method: (r: Reader) ReadIdentifier (VAR s: ARRAY OF CHAR)
Reads an Oberon-2 style identifier into s. An identifier is a sequence of letters and digits, which must begin with a letter. Sequences not beginning with a letter produce an invalidFormat error. If s is not large enough to hold the entire input, a valueOutOfRange error occurs. Upon encountering an error, the value of s is undefined.
Method: (r: Reader) ReadInt (VAR int: INTEGER)
Reads in characters in the form of a signed whole number and converts them to an INTEGER value. If the first character is not a digit, a "+" sign, or a "-" sign, then an invalidFormat error occurs. If the input is properly formatted as a signed whole number, but the value is out of range for an INTEGER, then a valueOutOfRange error occurs. Upon encountering an error, the value of int is undefined.
Method: (r: Reader) ReadLInt (VAR lint: LONGINT)
This method provides the same facility as ReadInt, except that it deals with LONGINT values.
Method: (r: Reader) ReadSInt (VAR sint: SHORTINT)
This method provides the same facility as ReadInt, except that it deals with SHORTINT values.
Method: (r: Reader) ReadLine (VAR s: ARRAY OF CHAR)
Reads a sequence of characters into s; reading continues until an end-of-line character is encountered, the array s is full, or r reaches the end of the channel. The end-of-line character is discarded and s is always terminated with 0X. If r is already positioned at an end-of-line character, s returns as an empty string. If s is not large enough to hold the entire input, a valueOutOfRange error occurs; s returns with the sequence of characters that have been read so far (terminated by 0X). If r has already reached the end of the channel (i.e., there are no more characters left to read), a readAfterEnd error occurs and s returns as an empty string.
Method: (r: Reader) ReadLn
This method reads and discards all characters up to and including the next end-of-line character. If the end of the channel is reached before encountering an end-of-line character, a readAfterEnd error occurs.
Method: (r: Reader) ReadString (VAR s: ARRAY OF CHAR)
Reads in a sequence of characters enclosed in single (') or double (") quote marks. The opening quote must be the same as the closing quote and must not occur within the string. Characters will be read until the terminating quote mark is encountered, an invalid character is read (end-of-line is always considered invalid), there are no more characters available in the channel, or the string s is full. s is always terminated with 0X. Unquoted strings produce an invalidFormat error. Strings with no terminating quote mark also result in an invalidFormat error. If s is not large enough to hold the entire input, a valueOutOfRange error occurs. Upon encountering an error, the value of s is undefined.
Method: (r: Reader) ReadReal (VAR real: REAL)
Reads in characters in the form of a signed fixed or floating-point number and converts them to a REAL value. If the first character is not a digit, a "+" sign, or a "-" sign, then an invalidFormat error occurs. If the input is properly formatted as a signed fixed or floating-point number, but the value is out of range for a REAL, then a valueOutOfRange error occurs. Upon encountering an error, the value of real is undefined.
Method: (r: Reader) ReadLReal (VAR lreal: LONGREAL)
This method provides the same facility as ReadReal, except that it deals with LONGREAL values.
Method: (r: Reader) ReadSet (VAR s: SET)
Reads in characters in the form of a set constructor and converts them to a SET. If the sequence of characters does not form a valid set constructor, then an invalidFormat error occurs. If the input is properly formatted as a set constructor, but a set element has a value out of the range 0..MAX(SET), then a valueOutOfRange error occurs. Upon encountering an error, the value of s is undefined.

Class Writer (Rider)

Abstract Class: Writer = POINTER TO WriterDesc
This class provides facilities for writing various types of text. Note that this type does not inherit properties from any basic writer type; rather it uses the basic writer type associated with the channel it is attached to.

See section Class Writer (TextRider), for examples of usage.

Field: opt-: SET
The current write options setting for the writer. See section Summary of TextRider Constants for possible option values.
Field: res-: Msg.Msg
This field indicates the status of the last write operation (e.g., WriteBytes, WriteInt, SetPos, etc.). Error codes are highly dependent on the channel being written to (and therefore on the basic riders provided for that channel), so you must look at the result codes for the basic writer that is associated with that particular channel (e.g., Files.Writer error codes). See the various channel types for details of these error codes (i.e., section Module Files, section Module StdChannels, or section Module ProgramArgs). If res#done, use either res.GetLText() or res.GetText() to get a plain text error description corresponding to the error code.
Field: base-: Channel.Channel
This field refers to the channel the writer is connected to.

The following methods can be used to check the status of a writer or, in some cases, change its state. Some methods are fully described in the abstract writer section (see section Abstract Class Writer), so only brief descriptions of those are given here.

Method: (w: Writer) ClearError
Clears error conditions on the writer w, re-enabling further write operations.
Method: (w: Writer) Pos () : LONGINT
Returns the current writing position associated with the writer w in channel w.base.
Method: (w: Writer) SetEol (marker: ARRAY OF CHAR; markerLen: INTEGER)
This method sets the end-of-line marker; that is, what character(s) is used to mark the end of a line. If the passed string marker does not fit into the field eol, then w.res.code is set to invalidFormat. The empty marker is permitted. The default value for a newly created writer is CharClass.systemEol. Pre-condition: All of the following apply:
  1. w.res = done, and
  2. 0 <= markerLen < LEN (marker), and
  3. markerLen <= maxLengthEol.
Method: (w: Writer) SetOpts (opts: SET)
This method is used to set the writer options w.opt.
Method: (w: Writer) SetPos (newPos: LONGINT)
Sets the writing position to newPos.

The following writer methods are used to write values in text format to the underlying channel. In some situations, it is possible for only part of the value to be actually written.

Method: (w: Writer) WriteBool (bool: BOOLEAN)
Writes the value of bool as text. That is, either TRUE or FALSE.
Method: (w: Writer) WriteChar (ch: CHAR)
Writes a single character value ch.
Method: (w: Writer) WriteHex (lint: LONGINT; d: LONGINT)
Writes the value of lint as an unsigned hexadecimal number with a minimum field width of d. Leading zeros are written if the value of lint requires less than d places. If d is less than or equal to zero, field width is 8.
Method: (w: Writer) WriteInt (int: INTEGER; n: LONGINT)
Writes the value of int as a decimal number with a minimum field width of n. Leading spaces are written if the value of int requires less than n places. A sign is written only for negative values.
Method: (w: Writer) WriteLInt (lint: LONGINT; n: LONGINT)
This method provides the same facility as WriteInt, except that it deals with LONGINT values.
Method: (w: Writer) WriteSInt (sint: SHORTINT; n: LONGINT)
This method provides the same facility as WriteInt, except that it deals with SHORTINT values.
Method: (w: Writer) WriteReal (real: REAL; n, k: LONGINT)
Writes the value of real as a floating-point number with a minimum field width of n. If the value of k is greater than 0, that number of significant digits is included. Otherwise, an implementation-defined number of significant digits is included. The decimal point is not included if there are no significant digits in the fractional part. The number is scaled with one digit in the whole number part. A sign is included only for negative values.
Method: (w: Writer) WriteLReal (lreal: LONGREAL; n, k: LONGINT)
This method provides the same facility as WriteReal, except that it deals with LONGREAL values.
Method: (w: Writer) WriteRealEng (real: REAL; n, k: LONGINT)
Writes the value of real as a floating-point number with a minimum field width of n. If the value of k is greater than 0, that number of significant digits is included. Otherwise, an implementation-defined number of significant digits is included. The decimal point is not included if there are no significant digits in the fractional part. The number is scaled with one to three digits in the whole number part and with an exponent that is a multiple of three. A sign is included only for negative values.
Method: (w: Writer) WriteLRealEng (lreal: LONGREAL; n, k: LONGINT)
This method provides the same facility as WriteRealEng, except that it deals with LONGREAL values.
Method: (w: Writer) WriteRealFix (real: REAL; n, k: LONGINT)
Writes the value of real as a fixed-point number with a minimum field width of n. The value is rounded to the given value of k relative to the decimal point. The decimal point is suppressed if k is less than 0. The number will have at least one digit in the whole number part. A sign is included only for negative values.
Method: (w: Writer) WriteLRealFix (lreal: LONGREAL; n, k: LONGINT)
This method provides the same facility as WriteRealFix, except that it deals with LONGREAL values.
Method: (w: Writer) WriteSet (s: SET)
Writes the value of s as an Oberon-2 set constructor, including curly braces, commas, and range indicators ("..") where appropriate.
Method: (w: Writer) WriteString (s: ARRAY OF CHAR)
Writes a string value up to, but not including, the terminating 0X character. The behaviour of this method is undefined if s is an unterminated character array. Please note: ReadString and WriteString are not symmetric. That is, WriteString does not enclose the written string in quote marks; only the actual character values contained in s are written.
Method: (w: Writer) WriteLn
Writes an end-of-line marker (i.e., a "newline"). The default value for a newly created writer is CharClass.systemEol (see SetEol above).

Class Scanner (Rider)

A text scanner is a special type of reader, which is used to parse text for different kinds of tokens. Integers, reals, strings, identifiers, set constructors, the boolean tokens TRUE and FALSE, and other special symbols are all tokens recognized by this kind of scanner.

These tokens are scanned sequentially, converted to an appropriate type, and then returned in one of the scanner's fields. The scanner's type field is then used to determine the type of token which has been scanned.

Along with some typical reader methods, such as SetPos, the primary method of a scanner is Scan, which simply scans the next token based on the scanner's current options setting.

See section Class Scanner (TextRider), for examples of usage.

Data type: String
A string type of pre-defined length for use within a scanner. Note that because this type is of finite length, a scanner is limited in the length of string it can scan.

Please note: LEN() can be used on a variable of type String to determine the maximum size that can be held by a scanner string.

Abstract Class: Scanner = POINTER TO ScannerDesc
This class provides facilities for scanning sequences of characters from a channel and parsing those characters into various tokens. The tokens a scanner can recognize are defined by the constants provided for its type field (section Summary of TextRider Constants).

Note that a scanner will not continue to read (via calls to Scan) if it has scanned an invalid token or an error occurs; ClearError must be called explicitly before scanning can continue. The difference is that invalid means that the token could not be interpreted; a sequence of characters was read, but could not be interpreted as a valid token. An error occurs when there is a problem with the underlying Reader; so, error is used to determine when you have reached end-of-text.

Field: base-: Channel.Channel
This field refers to the channel the scanner is connected to.
Field: lines-: LONGINT
Total number of lines (i.e., end-of-line characters) that have been scanned. This number is updated by Scan.
Field: opt-: SET
The current read options setting for the scanner. See section Summary of TextRider Constants for possible option values.
Field: pos-: LONGINT
Starting position of the most recently scanned token. Note that this is not the same as the value returned by the Pos() method. This value may be useful when an invalid token is scanned, as it will point to the start of the invalid token (whereas Pos() would be positioned after the invalid token). You could, for example, reset the scanner options and re-position the scanner back at the invalid token to attempt a re-scan.
Field: res-: Msg.Msg
This field indicates the status of the last read operation (e.g., Scan, SetPos, etc.). If res#done, use either res.GetLText() or res.GetText() to get a plain text error description corresponding to the error code.
Field: type-: INTEGER
The type of the token that has been most recently scanned. The constants bool, char, error, int, invalid, line, ident, real, set, string, tab, and undefined are possible values for type. See also the related output fields listed below.

The following are the output fields within a scanner. Before the first call to the Scan method, the values of these fields are undefined. After each successive call to Scan, type is set and the matching output field contains the value of the scanned token. The value of output fields not corresponding to type are undefined.

Field: bool-: BOOLEAN
This field will contain a valid value only if the interpretBools option is set and one of the tokens TRUE or FALSE is scanned.
Field: char-: CHAR
Contains a value if type is char, line, or tab.
Field: int-: LONGINT
Contains a value if type is int. Please note: Valid integers are in either signed decimal or unsigned hexadecimal formats (hexadecimal tokens must be terminated with an "H" character).
Field: real-: LONGREAL
Contains a value if type is real.
Field: set-: SET;
Contains a value if type is set.
Field: string-: String;
Contains a value if type is string or ident.

The following scanner methods are equivalent to the corresponding reader methods described in section Class Reader (TextRider), so only brief descriptions are given here.

Method: (s: Scanner) Available () : LONGINT
Returns the number of bytes available for the next scanning operation.
Method: (s: Scanner) ClearError
Clears error conditions on the scanner s, re-enabling further operations on s.
Method: (s: Scanner) Pos (): LONGINT
Returns the current reading position associated with the scanner s in channel s.base. Note that the value returned by this method is different from the position indicated by the scanner's pos field.
Method: (s: Scanner) SetEol (marker: ARRAY OF CHAR; markerLen: INTEGER)
This method sets the end-of-line marker; it provides the same facility as Reader.SetEol. A marker length markerLen=-1 enables auto detection of the end-of-line convention used by the channel.
Method: (s: Scanner) SetOpts (opts: SET)
This method is used to set the scanner options s.opt. See section Summary of TextRider Constants for possible option values.
Method: (s: Scanner) SetPos (newPos: LONGINT)
Sets the current scanning position to newPos.
Method: (s: Scanner) Scan
This method skips whitespace, and then scans for the next token as specified by the scanning options. Based on the type of token scanned, s.type is set and the matching output field is assigned a value. If the end of the valid text is reached, s.type is set to error. (Note that error is set when the last available valid token is read, not necessarily by a readAfterEnd condition.) Valid tokens are described as follows:
bool
If interpretBools is set as a scanner option, the text tokens TRUE or FALSE are read as bool. (Otherwise, these tokens are read as type ident.)
char
Normally, any printable characters other than a letter or number and any non-printable control character. However, scanner options will affect what a scanner interprets to be a char:
  • If interpretSets is not set, elements of a set constructor, "{", "}", ",", are read as char (and the associated integer constants are read as separate tokens).
  • If interpretStrings is not set, quote characters are read as char (and string contents are then read as separate tokens).
  • If useSignedNumbers is not set, "+" and "-" are read as char. (Otherwise, they are always considered part of a number.)
int
Any Oberon-2 integer constant. (Note that hexadecimal numbers must be unsigned and be terminated with an "H". Also, lower-case letters, `a..f', are not valid hex digits.)
line
If returnCtrlChars is set, an end-of-line character is read as s.type = line. Otherwise, it is counted as whitespace.
ident
Any Oberon-2 identifier. (Note that "_" is not considered as part of an identifier, nor is a selector ".".)
real
Any Oberon-2 real number constant.
set
Any Oberon-2 set constructor.
string
Any Oberon-2 string constant.
tab
If returnCtrlChars is set, a tab character is read as s.type = tab. Otherwise, it is counted as whitespace.

Module LongRider

Module `LongRider' extends the classes of `Rider' to provide support for types LONGCHAR and LongString. The classes of `LongRider' (Reader, Writer, and Scanner) are also abstract, and only extensions are described in this section; see section Module Rider for descriptions of other facilities.

Also, see the concrete text mapper classes for more detail and examples of usage (see section Module TextRider and section Module UnicodeRider).

Please note: Care should be taken when using the method SetPos for classes based on `LongRider'. Recall that SetPos operates just like the corresponding method from class Channel; that is, position is set directly within the byte stream. Setting the position is based on byte position rather than character position.

Because `LongRider' based classes deal with multi-byte character encodings, which may be of variable width, and because SetPos positions a reader on a byte level, a user cannot necessarily set a rider to an arbitrary character position within a channel. For practical purposes, variable width encodings may limit usage to saving the position of a reader based on a call like pos := reader.Pos(), which can later be restored via reader.SetPos(pos).

However, even in such a case, moving to a previously saved position might fail for encodings that use different states during decoding. For example, if the encoding uses special byte sequences to switch between different mappings while decoding, the actual mapping in use at file position `x' will not be reinstated correctly when calling `SetPos(x)'.

Class Reader (LongRider)

Abstract Class: Reader = POINTER TO ReaderDesc
This is an abstract subclass of Rider.Reader that provides support for LONGCHAR and LongString.

The specification for ReadChar is changed for LongRider.Reader in that it actually reads a LONGCHAR value (2 bytes) from the channel and then attempts to map it to a CHAR value (ISO-Latin-1). If the value cannot be mapped, a valueOutOfRange error occurs. Consequently for `LongRider', ReadLine, ReadIdentifier, and ReadString produce the same error in similar situations.

Also note that a valueOutOfRange error occurs for methods reading into an ARRAY OF LONGCHAR (i.e., ReadLLine, ReadLIdentifier, and ReadLString) if the (long) character array is not large enough to hold the entire input.

Reader adds the following methods:

Method: (r: Reader) ReadLChar (VAR ch: LONGCHAR)
Reads in a single (LONGCHAR) character value and places it in ch.
Method: (r: Reader) ReadLIdentifier (VAR s: ARRAY OF LONGCHAR)
Reads an Oberon-2 style identifier into s. An identifier is a sequence of letters and digits, which must begin with a letter. Sequences not beginning with a letter produce an invalidFormat error. If s is not large enough to hold the entire input, a valueOutOfRange error occurs. Upon encountering an error, the value of s is undefined.
Method: (r: Reader) ReadLLine (VAR s: ARRAY OF LONGCHAR)
Reads a sequence of (LONGCHAR) characters into s; reading continues until an end-of-line character is encountered, the array s is full, or r reaches the end of the channel. The end-of-line character is discarded and s is always terminated with 0X. If r is already positioned at an end-of-line character, s returns as an empty string. If s is not large enough to hold the entire input, a valueOutOfRange error occurs; s returns with the sequence of characters that have been read so far (terminated by 0X). If r has already reached the end of the channel (i.e., there are no more characters left to read), a readAfterEnd error occurs and s returns as an empty string.
Method: (r: Reader) ReadLString (VAR s: ARRAY OF CHAR)
Reads in a sequence of (LONGCHAR) characters enclosed in single (') or double (") quote marks. The opening quote must be the same as the closing quote and must not occur within the string. Characters will be read until the terminating quote mark is encountered, an invalid character is read (end-of-line is always considered invalid), there are no more characters available in the channel, or the string s is full. s is always terminated with 0X. Unquoted strings produce an invalidFormat error. Strings with no terminating quote mark also result in an invalidFormat error. If s is not large enough to hold the entire input, a valueOutOfRange error occurs. Upon encountering an error, the value of s is undefined.

Class Writer (LongRider)

Abstract Class: Writer = POINTER TO WriterDesc
This is an abstract subclass of Rider.Writer that provides support for LONGCHAR and LongString.

Note that the specification for WriteChar is changed for LongRider.Writer in that it actually writes 2 bytes at a time to the channel (i.e., CHAR values are actually written as Unicode values). ReadLine, ReadIdentifier, and ReadString behave similarly for `LongRider'.

LongRider.Writer adds the following methods:

Method: (w: Writer) WriteLChar (ch: LONGCHAR)
Writes a single (LONGCHAR) character value ch.
Method: (w: Writer) WriteLString (s: ARRAY OF LONGCHAR)
Writes a long string value up to, but not including, the terminating 0X character. The behaviour of this method is undefined if s is an unterminated (LONGCHAR) character array. Please note: ReadLString and WriteLString are not symmetric. That is, WriteLString does not enclose the written string in quote marks; only the actual (LONGCHAR) character values contained in s are written.

Class Scanner (LongRider)

Abstract Class: Scanner = POINTER TO ScannerDesc
This is an abstract subclass of Rider.Scanner that provides support for LONGCHAR and LongString.

Data type: LongString
A (long) string type of pre-defined length for use within a scanner. Note that because this type is of finite length, a scanner is limited in the length of string it can scan.

Please note: LEN() can be used on a variable of type LongString to determine the maximum size that can be held by a scanner string.

Field: type-: INTEGER
This is an inherited field, however, it now has the additional possible values: lchar, lident, lline, lstring, ltab.
Field: lchar-: LONGCHAR
Contains a value if type is lchar, lline, or ltab.
Field: lstring-: LongString;
Contains a value if type is lstring or lident.

Module TextRider

Module `TextRider' provides concrete classes derived from the abstract base classes of module `Rider'. `TextRider' is used for reading and writing data as character type CHAR (i.e., interpreting byte streams as ISO-Latin-1 characters). The descriptions below include details of the `TextRider' facilities (much of which is repeated from the section on module `Rider') as well as many examples of use.

The following program fragment gives an example of how you could use `TextRider' facilities to read the entire contents of a file, one line at a time, and echo each line to the screen (note that no error checking is done):

VAR r: TextRider.Reader;
    f: Files.File;
    str: ARRAY 256 OF CHAR;
    res: Result;
    
  f := Files.Old("Sample.txt", {Files.read}, res);
  r := TextRider.ConnectReader(f); 

  r.ReadLine(str);	
  WHILE r.res=Files.done DO
     Out.String(str); Out.Ln;
     r.ReadLine(str);	
  END;

Class Reader (TextRider)

Constant: maxLengthEol
The maximum number of characters allowed in Reader.eol.

Class: Reader = POINTER TO ReaderDesc
This is the concrete subclass of Rider.Reader that provides facilities for reading various kinds of text. Note that this type does not inherit properties from any basic reader type; rather it uses the basic reader type associated with the channel it is attached to.

Also note that, after any failed read operation, all further attempts to read will be ignored until the error is cleared using ClearError.

Field: opt-: SET
The current read options setting for the reader. See section Summary of TextRider Constants for possible option values.
Field: res-: Msg.Msg
This field indicates the status of the last read operation (e.g., ReadLine, ReadInt, SetPos, etc.). Error codes (for res.code) are highly dependent on the channel being read, and therefore on the basic riders provided by that channel, so you must look at the result codes for a particular channel's reader type (e.g., Files.Reader error codes). See the various channel types for details of these error codes (i.e., section Module Files, section Module StdChannels, or section Module ProgramArgs). If res#done, use either res.GetLText() or res.GetText() to get a plain text error description corresponding to the error code.
Field: base-: Channel.Channel
This field refers to the channel the reader is connected to.

The following fields determine how the reader interprets end-of-line markers. Note that the end-of-line marker may contain the character `0X', which means its length must be stored in a separate field. The eol marker cannot be empty, and all characters must be an ASCII code in the range 00X..1FX.

Field: eol-: ARRAY maxLengthEol OF CHAR
The character sequence that represents an end-of-line marker. Note that this is a character array, not a string (i.e., it may contain the character `0X').
Field: eolLen-: INTEGER
The number of characters in eol. The default value for this is `-1', which means that end-of-line is auto detected (see SetEol below). Otherwise, this value is in the range 1 <= eolLen <= maxLengthEol.

The following methods can be used to check the status of a reader or, in some cases, change its state. Some methods are fully described in the abstract reader section (see section Abstract Class Reader), so only brief descriptions of those are given here.

Method: (r: Reader) Available () : LONGINT
Returns the number of bytes available for the next read operation.
Method: (r: Reader) ClearError
Clears error conditions on the reader r, re-enabling further read operations.
Method: (r: Reader) Eol (): BOOLEAN
This method returns TRUE if the reader is currently positioned at an end-of-line marker (see SetEol below). This will also return TRUE if r.res # done. Otherwise, FALSE is returned.
Method: (r: Reader) Pos (): LONGINT
Returns the current reading position associated with the reader r in channel r.base.
Method: (r: Reader) SetEol (marker: ARRAY OF CHAR; markerLen: INTEGER)
This method sets the end-of-line marker; that is, what character(s) is used to mark the end of a line. If the passed string marker does not fit into the field eol, or if it contains a character >= ` ', then r.res.code is set to invalidFormat. A marker length markerLen=-1 enables auto detection of the end-of-line convention used by the channel. For auto detection to work, the channel is required to use one of the following eol markers:
`LF'
used by Unix
`CR'
used by MacOS
`CR/LF'
used by MS-DOS and Windows
Please note: ReadChar is unaffected by the current eol setting. That is, if the end-of-line marker consists of more than one character (like `CR/LF'), each character is read separately. All other read operations view an end-of-line marker at an atomic entity when the channel is read sequentially. If auto detection is enabled, and the eol convention of the file is `CR/LF', then the first end-of-line marker is not skipped completely when reached by the reader (r.Pos() is at the `LF'). This is transparent to all reading procedures except ReadChar and Pos; the `LF' will be skipped automatically on the next read. This positioning inconsistency only applies for the very first eol encountered. Pre-condition: All of the following apply:
  1. r.res = done, and
  2. (markerLen = -1) OR (1 <= markerLen < LEN (marker)), and
  3. markerLen <= maxLengthEol, and
  4. for all i: marker[i] < 20X
Method: (r: Reader) SetOpts (opts: SET)
This method is used to set the reader options r.opt. See section Summary of TextRider Constants for possible option values. Example:
r.SetOpts({TextRider.returnCtrlChars});
   => read operations using r do not treat EOL and TAB 
        characters as whitespace.  
r.SetOpts(TextRider.defReaderOptions);
   => reader options set to default values.
Method: (r: Reader) SetPos (newPos: LONGINT)
Sets the reading position to newPos.

The following methods read a value of the given type from the current position of the reader. Most read operations skip leading whitespace before reading a token; there are only three methods that do not skip whitespace: ReadChar, ReadLn, and ReadLine.

When attempting to read, and if the value is not properly formatted for its type, r.res.code is set to invalidFormat. The reader remains positioned at the character which caused the invalidFormat error, but further reading can not take place until the error is cleared.

If a number, or potential set element, is properly formatted, but has a value that is out of range of the target type, then a valueOutOfRange error occurs. In this case, the reader is positioned after the last character that was read. Again, further reading can not take place until the error is cleared.

A valueOutOfRange error also occurs for methods reading into an ARRAY OF CHAR (i.e., ReadLine, ReadIdentifier, and ReadString) if the character array is not large enough to hold the entire input.

Otherwise, for any operation attempting to read when there are no characters left to be read, a read-after-end error occurs and Reader.res.code is set to readAfterEnd.

In any case, whenever an error occurs, it is safest to assume that no value has been read. That is, the variable being read into is left with an undefined value.

All further calls of these read methods will be ignored if r.res#done. That is, no new characters will be read if an error has occurred previously.

Method: (r: Reader) ReadBool (VAR bool: BOOLEAN)
Reads in an identifier (see ReadIdentifier below), and if it is either of the tokens TRUE or FALSE, it is converted to a BOOLEAN value. If this method encounters any other token, an invalidFormat error occurs and the value of bool is undefined.
Method: (r: Reader) ReadChar (VAR ch: CHAR)
Reads in a single character value and places it in ch.
Method: (r: Reader) ReadHex (VAR lint: LONGINT)
Reads in characters in the form of an unsigned hexadecimal number and converts them to a LONGINT value. The first character must be a decimal digit (i.e., `0..9') and subsequent characters must be valid hexadecimal digits (i.e., `0..9' or `A..F'). If the first non-whitespace character is not a digit, then an invalidFormat error occurs. If the input is properly formatted as an unsigned hex number, but the value is out of range for a LONGINT, then a valueOutOfRange error occurs. Upon encountering an error, the value of lint is undefined. Please note: Because LONGINT values are signed, hex numbers in the range `80000000H..FFFFFFFFH' are interpreted as negative LONGINT values.
Method: (r: Reader) ReadIdentifier (VAR s: ARRAY OF CHAR)
Reads an Oberon-2 style identifier into s. An identifier is a sequence of letters and digits, which must begin with a letter. Sequences not beginning with a letter produce an invalidFormat error. If s is not large enough to hold the entire input, a valueOutOfRange error occurs. Upon encountering an error, the value of s is undefined. Example:
(* Input is as follows:  
myIdentifier x y2 3z 
*)

VAR str: ARRAY 256 OF CHAR;

r.ReadIdentifier(str)
   => str = "myIdentifier"
r.ReadIdentifier(str)
   => str = "x"
r.ReadIdentifier(str)
   => str = "y2"
r.ReadIdentifier(str)
   => r.res.code = invalidFormat, str = undefined
Method: (r: Reader) ReadInt (VAR int: INTEGER)
Reads in characters in the form of a signed whole number and converts them to an INTEGER value. If the first character is not a digit, a "+" sign, or a "-" sign, then an invalidFormat error occurs. If the input is properly formatted as a signed whole number, but the value is out of range for an INTEGER, then a valueOutOfRange error occurs. Upon encountering an error, the value of int is undefined. Example:
(* Input is as follows:
12345
999999999999999
forty-two
*)

VAR intVar: INTEGER;

r.ReadInt(intVar);
   => intVar = 12345
r.ReadInt(intVar);
   => r.res.code = valueOutOfRange, intVar = undefined
r.ClearError;
r.ReadInt(intVar); (* attempting to read `forty-two' *)
   => r.res.code = invalidFormat, intVar = undefined
        (* r.Pos() is still at the `f' in `forty-two' *)
Method: (r: Reader) ReadLInt (VAR lint: LONGINT)
This method provides the same facility as ReadInt, except that it deals with LONGINT values.
Method: (r: Reader) ReadSInt (VAR sint: SHORTINT)
This method provides the same facility as ReadInt, except that it deals with SHORTINT values.
Method: (r: Reader) ReadLine (VAR s: ARRAY OF CHAR)
Reads a sequence of characters into s; reading continues until an end-of-line character is encountered, the array s is full, or r reaches the end of the channel. The end-of-line character is discarded and s is always terminated with 0X. If r is already positioned at an end-of-line character, s returns as an empty string. If s is not large enough to hold the entire input, a valueOutOfRange error occurs; s returns with the sequence of characters that have been read so far (terminated by 0X). If r has already reached the end of the channel (i.e., there are no more characters left to read), a readAfterEnd error occurs and s returns as an empty string.
Method: (r: Reader) ReadLn
This method reads and discards all characters up to and including the next end-of-line character. If the end of the channel is reached before encountering an end-of-line character, a readAfterEnd error occurs.
Method: (r: Reader) ReadString (VAR s: ARRAY OF CHAR)
Reads in a sequence of characters enclosed in single (') or double (") quote marks. The opening quote must be the same as the closing quote and must not occur within the string. Characters will be read until the terminating quote mark is encountered, an invalid character is read (end-of-line is always considered invalid), there are no more characters available in the channel, or the string s is full. s is always terminated with 0X. Unquoted strings produce an invalidFormat error. Strings with no terminating quote mark also result in an invalidFormat error. If s is not large enough to hold the entire input, a valueOutOfRange error occurs. Upon encountering an error, the value of s is undefined. Example:
(* Input is as follows:
"A well-formed string"
'This is well-formed too'
"Not well-formed
because of line break"
*)

VAR str: ARRAY 256 OF CHAR;

r.ReadString(str);
   => str = "A well-formed string"
r.ReadString(str);
   => str = "This is well-formed too"
r.ReadString(str);
   => r.res.code = invalidFormat, str = undefined
        (* r.Pos() is now at the end of this line *)
r.ClearError;
r.ReadString(str);  
   (* attempting to read `because of line break"' *)
   => r.res.code = invalidFormat, str = undefined
Method: (r: Reader) ReadReal (VAR real: REAL)
Reads in characters in the form of a signed fixed or floating-point number and converts them to a REAL value. If the first character is not a digit, a "+" sign, or a "-" sign, then an invalidFormat error occurs. If the input is properly formatted as a signed fixed or floating-point number, but the value is out of range for a REAL, then a valueOutOfRange error occurs. Upon encountering an error, the value of real is undefined. Example:
(* Input is as follows:
-42
3.1415
+54321E+30
2.34E+56
+A
_34.56
*)

VAR realVar: REAL;

r.ReadReal(realVar);
   => realVar = -4.200000E+1
r.ReadReal(realVar);
   => realVar = 3.141500
r.ReadReal(realVar);
   => realVar = 5.432100E+34
r.ReadReal(realVar);
   => r.res.code = valueOutOfRange, realVar = undefined
r.ReadReal(realVar);
   => r.res = done, realVar = 0.000000
        (* r.Pos() is now at `A' *)
r.ClearError; r.ReadLn; 
   => Clear error and skip to the beginning of the next line 
r.ReadReal(realVar);
   => r.res.code = invalidFormat, realVar = undefined
        (* r.Pos() is still at the `_' in `_34.56' *)
Method: (r: Reader) ReadLReal (VAR lreal: LONGREAL)
This method provides the same facility as ReadReal, except that it deals with LONGREAL values.
Method: (r: Reader) ReadSet (VAR s: SET)
Reads in characters in the form of a set constructor and converts them to a SET. If the sequence of characters does not form a valid set constructor, then an invalidFormat error occurs. If the input is properly formatted as a set constructor, but a set element has a value out of the range 0..MAX(SET), then a valueOutOfRange error occurs. Upon encountering an error, the value of s is undefined. Example:
(* Input is as follows:
{0, 1, 2, 3, 4, 5}
{ 0..5 }
{6, 7, 1024}
{6, 7, W}
9..11
{13..12}
*)

VAR setVar: SET;

r.ReadSet(setVar);
   => setVar = {0..5}
r.ReadSet(setVar);
   => setVar = {0..5}
r.ReadSet(setVar);
   => r.res.code = valueOutOfRange, setVar = undefined
        (* r.Pos() is now at the `}' after the `1024' *)
r.ClearError; r.ReadLn; 
   => Clear error and skip to the beginning of the next line 

r.ReadSet(setVar);  (* attempt to read `{6, 7, W}' *)
   => r.res.code = invalidFormat, setVar = undefined
        (* r.Pos() is now at `W' *)
r.ClearError; r.ReadLn; 
   => Clear error and skip to the beginning of the next line 

r.ReadSet(setVar);  (* attempt to read `9..11' *)
   => r.res.code = invalidFormat, setVar = undefined
        (* r.Pos() is now at `9' *)
r.ClearError; r.ReadLn; 
   => Clear error and skip to the beginning of the next line 

r.ReadSet(setVar);  (* attempt to read `{13..12}' *)
   => r.res.code = invalidFormat, setVar = undefined
        (* r.Pos() is now at the `}' after the `12' *)

Class Writer (TextRider)

Class: Writer = POINTER TO WriterDesc
This class provides facilities for writing various types of text. Note that this type does not inherit properties from any basic writer type; rather it uses the basic writer type associated with the channel it is attached to.
Field: opt-: SET
The current write options setting for the writer. See section Summary of TextRider Constants for possible option values.
Field: res-: Msg.Msg
This field indicates the status of the last write operation (e.g., WriteBytes, WriteInt, SetPos, etc.). Error codes are highly dependent on the channel being written to (and therefore on the basic riders provided for that channel), so you must look at the result codes for the basic writer that is associated with that particular channel (e.g., Files.Writer error codes). See the various channel types for details of these error codes (i.e., section Module Files, section Module StdChannels, or section Module ProgramArgs). If res#done, use either res.GetLText() or res.GetText() to get a plain text error description corresponding to the error code.
Field: base-: Channel.Channel
This field refers to the channel the writer is connected to.

The following methods can be used to check the status of a writer or, in some cases, change its state. Some methods are fully described in the abstract writer section (see section Abstract Class Writer), so only brief descriptions of those are given here.

Method: (w: Writer) ClearError
Clears error conditions on the writer w, re-enabling further write operations.
Method: (w: Writer) Pos () : LONGINT
Returns the current writing position associated with the writer w in channel w.base.
Method: (w: Writer) SetEol (marker: ARRAY OF CHAR; markerLen: INTEGER)
This method sets the end-of-line marker; that is, what character(s) is used to mark the end of a line. If the passed string marker does not fit into the field eol, then w.res.code is set to invalidFormat. The empty marker is permitted. The default value for a newly created writer is CharClass.systemEol. Pre-condition: All of the following apply:
  1. w.res = done, and
  2. 0 <= markerLen < LEN (marker), and
  3. markerLen <= maxLengthEol.
Method: (w: Writer) SetOpts (opts: SET)
This method is used to set the writer options w.opt. See section Summary of TextRider Constants for possible option values. Example:
w.SetOpts({TextRider.noBuffering});
   => output is not buffered.
w.SetOpts(TextRider.defWriterOptions);
   => writer options set to default values.
Method: (w: Writer) SetPos (newPos: LONGINT)
Sets the writing position to newPos.

The following writer methods are used to write values in text format to the underlying channel. In some situations, it is possible for only part of the value to be actually written.

Method: (w: Writer) WriteBool (bool: BOOLEAN)
Writes the value of bool as text. That is, either TRUE or FALSE.
Method: (w: Writer) WriteChar (ch: CHAR)
Writes a single character value ch. Example:
w.WriteChar("A");
   => writes one character = "A"
Method: (w: Writer) WriteHex (lint: LONGINT; d: LONGINT)
Writes the value of lint as an unsigned hexadecimal number with a minimum field width of d. Leading zeros are written if the value of lint requires less than d places. If d is less than or equal to zero, field width is 8. Example:
w.WriteHex(127, 3);
   => writes "07F"  
w.WriteHex(127, 0);
   => writes "0000007F"  
w.WriteHex(-128, 0);
   => writes "FFFFFF80"
Method: (w: Writer) WriteInt (int: INTEGER; n: LONGINT)
Writes the value of int as a decimal number with a minimum field width of n. Leading spaces are written if the value of int requires less than n places. A sign is written only for negative values. Example:
w.WriteInt(54321, 0);
   => writes "54321"
w.WriteInt(54321, 10);
   => writes "     54321"
Method: (w: Writer) WriteLInt (lint: LONGINT; n: LONGINT)
This method provides the same facility as WriteInt, except that it deals with LONGINT values.
Method: (w: Writer) WriteSInt (sint: SHORTINT; n: LONGINT)
This method provides the same facility as WriteInt, except that it deals with SHORTINT values.
Method: (w: Writer) WriteReal (real: REAL; n, k: LONGINT)
Writes the value of real as a floating-point number with a minimum field width of n. If the value of k is greater than 0, that number of significant digits is included. Otherwise, an implementation-defined number of significant digits is included. The decimal point is not included if there are no significant digits in the fractional part. The number is scaled with one digit in the whole number part. A sign is included only for negative values. Example:
w.WriteReal(3923009, 0, 0);
   => writes "3.923009E+6"
w.WriteReal(3923009, 0, 1);
   => writes "4E+6"
w.WriteReal(3923009, 0, 4);
   => writes "3.923E+6"
w.WriteReal(3923009, 10, 1);
   => writes "      4E+6"

w.WriteReal(-39.23009, 12, 2);
   => writes "     -3.9E+1"
w.WriteReal(-39.23009, 1, 5);
   => writes "-3.9230E+1"

w.WriteReal(0.0003923009, 6, 1);
   => writes "  4E-4"
Method: (w: Writer) WriteLReal (lreal: LONGREAL; n, k: LONGINT)
This method provides the same facility as WriteReal, except that it deals with LONGREAL values.
Method: (w: Writer) WriteRealEng (real: REAL; n, k: LONGINT)
Writes the value of real as a floating-point number with a minimum field width of n. If the value of k is greater than 0, that number of significant digits is included. Otherwise, an implementation-defined number of significant digits is included. The decimal point is not included if there are no significant digits in the fractional part. The number is scaled with one to three digits in the whole number part and with an exponent that is a multiple of three. A sign is included only for negative values. Example:
w.WriteRealEng(39.23009, 0, 1);
   => writes "40"
w.WriteRealEng(39.23009, 5, 2);
   => writes "   39"
w.WriteRealEng(39.23009, 10, 5);
   => writes "    39.230"

w.WriteRealEng(-3923009, 13, 1);
   => writes "        -4E+6"
w.WriteRealEng(-3923009, 7, 3);
   => writes " -3.92E+6"
w.WriteRealEng(-3923009, 0, 6);
   => writes "-3.92301E+6"

w.WriteRealEng(0.0003923009, 1, 1);
   => writes "400E-6"
w.WriteRealEng(0.0003923009, 4, 2);
   => writes "  390E-6"
w.WriteRealEng(0.0003923009, 16, 5);
   => writes "       392.30E-6"
Method: (w: Writer) WriteLRealEng (lreal: LONGREAL; n, k: LONGINT)
This method provides the same facility as WriteRealEng, except that it deals with LONGREAL values.
Method: (w: Writer) WriteRealFix (real: REAL; n, k: LONGINT)
Writes the value of real as a fixed-point number with a minimum field width of n. The value is rounded to the given value of k relative to the decimal point. The decimal point is suppressed if k is less than 0. The number will have at least one digit in the whole number part. A sign is included only for negative values. Example:
w.WriteRealFix(3923009, 0, -5);
   => writes "3920000"  (* rounded to the 
                        ten-thousands place *)
w.WriteRealFix(3923009, 0, 4);
   => writes "3923009.0000"

w.WriteRealFix(3923.5, 0, -1);
   => writes "3924" (* rounded to the "ones" place *)
w.WriteRealFix(3923.5, 0, 0);
   => writes "3924." (* same as above, 
                        but writes a decimal point *)

w.WriteRealFix(-39.23009, 10, 1);
   => writes "     -39.2"
w.WriteRealFix(-39.23009, 20, 4);
   => writes "            -39.2301"

w.WriteRealFix(0.0003923009, 5, 1);
   => writes "  0.0"
w.WriteRealFix(0.0003923009, 11, 4);
   => writes "     0.0004"
Method: (w: Writer) WriteLRealFix (lreal: LONGREAL; n, k: LONGINT)
This method provides the same facility as WriteRealFix, except that it deals with LONGREAL values.
Method: (w: Writer) WriteSet (s: SET)
Writes the value of s as an Oberon-2 set constructor, including curly braces, commas, and range indicators ("..") where appropriate. Example:
w.WriteSet({});
   => writes "{}"
w.WriteSet({1,6,10});
   => writes "{1, 6, 10}"
w.WriteSet({0, 1, 2, 3, 4, 5});
   => writes "{0..5}"
w.WriteSet({0, 2, 3, 4, 8});
   => writes "{0, 2..4, 8}"
w.WriteSet({0, 2..7, 8});
   => writes "{0, 2..8}"
w.WriteSet({0, 2, 4, 6} + {1, 3, 5, 7});
   => writes "{0..7}"
Method: (w: Writer) WriteString (s: ARRAY OF CHAR)
Writes a string value up to, but not including, the terminating 0X character. The behaviour of this method is undefined if s is an unterminated character array. Please note: ReadString and WriteString are not symmetric. That is, WriteString does not enclose the written string in quote marks; only the actual character values contained in s are written.
Method: (w: Writer) WriteLn
Writes an end-of-line marker (i.e., a "newline"). The default value for a newly created writer is CharClass.systemEol (see SetEol above).

Class Scanner (TextRider)

A text scanner is a special type of reader, which is used to parse text for different kinds of tokens. Integers, reals, strings, identifiers, set constructors, the boolean tokens TRUE and FALSE, and other special symbols are all tokens recognized by this kind of scanner.

These tokens are scanned sequentially, converted to an appropriate type, and then returned in one of the scanner's fields. The scanner's type field is then used to determine the type of token which has been scanned.

Along with some typical reader methods, such as SetPos, the primary method of a scanner is Scan, which simply scans the next token based on the scanner's current options setting. A typical use of a scanner might look similar the following program fragment:

Example:

VAR s: TextRider.Scanner;
    f: Files.File;
    res: Files.Result;
    
  f := Files.Old("Sample.txt", {Files.read}, res);
  s := TextRider.ConnectScanner(f); 

  s.Scan;
  WHILE s.type # TextRider.error DO
     IF s.type = TextRider.string THEN
        ... (* Process string tokens *)
     ELSIF s.type = TextRider.ident THEN
        ... (* Process identifier tokens *)
     ELSIF s.type = TextRider.int THEN   
        ... (* Process integer tokens *)
     ELSIF ...
        ... (* Process other token types *)
     END; 
     s.Scan;
  END;

  Out.String("Total lines scanned="); 
  Out.LongInt(s.lines, 0); Out.Ln;

Data type: String
A string type of pre-defined length for use within a scanner. Note that because this type is of finite length, a scanner is limited in the length of string it can scan.

Please note: LEN() can be used on a variable of type String to determine the maximum size that can be held by a scanner string.

Class: Scanner = POINTER TO ScannerDesc
This class provides facilities for scanning sequences of characters from a channel and parsing those characters into various tokens. The tokens a scanner can recognize are defined by the constants provided for its type field (section Summary of TextRider Constants).

Note that a scanner will not continue to read (via calls to Scan) if it has scanned an invalid token or an error occurs; ClearError must be called explicitly before scanning can continue. The difference is that invalid means that the token could not be interpreted; a sequence of characters was read, but could not be interpreted as a valid token. An error occurs when there is a problem with the underlying Reader; so, error is used to determine when you have reached end-of-text.

Field: base-: Channel.Channel
This field refers to the channel the scanner is connected to.
Field: lines-: LONGINT
Total number of lines (i.e., end-of-line characters) that have been scanned. This number is updated by Scan.
Field: opt-: SET
The current read options setting for the scanner. See section Summary of TextRider Constants for possible option values.
Field: pos-: LONGINT
Starting position of the most recently scanned token. Note that this is not the same as the value returned by the Pos() method. This value may be useful when an invalid token is scanned, as it will point to the start of the invalid token (whereas Pos() would be positioned after the invalid token). You could, for example, reset the scanner options and re-position the scanner back at the invalid token to attempt a re-scan.
Field: res-: Msg.Msg
This field indicates the status of the last read operation (e.g., Scan, SetPos, etc.). If res#done, use either res.GetLText() or res.GetText() to get a plain text error description corresponding to the error code.
Field: type-: INTEGER
The type of the token that has been most recently scanned. The constants bool, char, error, int, invalid, line, ident, real, set, string, tab, and undefined are possible values for type. See also the related output fields listed below.

The following are the output fields within a scanner. Before the first call to the Scan method, the values of these fields are undefined. After each successive call to Scan, type is set and the matching output field contains the value of the scanned token. The value of output fields not corresponding to type are undefined.

Field: bool-: BOOLEAN
This field will contain a valid value only if the interpretBools option is set and one of the tokens TRUE or FALSE is scanned.
Field: char-: CHAR
Contains a value if type is char, line, or tab.
Field: int-: LONGINT
Contains a value if type is int. Please note: Valid integers are in either signed decimal or unsigned hexadecimal formats (hexadecimal tokens must be terminated with an "H" character).
Field: real-: LONGREAL
Contains a value if type is real.
Field: set-: SET;
Contains a value if type is set.
Field: string-: String;
Contains a value if type is string or ident.

The following scanner methods are equivalent to the corresponding reader methods described in section Class Reader (TextRider), so only brief descriptions are given here.

Please note: Normally when scanning text, a program will monitor a scanner's type field and check for invalid tokens and the occurance of error. The res.code needs to be checked only to find out error details (and then, possibly, the ClearError method can be used to clear the error).

Example:

VAR s: TextRider.Scanner;
    f: Files.File;
    res: Files.Result;
    str: ARRAY 256 OF CHAR;
    
f := Files.Old("Sample.txt", {Files.read}, res);
s := TextRider.ConnectScanner(f); 

f.Close;
s.Scan;
   => s.type = error
s.res.GetText(str);
   => str = "File has been closed"
Method: (s: Scanner) Available () : LONGINT
Returns the number of bytes available for the next scanning operation.
Method: (s: Scanner) ClearError
Clears error conditions on the scanner s, re-enabling further operations on s.
Method: (s: Scanner) Pos (): LONGINT
Returns the current reading position associated with the scanner s in channel s.base. Note that the value returned by this method is different from the position indicated by the scanner's pos field.
Method: (s: Scanner) SetEol (marker: ARRAY OF CHAR; markerLen: INTEGER)
This method sets the end-of-line marker; it provides the same facility as Reader.SetEol. A marker length markerLen=-1 enables auto detection of the end-of-line convention used by the channel.
Method: (s: Scanner) SetOpts (opts: SET)
This method is used to set the scanner options s.opt. See section Summary of TextRider Constants for possible option values. Example:
s.SetOpts({TextRider.returnCtrlChars, 
        TextRider.useSignedNumbers});
   => s.opt = {returnCtrlChars, useSignedNumbers}
s.SetOpts(s.opt + {TextRider.interpretBools});
   => s.opt = {interpretBools, returnCtrlChars, 
        useSignedNumbers}
s.SetOpts(TextRider.defScannerOptions);
   => scanner options set to default values.
Method: (s: Scanner) SetPos (newPos: LONGINT)
Sets the current scanning position to newPos.
Method: (s: Scanner) Scan
This method skips whitespace, and then scans for the next token as specified by the scanning options. Based on the type of token scanned, s.type is set and the matching output field is assigned a value. If the end of the valid text is reached, s.type is set to error. (Note that error is set when the last available valid token is read, not necessarily by a readAfterEnd condition.) Valid tokens are described as follows:
bool
If interpretBools is set as a scanner option, the text tokens TRUE or FALSE are read as bool. (Otherwise, these tokens are read as type ident.)
char
Normally, any printable characters other than a letter or number and any non-printable control character. However, scanner options will affect what a scanner interprets to be a char:
  • If interpretSets is not set, elements of a set constructor, "{", "}", ",", are read as char (and the associated integer constants are read as separate tokens).
  • If interpretStrings is not set, quote characters are read as char (and string contents are then read as separate tokens).
  • If useSignedNumbers is not set, "+" and "-" are read as char. (Otherwise, they are always considered part of a number.)
int
Any Oberon-2 integer constant. (Note that hexadecimal numbers must be unsigned and be terminated with an "H". Also, lower-case letters, `a..f', are not valid hex digits.)
line
If returnCtrlChars is set, an end-of-line character is read as s.type = line. Otherwise, it is counted as whitespace.
ident
Any Oberon-2 identifier. (Note that "_" is not considered as part of an identifier, nor is a selector ".".)
real
Any Oberon-2 real number constant.
set
Any Oberon-2 set constructor.
string
Any Oberon-2 string constant.
tab
If returnCtrlChars is set, a tab character is read as s.type = tab. Otherwise, it is counted as whitespace.

Connecting TextRiders to Channels

The following procedures are provided for creating instances of `TextRider' objects and connecting them to a channel. If the channel being passed as an argument to any of these functions has a value of NIL, behavior is undefined.

Also, for any of these functions, the returned rider is positioned at the beginning of the channel for positionable channels and at the current position for non-positionable channels.

Function: ConnectReader (ch: Channel.Channel): Reader
This function creates a new reader and attaches it to the channel ch. ch.res is set to done on success and the new reader is returned. Otherwise, it returns NIL and ch.res.code is set to indicate the error cause.

Function: ConnectWriter (ch: Channel.Channel): Writer
This function creates a new writer and attaches it to the channel ch. ch.res is set to done on success and the new writer is returned. Otherwise, it returns NIL and ch.res.code is set to indicate the error cause.

Function: ConnectScanner (ch: Channel.Channel): Scanner
This function creates a new scanner and attaches it to the channel ch. ch.res is set to done on success and the new scanner is returned. Otherwise, it returns NIL and ch.res.code is set to indicate the error cause.

Example:

VAR
  r: TextRider.Reader;
  f: Files.File;
  res: Files.Result;
  
  f := Files.Old("test.dat", {Files.read}, res);
  IF (res # Files.done) THEN (* error processing *) END;
  
  r := TextRider.ConnectReader(f);
  IF (r = NIL) THEN (* error processing *) END;

Summary of TextRider Constants

Constant: maxLengthEol
The maximum number of characters allowed in Reader.eol.

For other constant values that may be applicable when using module `TextRider', see the specific channel implementation that you are reading to or writing from, such as section Module Files, section Module StdChannels, or section Module ProgramArgs.

The following constant applies to the res field, and may be compared to it. (i.e., rider.res = done or rider.res # done.)

Constant: done
This indicates successful completion of the last operation.

The following are possible values for res.code:

Constant: invalidFormat
Indicates that the text at the current reading (or scanning) position is not properly formatted as the requested type.

Constant: valueOutOfRange
Indicates that a number, or potential set element, is in the proper format, but has a value that is out of range of the target type.

The following are all possible values for a scanner's type field:

Constant: bool
The scanner has read a valid boolean value. This can only be set when the scanner's options include interpretBools.

Constant: char
The scanner has read a valid character value.

Constant: error
Indicates that an error has occured while scanning. This could be an error condition resulting from one of the scanner's own operations (for example, an attempt to Scan when the scanner has reached the end of available text), or a result of a lower level error (say, an error occured in the underlying channel).

Constant: ident
The scanner has read a valid (Oberon-2) identifier.

Constant: int
The scanner has read a valid integer value.

Constant: invalid
The scanner has read an invalid value. Note that when type = invalid, the contents of all of the scanner's output fields are undefined.

Constant: line
The scanner has read a valid end-of-line character. This can only be set when the scanner's options include returnCtrlChars.

Constant: real
The scanner has read a valid real number value.

Constant: set
The scanner has read a valid set constructor value. This can only be set when the scanner's options include interpretSets.

Constant: string
The scanner has read a valid (Oberon-2) string value. This can only be set when the scanner's options include interpretStrings.

Constant: tab
The scanner has read a valid tab character. This can only be set when the scanner's options include returnCtrlChars.

Constant: undefined
This is the initial value of Scanner.type after ConnectScanner or ClearError (before any calls to Scan).

The following is a possible writer option (i.e., a valid setting for the writer's opt field):

Constant: noBuffering
When this option is set for a writer, output is not buffered. This allows, for example, for interactive output prompts to appear as soon as they are written.

The following is a possible reader or scanner option (i.e., a valid setting for the opt field):

Constant: returnCtrlChars
When this option is set, end-of-line and tab characters are not counted as whitespace.
Scanners also permit the following additional options:

Constant: interpretBools
When this option is set, the text tokens TRUE or FALSE are read as boolean values (i.e., scanner.type = bool). Otherwise, these tokens are read as identifiers (i.e., scanner.type = ident.)

Constant: interpretSets
When set, text in the form a set constructor (with "{", "}", ",", and associated integer constants) are read as SET values. Otherwise, these are read as separate tokens.

Constant: interpretStrings
When set, quoted character sequences are read as string values. Otherwise, quote characters and string contents are read as separate tokens.

Constant: useSignedNumbers
When set, "+" and "-" characters are always considered part of a number. Otherwise, they are read as separate characters.

Constant: defReaderOptions
The default reader options setting, which is equivalent to having no options set (i.e., {}).

Constant: defWriterOptions
The default writer options setting, which is equivalent to having no options set (i.e., {}).

Constant: defScannerOptions
The default scanner options setting, which is equivalent to setting the options interpretBools, interpretSets, interpretStrings, and
useSignedNumbers.

Module UnicodeRider

Module `UnicodeRider' provides concrete classes derived from the abstract base classes of module `LongRider'. `UnicodeRider' is used for reading and writing data as (long) character type LONGCHAR (i.e., interpreting byte streams as Unicode characters). The following sections describe only `UnicodeRider' specific facilities; see section Module TextRider for examples of usage and descriptions of facilities inherited from section Module Rider.

Class Reader (UnicodeRider)

Class: Reader = POINTER TO ReaderDesc
This is the concrete subclass of LongRider.Reader that provides facilities for reading various kinds of unicode text.

Note that, in UnicodeRider.Reader, ReadChar actually reads a LONGCHAR value (2 bytes) from the channel and then attempts to map it to a CHAR value (ISO-Latin-1). If the value cannot be mapped, a valueOutOfRange error occurs. Consequently for `UnicodeRider', ReadLine, ReadIdentifier, and ReadString produce the same error in similar situations.

Also note that a valueOutOfRange error occurs for methods reading into an ARRAY OF LONGCHAR (i.e., ReadLLine, ReadLIdentifier, and ReadLString) if the (long) character array is not large enough to hold the entire input.

UnicodeRider.Reader adds the following methods:

Method: (r: Reader) ReadLChar (VAR ch: LONGCHAR)
Reads in a single (LONGCHAR) character value and places it in ch.
Method: (r: Reader) ReadLIdentifier (VAR s: ARRAY OF LONGCHAR)
Reads an Oberon-2 style identifier into s. An identifier is a sequence of letters and digits, which must begin with a letter. Sequences not beginning with a letter produce an invalidFormat error. If s is not large enough to hold the entire input, a valueOutOfRange error occurs. Upon encountering an error, the value of s is undefined.
Method: (r: Reader) ReadLLine (VAR s: ARRAY OF LONGCHAR)
Reads a sequence of (LONGCHAR) characters into s; reading continues until an end-of-line character is encountered, the array s is full, or r reaches the end of the channel. The end-of-line character is discarded and s is always terminated with 0X. If r is already positioned at an end-of-line character, s returns as an empty string. If s is not large enough to hold the entire input, a valueOutOfRange error occurs; s returns with the sequence of characters that have been read so far (terminated by 0X). If r has already reached the end of the channel (i.e., there are no more characters left to read), a readAfterEnd error occurs and s returns as an empty string.
Method: (r: Reader) ReadLString (VAR s: ARRAY OF CHAR)
Reads in a sequence of (LONGCHAR) characters enclosed in single (') or double (") quote marks. The opening quote must be the same as the closing quote and must not occur within the string. Characters will be read until the terminating quote mark is encountered, an invalid character is read (end-of-line is always considered invalid), there are no more characters available in the channel, or the string s is full. s is always terminated with 0X. Unquoted strings produce an invalidFormat error. Strings with no terminating quote mark also result in an invalidFormat error. If s is not large enough to hold the entire input, a valueOutOfRange error occurs. Upon encountering an error, the value of s is undefined.

Class Writer (UnicodeRider)

Class: Writer = POINTER TO WriterDesc
This is the concrete subclass of LongRider.Writer that provides facilities for writing various kinds of unicode text.

For UnicodeRider.Writer, note that WriteChar actually writes 2 bytes at a time to the channel (i.e., CHAR values are actually written as Unicode values). ReadLine, ReadIdentifier, and ReadString behave similarly for `LongRider'.

UnicodeRider.Writer adds the following methods:

Method: (w: Writer) WriteLChar (ch: LONGCHAR)
Writes a single (LONGCHAR) character value ch.
Method: (w: Writer) WriteLString (s: ARRAY OF LONGCHAR)
Writes a long string value up to, but not including, the terminating 0X character. The behaviour of this method is undefined if s is an unterminated (LONGCHAR) character array. Please note: ReadLString and WriteLString are not symmetric. That is, WriteLString does not enclose the written string in quote marks; only the actual (LONGCHAR) character values contained in s are written.

Class Scanner (UnicodeRider)

Class: Scanner = POINTER TO ScannerDesc
This is the concrete subclass of LongRider.Scanner that provides facilities for scanning sequences of (long) characters from a channel and parsing those characters into various tokens. The tokens a scanner can recognize are defined by the constants provided for its type field (section Summary of UnicodeRider Constants).

Data type: LongString
A (long) string type of pre-defined length for use within a scanner. Note that because this type is of finite length, a scanner is limited in the length of string it can scan.

Please note: LEN() can be used on a variable of type LongString to determine the maximum size that can be held by a scanner string.

Field: type-: INTEGER
This is an inherited field, however, it now has the additional possible values: lchar, lident, lline, lstring, ltab.
Field: lchar-: LONGCHAR
Contains a value if type is lchar, lline, or ltab.
Field: lstring-: LongString;
Contains a value if type is lstring or lident.

Please note: After a call to Scan, the type field of UnicodeRider.Scanner is never expected to contain any of the following values: char, ident, line, string, tab. But rather, the "long" versions of these type values are set as appropriate.

Connecting UnicodeRiders to Channels

The following procedures are provided for creating instances of `UnicodeRider' objects and connecting them to a channel. If the channel being passed as an argument to any of these functions has a value of NIL, behavior is undefined.

Also, for any of these functions, the returned rider is positioned at the beginning of the channel for positionable channels and at the current position for non-positionable channels.

Function: ConnectReader (ch: Channel.Channel): Reader
This function creates a new reader and attaches it to the channel ch. ch.res is set to done on success and the new reader is returned. Otherwise, it returns NIL and ch.res.code is set to indicate the error cause.

Function: ConnectWriter (ch: Channel.Channel): Writer
This function creates a new writer and attaches it to the channel ch. ch.res is set to done on success and the new writer is returned. Otherwise, it returns NIL and ch.res.code is set to indicate the error cause.

Function: ConnectScanner (ch: Channel.Channel): Scanner
This function creates a new scanner and attaches it to the channel ch. ch.res is set to done on success and the new scanner is returned. Otherwise, it returns NIL and ch.res.code is set to indicate the error cause.

Summary of UnicodeRider Constants

Constant: maxLengthEol
The maximum number of characters allowed in Reader.eol.

For other constant values that may be applicable when using module `UnicodeRider', see the specific channel implementation that you are reading to or writing from, such as section Module Files, section Module StdChannels, or section Module ProgramArgs.

The following constant applies to the res field, and may be compared to it. (i.e., rider.res = done or rider.res # done.)

Constant: done
This indicates successful completion of the last operation.

The following are possible values for res.code:

Constant: invalidFormat
Indicates that the text at the current reading (or scanning) position is not properly formatted as the requested type.

Constant: valueOutOfRange
Indicates that a number, or potential set element, is in the proper format, but has a value that is out of range of the target type.

The following are all possible values for a scanner's type field:

Constant: bool
The scanner has read a valid boolean value. This can only be set when the scanner's options include interpretBools.

Constant: char
The scanner has read a valid character value.

(For `UnicodeRider', type is never expected to contain this value. But rather, the "long" version is set when appropriate.)

Constant: lchar
The scanner has read a valid (long) character value.

Constant: error
Indicates that an error has occured while scanning. This could be an error condition resulting from one of the scanner's own operations (for example, an attempt to Scan when the scanner has reached the end of available text), or a result of a lower level error (say, an error occured in the underlying channel).

Constant: ident
The scanner has read a valid (Oberon-2) identifier.

(For `UnicodeRider', type is never expected to contain this value. But rather, the "long" version is set when appropriate.)

Constant: lident
The scanner has read a valid (Oberon-2) identifier (as a LongString).

Constant: int
The scanner has read a valid integer value.

Constant: invalid
The scanner has read an invalid value. Note that when type = invalid, the contents of all of the scanner's output fields are undefined.

Constant: line
The scanner has read a valid end-of-line character. This can only be set when the scanner's options include returnCtrlChars.

(For `UnicodeRider', type is never expected to contain this value. But rather, the "long" version is set when appropriate.)

Constant: lline
The scanner has read a valid (long) end-of-line character. This can only be set when the scanner's options include returnCtrlChars.

Constant: real
The scanner has read a valid real number value.

Constant: set
The scanner has read a valid set constructor value. This can only be set when the scanner's options include interpretSets.

Constant: string
The scanner has read a valid (Oberon-2) string value. This can only be set when the scanner's options include interpretStrings.

(For `UnicodeRider', type is never expected to contain this value. But rather, the "long" version is set when appropriate.)

Constant: lstring
The scanner has read a valid (Oberon-2) (long) string value. This can only be set when the scanner's options include interpretStrings.

Constant: tab
The scanner has read a valid (long) tab character. This can only be set when the scanner's options include returnCtrlChars.

(For `UnicodeRider', type is never expected to contain this value. But rather, the "long" version is set when appropriate.)

Constant: ltab
The scanner has read a valid tab character. This can only be set when the scanner's options include returnCtrlChars.

Constant: undefined
This is the initial value of Scanner.type after ConnectScanner or ClearError (before any calls to Scan).

The following is a possible writer option (i.e., a valid setting for the writer's opt field):

Constant: noBuffering
When this option is set for a writer, output is not buffered. This allows, for example, for interactive output prompts to appear as soon as they are written.

The following is a possible reader or scanner option (i.e., a valid setting for the opt field):

Constant: returnCtrlChars
When this option is set, end-of-line and tab characters are not counted as whitespace.
Scanners also permit the following additional options:

Constant: interpretBools
When this option is set, the text tokens TRUE or FALSE are read as boolean values (i.e., scanner.type = bool). Otherwise, these tokens are read as identifiers (i.e., scanner.type = ident.)

Constant: interpretSets
When set, text in the form a set constructor (with "{", "}", ",", and associated integer constants) are read as SET values. Otherwise, these are read as separate tokens.

Constant: interpretStrings
When set, quoted character sequences are read as string values. Otherwise, quote characters and string contents are read as separate tokens.

Constant: useSignedNumbers
When set, "+" and "-" characters are always considered part of a number. Otherwise, they are read as separate characters.

Constant: defReaderOptions
The default reader options setting, which is equivalent to having no options set (i.e., {}).

Constant: defWriterOptions
The default writer options setting, which is equivalent to having no options set (i.e., {}).

Constant: defScannerOptions
The default scanner options setting, which is equivalent to setting the options interpretBools, interpretSets, interpretStrings, and
useSignedNumbers.

Module BinaryRider

Module BinaryRider provides facilities for reading and writing binary data. Binary data are simple sequences of byte values that may be interpreted in any number of ways. This corresponds closely to the way information is stored within a running program. Values are stored as a fixed number of bytes rather than as a delimited sequence of characters. For example, if SIZE(INTEGER) = 2, then an INTEGER value is always stored as 2 bytes. If SIZE(LONGINT) = 4, then a LONGINT is stored as 4 bytes.

The following program fragment gives an example of how you could read the entire contents of a file and echo each character to the screen (note that no error checking is done):

VAR r: BinaryRider.Reader;
    f: Files.File;
    ch: CHAR;
    res: Files.Result;
    
  f := Files.Old("Sample.txt", {Files.read}, res);
  r := BinaryRider.ConnectReader(f); 

  r.ReadChar(ch);	
  WHILE r.res=Files.done DO
     Out.Char(ch);
     r.ReadChar(ch);	
  END;

Please note: Different kinds of computers use different conventions for the ordering of bytes within a word. Some computers put the most significant byte within a word first (this is called big-endian order), and others put it last (little-endian order). A small number of systems use different byte order schemes; they aren't supported by this module (yet). Operations provided by BinaryRider default to the little-endian byte order. However, byte order can be specified using the SetByteOrder methods provided by classes Reader and Writer.

Thus, programs can be written that produce files that are portable to machines with different byte orderings. It should be noted, however, that file I/O using the native byte order provides better speed efficiency.

Class Reader (BinaryRider)

Class: Reader = POINTER TO ReaderDesc
This class provides facilities for reading various types of data in binary format. Note that this type does not inherit properties from any basic reader type; rather it uses the basic reader type associated with the channel it is attached to.

Please note: Many of the methods for BinaryRider.Reader perform typical Reader operations. Rather than duplicate descriptions of those methods here, a reference to the abstract reader type is provided instead.

Field: res-: Msg.Msg
This field indicates the status of the last read operation (e.g., ReadLine, ReadInt, SetPos, etc.). Error codes (for res.code) are highly dependent on the channel being read, and therefore on the basic riders provided by that channel, so you must look at the result codes for a particular channel's reader type (e.g., Files.Reader error codes). See the various channel types for details of these error codes (i.e., section Module Files, section Module StdChannels, or section Module ProgramArgs). If res#done, use either res.GetLText() or res.GetText() to get a plain text error description corresponding to the error code.
Field: byteOrder-: SHORTINT
The current endian (byte order) setting for the reader.
Field: base-: Channel.Channel
This field refers to the channel the reader is connected to.

The following methods are all fully described in the section on abstract readers (see section Abstract Class Reader), so only brief descriptions are given here.

Method: (r: Reader) Available () : LONGINT
Returns the number of bytes available for the next reading operation.
Method: (r: Reader) ClearError
Clears error conditions on the reader r, re-enabling further read operations.
Method: (r: Reader) Pos () : LONGINT
Returns the current reading position associated with the reader r in channel r.base.
Method: (r: Reader) SetByteOrder (order: SHORTINT)
Sets byteOrder in reader r to order. This affects the interpretation of byte values for applicable read operations. Pre-condition: order is one of nativeEndian, littleEndian, or bigEndian. Example:
VAR rBig, rLittle, r: BinaryRider.Reader;
    f: Files.File;
    
f := Files.Old("test.dat", {Files.read}, res);

r := BinaryRider.ConnectReader(f);
   => r reads from f using the default byte order 
        (i.e., little endian)

rBig := BinaryRider.ConnectReader(f);
rBig.SetByteOrder(BinaryRider.bigEndian);
   => rBig reads from f using big endian byte order

rLittle := BinaryRider.ConnectReader(f);
rLittle.SetByteOrder(BinaryRider.littleEndian);
   => rLittle reads from f using little endian byte order
Method: (r: Reader) SetPos (newPos: LONGINT)
Sets the reading position to newPos.

The following methods read a value of the given type from the current position of the Reader. If the value is invalid for its type, Reader.res.code is set to invalidFormat.

Otherwise, if there aren't enough bytes to satisfy the request, Reader.res.code is set to readAfterEnd.

Method: (r: Reader) ReadBool (VAR bool: BOOLEAN)
Reads in a single byte and interprets it as a BOOLEAN value. Zero values are read as FALSE and non-zero values are read as TRUE. Example:
VAR bool: BOOLEAN;

r.ReadBool(bool);
   => if byte read = 0, then bool = FALSE; 
        otherwise, bool = TRUE
Method: (r: Reader) ReadBytes (VAR x: ARRAY OF SYSTEM.BYTE; start, n: LONGINT)
Read n bytes from the channel r.base according to the native machine byte order. That is, ReadBytes is not affected by calls to SetByteOrder. Thus this method is equivalent to any basic rider Reader.ReadBytes method (see section Abstract Class Reader) Example:
VAR byteArr: ARRAY 256 OF CHAR;

r.ReadBytes(byteArr, 0, 16);
   => reads the next 16 bytes from r.base 
        into byteArr[0..15]  

r.ReadBytes(byteArr, 16, 100);
   => reads the next 100 bytes from r.base 
        into byteArr[16..115]  
Method: (r: Reader) ReadChar (VAR ch: CHAR)
Reads in a single character value and places it in ch. Please note: OOC assumes that SIZE(SYSTEM.BYTE) = SIZE(CHAR). Example:
VAR ch: CHAR;

r.ReadChar(ch);
   => reads one byte and assigns it to ch
Method: (r: Reader) ReadLChar (VAR ch: LONGCHAR)
Reads in a single (long) character value and places it in ch. SIZE(LONGCHAR) bytes are read and interpreted based on the current byte order setting for reader r (see SetByteOrder).
Method: (r: Reader) ReadInt (VAR int: INTEGER)
Reads in an INTEGER value. SIZE(INTEGER) bytes are read and interpreted based on the current byte order setting for reader r (see SetByteOrder).
Method: (r: Reader) ReadLInt (VAR lint: LONGINT)
Reads in a LONGINT value. SIZE(LONGINT) bytes are read and interpreted based on the current byte order setting for reader r.
Method: (r: Reader) ReadLReal (VAR lreal: LONGREAL)
Reads in a LONGREAL value. SIZE(LONGREAL) bytes are read and interpreted based on the current byte order setting for reader r.
Method: (r: Reader) ReadNum (VAR num: LONGINT)
Reads an integer value in a compressed and portable format. This format is the same no matter what the byteOrder setting. Therefore, ReadNum is not affected by calls to SetByteOrder.
Method: (r: Reader) ReadReal (VAR real: REAL)
Reads in a REAL value. SIZE(REAL) bytes are read and interpreted based on the current byte order setting for reader r.
Method: (r: Reader) ReadSet (VAR s: SET)
Reads in a SET value. SIZE(SET) bytes are read and interpreted based on the current byte order setting for reader r.
Method: (r: Reader) ReadSInt (VAR sint: SHORTINT)
Reads in a SHORTINT value. Please note: OOC assumes that SIZE(SYSTEM.BYTE) = SIZE(SHORTINT) so that the current byte order setting for reader r (see SetByteOrder) does not matter for calls to ReadSInt.
Method: (r: Reader) ReadString (VAR s: ARRAY OF CHAR)
Reads in a sequence of characters until either the string terminator 0X is encountered, there are no more characters available in the channel, or the string s is full. s is always terminated with 0X. Example:
VAR str: ARRAY 256 OF CHAR;

r.ReadString(str);
   => reads up to 256 characters, stops when encounters 0X
Method: (r: Reader) ReadLString (VAR s: ARRAY OF LONGCHAR)
Reads in a sequence of (long) characters until either the string terminator 0X is encountered, there are no more characters available in the channel, or the string s is full. s is always terminated with 0X. For each character, SIZE(LONGCHAR) bytes are read and interpreted based on the current byte order setting for reader r (see SetByteOrder).

Class Writer (BinaryRider)

Class: Writer = POINTER TO WriterDesc
This class provides facilities for writing various types of data in binary format. Note that this type does not inherit properties from any basic writer type; rather it uses the basic writer type associated with the channel it is attached to.

Please note: Many of the methods for BinaryRider.Writer perform typical Writer operations. Rather than duplicate descriptions of those methods here, a reference to the abstract writer type is provided instead.

Field: res-: Msg.Msg
This field indicates the status of the last write operation (e.g., WriteBytes, WriteInt, SetPos, etc.). Error codes are highly dependent on the channel being written to (and therefore on the basic riders provided for that channel), so you must look at the result codes for the basic writer that is associated with that particular channel (e.g., Files.Writer error codes). See the various channel types for details of these error codes (i.e., section Module Files, section Module StdChannels, or section Module ProgramArgs). If res#done, use either res.GetLText() or res.GetText() to get a plain text error description corresponding to the error code.
Field: base-: Channel.Channel
This field refers to the channel the writer is connected to.
Field: byteOrder-: SHORTINT
The current endian (byte order) setting for the writer.

The following methods are all fully described in the section on abstract writers (see section Abstract Class Writer), so only brief descriptions are given here.

Method: (w: Writer) ClearError
Clears error conditions on the writer w, re-enabling further write operations.
Method: (w: Writer) Pos () : LONGINT
Returns the current writing position associated with the writer w in channel w.base.
Method: (w: Writer) SetPos (newPos: LONGINT)
Sets the writing position to newPos.

The following writer methods are used to write values to the underlying channel. In some situations, it is possible for only part of the value to be written.

Method: (w: Writer) WriteBytes (VAR x: ARRAY OF SYSTEM.BYTE; start, n: LONGINT)
Write n bytes to the channel w.base according to the native machine byte order (i.e., WriteBytes is not affected by calls to SetByteOrder). Thus this method is equivalent to any basic rider Writer.WriteBytes method (see section Abstract Class Writer) Example:
VAR byteArr: ARRAY 256 OF CHAR;

w.WriteBytes(byteArr, 0, 16);
   => writes the values of byteArr[0..15] 
        to the current writing position of w

w.WriteBytes(byteArr, 16, 100);
   => writes the values of byteArr[16..115] 
        to the current writing position of w
Method: (w: Writer) WriteBool (bool: BOOLEAN)
Writes a BOOLEAN value as a single byte. FALSE is written as 0 and TRUE is written as 1. Example:
w.WriteBool(TRUE);
   => writes one byte = 01H

w.WriteBool(FALSE);
   => writes one byte = 00H
Method: (w: Writer) WriteChar (ch: CHAR)
Writes the character value ch as a single byte. Please note: OOC assumes that SIZE(SYSTEM.BYTE) = SIZE(CHAR). Example:
VAR ch: CHAR:

w.WriteChar("A");
   => writes one byte = "A"

ch := 41X;
w.WriteChar(ch);
   => writes one byte = 41X (i.e., "A" in ASCII)
Method: (w: Writer) WriteLChar (ch: LONGCHAR)
Writes the (long) character value ch as SIZE(LONGCHAR) bytes based on the current byte order setting for writer w (see SetByteOrder).
Method: (w: Writer) WriteString (s: ARRAY OF CHAR)
Writes the string value of s (recall that a string is a character array containing 0X as an embedded terminator). The terminating 0X is also written. Example:
VAR str: ARRAY 256 OF CHAR;

w.WriteString("abcdefg");
   => writes a total of 8 characters including 0X

str := "hijkl";
w.WriteString(str);
   => writes a total of 6 characters including 0X
Method: (w: Writer) WriteLString (s: ARRAY OF LONGCHAR)
Writes the string value of s including the terminating 0X character. Each character is written as SIZE(LONGCHAR) bytes based on the current byte order setting for writer w (see SetByteOrder).
Method: (w: Writer) WriteSInt (sint: SHORTINT)
Writes a SHORTINT value. Please note: OOC assumes that SIZE(SYSTEM.BYTE) = SIZE(SHORTINT) so that the current byte order setting for writer w (see SetByteOrder) does not matter for calls to WriteSInt.
Method: (w: Writer) WriteInt (int: INTEGER)
Writes an INTEGER value. SIZE(INTEGER) bytes are written based on the current byte order setting for writer w (see SetByteOrder).
Method: (w: Writer) WriteLInt (lint: LONGINT)
Writes a LONGINT value. SIZE(LONGINT) bytes are written based on the current byte order setting for writer w.
Method: (w: Writer) WriteNum (lint: LONGINT)
Write an integer value in a compressed and portable format. This format is the same no matter what the byteOrder setting. Therefore, WriteNum is not affected by calls to SetByteOrder.
Method: (w: Writer) WriteReal (real: REAL)
Writes a REAL value. SIZE(REAL) bytes are written based on the current byte order setting for writer w.
Method: (VAR w: Writer) WriteLReal (VAR lreal: LONGREAL)
Writes a LONGREAL value. SIZE(LONGREAL) bytes are written based on the current byte order setting for writer w.
Method: (VAR w: Writer) WriteSet (VAR s: SET)
Writes a SET value. SIZE(SET) bytes are written based on the current byte order setting for writer w.
Method: (VAR w: Writer) SetByteOrder (VAR order: SHORTINT)
Sets byteOrder in writer w to order. This affects the interpretation of byte values for applicable write operations. Pre-condition: order is one of nativeEndian, littleEndian, or bigEndian. Example:
VAR wBig, wLittle, w: BinaryRider.Writer;
    f: Files.File;
    
f := Files.Old("test.dat", {Files.write}, res);

w := BinaryRider.ConnectWriter(f);
   => w writes to f using native byte order

wBig := BinaryRider.ConnectWriter(f);
wBig.SetByteOrder(BinaryRider.bigEndian);
   => wBig writes to f using big endian byte order

wLittle := BinaryRider.ConnectWriter(f);
wLittle.SetByteOrder(BinaryRider.littleEndian);
   => wLittle writes to f using little endian byte order

Connecting BinaryRiders to Channels

Functions are provided by module BinaryRider to connect readers and writers to open channels. If the channel being passed as an argument to either of these functions has a value of NIL, behavior is undefined.

Also, for either of these functions, the returned rider is positioned at the beginning of the channel for positionable channels and at the current position for non-positionable channels.

Function: ConnectReader (VAR ch: Channel.Channel): Reader
This function creates a new reader and attaches it to the channel ch. ch.res is set to done on success and the new reader is returned. Otherwise, it returns NIL and ch.res.code is set to indicate the error cause.

Function: ConnectWriter (VAR ch: Channel.Channel): Writer
This function creates a new writer and attaches it to the channel ch. ch.res is set to done on success and the new writer is returned. Otherwise, it returns NIL and ch.res.code is set to indicate the error cause.

Example:

VAR
  f: Files.File;
  r: BinaryRider.Reader;
  res: Files.Result;
  
  f := Files.Old("test.dat", {Files.read, Files.write}, res);
  IF (res # Files.done) THEN (* error processing *) END;
  
  r := BinaryRider.ConnectReader(f);
  IF (r = NIL) THEN (* error processing *) END;

Summary of BinaryRider Constants

For other constant values that may be applicable when using module `BinaryRider', see the specific channel implementation that you are reading to or writing from.

The following constant applies to the res field, and may be compared to it. (i.e., rider.res = done or rider.res # done.)

Constant: done
This indicates successful completion of the last operation.

The following are possible values for res.code:

Constant: invalidFormat
Indicates that the data at the current reading position is not properly formatted as the requested type.

Constant: readAfterEnd
A read operation has tried to access a byte beyond the end of the channel. This means that there weren't enough bytes available or the read operation started at (or after) the end.

The following are possible endian (byte order) settings:

Constant: nativeEndian
Use the host machine's byte order.

Constant: littleEndian
Read/write least significant byte first.

Constant: bigEndian
Read/write most significant byte first.

Standard I/O

Modules In, Out, and Err provide simple interfaces to the standard channels (see section Module StdChannels) These modules can be used to read from predefined input (typically the keyboard) and write to predefined output (typically the computer screen) locations.

Historically, the various Oberon systems/ compilers have furnished modules called In and Out, which were intended primarily as aids for learning the Oberon(-2) programming language. These modules were often over-simplified to such a degree that they were of limited use beyond the initial learning stage. The intention was that, after learning the language, a programmer would learn other, more sophisticated methods for I/O.

Although the modules In, Out, and Err in the OOC library are simple enough to be used by novices, they are not nearly as limited as the corresponding modules from the original Oberon system. Hence, they are still useful to programmers well beyond the beginning stages.

These modules give simplified facilities similar to module TextRider applied to the standard channels; they allow reading and writing of data as text. If these prove to be insufficient for your needs, then modules TextRider or BinaryRider may be used instead (see section Standard Mappers)

Module In

Module In provides a set of basic read operations for text. It is initially set to read from the standard input channel StdChannels.stdin (see section Module StdChannels), but this may be changed with the SetReader procedure.

Each of the procedures in this module is designed to interpret a specific type of text token. That is, Char will read in a single CHAR value, Int will read in an INTEGER value, and so forth. For exact syntax of each of these tokens see section Syntax of Text Tokens.

The following program fragment gives an example of how you could read input a single line at a time (input stops after reading a blank line):

VAR str: ARRAY 256 OF CHAR;
    
  In.Line(str);	
  WHILE In.Done() & (str # "") DO
     (* process each line *)
     In.Line(str);	
  END;

Read-only Variable: reader
The reader used for all read operations in module In. The type of reader is TextRider.Reader, and it is initialized to refer to a text reader connected to the channel StdChannels.stdin.

The SetReader procedure may be used to change this to refer to another TextRider.Reader.

Function: Done (): BOOLEAN
This function returns FALSE after an unsuccessful read operation. This may be caused by attempting to read improperly formatted text (e.g., attempting to read non-numeric text using Int), or if the underlying reader has encountered an error. Further reading is not possible until the error is cleared using the ClearError procedure.

Procedure: ClearError
Clears error conditions, re-enabling further read operations.

Procedure: SetReader (r: TextRider.Reader)
This procedure is used to change the reader used by all read operations in module In. Refer to section Module TextRider for details on how to open other readers. If r=NIL, the reader is set to read from StdChannels.stdin.

All of the following read operations require that Done() => TRUE; that is, they will not read anything else after an unsuccessful read operation has occured. Further reading cannot take place until the error is cleared using ClearError.

Most of these read operations skip leading whitespace (i.e., spaces, tabs, end-of-line characters, etc.) before reading a token; the only procedures that do not skip whitespace are Char and Line.

A read error will occur, not only for improperly formatted text, but for numbers (i.e., reading using Int, Real, and so forth) and set elements that have values out of range of the target type. For example, attempting to read `999999999999999999' using Int will give Done() => FALSE.

An error will also occur for procedures that read into an ARRAY OF CHAR, when the array is not large enough to hold the entire input.

Procedure: Bool (VAR bool: BOOLEAN)
Reads in the text `TRUE' or `FALSE'; any other text results in an error. When an error occurs, the value of bool is undefined.

Procedure: Char (VAR ch: CHAR)
Reads in a single character.

Procedure: Hex (VAR lint: LONGINT)
Reads in text in the form of an unsigned hexadecimal number. The first character must be a decimal digit (i.e., `0..9') and subsequent characters must be valid hexadecimal digits (i.e., `0..9' or `A..F'). The value read must be in the valid range for a LONGINT.

Upon encountering an error, the value of lint is undefined.

Please note: Because LONGINT values are signed, hex numbers in the range `80000000H..FFFFFFFFH' are interpreted as negative LONGINT values.

Procedure: Identifier (VAR s: ARRAY OF CHAR)
Reads an Oberon-2 style identifier. The first character must be a letter, which is followed by any sequence of letters and digits. An error will occur if s is not large enough to hold the entire input.

Upon encountering an error, the value of s is undefined. Example:

(* Input is as follows:  
myIdentifier 3isBad 
*)

VAR str: ARRAY 256 OF CHAR;

In.Identifier(str)
   => Done() = TRUE, str = "myIdentifier"
In.Identifier(str)
   => Done() = FALSE, str = undefined

Procedure: Int (VAR int: INTEGER)
Reads in text in the form of a signed whole number. The first character must be a digit, a "+" sign, or a "-" sign. The value read must be in the valid range for an INTEGER.

Upon encountering an error, the value of int is undefined.

Example:

(* Input is as follows:
12345
999999999999999
forty-two
*)

VAR intVar: INTEGER;

In.Int(intVar);
   => Done() = TRUE, intVar = 12345
In.Int(intVar);
   => Done() = FALSE, intVar = undefined
In.ClearError;
In.Int(intVar); (* attempting to read `forty-two' *)
   => Done() = FALSE, intVar = undefined
        (* reading position is still at the `f' in
           `forty-two' *)

Procedure: LongInt (VAR lint: LONGINT)
This procedure provides the same facility as Int, except that it deals with LONGINT values.

Procedure: ShortInt (VAR int: SHORTINT)
This procedure provides the same facility as Int, except that it deals with SHORTINT values.

Procedure: Line (VAR s: ARRAY OF CHAR)
Reads text until an end-of-line character is encountered. The end-of-line character is discarded and s is always terminated with 0X. An error will occur if s is not large enough to hold the entire input.

Upon encountering an error, the value of s is undefined.

Please note: This procedure returns an empty string if already at at the end-of-line.

Procedure: String (VAR s: ARRAY OF CHAR)
Reads in any text enclosed in single (') or double (") quote marks. The opening quote must be the same as the closing quote and must not occur within the string. Reading will continue until the terminating quote mark is encountered, an invalid character is read (end-of-line is always considered invalid), or there are no more characters available to be read. s is always terminated with 0X.

Unquoted strings or strings with no terminating quote mark result in an error. An error will also occur if s is not large enough to hold the entire input.

Upon encountering an error, the value of s is undefined.

Example:

(* Input is as follows:
"A well-formed string"
"No end quote
*)

VAR str: ARRAY 256 OF CHAR;

In.String(str);
   => Done() = TRUE, str = "A well-formed string"
In.String(str);
   => Done() = FALSE, str = undefined
        (* reading position is now at the end of this line *)

Procedure: Real (VAR real: REAL)
Reads in text in the form of a signed fixed or floating-point number. The first character must be a digit, a "+" sign, or a "-" sign. The value read must be in the valid range for a REAL.

Upon encountering an error, the value of real is undefined.

Example:

(* Input is as follows:
3.1415
+54321E+30
2.34E+56
*)

VAR realVar: REAL;

In.Real(realVar);
   => Done() = TRUE, realVar = 3.141500
In.Real(realVar);
   => Done() = TRUE, realVar = 5.432100E+34
In.Real(realVar);
   => Done() = FALSE, realVar = undefined
        (* value is out of range for REAL *)

Procedure: LongReal (VAR lreal: LONGREAL)
This procedure provides the same facility as Real, except that it deals with LONGREAL values.

Procedure: Set (VAR s: SET)
Reads in text in the form of a set constructor. The values of set elements must be in the range `0..MAX(SET)'.

Upon encountering an error, the value of s is undefined.

Example:

(* Input is as follows:
{0, 1, 2, 3, 4, 5}
{6, 7, 1024}
*)

VAR setVar: SET;

In.Set(setVar);
   => Done() = TRUE, setVar = {0..5}
In.Set(setVar);
   => Done() = FALSE, setVar = undefined
        (* reading position is now at the `}' after 
           the `1024' *)

Module Out

Module Out provides a set of basic write operations for text. It is initially set to write to the standard output channel StdChannels.stdout (see section Module StdChannels), but this may be changed with the SetWriter procedure.

Read-only Variable: writer
The writer used for all write operations in module Out. The type of writer is TextRider.Writer, and it is initialized to refer to a text reader connected to the channel StdChannels.stdout.

The SetWriter procedure may be used to change this to refer to another TextRider.Writer.

Function: Done (): BOOLEAN
This function returns FALSE after an unsuccessful write operation. This may happen when underlying writer has encountered an error. Further writing is not possible until the error is cleared using the ClearError procedure.

Procedure: ClearError
Clears error conditions, re-enabling further read operations.

Procedure: SetWriter (w: TextRider.Writer)
This procedure is used to change the writer used by all write operations in module Out. Refer to section Module TextRider for details on how to open other writers. If w=NIL, the writer is set to write to StdChannels.stdout.

Procedure: Flush
Flushes all buffers associated with Out.writer. Any pending write operations are passed to the underlying system. If a writing error occurs while flushing buffers, Out.Done() will subsequently return FALSE. Otherwise, Out.Done() will return TRUE.

Procedure: Bool (bool: BOOLEAN)
Writes the value of bool as text. That is, either `TRUE' or `FALSE'.
Procedure: Char (ch: CHAR)
Writes a single character value ch.

Example:

Out.Char("A");
   => writes one character = "A"

Procedure: Hex (lint: LONGINT; n: LONGINT)
Writes the value of lint as an unsigned hexadecimal number with a minimum field width of n. Leading zeros are written if the value of lint requires less than n places. If n is less than or equal to zero, field width is 8.

Example:

Out.Hex(127, 4);
   => writes "007F"  
Out.Hex(-128, 0);
   => writes "FFFFFF80"

Procedure: Int (int: INTEGER; n: LONGINT)
Writes the value of int as a decimal number with a minimum field width of n. Leading spaces are written if the value of int requires less than n places. A sign is written only for negative values.

Example:

Out.Int(54321, 0);
   => writes "54321"
Out.Int(54321, 10);
   => writes "     54321"

Procedure: LongInt (lint: LONGINT; n: LONGINT)
This procedure provides the same facility as Int, except that it deals with LONGINT values.

Procedure: ShortInt (sint: SHORTINT; n: LONGINT)
This procedure provides the same facility as Int, except that it deals with SHORTINT values.

Procedure: Real (real: REAL; n, k: LONGINT)
Writes the value of real as a floating-point number with a minimum field width of n.

If the value of k is greater than 0, that number of significant digits is included. Otherwise, an implementation-defined number of significant digits is included. The decimal point is not included if there are no significant digits in the fractional part.

The number is scaled with one digit in the whole number part. A sign is included only for negative values.

Example:

Out.Real(3923009, 0, 0);
   => writes "3.923009E+6"
Out.Real(3923009, 10, 1);
   => writes "      4E+6"

Out.Real(-39.23009, 12, 2);
   => writes "     -3.9E+1"

Out.Real(0.0003923009, 6, 1);
   => writes "  4E-4"

Procedure: LongReal (lreal: LONGREAL; n, k: LONGINT)
This procedure provides the same facility as Real, except that it deals with LONGREAL values.

Procedure: RealEng (real: REAL; n, k: LONGINT)
This procedure provides the same facility as Real, except that the number is scaled with one to three digits in the whole number part and has an exponent that is a multiple of three.

Example:

Out.RealEng(39.23009, 10, 5);
   => writes "    39.230"

Out.RealEng(-3923009, 7, 3);
   => writes " -3.92E+6"

Out.RealEng(0.0003923009, 1, 1);
   => writes "400E-6"
Out.RealEng(0.0003923009, 4, 2);
   => writes "  390E-6"

Procedure: LongRealEng (lreal: LONGREAL; n, k: LONGINT)
This procedure provides the same facility as RealEng, except that it deals with LONGREAL values.

Procedure: RealFix (real: REAL; n, k: LONGINT)
Writes the value of real as a fixed-point number with a minimum field width of n.

The value is rounded to the given value of k relative to the decimal point. The decimal point is suppressed if k is less than 0.

The number will have at least one digit in the whole number part. A sign is included only for negative values.

Example:

Out.RealFix(3923009, 0, -5);
   => writes "3920000"  (* rounded to the 
                        ten-thousands place *)

Out.RealFix(3923.5, 0, -1);
   => writes "3924" (* rounded to the "ones" place *)

Out.RealFix(-39.23009, 10, 1);
   => writes "     -39.2"

Out.RealFix(0.0003923009, 11, 4);
   => writes "     0.0004"

Procedure: LongRealFix (lreal: LONGREAL; n, k: LONGINT)
This procedure provides the same facility as RealFix, except that it deals with LONGREAL values.

Procedure: Set (s: SET)
Writes the value of s as an Oberon-2 set constructor, including curly braces, commas, and range indicators (`..') where appropriate.

Example:

Out.Set({1,6,10});
   => writes "{1, 6, 10}"
Out.Set({0, 1, 2, 3, 4, 5});
   => writes "{0..5}"
Out.Set({0, 2, 4, 6} + {1, 3, 5, 7});
   => writes "{0..7}"

Procedure: String (s: ARRAY OF CHAR)
Writes a string value up to, but not including, the terminating 0X character. The behaviour of this procedure is undefined if s is an unterminated character array.

Please note: In.String and Out.String are not symmetric. That is,
Out.String does not enclose the written string in quote marks; only the actual character values contained in s are written.

Procedure: Ln
Writes an end-of-line marker (i.e., a "newline").

Module Err

Module Err provides a set of basic write operations for text, which exactly mirror those in module Out. The difference is that Err is initially set to write to the standard error channel StdChannels.stderr (see section Module StdChannels). Also note that the call Err.SetWriter(NIL) will reset the writer for Err to StdChannels.stderr.

Because the interfaces of Out and Err are identical, decriptions of facilities are not duplicated here.


Go to the first, previous, next, last section, table of contents.