10.4 Functions for Reading

10.4.1 Reading S-Expressions

(read): any expr
(channelread CHAN:io-channel): any expr
Reads and returns the next S-expression from input channel CHAN. Valid input forms are: vector-notation, dot-notation, list-notation, numbers, strings, and identifiers. Identifiers are interned (see the intern function in Chapter 4), unless the fluid variable *compressing is non-nil. Channelread returns the value of the global variable $eof$ when the end of the currently selected input channel is reached.
    (de channelread (ch)  
      (let ((currentscantable⋆ lispscantable⋆)  
            (currentreadmacroindicator⋆ 'lispreadmacro))  
        (channelreadtokenwithhooks ch)))

Channelread uses the function channelreadtokenwithhooks. Tokens are scanned within the context of the scan table bound to lispscantable*. The scan table is used to recognize special character types, for example, delimiters like (, ), and space. The read macro mechanism is used to parse s-expressions (see section 12.4.5 for more information on read macros). PSL uses a number of read macros which are described below.


S-expressions are gathered into a list until the matching right parenthesis is read. Both list and dot notation are recognized. A pair or list is returned.


If a right parenthesis is read and a matching left parenthesis had not been previously read then it will be ignored.


S-expressions are gathered into a vector until a matching right bracket is read. A vector is returned.

The next s-expression read is quoted, the result is of the form (QUOTE S-EXPRESSION).

(char eof)

If an end of file character is read while a list or vector is being constructed then the following error will occur. ***** Unexpected EOF while reading on channel.


Otherwise the value of eof is returned.

The following read macros are defined in the useful module.


this is like the quote mark ’ but it is used for function instead of quote. For example #’name reads as (FUNCTION NAME).


this returns the numeric form of the following character read without raising it. For example #/a is 97 while #/A is 65.


This is a read macro for char (see Chapter 6 for more information). Note that the argument will be raised if *raise is non-nil. For example, #\a = #\A = 65.


This causes the following expression to be evaluated at read time. For example, ‘(1 2 #.(plus 1 2) 4) reads as (1 2 3 4).


The next two expressions are read. If the first is a system name which is the current system then the second expression is returned. Otherwise another expression is read and it becomes the value returned.


The next two expressions are read. If the first is a system name which is not the current system then the second expression is returned. Otherwise another expression is read and it becomes the value returned.

10.4.2 Reading Single Characters

(readchar): character expr

(channelreadchar CHANNEL:io-channel): character expr
Reads one character (an integer) from CHANNEL. All input is defined in terms of this function. If CHANNEL is not open or is open for writing only, an error is generated. If there is a non-zero value in the backup buffer associated with CHANNEL, the buffer is emptied (set to zero) and the value returned. Otherwise, the reading function associated with CHANNEL is called with CHANNEL as argument, and the value it returns is returned by channelreadchar.
    ⋆⋆⋆⋆⋆ Channel not open

    ⋆⋆⋆⋆⋆ Channel open for write only

(readch): id expr

(channelreadch CHAN:io-channel): id expr
Like channelreadchar, but returns the id for the character rather than its ASCII code.

(unreadchar CH:character): Undefined expr

(channelunreadchar CHAN:io-channel CH:character): Undefined expr
The input backup function. Ch is deposited in the backup buffer associated with Chan. This function should be only called after channelreadchar is called, and before any intervening input operations, since it is used by the token scanner. The unread buffer only holds one character, so it is generally useless to unread more than one character.

10.4.3 Reading Tokens

The functions described here pertain to the token scanner and reader. Globals and switches used by these functions are defined at the end of this section.

(channelreadtoken CHANNEL:io-channel): {id, number, string} expr
This is the basic PSL token scanner. The value returned is an item corresponding to the next token from the input stream. An id will be interned unless the switch *compressing is non-nil. If *compressing is t then the print name of the id is passed to newid, otherwise it is passed to intern.

The global variable toktype* is set as follows.

0if the token is an ordinary id,
1 if the token is a string,
2 if the token is a number, or
3 if the token is an unescaped delimiter such as
”(”, but not ”!(” In this last case, the value
returned is the id whose print name is the
same as the delimiter.

The precise behavior of this function depends on two fluid variables:

Currentscantable* is bound to a vector known as a scan table. Described below.

Currentreadmacroindicator* is bound to an id known as a read macro indicator. Described below.

(ratom): {id, number, string} expr
Reads a token from the current input channel.

(channelreadtokenwithhooks CHANNEL:io-channel):
any expr
This function reads a token and performs any action specified if the token is marked as a read macro under the current indicator. Read uses this function internally. Uses the variable currentreadmacroindicator* urrentreadmacroindicator* to determine the current indicator.

10.4.4 Reading Entire Lines

(readline): string expr
Same as (channelreadline in*)
(channelreadline CHANNEL:io-channel): string expr
A string is returned which contains each character from the current position of the scanner to the next end-of-line or end-of-file character.

10.4.5 Read Macros

At the top level of PSL, an expression is read, it is evaluated, and then the result is printed. Normally a macro is expanded during evaluation. The read macro is a different type of macro, it is expanded during the reading phase. When the reader encounters a read macro character, the the macro is executed and the result is inserted in place of the read macro character.

A read macro must be a function of two arguments, the first should represent a IO channel, the second a character. A character which represents a read macro must be set to one of two types in the scan table. It may be either a delimiter or a diphthong. Diphthong corresponds to double character read macros, delimiter to single character read macros. In addition, the id which corresponds to the character must have a reference to the name of the function on its property list. For a diphthong the indicator must be lispdiphthong, for a delimiter it must be lispreadmacro.

The quote macro may be defined as follows. Note that we cannot use ’form in place of (quote form) until we have defined the read macro.

(de doquote (channel ch)  
  (list (quote quote) (channelread channel)))

(put (quote !') (quote lispreadmacro)(function doquote))

(putv lispscantable⋆ (char !') delimiter)

This says that when a single quote is read, PSL should replace it with a list consisting of quote and the next expression in the input (obtained by an explicit call to channelread). Since defining a character as a read macro makes it difficult to use the character in a normal way, read macros should not be letters or digits.

10.4.6 Terminal Interaction

(yesp MESSAGE:string): boolean expr
If the user responds y or yes, yesp returns a non-nil value. A response of n or no results in a value of nil. It is possible to enter a break loop by responding with b. After quitting the break loop, one must still respond y, yes, n, or no.

10.4.7 Input Status and Mode

promptstring* = [Initially: ”x lisp>”] global
Displayed as a prompt when any input is taken from TTY. Prompts should therefore not be directly printed. Instead the value should be bound to promptstring*.

*eolinstringok = [Initially: nil] switch
If *eolinstringok is non-nil, the warning message

⋆⋆⋆ String continued over end-of-line
is suppressed.

*raise = [Initially: t] switch
If *raise is non-nil, all characters input for ids through PSL input functions are raised to upper case. If *raise is nil, characters are input as is. A string is unaffected by *raise.

*compressing = [Initially: nil] switch
If *compressing is non-nil, channelreadtoken and other functions that call it do not intern ids.

currentscantable* = [Initially: NIL] global
This variable is set to lispscantable* by the function read

currentreadmacroindicator* = [Initially: NIL ] global
The function read binds this variable to the value LISPREADMACRO. Its value determines the property list indicator used in looking up read macros. The user may define a set of read macros using some new indicator and rebind this variable. Ordinary read macros may be added by putting properties on ids under the LISPREADMACRO indicator.