11.2 The General Purpose Top Loop Function

The user interacts with PSL through toploop. The purpose of this loop is to read expressions typed at the terminal, evaluate them, and type the result back on the terminal. Such expressions may be entered to see what value they return or to produce a side-effect on the global environment.

An expression which has a side effect on the global environment

  (setq base 10)

Another example of a side effect, the definition of a function

  (de square (n) (⋆ n n))

Evaluation, just to see what value the expression returns

  (square 2)

A close approximation to the behavior of this loop is given by the following functional description:

(de toploop (toploopread⋆  
  (while t  
    (funcall toploopprint⋆  
             (funcall toploppeval⋆  
                      (funcall toploopread⋆)))))

The actual toploop of PSL is somewhat more complex than the simple one defined here because it provides a few extra features (these are described below). The important point is that the primitives required for writing such a loop are all available to the user.

Syntactically correct expressions have a well defined mapping into PSL data structures, read accomplishes this mapping. For example, linear list notation is converted into the internal tree structure representation, strings are stored in a more efficient non-list form, and numbers are internalized to a form compatible with the arithmetic unit of the machine. The most primitive piece of read is the scanner. This routine recognizes characters special to PSL. For example, space, (, and ). The scanner is also responsible for building the internal representation of identifiers. The scanner must make every reference to a particular id point to the same internal structure. This is accomplished at the time an id is created. The character sequence is compared against sequences which have already been converted into ids. This comparison employs an efficient search technique so that not every id is compared (currently a hash algorithm is used).

The eval function is responsible for the evaluation of data structures interpreted as programs. A through discussion of this can be found in Chapter 11.

Print displays data structures in a way which could later be typed back in to read. Some of the more interesting print routines do prettyprinting. That is, they format the output using conventions based on the structural nesting of the expressions.

Giving the user the power to invoke all of these operation from his code is a very powerful feature of LISP which sets it apart from most other languages.

(toploop TOPLOOPREAD*:function TOPLOOPPRINT*:function
TOPLOOPEVAL*:function TOPLOOPNAME*:string WELCOMEBANNER:string): nil expr
This function is called to establish a new Top Loop. It prints the WELCOMEBANNER and then invokes a Read-Eval-Print loop, using the functions defined by TOPLOOPREAD*, TOPLOOPEVAL*, and TOPLOOPPRINT*. Since each of the parameters to toploop is fluid they may be examined or changed. Timing and history mechanisms are provided. A prompt is constructed by prefixing TOPLOOPNAME* with the history count. As a convention, the name is followed by a number of right angle brackets (>), to indicate the depth of toploop invocations.

toploopread* = [Initially: nil] global
When toploop is called this id is bound to the function used for reading.

toploopeval* = [Initially: nil] global
When toploop is called this id is bound to the function which evaluates input.

toploopname* = [Initially: string] global
Bound to a string (currently ”lisp”), which will be part of the prompt for input.

toplooplevel* = [Initially: 0] global
Depth of top loop invocations.

initforms* = [Initially: nil] global
A list of forms to be evaluated at startup, (prior to calling main). The forms are evaluated in a left to right order, once the last form is evaluated initforms* is set to nil.

*output = [Initially: T] switch
If non-nil, the result of evaluating top level forms is printed.

*time = [Initially: nil] switch
If non-nil, a step evaluation time is printed after each top level form is processed within toploop.

(hist [N:integer]): nil nexpr
Hist is called with 0, 1 or 2 integers, which control how much history is to be printed out:

(hist) Display full history.
(hist n m)Display history from n to m.
(hist n) Display history from n to present.
(hist -n) Display last n entries.

The following functions permit the user to access and resubmit previous expressions, and to re-examine previous results.

(inp N:integer): any expr
Return N’th input at this level.

(redo N:integer): any expr
Reevaluate N’th input.

(ans N:integer): any expr
Return N’th result.

historycount* = [Initially: 0] global
The number of entries which have been read so far.

historylist* = [Initially: nil] global
A list of pairs, the first element of each pair represents an input form, the second is the result of evaluating the frst. Note that the top level evaluation of historylist* will result in a circular list.