1;;; sl-on-cl.lisp --- Standard Lisp on Common Lisp
2
3;; Copyright (C) 2018, 2019 Francis J. Wright
4
5;; Author: Francis J. Wright <https://sourceforge.net/u/fjwright>
6;; Created: 4 November 2018
7
8;; Current target implementations of Common Lisp:
9;; - Windows and Linux SBCL (Steel Bank Common Lisp); see http://www.sbcl.org/
10;; - Cygwin and Linux CLISP 2.49 (2010-07-07); see https://clisp.sourceforge.io/
11
12;; This file implements a superset of Standard Lisp that is a subset
13;; of PSL and CSL in a package called STANDARD-LISP with nickname SL.
14;; It does not provide a Standard Lisp REPL and is intended only
15;; for running REDUCE (which provides its own REPL) on Common Lisp.
16;; This implementation of Standard Lisp is lower-case.  It uses case
17;; inversion of symbol names and is case-sensitive internally.
18
19;; (eval-when (:compile-toplevel :load-toplevel :execute) (push :debug *features*))
20
21#-DEBUG (declaim (optimize speed))
22#+DEBUG (declaim (optimize debug safety))
23#+SBCL (declaim (sb-ext:muffle-conditions sb-ext:compiler-note style-warning))
24#+CLISP (setq custom:*suppress-check-redefinition* t
25              custom:*compile-warnings* nil)
26
27#+SBCL (eval-when (:compile-toplevel :load-toplevel :execute)
28         (require :sb-posix))
29
30#+ABCL (eval-when (:load-toplevel :execute)
31         (require :abcl-contrib)
32         (require :asdf-jar))
33
34(defpackage :standard-lisp
35  (:nicknames :sl)
36  (:documentation "Lower-case Standard Lisp on Common Lisp")
37  (:use :common-lisp)
38
39  ;; Best to use the shadow option here and not separate calls of the
40  ;; shadow function, mainly because the shadow function is not
41  ;; evaluated at compile time!
42  (:shadow :constantp :equal :minusp :vectorp :zerop :nth :pnth
43           :gensym :intern :get :remprop :error :expt :float :map
44           :mapc :mapcan :mapcar :mapcon :maplist :append :assoc
45           :delete :length :member :sublis :subst :rassoc :apply :eval
46           :function :close :open :princ :print :prin1 :read
47           :terpri :complexp :union :load :time
48           :char-downcase :char-upcase :string-downcase :mod
49           :char-code :symbol-name :number)
50
51  #+SBCL (:import-from :sb-ext :quit :gc)
52  #+SBCL (:import-from :sb-posix :getenv)
53
54  #+CLISP (:import-from :ext :quit :gc :getenv)
55  )
56
57(in-package :standard-lisp)
58
59;; The following definitions roughly follow the order in the Standard
60;; Lisp Report.
61
62;;; System GLOBAL Variables
63;;; =======================
64
65(defvar *comp nil
66  "*COMP = NIL global
67The value of !*COMP controls whether or not PUTD compiles the
68function defined in its arguments before defining it. If !*COMP is
69NIL the function is defined as an EXPR. If !*COMP is something
70else the function is first compiled. Compilation will produce certain
71changes in the semantics of functions particularly FLUID type access.")
72
73(defvar emsg* nil
74  "EMSG* = NIL global
75Will contain the MESSAGE generated by the last ERROR call.")
76
77(defconstant $eof$ '$eof$
78  "$EOF$ = <an uninterned identifier> global
79The value of !$EOF!$ is returned by all input functions when the end
80of the currently selected input file is reached.")
81
82(defconstant $eol$ (values (cl:intern (string #\Newline))) ; cf. PSL & CSL
83  "$EOL$ = <an uninterned identifier> global
84The value of !$EOL!$ is returned by READCH when it reaches the
85end of a logical input record. Likewise PRINC will terminate its
86current line (like a call to TERPRI) when !$EOL!$ is its argument.")
87
88(defvar *gc nil
89  "*GC = NIL global
90!*GC controls the printing of garbage collector messages. If NIL
91no indication of garbage collection may occur. If non-NIL various
92system dependent messages may be displayed.")
93
94(import '(nil))                         ; this list form is necessary!
95;; NIL = NIL global
96;; NIL is a special global variable. It is protected from being modifed
97;; by SET or SETQ.
98
99;; **********************************************************************
100;; Make REDUCE case-insensitive for now to facilitate comparison
101;; with the test output from CSL/PSL REDUCE.  Later, could make it
102;; case-sensitive with a switch *legacy that enables *raise for
103;; compatibility with legacy REDUCE.
104;; **********************************************************************
105
106(defvar *raise t
107  "*RAISE = NIL global
108Follow the PSL convention: If !*RAISE is non-NIL all characters input
109through Standard LISP input functions will be converted to a standard
110case.  Currently, this is upper case on SBCL and lower case on CLISP.
111If !*RAISE is NIL characters will be input as is.")
112
113(import 't)
114;; T = T global
115;; T is a special global variable. It is protected from being modifed by
116;; SET or SETQ.
117
118;; Not Standard LISP but PSL and assumed by REDUCE:
119
120(defvar *echo nil
121  "*echo = [Initially: nil] switch
122The switch echo is used to control the echoing of input. When (on echo)
123is placed in an input file, the contents of the file are echoed on the standard
124output device. Dskin does not change the value of *echo, so one may say
125(on echo) before calling dskin, and the input will be echoed.")
126
127(defvar *redefmsg t
128  "*redefmsg = [Initially: t] switch
129If *redefmsg is not nil, the message
130*** Function `FOO' has been redefined
131is printed whenever a function is redefined by PUTD.")
132;; Also applies to DE & DM.
133
134(defun %redefmsg (fname)
135  "Optionally warn about function redefinition."
136  ;; Assume fname is input quoted.
137  (when (and *redefmsg (fboundp fname))
138    ;; (warn "Function ~a has been redefined" fname)
139    ;; Warnings are currently suppressed!
140    ;; (format t "~&*** Function `~(~a~)' has been redefined~%" fname)
141    (format t "~&*** Function `~a' has been redefined~%" fname)))
142
143;;; FUNCTIONS
144;;; =========
145
146(deftype function (&rest etc) `(or symbol cons (cl:function ,@etc)))
147
148(deftype filehandle () '(or null cons))
149
150(deftype number () '(or integer double-float))
151
152;; NB: CLISP only accepts (typespec var*) as an abbreviation for (type
153;; typespec var*) for standardized atomic type specifiers, which I
154;; think is a bug!
155
156;; First, some utility functions used only internally:
157
158;; For ABCL, autoloaded functios must be loaded before copying the
159;; function cell. Otherwise only the autoload stub is copied.
160;; The call to resolve does this
161(defmacro defalias (symbol definition &optional docstring)
162  "Set SYMBOL's function definition to DEFINITION.
163The optional third argument DOCSTRING specifies the documentation string
164for SYMBOL; if it is omitted or nil, SYMBOL uses the documentation string
165determined by DEFINITION.  The return value is undefined."
166  (declare (list symbol definition) (type (or null simple-string) docstring))
167  `(progn
168#+ABCL (if (ext:autoloadp ,definition) (ext:resolve ,definition))
169  (setf ,@(if docstring `((documentation ,symbol 'cl:function) ,docstring))
170         (symbol-function ,symbol) (symbol-function ,definition))))
171
172(eval-when (:compile-toplevel :load-toplevel :execute)
173  ;; Needed to expand macros fluid and global when compiling.
174  (defun eqcar (u v)
175    "Return true if U is a cons cell and its car is eq to V."
176    (and (consp u) (eq (car u) v))))
177
178(defun %character-invert-case (c)
179  "Invert the case of character C (if it is a letter)."
180  (declare (character c))
181  (the character (if (cl:both-case-p c)
182                     (if (cl:lower-case-p c)
183                         (cl:char-upcase c)
184                         (cl:char-downcase c))
185                     c)))
186
187(defun %string-invert-case (s)
188  "Return a copy of string S with the case of each letter inverted."
189  ;; The consequences are undefined if a symbol name is ever modified!
190  (declare (simple-string s))
191  (cl:map 'string #'%character-invert-case s))
192
193(defun %intern-character-preserve-case (c)
194  "Convert character C to an interned (case-preserved) symbol."
195  (declare (character c))
196  (values (cl:intern (string c))))
197
198(defun %intern-character-invert-case (c)
199  "Convert character C to an interned (case-inverted) symbol."
200  (declare (character c))
201  (values (cl:intern (string (%character-invert-case c)))))
202
203
204;;; Elementary Predicates
205;;; =====================
206
207(import 'cl:atom)
208;; ATOM(U:any):boolean eval, spread
209;; Returns T if U is not a pair.
210;; EXPR PROCEDURE ATOM(U);
211;;    NULL PAIRP U;
212
213(defalias 'codep 'cl:compiled-function-p
214  "CODEP(U:any):boolean eval, spread
215Returns T if U is a function-pointer.")
216;; This means compiled code only!
217
218(defun constantp (u)
219  "CONSTANTP(U:any):boolean eval, spread
220Returns T if U is a constant (a number, string, function-pointer, or vector).
221EXPR PROCEDURE CONSTANTP(U);
222   NULL OR(PAIRP U, IDP U);"
223  (null (or (consp u) (symbolp u))))
224
225(import 'cl:eq)
226;; EQ(U:any, V:any):boolean eval, spread
227;; Returns T if U points to the same object as V. EQ is not a reliable
228;; comparison between numeric arguments.
229
230;; The following code seems to cause problems if used in eq or equal.
231
232;; (defun eq (u v)
233;;   "EQ(U:any, V:any):boolean eval, spread
234;; Returns T if U points to the same object as V. EQ is not a reliable
235;; comparison between numeric arguments."
236;;   ;; After evaluating (setq gg intern(setq g (gensym))) in PSL/CSL
237;;   ;; then (eq g gg) gives true, but in Common Lisp the equivalent
238;;   ;; gives false, so...
239;;   (if (and (cl:symbolp u) (cl:symbolp v)) ; both symbols
240;;       ;; Look for both symbols NOW in the current package:
241;;       (let ((uu (find-symbol (symbol-name u)))
242;;             (vv (find-symbol (symbol-name v))))
243;;         (and uu vv (cl:eq uu vv)))
244;;       (cl:eq u v)))
245
246(defun eqn (u v)
247  "EQN(U:any, V:any):boolean eval, spread
248Returns T if U and V are EQ or if U and V are numbers and have
249the same value and type."               ; i.e. the same SL type!
250  ;;  eql/equal may not be true of two floats even when they represent
251  ;;  the same value.  = is used to compare mathematical values.
252  ;;  (eql/equal -0.0 0.0) is false in SBCL although true in CLISP!
253  (if (and (floatp u) (floatp v)) (= u v) (eql u v)))
254
255;; (defun equal (u v)
256;;   "EQUAL(U:any, V:any):boolean eval, spread
257;; Returns T if U and V are the same. Dotted-pairs are compared
258;; recursively to the bottom levels of their trees. Vectors must
259;; have identical dimensions and EQUAL values in all
260;; positions. Strings must have identical characters. Function
261;; pointers must have EQ values. Other atoms must be EQN equal."
262;;   (or (cl:equal u v)
263;;    ;;  equal may not be true of two floats even when they represent
264;;    ;;  the same value. = is used to compare mathematical values.
265;;    (and (floatp u) (floatp v) (= u v))
266;;    (and (vectorp u) (vectorp v) (equalp u v))))
267
268(defun equal (u v)
269  "EQUAL(U:any, V:any):boolean eval, spread
270Returns T if U and V are the same. Dotted-pairs are compared
271recursively to the bottom levels of their trees. Vectors must
272have identical dimensions and EQUAL values in all
273positions. Strings must have identical characters. Function
274pointers must have EQ values. Other atoms must be EQN equal."
275  (and (cl:equal (type-of u) (type-of v))
276       (if (atom u) (cond ((cl:symbolp u) (eq u v))
277                          ((cl:floatp u) (= u v))
278                          ((cl:numberp u) (eql u v))
279                          ((cl:stringp u) (string= u v))
280                          ((cl:vectorp u) (equalp u v)))
281           ;; (and (equal (car u) (car v)) (equal (cdr u) (cdr v)))
282           (loop for utail on u for vtail on v
283              unless (equal (car utail) (car vtail)) do (return nil)
284              while (and (consp (cdr utail)) (consp (cdr vtail)))
285              finally (return (equal (cdr utail) (cdr vtail)))))))
286
287(defalias 'fixp 'cl:integerp
288  "FIXP(U:any):boolean eval, spread
289Returns T if U is an integer (a fixed number).")
290
291(import 'cl:floatp)
292;; FLOATP(U:any):boolean eval, spread
293;; Returns T if U is a floating point number.
294
295(defalias 'idp 'cl:symbolp
296  "IDP(U:any):boolean eval, spread
297Returns T if U is an id.")
298
299(defun minusp (u)
300  "MINUSP(U:any):boolean eval, spread
301Returns T if U is a number and less than 0. If U is not a number
302or is a positive number, NIL is returned.
303EXPR PROCEDURE MINUSP(U);
304   IF NUMBERP U THEN LESSP(U, 0) ELSE NIL;"
305  (and (realp u) (cl:minusp u)))
306
307(import 'cl:null)
308;; NULL(U:any):boolean eval, spread
309;; Returns T if U is NIL.
310;; EXPR PROCEDURE NULL(U);
311;;    U EQ NIL;
312
313(import 'cl:numberp)
314;; NUMBERP(U:any):boolean eval, spread
315;; Returns T if U is a number (integer or floating).
316;; EXPR PROCEDURE NUMBERP(U);
317;;    IF OR(FIXP U, FLOATP U) THEN T ELSE NIL;
318
319(defun onep (u)
320  "ONEP(U:any):boolean eval, spread.
321Returns T if U is a number and has the value 1 or 1.0. Returns NIL
322otherwise.
323EXPR PROCEDURE ONEP(U);
324   OR(EQN(U, 1), EQN(U, 1.0));"
325  (equalp u 1))
326
327(defalias 'pairp 'cl:consp
328  "PAIRP(U:any):boolean eval, spread
329Returns T if U is a dotted-pair.")
330
331(import 'cl:stringp)
332;; STRINGP(U:any):boolean eval, spread
333;; Returns T if U is a string.
334
335(defun vectorp (u)
336  "VECTORP(U:any):boolean eval, spread
337Returns T if U is a vector."
338  ;; Must exclude strings, which are also vectors in CL.
339  ;; (and (vectorp u) (not (stringp u)))
340  (typep u '(vector t)))
341
342(defun zerop (u)
343  "ZEROP(U:any):boolean eval, spread
344Returns T if U is a number and has the value 0 or 0.0. Returns
345NIL otherwise.
346EXPR PROCEDURE ZEROP(U);
347   OR(EQN(U, 0), EQN(U, 0.0));"
348  (and (numberp u) (cl:zerop u)))
349
350
351;;; Functions on Dotted-Pairs
352;;; =========================
353
354(import 'cl:car)
355;; CAR(U:dotted-pair):any eval, spread
356;; CAR(CONS(a, b)) --> a. The left part of U is returned. The type
357;; mismatch error occurs if U is not a dotted-pair.
358
359(import 'cl:cdr)
360;; CDR(U:dotted-pair):any eval, spread
361;; CDR(CONS(a, b)) --> b. The right part of U is returned. The type
362;; mismatch error occurs if U is not a dotted-pair.
363
364;; The composites of CAR and CDR are supported up to 4 levels:
365(import '(cl:caar cl:cadr cl:cdar cl:cddr cl:caaar cl:caadr cl:cadar
366          cl:caddr cl:cdaar cl:cdadr cl:cddar cl:cdddr cl:caaaar
367          cl:caaadr cl:caadar cl:caaddr cl:cadaar cl:cadadr cl:caddar
368          cl:cadddr cl:cdaaar cl:cdaadr cl:cdadar cl:cdaddr cl:cddaar
369          cl:cddadr cl:cdddar cl:cddddr))
370
371(import 'cl:cons)
372;; CONS(U:any, V:any):dotted-pair eval, spread
373;; Returns a dotted-pair which is not EQ to anything and has U as its
374;; CAR part and V as its CDR part.
375
376(import 'cl:list)
377;; LIST([U:any]):list noeval, nospread, or macro
378;; A list of the evaluation of each element of U is returned. The order of
379;; evaluation need not be first to last as the following definition implies.
380;; FEXPR PROCEDURE LIST(U);
381;;    EVLIS U;
382
383(import 'cl:rplaca)
384;; RPLACA(U:pair, V:any):pair eval, spread
385;; The car of the pair U is replaced by V and the modified pair U is
386;; returned.  A type mismatch error occurs if U is not a pair.
387
388(import 'cl:rplacd)
389;; RPLACD(U:pair, V:any):pair eval, spread
390;; The cdr of the pair U is replaced by V and the modified pair U is
391;; returned.  A type mismatch error occurs if U is not a pair.
392
393;; PSL functions:
394
395(import '(cl:first cl:second cl:third cl:fourth cl:rest))
396
397(declaim (inline lastpair lastcar nth pnth))
398
399(defun lastpair (l)
400  "(lastpair L:pair): any expr
401Returns the last pair of a L. It is often useful to think of this as a
402pointer to the last element for use with destructive functions such as
403rplaca. If L is not a pair then a type mismatch error occurs.
404\(de lastpair (l)
405  (if (or (atom l) (atom (cdr l)))
406    l
407    (lastpair (cdr l))))"
408  ;; The inconsistent description above is from the PSL manual!
409  (if (atom l) l (cl:last l)))
410
411(defun lastcar (l)                      ; inlined
412  "(lastcar L:pair): any expr
413Returns the last element of the pair L. A type mismatch error results
414if L is not a pair.
415\(de lastcar (l)
416  (if (atom l) l (car (lastpair l))))"
417  ;; The inconsistent description above is from the PSL manual!
418  (if (atom l) l (car (cl:last l))))
419
420(defun nth (l n)                        ; inlined
421  "(nth L:pair N:integer): any expr
422Returns the Nth element of the list L. If L is atomic or contains
423fewer than N elements, an out of range error occurs.
424\(de nth (l n)
425    (cond ((null l) (range-error))
426          ((onep n) (first l))
427          (t (nth (rest l) (sub1 n)))))
428Note that this definition is not compatible with Common LISP. The
429Common LISP definition reverses the arguments and defines the car
430of a list to be the \"zeroth\" element."
431  (declare (list l) (fixnum n))
432  (cl:nth (1- n) l))
433
434(defun pnth (l n)                       ; inlined
435  "(pnth L:list N:integer): any expr
436Returns a list starting with the nth element of the list L. Note
437that the result is a pointer to the nth element of L, a
438destructive function like rplaca can be used to modify the
439structure of L. If L is atomic or contains fewer than N elements,
440an out of range error occurs.
441\(de pnth (l n)
442    (cond ((onep n) l)
443          ((not (pairp l)) (range-error))
444          (t (pnth (rest l) (sub1 n)))))"
445  (declare (list l) (fixnum n))
446  (nthcdr (1- n) l))
447
448
449;;; Identifiers
450;;; ===========
451
452(defun %id-to-char-invert-case (c)
453  "As `character', but case-inverted."
454  (declare (symbol c))
455  (the character (%character-invert-case (character c))))
456
457(defun compress (u)                     ; PSL spec
458  "COMPRESS(U:id-list):{atom-vector} eval, spread
459U is a list of single character identifiers which is built into a
460Standard LISP entity and returned.  Recognized are lists, numbers,
461strings, and identifiers with the escape character prefixing special
462characters.  Identifiers are not interned.  Function pointers may not
463be compressed.  If an entity cannot be parsed out of U an error
464occurs:
465***** Poorly formed atom in COMPRESS"
466  (declare (list u))
467  (labels
468      ((compress () ; This internal function recursively process lists.
469         ;; Concatenate the characters into a string and then handle any !
470         ;; characters as follows:
471         ;; A string begins with " and should retain any ! characters without
472         ;; change.
473         ;; A number begins with - or a digit and should not contain any !
474         ;; characters.
475         ;; Otherwise, assume an identifier and replace ! by \, but !! by \!
476         (let (u0)                      ; first element
477           (compress-skip-spaces)       ; skip leading spaces
478           (if (or (null u)
479                   (cl:member (setq u0 (car u))
480                              '(\' \) \, \% \[ \\ \`))) ; PSL
481               (cl:error "Poorly formed S-expression in COMPRESS"))
482           (cond
483             ;; LIST?
484             ((eq u0 '|(|) (setq u (cdr u))
485              (compress-skip-spaces)    ; skip leading spaces
486              (loop
487                 while (not (eq (car u) '|)|))
488                 collect (compress)
489                 do (compress-skip-spaces)))
490             ;; STRING?
491             ((eq u0 '\")
492              ;; In Standard Lisp, "" in a string represents ":
493              (loop with newu while (setq u (cdr u)) do
494                   (when (eq (car u) '\")
495                     (setq u (cdr u))
496                     (if (not (and u (eq (car u) '\"))) ; end of string
497                         (return-from compress
498                           (cl:map 'string #'%id-to-char-invert-case
499                                   (nreverse newu)))))
500                   (push (car u) newu))
501              ;; String not terminated:
502              (cl:error "Poorly formed S-expression in COMPRESS"))
503             ;; NUMBER?
504             ((or (digit u0) (char= (character u0) #\-))
505              ;; (eq u0 '-) fails because u0 is in SL but - is (an operator) in CL.
506              (multiple-value-bind (obj pos)
507                  (read-from-string (cl:map 'string #'character u))
508                (setq u (nthcdr pos u))
509                obj))
510             ;; IDENTIFIER
511             (t
512              ;; Delete a single ! but replace !! by !
513              ;; In PSL, an identifier can contain any of the special characters
514              ;; + - $ & * / : ; | < = > ? ^ _ { } ~ @
515              ;; and hence not any of
516              ;; space ! " ' ( ) , . # % [ \ ] `
517              ;; unless they are escaped with ! (which must be handled specially).
518              (loop with newu do
519                   (cond ((or (null u)
520                              (cl:member (car u)
521                                         '(\  \" \' \( \) \, \% \[ \\ \] \`)))
522                          (return
523                            (make-symbol ; uninterned symbol
524                             (cl:map 'string #'character (nreverse newu)))))
525                         ((eq (car u) '!) ; ignore ! but keep WHATEVER follows it
526                          (if (setf u (cdr u))
527                              (push (car u) newu)))
528                         (t (push (car u) newu)))
529                   (setf u (cdr u)))))))
530       ;;
531       (compress-skip-spaces ()
532         (loop while (eq (car u) '| |) do (setq u (cdr u)))))
533    ;;
534    (compress)))
535
536(defun explode (u)                      ; PSL spec
537  "(explode U:any): id-list expr
538Explode returns a list of interned single-character identifiers
539representing the characters required to print the S-expression U in a
540way that could be read by Lisp.  It is implemented by effectively
541printing (using prin1) to a list.  E.g.
5421 lisp> (explode 'foo)
543\(f o o)
5442 lisp> (explode '(a . b))
545\(!( a !  !. !  b !))"
546  ;; Add support for vectors?  Share code with print routines?
547  (the list
548       (if (consp u)
549           ;; Exploding a cons:
550           (let ((ll (list (explode (car u)) (list '|(|))))
551             (loop while (consp (setq u (cdr u)))
552                do (push (list '| |) ll)
553                do (push (explode (car u)) ll))
554             (when u
555               (push (list '| | '|.| '| |) ll)
556               (push (explode u) ll))
557             (push (list '|)|) ll)
558             (cl:apply #'nconc (nreverse ll)))
559           ;; Exploding an atom:
560           (cond ((stringp u)
561                  ;; Add leading and trailing " and convert internal " to "":
562                  (nconc
563                   (list '\")
564                   (loop for c across u
565                      collect (%intern-character-invert-case c)
566                      when (char= c #\") collect '\")
567                   (list '\")))
568                 ((numberp u)
569                  (cl:map 'list #'%intern-character-invert-case ; might not be portable!
570                          (princ-to-string u)))
571                 (t
572                  ;; Assume identifier -- insert ! before an upper-case
573                  ;; letter, leading digit or _, or special character
574                  ;; (except _):
575                  (loop with s = (cl:symbol-name u) and c
576                     for i below (cl:length s)
577                     do (setq c (aref s i))
578                     unless (or (upper-case-p c) ; case-inverted!
579                                (and (not (eql i 0))
580                                     (or (digit-char-p c) (char= c #\_))))
581                     collect '\!
582                     collect (%intern-character-preserve-case c)))))))
583
584(defalias 'gensym 'cl:gensym)
585;; GENSYM():identifier eval, spread
586;; Creates an identifier which is not interned on the OBLIST and
587;; consequently not EQ to anything else.
588;; Defined this way so that I can overwrite it in faslout.
589
590(defun gensymp (u)                      ; from pslrend
591  (and (symbolp u) (not (cl:find-symbol (cl:symbol-name u)))))
592
593(defun intern (u)
594  "INTERN(U:{id,string}):id eval, spread
595INTERN searches the OBLIST for an identifier with the same print
596name as U and returns the identifier on the OBLIST if a match is
597found. Any properties and global values associated with U may be
598lost. If U does not match any entry, a new one is created and
599returned. If U has more than the maximum number of characters
600permitted by the implementation (the minimum number is 24) an
601error occurs:
602***** Too many characters to INTERN"
603  (declare (type (or symbol simple-string) u))
604  (values (cl:intern (if (symbolp u)
605                         (cl:symbol-name u)          ; symbol
606                         (%string-invert-case u))))) ; string
607
608(defun remob (u)
609  "REMOB(U:id):id eval, spread
610If U is present on the OBLIST it is removed. This does not affect U
611having properties, flags, functions and the like. U is returned."
612  (declare (symbol u))
613  (unintern u)
614  (the symbol u))
615
616
617;;; Property List Functions
618;;; =======================
619
620;; In file "rlisp/superv.red" is the statement
621;;
622;; FLAG('(DEFLIST FLAG FLUID GLOBAL REMFLAG REMPROP UNFLUID),'EVAL);
623;;
624;; which (I think) means that the functions listed are evaluated even
625;; after `ON DEFN', which is necessary to ensure that some source code
626;; reads correctly.  However, `REMPROP' is usually followed by `PUT'
627;; to reinstate whatever property was removed, but `PUT' is not
628;; flagged `EVAL', so this reinstatement doesn't happen because
629;; evaluating `PUT' at the wrong time can cause similar problems,
630;; e.g. with `rlisp88'.  Hence, viewing code with `ON DEFN' can break
631;; subsequent code.  For example, inputting "rlisp/module.red" with
632;; `ON DEFN' removes the `STAT' property from `LOAD_PACKAGE', which
633;; then no longer works correctly.  This is a major problem for the
634;; way I generate fasl files!
635;;
636;; I therefore provide a workaround to make the functions DEFLIST,
637;; FLAG, REMFLAG and REMPROP save the property list of any identifier
638;; before modifying it if it has not already been saved, and provide a
639;; function to reinstate the saved property list.  I use this facility
640;; when generating fasl files and in `OFF DEFN' (see "eslrend.red"),
641;; so that ESL REDUCE should be immune to this `ON DEFN' side-effect.
642;;
643;; However, this facility only applies to reading REDUCE code and must
644;; be disabled when loading a Lisp file, i.e. when the variable
645;; `*load-pathname*' is non-nil.  This is particularly important when
646;; building REDUCE.
647
648(defvar *defn nil)
649
650(defvar %saved-plist-alist nil
651  "Association list of symbols and their saved property lists.
652Its value should normally be nil, except while ON DEFN.")
653
654(defun %save-plist (symbol)
655  "Save property list of symbol SYMBOL if not already saved.
656Do not do this if Lisp file load in progress."
657  (declare (symbol symbol))
658  (or *load-pathname*
659      (cl:assoc symbol %saved-plist-alist :test #'eq)
660      (push (cons symbol (cl:copy-tree (symbol-plist symbol)))
661            %saved-plist-alist))
662  nil)
663
664(defun %reinstate-plists ()
665  "Reinstate all saved property lists.
666Do not do this if Lisp file load in progress."
667  (unless *load-pathname*
668    (cl:mapc #'(lambda (s) (setf (symbol-plist (car s)) (cdr s)))
669             %saved-plist-alist)
670    (setf %saved-plist-alist nil))
671  nil)
672
673(defun flag (u v)
674  "FLAG(U:id-list, V:id):NIL eval, spread
675U is a list of ids which are flagged with V. The effect of FLAG is
676that FLAGP will have the value T for those ids of U which were
677flagged. Both V and all the elements of U must be identifiers or the
678type mismatch error occurs."
679  (declare (list u) (symbol v))
680  (if *defn (cl:mapc #'%save-plist u))
681  (cl:mapc #'(lambda (x) (put x v t)) u)
682  nil)
683
684(defun flagp (u v)
685  "FLAGP(U:any, V:any):boolean eval, spread
686Returns T if U has been previously flagged with V, else NIL. Returns
687NIL if either U or V is not an id."
688  (if (and (symbolp u) (symbolp v)) (cl:get u v)))
689
690(defun get (u ind)
691  "GET(U:any, IND:id):any eval, spread
692Returns the property associated with indicator IND from the
693property list of U. If U does not have indicator IND, NIL is
694returned.  GET cannot be used to access functions (use GETD
695instead)."
696  ;; MUST return nil if u is not a symbol.
697  (declare (symbol ind))
698  (if (symbolp u) (cl:get u ind)))
699
700(defun put (u ind prop)
701  "PUT(U:id, IND:id, PROP:any):any eval, spread
702The indicator IND with the property PROP is placed on the
703property list of the id U. If the action of PUT occurs, the value
704of PROP is returned. If either of U and IND are not ids the type
705mismatch error will occur and no property will be placed. PUT
706cannot be used to define functions (use PUTD instead)."
707  (declare (symbol u ind))
708  (setf (cl:get u ind) prop))
709
710(defun remflag (u v)
711  "REMFLAG(U:any-list, V:id):NIL eval, spread
712Removes the flag V from the property list of each member of the
713list U. Both V and all the elements of U must be ids or the type
714mismatch error will occur."
715  (declare (list u) (symbol v))
716  (if *defn (cl:mapc #'%save-plist u))
717  (cl:mapc #'(lambda (x) (cl:remprop x v)) u)
718  nil)
719
720(defun remprop (u ind)
721  "REMPROP(U:any, IND:any):any eval, spread
722Removes the property with indicator IND from the property list of U.
723Returns the removed property or NIL if there was no such indicator."
724  (declare (symbol ind))
725  (prog1
726      (get u ind)
727    (if *defn (%save-plist u))
728    (cl:remprop u ind)))
729
730
731;;; Function Definition
732;;; ===================
733
734;; NOTE that Standard Lisp macros are nospread and therefore take a
735;; single parameter that gets the list of actual arguments, so `DM'
736;; and `PUTD' must convert the macro parameter into an &rest
737;; parameter.  Also, when a Standard Lisp macro is called it receives
738;; its name as its first argument, i.e. the single parameter evaluates
739;; to the COMPLETE function call, so `DM' and `PUTD' must modify the
740;; macro argument list within the body lambda expression.
741
742;; Ref. Standard LISP Report, page 9: "When a macro invocation is
743;; encountered, the body of the macro, a lambda expression, is invoked
744;; as a NOEVAL, NOSPREAD function with the macro's invocation bound as
745;; a list to the macros single formal parameter."
746
747;; REDUCE handles macros specially, assuming they are Standard LISP
748;; macros, whereas SL functions that are actually defined as Common
749;; Lisp macros need to be handled by REDUCE as if they were
750;; EXPRs. Therefore, it is important that the function type defaults
751;; to EXPR, so only macros defined using DM or PUTD are given the
752;; property %FTYPE with value MACRO. The %FTYPE property is required
753;; so that macros defined in REDUCE can be distinguished from Common
754;; Lisp macros. Normal functions defined using DE or PUTD are given
755;; the property %FTYPE with value EXPR just for symmetry, but this
756;; property value is not actually used by GETD.
757
758(defmacro de (fname params &rest fn)    ; PSL definition
759  "(de Fname:id PARAMS:id-list [FN:form]): id macro
760Defines the function named FNAME, of type expr. The forms FN are made
761into a lambda expression with the formal parameter list PARAMS, and
762this is used as the body of the function.  Previous definitions of the
763function are lost. The name of the defined function, FNAME, is
764returned."
765  (declare (symbol fname) (list params fn))
766  `(progn
767     (%redefmsg ',fname)
768     (put ',fname '%ftype 'expr)
769     (defun ,fname ,params ,@fn)
770     ;; It makes no sense to include code to compile this function
771     ;; when the function definition is being compiled into a fasl
772     ;; file, so examine *COMP when the macro is expanded/compiled and
773     ;; ensure that *COMP is nil when fasl files are being generated.
774     ;; Splice in *list* of content or nil.
775     ,@(if *comp `((values (compile ',fname))))))
776
777;; *** I'm hoping df is not actually required! ***
778;; DF(FNAME:id, PARAM:id-list, FN:any):id noeval, nospread
779;; The function FN with formal parameter PARAM is added to the set
780;; of defined functions with the name FNAME. Any previous definitions
781;; of the function are lost. The function created is of type FEXPR. If
782;; the !*COMP variable is T the FEXPR is first compiled. The name
783;; of the defined function is returned.
784;; FEXPR PROCEDURE DF(U);
785;;    PUTD(CAR U, 'FEXPR, LIST('LAMBDA, CADR U, CADDR U));
786
787(defmacro dm (mname param fn)
788  "DM(MNAME:id, PARAM:id-list, FN:any):id noeval, nospread
789The macro FN with the formal parameter PARAM is added to the set
790of defined functions with the name MNAME. Any previous
791definitions of the function are overwritten. The function created
792is of type MACRO. The name of the macro is returned.
793FEXPR PROCEDURE DM(U);
794   PUTD(CAR U, 'MACRO, LIST('LAMBDA, CADR U, CADDR U));"
795  (declare (symbol mname) (list param fn))
796  `(progn
797     (%redefmsg ',mname)
798     (put ',mname '%ftype 'macro)
799     ;; Save the (uncompiled) SL macro form:
800     ;; (put ',mname '%macro '(macro lambda ,param ,fn)) ; not currently used
801     ;; param must be a list containing a single identifier, which
802     ;; must therefore be spliced into the macro definition.
803     ;; Spread the arguments and include macro name as first arg:
804     (defmacro ,mname (&whole ,@param &rest r)
805       ;; The parameter r should probably be a gensym to avoid
806       ;; potential name clashes!
807       (declare (ignore r))
808       ,fn)
809     ,@(if *comp `((values (compile ',mname)))))) ; see DE
810
811(defun getd (fname)
812  "GETD(FNAME:any):{NIL, dotted-pair} eval, spread
813If FNAME is not the name of a defined function, return NIL. If
814FNAME is a defined function then return the dotted-pair
815\(TYPE:ftype . DEF:{function-pointer, lambda})."
816  (the list
817       (and (symbolp fname) (fboundp fname)
818            ;; Assume expr unless fname was defined using SL dm macro.
819            (cond ((eq (cl:get fname '%ftype) 'macro)
820                   ;; ;; Return the (uncompiled) SL macro form:
821                   ;; (cl:get fname '%macro)
822                   ;; This may need more work.
823                   ;; A CL macro expansion needs an environment.
824                   ;; Try the null environment (nil) initially.
825                   ;; (The parameter x should perhaps be a gensym.)
826                   (cons 'macro
827                         `(lambda (x)
828                            (funcall ,(macro-function fname) x nil))))
829                  (t
830                   ;; Return a lambda expression if possible, since this is
831                   ;; most useful (although perhaps not most efficient in
832                   ;; some cases):
833                   (let (f)
834                     ;; Note that a CL function definition may contain
835                     ;; declarations and a documentation string, and the
836                     ;; body MAY BE wrapped in a block form, i.e.
837                     ;; (lambda params [decls] [doc] (block name body))
838                     ;; [A compiled CLISP function may not contain a block!]
839                     ;; Extract the function body:
840                     (when (and (functionp (setq fname (symbol-function fname)))
841                                (setq f (function-lambda-expression fname)))
842                       (setq fname (car (last f))) ; block or body form
843                       (if (eqcar fname 'block) (setq fname (caddr fname)))
844                       (setq fname `(lambda ,(cadr f) ,fname))))
845                   (cons 'expr fname))))))
846
847(defun putd (fname type body)
848  "PUTD(FNAME:id, TYPE:ftype, BODY:function):id eval, spread
849Creates a function with name FNAME and definition BODY of type
850TYPE. If PUTD succeeds the name of the defined function is
851returned. The effect of PUTD is that GETD will return a
852dotted-pair with the functions type and definition. Likewise the
853GLOBALP predicate will return T when queried with the function
854name. If the function FNAME has already been declared as a
855GLOBAL or FLUID variable the error:
856***** FNAME is a non-local variable
857occurs and the function will not be defined. If function FNAME
858already exists a warning message will appear:
859*** FNAME redefined
860The function defined by PUTD will be compiled before definition if
861the !*COMP global variable is non-NIL."
862  (declare (symbol fname type) (type function body))
863  (if (or (cl:get fname 'global)        ; only if explicitly declared
864          (fluidp fname))
865      (cl:error "~a is a non-local variable" fname))
866  (%redefmsg fname)
867  ;; body = (lambda (u) body-form) or function-pointer
868  (let (*redefmsg)                  ; don't report redefinitions twice
869    (cond ((eq type 'expr)
870           (cond ((eqcar body 'lambda)
871                  (eval `(de ,fname ,(cadr body) ,@(cddr body))))
872                 ((functionp body)
873                  (setf (symbol-function fname) body)
874                  (put fname '%ftype 'expr))
875                 (t (cl:error "Invalid expr body in PUTD"))))
876          ((eq type 'macro)
877           (cond ((eqcar body 'lambda)
878                  (if (eq (car (caddr body)) 'funcall)
879                      ;; This "hybrid form" is returned by getd.
880                      (progn
881                        (setf (macro-function fname) (cadr (caddr body)))
882                        (put fname '%ftype 'macro))
883                      ;; This "pure source form" is used in "rlisp/block.red".
884                      (eval `(dm ,fname ,(cadr body) ,@(cddr body)))))
885                 ;; ((functionp body)       ; This case should not happen!
886                 ;;  (setf (macro-function fname) body)
887                 ;;  (put fname '%ftype 'macro))
888                 (t (cl:error "Invalid macro body in PUTD"))))
889          (t (cl:error "Invalid type in PUTD"))))
890  (the symbol fname))
891
892(defun remd (fname)
893  "REMD(FNAME:id):{NIL, dotted-pair} eval, spread
894Removes the function named FNAME from the set of defined
895functions. Returns the (ftype . function) dotted-pair or NIL as
896does GETD. The global/function attribute of FNAME is removed and
897the name may be used subsequently as a variable."
898  (declare (symbol fname))
899  (the list
900       (let ((def (getd fname)))
901         (when def
902           (fmakunbound fname)
903           (cl:remprop fname '%ftype))
904         def)))
905
906
907;;; Variables and Bindings
908;;; ======================
909
910(defun %fluid (x)
911  "If id X is already GLOBAL then display a warning; otherwise flag X as FLUID."
912  (declare (symbol x))
913  (unless (fluidp x)
914    (if (globalp x)
915        (warn "GLOBAL ~a cannot be changed to FLUID" x)
916        (progn
917          ;; defvar is a macro, so ...
918          (cl:eval `(defvar ,x nil "Standard LISP fluid variable."))
919          (put x 'fluid t))))
920  nil)
921
922(defmacro fluid (idlist)
923  "FLUID(IDLIST:id-list):NIL eval, spread
924The ids in IDLIST are declared as FLUID type variables (ids not
925previously declared are initialized to NIL). Variables in IDLIST
926already declared FLUID are ignored. Changing a variable's type
927from GLOBAL to FLUID is not permissible and results in the error:
928***** ID cannot be changed to FLUID"
929  ;; A warning, as for PSL, is more convenient than an error!
930  (declare (type (or list symbol) idlist))
931  (the list
932       (if (eqcar idlist 'quote)
933           ;; Assume a top-level call that needs to output `defvar' forms
934           ;; at compile time.
935           (cons 'prog1
936                 (cons nil
937                       (cl:mapcan
938                        #'(lambda (x) `((%fluid ',x)))
939                        (cl:eval idlist))))
940           ;; Assume a run-time call.
941           `(prog1 nil
942              (cl:mapc #'%fluid ,idlist)))))
943
944(defun fluidp (u)
945  "FLUIDP(U:any):boolean eval, spread
946If U has been declared fluid then t is returned, otherwise nil is returned."
947  (get u 'fluid))
948
949(defun %global (x)
950  "If id X is already FLUID then display a warning; otherwise flag X as GLOBAL."
951  (declare (symbol x))
952  (unless (globalp x)
953    (if (fluidp x)
954        (warn "FLUID ~a cannot be changed to GLOBAL" x)
955        (progn
956          ;; defvar is a macro, so ...
957          (unless (cl:constantp x)      ; nil, t, $eol$, $eof$, etc.
958            (cl:eval `(defvar ,x nil "Standard LISP global variable.")))
959          (put x 'global t))))
960  nil)
961
962(defmacro global (idlist)
963  "GLOBAL(IDLIST:id-list):NIL eval, spread
964The ids of IDLIST are declared GLOBAL type variables. If an id
965has not been declared previously it is initialized to
966NIL. Variables already declared GLOBAL are ignored. Changing a
967variables type from FLUID to GLOBAL is not permissible and
968results in the error:
969***** ID cannot be changed to GLOBAL"
970  ;; A warning, as for PSL, is more convenient than an error!
971  (declare (type (or list symbol) idlist))
972  (the list
973       (if (eqcar idlist 'quote)
974           ;; Assume a top-level call that needs to output `defvar' forms
975           ;; at compile time.
976           (cons 'prog1
977                 (cons nil
978                       (cl:mapcan
979                        #'(lambda (x) `((%global ',x)))
980                        (cl:eval idlist))))
981           ;; Assume a run-time call.
982           `(prog1 nil
983              (cl:mapc #'%global ,idlist)))))
984
985(defun globalp (u)
986  "GLOBALP(U:any):boolean eval, spread
987If U has been declared global then t is returned, otherwise nil is returned."
988  (get u 'global))                      ; PSL/CSL definition
989
990(import 'cl:set)
991;; Auto fluid not implemented!
992;; SET(EXP:id, VALUE:any):any eval, spread
993;; EXP must be an identifier or a type mismatch error occurs. The
994;; effect of SET is replacement of the item bound to the identifier
995;; by VALUE. If the identifier is not a local variable or has not
996;; been declared GLOBAL it is automatically declared FLUID with the
997;; resulting warning message:
998;; *** EXP declared FLUID
999;; EXP must not evaluate to T or NIL or an error occurs:
1000;; ***** Cannot change T or NIL
1001
1002(import 'cl:setq)
1003;; Auto fluid not implemented!
1004;; SETQ(VARIABLE:id, VALUE:any):any noeval, nospread
1005;; If VARIABLE is not local or GLOBAL it is by default declared
1006;; FLUID and the warning message:
1007;; *** VARIABLE declared FLUID
1008;; appears. The value of the current binding of VARIABLE is replaced
1009;; by the value of VALUE. VARIABLE must not be T or NIL or an
1010;; error occurs:
1011;; ***** Cannot change T or NIL
1012;; MACRO PROCEDURE SETQ(X);
1013;;    LIST('SET, LIST('QUOTE, CADR X), CADDR X);
1014
1015(defun unfluid (idlist)
1016  "UNFLUID(IDLIST:id-list):NIL eval, spread
1017The variables in IDLIST that have been declared as FLUID
1018variables are no longer considered as fluid variables. Others are
1019ignored. This affects only compiled functions as free variables
1020in interpreted functions are automatically considered fluid."
1021  (declare (list idlist))
1022  (cl:mapc #'(lambda (x) (if (fluidp x) (cl:remprop x 'fluid)))
1023           idlist)
1024  nil)
1025
1026;; SL declarations for special variables defined above:
1027(fluid '(*comp *gc *raise))
1028(global '(emsg*))
1029
1030
1031;;; Program Feature Functions
1032;;; =========================
1033
1034(import 'cl:go)
1035;; GO(LABEL:id) noeval, nospread
1036;; GO alters the normal flow of control within a PROG function. The
1037;; next statement of a PROG function to be evaluated is immediately
1038;; preceded by LABEL. A GO may only appear in the following situations:
1039;; 1. At the top level of a PROG referencing a label which also
1040;;    appears at the top level of the same PROG.
1041;; 2. As the consequent of a COND item of a COND appearing on the
1042;;    top level of a PROG.
1043;; 3. As the consequent of a COND item which appears as the
1044;;    consequent of a COND item to any level.
1045;; 4. As the last statement of a PROGN which appears at the top
1046;;    level of a PROG or in a PROGN appearing in the consequent of a
1047;;    COND to any level subject to the restrictions of 2 and 3.
1048;; 5. As the last statement of a PROGN within a PROGN or as the
1049;;    consequent of a COND in a PROGN to any level subject to the
1050;;    restrictions of 2, 3 and 4.
1051;; If LABEL does not appear at the top level of the PROG in which
1052;; the GO appears, an error occurs:
1053;; ***** LABEL is not a known label
1054;; If the GO has been placed in a position not defined by rules 1-5,
1055;; another error is detected:
1056;; ***** Illegal use of GO to LABEL
1057
1058(import 'cl:prog)
1059;; PROG(VARS:id-list, [PROGRAM:{id, any}]):any noeval, nospread
1060;; VARS is a list of ids which are considered fluid when the PROG is
1061;; interpreted and local when compiled. The PROGs variables are
1062;; allocated space when the PROG form is invoked and are deallocated
1063;; when the PROG is exited. PROG variables are initialized to
1064;; NIL. The PROGRAM is a set of expressions to be evaluated in order
1065;; of their appearance in the PROG function. Identifiers appearing
1066;; in the top level of the PROGRAM are labels which can be
1067;; referenced by GO. The value returned by the PROG function is
1068;; determined by a RETURN function or NIL if the PROG "falls
1069;; through".
1070
1071(import 'cl:progn)
1072;; PROGN([U:any]):any noeval, nospread
1073;; U is a set of expressions which are executed sequentially. The
1074;; value returned is the value of the last expression.
1075
1076(import 'cl:prog1)                      ; PSL
1077;; (prog1 [U:form]): any macro
1078;; Prog1 evaluates its arguments in order, like progn, but returns the
1079;; value of the first.
1080
1081(import 'cl:prog2)
1082;; PROG2(A:any, B:any)any eval, spread
1083;; Returns the value of B.
1084;; EXPR PROCEDURE PROG2(A, B);
1085;;    B;
1086
1087(import 'cl:return)
1088;; RETURN(U:any) eval, spread
1089;; Within a PROG, RETURN terminates the evaluation of a PROG
1090;; and returns U as the value of the PROG. The restrictions on the
1091;; placement of RETURN are exactly those of GO. Improper placement
1092;; of RETURN results in the error:
1093;; ***** Illegal use of RETURN
1094
1095
1096;;; Error Handling
1097;;; ==============
1098
1099;; THIS CODE COULD BE IMPROVED!
1100
1101(defun %princ-to-string (u)
1102  ;; Used only in error and princ (which is not used in REDUCE).
1103  "As cl:princ-to-string but invert case of a symbol."
1104  (the simple-string
1105       (if (symbolp u) (%string-invert-case (cl:princ-to-string u))
1106           (cl:princ-to-string u))))
1107
1108(defun error (number message)
1109  "ERROR(NUMBER:integer, MESSAGE:any) eval, spread
1110NUMBER and MESSAGE are passed back to a surrounding ERRORSET (the
1111Standard LISP reader has an ERRORSET). MESSAGE is placed in the
1112global variable EMSG!* and the error number becomes the value of
1113the surrounding ERRORSET. FLUID variables and local bindings are
1114unbound to return to the environment of the ERRORSET. Global
1115variables are not affected by the process."
1116  (if (consp message)
1117      (setq message
1118            (let ((*print-case* :downcase))
1119              (cl:apply #'concatenate 'string
1120                        (cons (%princ-to-string (car message))
1121                              (loop
1122                                 for x in (cdr message)
1123                                 collect " "
1124                                 collect (%princ-to-string x)))))))
1125  (setf emsg* message)
1126  ;; (cl:error "***** SL error ~a: ~a" number message)
1127  ;; Do not include number in the output:
1128  (cl:error "***** ~*~a" number message))
1129
1130(defun error1 ()
1131  "This is the simplest error return, without a message printed.
1132It can be defined as ERROR(99,NIL) if necessary.
1133In PSL it is throw('!$error!$,99)."
1134  (cl:error "***** SL no-message error"))
1135
1136(defvar *debug nil
1137  "If non-nil then errorset does not catch errors,
1138so they fall through to the debugger.")
1139
1140;; See also invoke-debugger in the CLHS.
1141
1142(defun errorset (u msgp tr)
1143  "ERRORSET(U:any, MSGP:boolean, TR:boolean):any eval, spread
1144If an error occurs during the evaluation of U, the value of
1145NUMBER from the ERROR call is returned as the value of
1146ERRORSET. In addition, if the value of MSGP is non-NIL, the
1147MESSAGE from the ERROR call is displayed upon both the standard
1148output device and the currently selected output device unless the
1149standard output device is not open. The message appears prefixed
1150with 5 asterisks. The MESSAGE list is displayed without top level
1151parentheses. The MESSAGE from the ERROR call will be available in
1152the global variable EMSG!*. The exact format of error messages
1153generated by Standard LISP functions described in this document
1154are not fixed and should not be relied upon to be in any
1155particular form. Likewise, error numbers generated by Standard
1156LISP functions are implementation dependent.
1157If no error occurs during the evaluation of U, the value of
1158  (LIST (EVAL U)) is returned.
1159If an error has been signaled and the value of TR is non-NIL a
1160trace-back sequence will be initiated on the selected output
1161device. The traceback will display information such as unbindings
1162of FLUID variables, argument lists and so on in an implementation
1163dependent format."
1164  (if (or *debug tr)
1165      ;; Enter the debugger if an error arises.
1166      ;; Probably not the optimal way to generate a traceback!
1167      (list (eval u))
1168      ;; Handle any error that arises.
1169      (handler-case (list (eval u))     ; protected form
1170        (simple-error
1171            (err)
1172          (let ((fmt (simple-condition-format-control err))
1173                (args (simple-condition-format-arguments err)))
1174            (when (and msgp (cdr args))
1175              (fresh-line)
1176              (cl:apply #'format t fmt args)
1177              (cl:terpri))
1178            (car args)))
1179        (cl:error
1180            (err)
1181          (if msgp (format t "~&***** CL error: ~a~%" err))
1182          ;; This doesn't really work because it breaks in the context
1183          ;; of the errorset rather than the error!
1184          ;; It also breaks building bootstrap REDUCE on SBCL!
1185          ;; (break "errorset(~a)" u)
1186          999))))
1187
1188
1189;;; Vectors
1190;;; =======
1191
1192(defun getv (v index)
1193  "GETV(V:vector, INDEX:integer):any eval, spread
1194Returns the value stored at position INDEX of the vector V. The
1195type mismatch error may occur. An error occurs if the INDEX does
1196not lie within 0...UPBV(V) inclusive:
1197***** INDEX subscript is out of range"
1198  (declare (simple-vector v) (fixnum index))
1199  (aref v index))
1200
1201(defalias 'igetv 'getv)
1202
1203(defun mkvect (uplim)                   ; PSL
1204  "(mkvect UPLIM:integer): vector expr
1205Defines and allocates space for a vector with UPLIM+1 elements accessed
1206as 0 ... UPLIM. Each element is initialized to nil. If UPLIM is -1, an
1207empty vector is returned. An error occurs if UPLIM is less than -1 or if the
1208amount of available memory is insufficient for a vector of this size:
1209***** A vector of size UPLIM cannot be allocated"
1210  (declare (fixnum uplim))
1211  (the simple-vector (make-array (1+ uplim) :initial-element nil)))
1212
1213(defun putv (v index value)
1214  "PUTV(V:vector, INDEX:integer, VALUE:any):any eval, spread
1215Stores VALUE into the vector V at position INDEX. VALUE is
1216returned. The type mismatch error may occur. If INDEX does not
1217lie in 0...UPBV(V) an error occurs:
1218***** INDEX subscript is out of range"
1219  (declare (simple-vector v) (fixnum index))
1220  (setf (aref v index) value))
1221
1222(defalias 'iputv 'putv)
1223
1224(defun upbv (u)
1225  "UPBV(U:any):NIL,integer eval, spread
1226Returns the upper limit of U if U is a vector, or NIL if it is not."
1227  (the (or null fixnum)
1228       (and (vectorp u) (1- (cl:length u)))))
1229
1230(defun getv8 (v index)                  ; CSL
1231  (declare (type (simple-array (signed-byte 8) (*)) v) (fixnum index))
1232  (the (signed-byte 8) (aref v index)))
1233
1234(defun mkvect8 (uplim)                  ; CSL
1235  "Make a vector of 8-bit signed integers, cf. mkvect."
1236  (declare (fixnum uplim))
1237  (the (simple-array (signed-byte 8) (*))
1238       (make-array (1+ uplim) :element-type '(signed-byte 8) :initial-element 0)))
1239
1240(defun putv8 (v index value)            ; CSL
1241  (declare (type (simple-array (signed-byte 8) (*)) v)
1242           (fixnum index)
1243           (type (signed-byte 8) value))
1244  (the (signed-byte 8) (setf (aref v index) value)))
1245
1246(defun getv16 (v index)           ; CSL
1247  (declare (type (simple-array (signed-byte 16) (*)) v) (fixnum index))
1248  (the (signed-byte 16) (aref v index)))
1249
1250(defun mkvect16 (uplim)                 ; CSL
1251  "Make a vector of 16-bit signed integers, cf. mkvect."
1252  (declare (fixnum uplim))
1253  (the (simple-array (signed-byte 16) (*))
1254       (make-array (1+ uplim) :element-type '(signed-byte 16) :initial-element 0)))
1255
1256(defun putv16 (v index value)           ; CSL
1257  (declare (type (simple-array (signed-byte 16) (*)) v)
1258           (fixnum index)
1259           (type (signed-byte 16) value))
1260  (the (signed-byte 16) (setf (aref v index) value)))
1261
1262
1263;;; Boolean Functions and Conditionals
1264;;; ==================================
1265
1266(import 'cl:and)
1267;; AND([U:any]):extra-boolean noeval, nospread
1268;; AND evaluates each U until a value of NIL is found or the end of the
1269;; list is encountered. If a non-NIL value is the last value it is returned,
1270;; or NIL is returned.
1271;; FEXPR PROCEDURE AND(U);
1272;; BEGIN
1273;;    IF NULL U THEN RETURN NIL;
1274;; LOOP: IF NULL CDR U THEN RETURN EVAL CAR U
1275;;       ELSE IF NULL EVAL CAR U THEN RETURN NIL;
1276;;    U := CDR U;
1277;;    GO LOOP
1278;; END;
1279
1280(import 'cl:cond)
1281;; COND([U:cond-form]):any noeval, nospread
1282;; The antecedents of all U's are evaluated in order of their
1283;; appearance until a non-NIL value is encountered. The consequent
1284;; of the selected U is evaluated and becomes the value of the
1285;; COND. The consequent may also contain the special functions GO
1286;; and RETURN subject to the restraints given for these functions in
1287;; \"Program Feature Functions\", section 3.7 on page 22. In these
1288;; cases COND does not have a defined value, but rather an
1289;; effect. If no antecedent is non-NIL the value of COND is NIL. An
1290;; error is detected if a U is improperly formed:
1291;; ***** Improper cond-form as argument of COND
1292
1293(import 'cl:not)
1294;; NOT(U:any):boolean eval, spread
1295;; If U is NIL, return T else return NIL (same as function NULL).
1296;; EXPR PROCEDURE NOT(U);
1297;;    U EQ NIL;
1298
1299(import 'cl:or)
1300;; OR([U:any]):extra-boolean noeval, nospread
1301;; U is any number of expressions which are evaluated in order of their
1302;; appearance. When one is found to be non-NIL it is returned as the
1303;; value of OR. If all are NIL, NIL is returned.
1304;; FEXPR PROCEDURE OR(U);
1305;; BEGIN SCALAR X;
1306;; LOOP: IF NULL U THEN RETURN NIL
1307;;        ELSE IF (X := EVAL CAR U) THEN RETURN X;
1308;;    U := CDR U;
1309;;    GO LOOP
1310;; END;
1311
1312
1313;;; Arithmetic Functions
1314;;; ====================
1315
1316;; All floats should be double precision.
1317
1318(import 'cl:abs)
1319;; ABS(U:number):number eval, spread
1320;; Returns the absolute value of its argument.
1321;; EXPR PROCEDURE ABS(U);
1322;;    IF LESSP(U, 0) THEN MINUS(U) ELSE U;
1323
1324(defalias 'add1 'cl:1+
1325  "ADD1(U:number):number eval, spread
1326Returns the value of U plus 1 of the same type as U (fixed or floating).
1327EXPR PROCEDURE ADD1(U);
1328   PLUS2(U, 1);")
1329
1330(defalias 'difference 'cl:-
1331  "DIFFERENCE(U:number, V:number):number eval, spread
1332The value U - V is returned.")
1333
1334;; The Euclidean division property of the integers state that for u, v
1335;; in Z, v /= 0, there exist a unique quotient q and remainder r such
1336;; that u = qv + r (0 <= |r| < |v|).
1337
1338;; In PSL:
1339;; divide( 5,  3) = ( 1 .  2)
1340;; divide( 5, -3) = (-1 .  2)
1341;; divide(-5,  3) = (-1 . -2)
1342;; divide(-5, -3) = ( 1 . -2)
1343
1344;; If u and v have the same sign then the quotient q is positive; if u
1345;; and v have opposite signs then q is negative.  The remainder r has
1346;; the same sign as u.
1347
1348;; The following definition agrees with that above:
1349
1350(defun divide (u v)
1351  "DIVIDE(U:number, V:number):dotted-pair eval, spread
1352The dotted-pair (quotient . remainder) is returned. The quotient
1353part is computed the same as by QUOTIENT and the remainder
1354the same as by REMAINDER. An error occurs if division by zero is
1355attempted:
1356***** Attempt to divide by 0 in DIVIDE
1357EXPR PROCEDURE DIVIDE(U, V);
1358   (QUOTIENT(U, V) . REMAINDER(U, V));"
1359  (declare (type number u v))
1360  (the cons (multiple-value-call #'cons (truncate u v))))
1361
1362(defun expt (u v)
1363  ;; Defined explicitly so that it can be redefined in arith/math
1364  "EXPT(U:number, V:integer):number eval, spread
1365Returns U raised to the V power. A floating point U to an integer
1366power V does not have V changed to a floating number before
1367exponentiation."
1368  (declare (type number u) (integer v))
1369  (the number (cl:expt u v)))
1370
1371(defun fix (u)
1372  "FIX(U:number):integer eval, spread
1373Returns an integer which corresponds to the truncated value of U.
1374The result of conversion must retain all significant portions of U. If
1375U is an integer it is returned unchanged."
1376  (declare (type number u))
1377  (the integer (values (truncate u))))
1378
1379(defun float (u)
1380  "FLOAT(U:number):floating eval, spread
1381The floating point number corresponding to the value of the
1382argument U is returned.  Some of the least significant digits of
1383an integer may be lost do to the implementation of floating point
1384numbers.  FLOAT of a floating point number returns the number
1385unchanged.  If U is too large to represent in floating point an
1386error occurs:
1387***** Argument to FLOAT is too large"
1388  ;; Floats must be double precision:
1389  (declare (type number u))
1390  (the double-float (cl:float u 1d0)))
1391
1392(defalias 'greaterp 'cl:>
1393  "GREATERP(U:number, V:number):boolean eval, spread
1394Returns T if U is strictly greater than V, otherwise returns NIL.")
1395
1396(defalias 'lessp 'cl:<
1397  "LESSP(U:number, V:number):boolean eval, spread
1398Returns T if U is strictly less than V, otherwise returns NIL.")
1399
1400;; The definitions in REDUCE don't work correctly with mixed integer
1401;; and float arguments, so...
1402(defalias 'geq 'cl:>=)
1403(defalias 'leq 'cl:<=)
1404
1405(import 'cl:max)
1406;; MAX([U:number]):number noeval, nospread, or macro
1407;; Returns the largest of the values in U. If two or more values are the
1408;; same the first is returned.
1409;; MACRO PROCEDURE MAX(U);
1410;;    EXPAND(CDR U, 'MAX2);
1411
1412(defalias 'max2 'cl:max
1413  "MAX2(U:number, V:number):number eval, spread
1414Returns the larger of U and V. If U and V are the same value U is
1415returned (U and V might be of different types).
1416EXPR PROCEDURE MAX2(U, V);
1417   IF LESSP(U, V) THEN V ELSE U;")
1418
1419(import 'cl:min)
1420;; MIN([U:number]):number noeval, nospread, or macro
1421;; Returns the smallest of the values in U. If two or more values are the
1422;; same the first of these is returned.
1423;; MACRO PROCEDURE MIN(U);
1424;;    EXPAND(CDR U, 'MIN2);
1425
1426(defalias 'min2 'cl:min
1427  "MIN2(U:number, V:number):number eval, spread
1428Returns the smaller of its arguments. If U and V are the same value,
1429U is returned (U and V might be of different types).
1430EXPR PROCEDURE MIN2(U, V);
1431   IF GREATERP(U, V) THEN V ELSE U;")
1432
1433(defalias 'minus 'cl:-
1434  "MINUS(U:number):number eval, spread
1435Returns -U.
1436EXPR PROCEDURE MINUS(U);
1437   DIFFERENCE(0, U);")
1438
1439(defalias 'plus 'cl:+
1440  "PLUS([U:number]):number noeval, nospread, or macro
1441Forms the sum of all its arguments.
1442MACRO PROCEDURE PLUS(U);
1443   EXPAND(CDR U, 'PLUS2);")
1444
1445(defalias 'plus2 'cl:+
1446  "PLUS2(U:number, V:number):number eval, spread
1447Returns the sum of U and V.")
1448
1449(defun quotient (u v)
1450  "QUOTIENT(U:number, V:number):number eval, spread
1451The quotient of U divided by V is returned. Division of two positive
1452or two negative integers is conventional. When both U and V are
1453integers and exactly one of them is negative the value returned is
1454the negative truncation of the absolute value of U divided by the
1455absolute value of V. An error occurs if division by zero is attempted:
1456***** Attempt to divide by 0 in QUOTIENT"
1457  ;; Can probably implement this better using generic functions!
1458  (declare (type number u v))
1459  (the number
1460       (if (or (floatp u) (floatp v))
1461           (/ u v)
1462           (values (truncate u v)))))
1463
1464(defalias 'remainder 'cl:rem
1465  "REMAINDER(U:number, V:number):number eval, spread
1466If both U and V are integers the result is the integer remainder of
1467U divided by V. If either parameter is floating point, the result is
1468the difference between U and V*(U/V) all in floating point. If either
1469number is negative the remainder is negative. If both are positive or
1470both are negative the remainder is positive. An error occurs if V is
1471zero:
1472***** Attempt to divide by 0 in REMAINDER
1473EXPR PROCEDURE REMAINDER(U, V);
1474   DIFFERENCE(U, TIMES2(QUOTIENT(U, V), V));")
1475
1476(defalias 'sub1 'cl:1-
1477  "SUB1(U:number):number eval, spread
1478Returns the value of U less 1. If U is a FLOAT type number, the
1479value returned is U less 1.0.
1480EXPR PROCEDURE SUB1(U);
1481   DIFFERENCE(U, 1);")
1482
1483(defalias 'times 'cl:*
1484  "TIMES([U:number]):number noeval, nospread, or macro
1485Returns the product of all its arguments.
1486MACRO PROCEDURE TIMES(U);
1487   EXPAND(CDR U, 'TIMES2);")
1488
1489(defalias 'times2 'cl:*
1490  "TIMES2(U:number, V:number):number eval, spread
1491Returns the product of U and V.")
1492
1493;; Small integer (fixnum) arithmetic operators defined in
1494;; alg/farith.red:
1495
1496(defun iplus2 (u v)
1497  (declare (fixnum u v))
1498  (the fixnum (+ u v)))
1499
1500(defun itimes2 (u v)
1501  (declare (fixnum u v))
1502  (the fixnum (* u v)))
1503
1504(defun isub1 (u)
1505  (declare (fixnum u))
1506  (the fixnum (1- u)))
1507
1508(defun iadd1 (u)
1509  (declare (fixnum u))
1510  (the fixnum (1+ u)))
1511
1512(defun iminus (u)
1513  (declare (fixnum u))
1514  (the fixnum (- u)))
1515
1516(defun idifference (u v)
1517  (declare (fixnum u v))
1518  (the fixnum (- u v)))
1519
1520(defun iquotient (u v)
1521  (declare (fixnum u v))
1522  (the fixnum (values (truncate u v))))
1523
1524(defun iremainder (u v)
1525  (declare (fixnum u v))
1526  (the fixnum (rem u v)))
1527
1528(defun igreaterp (u v)
1529  (declare (fixnum u v))
1530  (> u v))
1531
1532(defun ilessp (u v)
1533  (declare (fixnum u v))
1534  (< u v))
1535
1536(defun iminusp (u)
1537  (declare (fixnum u))
1538  (cl:minusp u))
1539
1540;; (defun iequal (u v)
1541;;   (declare (fixnum u v))
1542;;   (eql u v))
1543
1544;; iequal is defined in CSL (but not PSL).  It is called with a list
1545;; as its first argument in sqrt2top in int/df2q.red, so it does not
1546;; always have integer arguments!  But I assume it will not be called
1547;; with float arguments.
1548
1549(defalias 'iequal 'eql)
1550
1551;; Small integer (fixnum) arithmetic operators required but not defined:
1552
1553(defun itimes (u v)       ; used as a binary operator in dipoly/torder
1554  (declare (fixnum u v))
1555  (the fixnum (* u v)))
1556
1557(defun izerop (u)                       ; used in plot/plotexp3
1558  (declare (fixnum u))
1559  (cl:zerop u))
1560
1561;; Fast built-in floating point functions:
1562
1563;; (defalias 'ACOS 'acos)
1564;; (defalias 'ASIN 'asin)
1565;; (defalias 'ATAN 'atan)
1566;; (defalias 'ATAN2 'atan)
1567;; (defalias 'COS 'cos)
1568;; (defalias 'EXP 'exp)
1569;; (defalias 'LN 'log)
1570;; (defalias 'LOG 'log)
1571;; (defalias 'LOGB 'log)
1572;; (defsubst LOG10 (x) (log x 10))
1573;; (defalias 'SIN 'sin)
1574;; (defalias 'SQRT 'sqrt)
1575;; (defalias 'TAN 'tan)
1576;; ;; The following will fail for floats with very large magnitudes since
1577;; ;; they return fixnums rather than big integers.  If that is a problem
1578;; ;; then remove these aliases and in particular remove the lose flags
1579;; ;; in "eslrend.red".
1580;; (defalias 'CEILING 'ceiling)
1581;; (defalias 'FLOOR 'floor)
1582;; (defalias 'ROUND 'round)
1583
1584;; The above cause errors in the arith test file when trig results or
1585;; arguments are complex so all commented out for now.
1586
1587
1588;;; Map Composite Functions
1589;;; =======================
1590
1591(defun map (x fn)
1592  "MAP(X:list, FN:function):any eval, spread
1593Applies FN to successive CDR segments of X and returns NIL.
1594EXPR PROCEDURE MAP(X, FN);
1595   WHILE X DO << FN X; X := CDR X >>;"
1596  (declare (list x) (type function fn))
1597  (cl:mapl fn x)
1598  nil)
1599
1600(defun mapc (x fn)
1601  "MAPC(X:list, FN:function):any eval, spread
1602Applies FN to successive CAR segments of X and returns NIL.
1603EXPR PROCEDURE MAPC(X, FN);
1604   WHILE X DO << FN CAR X; X := CDR X >>;"
1605  (declare (list x) (type function fn))
1606  (cl:mapc fn x)
1607  nil)
1608
1609(defun mapcan (x fn)
1610  "MAPCAN(X:list, FN:function):any eval, spread
1611Returns a concatenated list of FN applied to successive CAR elements of X.
1612EXPR PROCEDURE MAPCAN(X, FN);
1613   IF NULL X THEN NIL
1614      ELSE NCONC(FN CAR X, MAPCAN(CDR X, FN));"
1615  (declare (list x) (type function fn))
1616  (the list (cl:mapcan fn x)))
1617
1618(defun mapcar (x fn)
1619  "MAPCAR(X:list, FN:function):any eval, spread
1620Returns a constructed list of FN applied to each CAR of list X.
1621EXPR PROCEDURE MAPCAR(X, FN);
1622   IF NULL X THEN NIL
1623      ELSE FN CAR X . MAPCAR(CDR X, FN);"
1624  (declare (list x) (type function fn))
1625  (the list (cl:mapcar fn x)))
1626
1627(defun mapcon (x fn)
1628  "MAPCON(X:list, FN:function):any eval, spread
1629Returns a concatenated list of FN applied to successive CDR segments of X.
1630EXPR PROCEDURE MAPCON(X, FN);
1631   IF NULL X THEN NIL
1632      ELSE NCONC(FN X, MAPCON(CDR X, FN));"
1633  (declare (list x) (type function fn))
1634  (the list (cl:mapcon fn x)))
1635
1636(defun maplist (x fn)
1637  "MAPLIST(X:list, FN:function):any eval, spread
1638Returns a constructed list of FN applied to successive CDR segments of X.
1639EXPR PROCEDURE MAPLIST(X, FN);
1640   IF NULL X THEN NIL
1641      ELSE FN X . MAPLIST(CDR X, FN);"
1642  (declare (list x) (type function fn))
1643  (the list (cl:maplist fn x)))
1644
1645
1646;;; Composite Functions
1647;;; ===================
1648
1649;; Common Lisp uses the test function eql by default
1650;; (see the CLHS 17.2.1 Satisfying a Two-Argument Test,
1651;; e.g. http://www.lispworks.com/documentation/HyperSpec/Body/17_ba.htm),
1652;; whereas Standard Lisp uses the test function equal, which must
1653;; therefore always be supplied to CL functions as the :test keyword
1654;; argument.
1655
1656(defun append (u v)
1657  "(append U:any V:any):any expr
1658Returns a constructed list in which the last element of U is followed by the
1659first element of V. The list U is copied, but V is not."
1660  ;; Some REDUCE code assumes the PSL definition, which allows U to
1661  ;; have any type:
1662  (declare (t u v))
1663  (the t (if (consp u) (cl:append u v) v)))
1664
1665(defun assoc (u v)                      ; PSL definition
1666  "(assoc U:any V:any): pair, nil expr
1667If U occurs as the car portion of an element of the a-list V, the pair in which
1668U occurred is returned, otherwise nil is returned. The function equal is used
1669to test for equality.
1670\(de assoc (u v)
1671  (cond ((not (pairp v)) nil)
1672        ((and (pairp (car v)) (equal u (caar v))) (car v))
1673        (t (assoc u (cdr v)))))"
1674  (declare (t u v))
1675  (the list
1676       (and (consp v)
1677            (loop for x in v do
1678                 (if (and (consp x) (equal u (car x)))
1679                     (return x))))))
1680
1681(defun deflist (u ind)
1682  "DEFLIST(U:dlist, IND:id):list eval, spread
1683A \"dlist\" is a list in which each element is a two element list: (ID:id
1684PROP:any). Each ID in U has the indicator IND with property
1685PROP placed on its property list by the PUT function. The value
1686of DEFLIST is a list of the first elements of each two element list.
1687Like PUT, DEFLIST may not be used to define functions.
1688EXPR PROCEDURE DEFLIST(U, IND);
1689   IF NULL U THEN NIL
1690      ELSE << PUT(CAAR U, IND, CADAR U);
1691              CAAR U >> . DEFLIST(CDR U, IND);"
1692  (declare (list u) (symbol ind))
1693  (the list
1694       (cl:mapcar #'(lambda (x)
1695                      (if *defn (%save-plist (car x)))
1696                      (put (car x) ind (cadr x))
1697                      (car x))
1698                  u)))
1699
1700(defun delete (u v)
1701  "DELETE(U:any, V:list):list eval, spread
1702Returns V with the first top level occurrence of U removed from it.
1703EXPR PROCEDURE DELETE(U, V);
1704   IF NULL V THEN NIL
1705      ELSE IF CAR V = U THEN CDR V
1706      ELSE CAR V . DELETE(U, CDR V);"
1707  (declare (list v))
1708  (the list (cl:remove u v :test #'equal :count 1)))
1709
1710(defun digit (u)
1711  "DIGIT(U:any):boolean eval, spread
1712Returns T if U is a digit, otherwise NIL.
1713EXPR PROCEDURE DIGIT(U);
1714   IF MEMQ(U, '(!0 !1 !2 !3 !4 !5 !6 !7 !8 !9))
1715      THEN T ELSE NIL;"
1716  (cl:member u '(\0 \1 \2 \3 \4 \5 \6 \7 \8 \9) :test #'eq))
1717
1718(defun length (x)
1719  "LENGTH(X:any):integer eval, spread
1720The top level length of the list X is returned.
1721EXPR PROCEDURE LENGTH(X);
1722   IF ATOM X THEN 0
1723      ELSE PLUS(1, LENGTH CDR X);"
1724  ;; The above recursive definition uses too much stack.
1725  ;; The CL length function cannot be used because it does not accept
1726  ;; atoms or dotted pairs!
1727  ;; This iterative implementation is based on the description of
1728  ;; list-length in the CLHS:
1729  (the (integer 0)
1730       (do ((n 0 (1+ n))                ; counter
1731            (p x (cdr p)))              ; pointer
1732           ;; When pointer hits an atom, return the count:
1733           ((atom p) n))))
1734
1735(defun liter (u)
1736  "LITER(U:any):boolean eval, spread
1737Returns T if U is a character of the alphabet, NIL otherwise.
1738EXPR PROCEDURE LITER(U);
1739   IF MEMQ(U, '(!A !B !C !D !E !F !G !H !I !J !K !L !M
1740                !N !O !P !Q !R !S !T !U !V !W !X !Y !Z
1741                !a !b !c !d !e !f !g !h !i !j !k !l !m
1742                !n !o !p !q !r !s !t !u !v !w !x !y !z))
1743      THEN T ELSE NIL;"
1744  (cl:member u '(\A \B \C \D \E \F \G \H \I \J \K \L \M
1745                 \N \O \P \Q \R \S \T \U \V \W \X \Y \Z
1746                 \a \b \c \d \e \f \g \h \i \j \k \l \m
1747                 \n \o \p \q \r \s \t \u \v \w \x \y \z) :test #'eq))
1748
1749(defun member (a l)
1750  "(member A:any L:any): extra-boolean expr
1751Returns nil if A is not equal to some top level element of the list L;
1752otherwise it returns the remainder of L whose first element is equal
1753to A."
1754  ;; This is the PSl definition, which accepts *anything* as its second argument!
1755  ;; REDUCE (crack in particular) requires this flexibility.
1756  ;; The second argument to Common Lisp member must be a proper list.
1757  ;; (cond ((atom l) nil)
1758  ;;       ((equal a (car l)) l)
1759  ;;       (t (member a (cdr l))))
1760  (the list
1761       (loop for tail on l do
1762            (if (atom tail) (return-from member nil))
1763            (if (equal a (car tail)) (return-from member tail)))))
1764
1765(defun memq (a l)
1766  "(memq A:any L:any): extra-boolean expr
1767Returns nil if A is not eq to some top level element of the list L;
1768otherwise it returns the remainder of L whose first element is equal
1769to A."
1770  ;; This is the PSl definition, which accepts *anything* as its second argument!
1771  ;; REDUCE probably requires this flexibility.
1772  ;; The second argument to Common Lisp member must be a proper list.
1773  ;; (cond ((atom l) nil)
1774  ;;       ((eq a (car l)) l)
1775  ;;       (t (memq a (cdr l))))
1776  (the list
1777       (loop for tail on l do
1778            (if (atom tail) (return-from memq nil))
1779            (if (eq a (car tail)) (return-from memq tail)))))
1780
1781(import 'cl:nconc)
1782;; NCONC(U:list, V:list):list eval, spread
1783;; Concatenates V to U without copying U. The last CDR of U is
1784;; modified to point to V.
1785;; EXPR PROCEDURE NCONC(U, V);
1786;; BEGIN SCALAR W;
1787;;    IF NULL U THEN RETURN V;
1788;;    W := U;
1789;;    WHILE CDR W DO W := CDR W;
1790;;    RPLACD(W, V);
1791;;    RETURN U
1792;; END;
1793
1794(defun pair (u v)
1795  ;; Could implement as pairlis, but pairlis doesn't guarantee the
1796  ;; ordering in the result list.
1797  "PAIR(U:list, V:list):alist eval, spread
1798U and V are lists which must have an identical number of elements.
1799If not, an error occurs (the 000 used in the ERROR call is arbitrary
1800and need not be adhered to). Returned is a list where each element
1801is a dotted-pair, the CAR of the pair being from U, and the CDR
1802the corresponding element from V.
1803EXPR PROCEDURE PAIR(U, V);
1804   IF AND(U, V) THEN (CAR U . CAR V) . PAIR(CDR U, CDR V)
1805      ELSE IF OR(U, V) THEN ERROR(000,
1806         \"Different length lists in PAIR\")
1807      ELSE NIL;"
1808  (declare (list u v))
1809  (the list
1810       (if (/= (cl:length u) (cl:length v))
1811           (cl:error "000 Different length lists in PAIR")
1812           (cl:map 'list #'cons u v))))
1813
1814(import 'cl:reverse)
1815;; REVERSE(U:list):list eval, spread
1816;; Returns a copy of the top level of U in reverse order.
1817;; EXPR PROCEDURE REVERSE(U);
1818;; BEGIN SCALAR W;
1819;;    WHILE U DO << W := CAR U . W;
1820;;                  U := CDR U >>;
1821;;    RETURN W
1822;; END;
1823
1824(defalias 'reversip 'cl:nreverse)           ; PSL function
1825
1826(defun sassoc (u v fn)
1827  "SASSOC(U:any, V:alist, FN:function):any eval, spread
1828Searches the alist V for an occurrence of U. If U is not in the alist
1829the evaluation of function FN is returned.
1830EXPR PROCEDURE SASSOC(U, V, FN);
1831   IF NULL V THEN FN()
1832      ELSE IF U = CAAR V THEN CAR V
1833      ELSE SASSOC(U, CDR V, FN);"
1834  (declare (list v) (type (function ()) fn))
1835  (or (cl:assoc u v :test #'equal) (funcall fn)))
1836
1837(defun sublis (x y)
1838  "SUBLIS(X:alist, Y:any):any eval, spread
1839The value returned is the result of substituting the CDR of each
1840element of the alist X for every occurrence of the CAR part of that
1841element in Y.
1842EXPR PROCEDURE SUBLIS(X, Y);
1843   IF NULL X THEN Y
1844      ELSE BEGIN SCALAR U;
1845                 U := ASSOC(Y, X);
1846                 RETURN IF U THEN CDR U
1847                        ELSE IF ATOM Y THEN Y
1848                        ELSE SUBLIS(X, CAR Y) .
1849                             SUBLIS(X, CDR Y)
1850                 END;"
1851  (declare (list x))
1852  (cl:sublis x y :test #'equal))
1853
1854(defun subst (u v w)
1855  "SUBST(U:any, V:any, W:any):any eval, spread
1856The value returned is the result of substituting U for all occurrences
1857of V in W.
1858EXPR PROCEDURE SUBST(U, V, W);
1859   IF NULL W THEN NIL
1860      ELSE IF V = W THEN U
1861      ELSE IF ATOM W THEN W
1862      ELSE SUBST(U, V, CAR W) . SUBST(U, V, CDR W);"
1863  (cl:subst u v w :test #'equal))
1864
1865;; This function is used in several places in REDUCE, but I can't find
1866;; a reference to it anywhere!  The documentation string below is
1867;; based on that in Emacs Lisp:
1868(defun rassoc (key list)
1869  "Return non-nil if KEY is equal to the cdr of an element of LIST.
1870The value is actually the first element of LIST whose cdr equals KEY."
1871  (declare (list list))
1872  (cl:rassoc key list :test #'equal))
1873
1874
1875;;; The Interpreter
1876;;; ===============
1877
1878;; In "alg/reval.red" is the code
1879;; deflist('( ... (!*sq (lambda (x) nil))),'rtypefn);
1880;; which leads in "alg/elem.red" to (apply (lambda (x) nil) (!*sq ...))
1881;; and this fails in Common Lisp because a lambda form is not a function!
1882;; It might be better to add the function call to the code in reval,
1883;; but try this for now...
1884
1885;; (defun %lam2fn (fn)
1886;;   "Make a lambda expression acceptable as a function by evaluating it."
1887;;   (declare ((or list function) fn))
1888;;   (the function (if (eqcar fn 'lambda) (eval fn) fn)))
1889
1890;; (defun apply (fn args)
1891;;   "Treat a lambda expression as an operator.
1892;; Otherwise revert to the Common Lisp apply."
1893;;   (cl:apply (%lam2fn fn) args))
1894
1895(defun apply (fn args)
1896  "Treat a lambda expression as an operator.
1897Otherwise revert to the Common Lisp apply."
1898  (declare (type function fn))
1899  (cl:apply (coerce fn 'cl:function) args))
1900
1901;; APPLY(FN:{id,function}, ARGS:any-list):any eval, spread
1902;; APPLY returns the value of FN with actual parameters ARGS. The
1903;; actual parameters in ARGS are already in the form required for
1904;; binding to the formal parameters of FN. Implementation specific
1905;; portions described in English are enclosed in boxes.
1906;; EXPR PROCEDURE APPLY(FN, ARGS);
1907;; BEGIN SCALAR DEFN;
1908;;    IF CODEP FN THEN RETURN
1909;;       | Spread the actual parameters in ARGS
1910;;    | following the conventions: for calling
1911;;    | functions, transfer to the entry point
1912;;    | of the function, and return the value
1913;;    | returned by the function.;
1914;;    IF IDP FN THEN RETURN
1915;;    IF NULL(DEFN := GETD FN) THEN
1916;;       ERROR(000, LIST(FN, \"is an undefined function\"))
1917;;    ELSE IF CAR DEFN EQ 'EXPR THEN
1918;;       APPLY(CDR DEFN, ARGS)
1919;;    ELSE ERROR(000,
1920;;       LIST(FN, \"cannot be evaluated by APPLY\"));
1921;;    IF OR(ATOM FN, NOT(CAR FN EQ 'LAMBDA)) THEN
1922;;       ERROR(000,
1923;;          LIST(FN, \"cannot be evaluated by APPLY\"));
1924;;    RETURN
1925;;       | Bind the actual parameters in ARGS to
1926;;       | the formal parameters of the lambda
1927;;       | expression. If the two lists are not
1928;;       | of equal length then ERROR(000, \"Number
1929;;       | of parameters do not match\"); The value
1930;;       | returned is EVAL CADDR FN.
1931;; END;
1932
1933(defun eval (u)
1934  "Treat (function foo) the same as the operator foo.
1935Otherwise revert to the Common Lisp eval."
1936  (if (and (consp u) (functionp (car u)))
1937      (cl:apply (car u) (evlis (cdr u)))
1938      (cl:eval u)))
1939
1940;; EVAL(U:any):any eval, spread
1941;; The value of the expression U is computed. Error numbers are
1942;; arbitrary. Portions of EVAL involving machine specific coding are
1943;; expressed in English enclosed in boxes.
1944;; EXPR PROCEDURE EVAL(U);
1945;; BEGIN SCALAR FN;
1946;;    IF CONSTANTP U THEN RETURN U;
1947;;    IF IDP U THEN RETURN
1948;;       | U is an id. Return the value most
1949;;       | currently bound to U or if there
1950;;       | is no such binding: ERROR(000,
1951;;       | LIST(\"Unbound:\", U));
1952;;    IF PAIRP CAR U THEN RETURN
1953;;    IF CAAR U EQ 'LAMBDA THEN APPLY(CAR U, EVLIS CDR U)
1954;;    ELSE ERROR(000, LIST(CAR U,
1955;;       \"improperly formed LAMBDA expression\"))
1956;;    ELSE IF CODEP CAR U THEN
1957;;       RETURN APPLY(CAR U, EVLIS CDR U);
1958;;    FN := GETD CAR U;
1959;;    IF NULL FN THEN
1960;;       ERROR(000, LIST(CAR U, \"is an undefined function\"))
1961;;    ELSE IF CAR FN EQ 'EXPR THEN
1962;;       RETURN APPLY(CDR FN, EVLIS CDR U)
1963;;    ELSE IF CAR FN EQ 'FEXPR THEN
1964;;       RETURN APPLY(CDR FN, LIST CDR U)
1965;;    ELSE IF CAR FN EQ 'MACRO THEN
1966;;       RETURN EVAL APPLY(CDR FN, LIST U)
1967;; END;
1968
1969(defun evlis (u)
1970  "EVLIS(U:any-list):any-list eval, spread
1971EVLIS returns a list of the evaluation of each element of U.
1972EXPR PROCEDURE EVLIS(U);
1973   IF NULL U THEN NIL
1974      ELSE EVAL CAR U . EVLIS CDR U;"
1975  (declare (list u))
1976  (the list (cl:mapcar #'eval u)))
1977
1978(defun expand (l fn)
1979  "EXPAND(L:list, FN:function):list eval, spread
1980FN is a defined function of two arguments to be used in the expansion
1981of a MACRO. EXPAND returns a list in the form:
1982   (FN L0 (FN L1 ... (FN Ln-1 Ln) ... ))
1983where n is the number of elements in L, Li is the ith element of L.
1984EXPR PROCEDURE EXPAND(L,FN);
1985   IF NULL CDR L THEN CAR L
1986      ELSE LIST(FN, CAR L, EXPAND(CDR L, FN));"
1987  (declare (list l) (type function fn))
1988  (if (null (cdr l))
1989      (car l)
1990    (list fn (car l) (expand (cdr l) fn))))
1991
1992(defmacro function (fn)
1993  "FUNCTION(FN:function):function noeval, nospread
1994The function FN is to be passed to another function. If FN is to have
1995side effects its free variables must be fluid or global. FUNCTION is
1996like QUOTE but its argument may be affected by compilation. We
1997do not consider FUNARGs in this report."
1998  ;; In Common Lisp, fn must be a *defined* function or a lambda
1999  ;; expression.  The symbol car satisfies fboundp and (lambda ...)
2000  ;; satisfies functionp, so
2001  ;; (if (or (eqcar fn 'lambda) (fboundp `,fn))
2002  ;; But with the test above, the eds package will not build because
2003  ;; (function list) evaluates to #<FUNCTION LIST>, which cannot be
2004  ;; serialized so faslout fails.  At least temporarily, this should
2005  ;; work:
2006  (if (eqcar fn 'lambda)
2007      `(cl:function ,fn)
2008      `(cl:quote ,fn)))
2009
2010(import 'cl:quote)
2011;; QUOTE(U:any):any noeval, nospread
2012;; Stops evaluation and returns U unevaluated.
2013;; FEXPR PROCEDURE QUOTE(U);
2014;;    CAR U;
2015
2016
2017;;; Input and Output
2018;;; ================
2019
2020;; An output filehandle is a (possibly dotted) list of the form
2021;; ('file output-stream) or ('pipe output-stream . process).
2022;; On CLISP, process is nil.
2023
2024;; An input filehandle is a pair of the form
2025;; (input-stream . echo-stream).
2026
2027;; Filehandles should probably be structures rather than lists!
2028
2029(defun close (filehandle)
2030  "CLOSE(FILEHANDLE:any):any eval, spread
2031Closes the file with the internal name FILEHANDLE writing any
2032necessary end of file marks and such. The value of FILEHANDLE
2033is that returned by the corresponding OPEN. The value returned is
2034the value of FILEHANDLE. An error occurs if the file can not be
2035closed.
2036***** FILEHANDLE could not be closed"
2037  ;; A null filehandle represents standard IO; ignore it.
2038  (declare (type filehandle filehandle))
2039  (the filehandle
2040       (if filehandle
2041           (prog1 filehandle
2042             (cond
2043               ((eq (car filehandle) 'file)
2044                ;; Output file stream ('file output-stream):
2045                (cl:close (cadr filehandle)))
2046               #+SBCL
2047               ((eq (car filehandle) 'pipe)
2048                ;; Output pipe stream ('pipe output-stream . process):
2049                (sb-ext:process-close (cddr filehandle)) ; closes output-stream
2050                (sb-ext:process-kill (cddr filehandle) 9)) ; 9 = SIGKILL
2051               #+CLISP
2052               ((eq (car filehandle) 'pipe)
2053                ;; Output pipe stream ('pipe output-stream):
2054                (cl:close (cadr filehandle))) ; closes output-stream
2055               (t
2056                ;; Input filehandle -- close echo stream then input stream:
2057                (cl:close (cdr filehandle))
2058                (cl:close (car filehandle))))))))
2059
2060(defun eject ()
2061  "EJECT():NIL eval, spread
2062Skip to the top of the next output page. Automatic EJECTs are
2063executed by the print functions when the length set by the PAGE-
2064LENGTH function is exceeded."
2065  nil)
2066
2067(defvar %linelength 80
2068  "Current Standard LISP line length accessed via function `LINELENGTH'.")
2069
2070(defun linelength (len)
2071  "LINELENGTH(LEN:{integer, NIL}):integer eval, spread
2072If LEN is an integer the maximum line length to be printed before
2073the print functions initiate an automatic TERPRI is set to the value
2074LEN. No initial Standard LISP line length is assumed. The previous
2075line length is returned except when LEN is NIL. This special case
2076returns the current line length and does not cause it to be reset. An
2077error occurs if the requested line length is too large for the currently
2078selected output file or LEN is negative or zero.
2079***** LEN is an invalid line length"
2080  (declare (type (or null fixnum) len))
2081  (the fixnum
2082       (if len
2083           (if (or (not (integerp len)) (<= len 0))
2084               (cl:error "~a is an invalid line length" len)
2085               (prog1 %linelength (setq %linelength len)))
2086           %linelength)))
2087
2088(defun lposn ()
2089  "LPOSN():integer eval, spread
2090Returns the number of lines printed on the current page. At the top
2091of a page, 0 is returned."
2092  0)
2093
2094(defun substitute-in-file-name (filename)
2095  "Return a copy of FILENAME with all environment variables expanded.
2096Replace every substring of the form `$name' terminated by a
2097non-alphanumeric character by its value.  Called by `open'."
2098  ;; A simplified version of the Elisp function
2099  ;; `substitute-in-file-name'.
2100  ;; Replace environment variables with their values:
2101  (declare (simple-string filename))
2102  (loop
2103     with beg and end = 0 and l
2104     while
2105       (and end (setq beg (position #\$ filename :start end)))
2106     do
2107       (push (subseq filename end beg) l)
2108       (setq end (position-if-not #'alphanumericp filename :start (1+ beg)))
2109       (push (getenv (subseq filename (1+ beg) end)) l)
2110     finally
2111       (if l (setq filename
2112                   (cl:apply #'concatenate 'string
2113                             (nreverse
2114                              (if end
2115                                  (push (subseq filename end) l)
2116                                  l))))))
2117  filename)
2118
2119(defun expand-file-name (filename)
2120  "Return a copy of FILENAME with a leading `.' replaced by the
2121current working directory and each leading `..' replaced by its
2122parent.  Called by `open' and `cd' on SBCL."
2123  ;; A simplified version of the Elisp function `expand-file-name'.
2124  ;; sb-ext:native-pathname seems necessary to preserve odd characters
2125  ;; such as ^ in a filename:
2126  (declare (type (or simple-string pathname) filename))
2127  #+SBCL (setq filename (sb-ext:native-pathname filename))
2128  (let ((d (copy-list (pathname-directory filename))))
2129    (when (eq (car d) :relative)
2130      ;; Replace a leading "." with the current working directory:
2131      (when (equal (cadr d) ".")
2132        (setf (cdr d) (cddr d))         ; remove "." component
2133        (setq filename (merge-pathnames
2134                        (make-pathname :directory d :defaults filename))))
2135      ;; Replace each leading ".." with the parent directory:
2136      (loop with cwd = (pathname-directory *default-pathname-defaults*)
2137         while (cl:member (cadr d) '(".." :up :back))
2138         do
2139           (setf (cdr d) (cddr d))      ; remove ".." component
2140           (setq cwd (butlast cwd))
2141         finally (setq filename (merge-pathnames
2142                                 (make-pathname :directory d :defaults filename)
2143                                 (make-pathname :directory cwd))))))
2144  filename)
2145
2146;; CLISP user variable CUSTOM:*DEVICE-PREFIX* controls translation
2147;; between Cygwin pathnames (e.g., #P"/cygdrive/c/gnu/clisp/") and
2148;; native Win32 pathnames (e.g., #P"C:\\gnu\\clisp\\").
2149
2150(defun open (file how)
2151  "OPEN(FILE:any, HOW:id):any eval, spread
2152Open the file with the system dependent name FILE for output if
2153HOW is EQ to OUTPUT, or input if HOW is EQ to INPUT. If the
2154file is opened successfully, a value which is internally associated with
2155the file is returned. This value must be saved for use by RDS and
2156WRS. An error occurs if HOW is something other than INPUT or
2157OUTPUT or the file can't be opened.
2158***** HOW is not option for OPEN
2159***** FILE could not be opened"
2160  (declare (type (or simple-string pathname) file) (symbol how))
2161  (setq file (substitute-in-file-name file)) ; substitute environment variables
2162  #+SBCL (setq file (expand-file-name file)) ; and then expand . and ..
2163  ;; #+cygwin (setq file (win-to-cyg file))
2164  #+cygwin (setq file (parse-namestring file)) ; convert Windows filename to Cygwin format
2165  (the filehandle
2166       (cond ((eq how 'input)
2167              (let ((fh (cl:open file :direction :input)))
2168                ;; An input filehandle is a pair of the form
2169                ;; (input-stream . echo-stream):
2170                (cons fh (make-echo-stream fh *standard-output*))))
2171             ((eq how 'output)
2172              (list 'file
2173                    (cl:open file :direction :output
2174                             :if-exists :supersede :if-does-not-exist :create)))
2175             (t (cl:error "~a is not option for OPEN" how)))))
2176
2177(defun pagelength (len)
2178  (declare (ignore len))
2179  "PAGELENGTH(LEN:{integer, NIL}):integer eval, spread
2180Sets the vertical length (in lines) of an output page. Automatic page
2181EJECTs are executed by the print functions when this length is
2182reached. The initial vertical length is implementation specific. The
2183previous page length is returned. If LEN is 0, no automatic page
2184ejects will occur."
2185  nil)
2186
2187(defvar %posn 0
2188  "Number of characters in the current line output by Standard LISP.
2189Accessed (read-only) via the function `POSN'.
2190It's value should be between 0 and `%linelength' inclusive.")
2191
2192(defun posn ()
2193  "POSN():integer eval, spread
2194Returns the number of characters in the output buffer. When the
2195buffer is empty, 0 is returned."
2196  (the fixnum %posn))
2197
2198(defvar %prin-space-maybe nil
2199  "True if there is a pending space to print.")
2200
2201(defun %prin-space-maybe ()
2202  "Record that a space should be printed and return t unless at the
2203beginning of a line."
2204  (if (> %posn 0)
2205      (setq %prin-space-maybe t)))
2206
2207(defun %prin-string (s)
2208  "Print string S preceded by a space or newline if necessary.
2209Check and update `%posn' to keep it <= `%linelength'.
2210This is the only function that actually produces graphical output."
2211  (declare (simple-string s))
2212  (let ((len (cl:length s)))
2213    (if %prin-space-maybe (incf %posn))
2214    (incf %posn len)                   ; posn after printing s
2215    (if (> %posn %linelength)
2216        (progn
2217          (cl:terpri)
2218          (setq %posn len))            ; posn after printing s
2219        (if %prin-space-maybe (cl:princ #\Space)))
2220    (setq %prin-space-maybe nil)
2221    (cl:princ s))
2222  nil)
2223
2224(defun princ (u)
2225  ;; Not used in REDUCE since redefined in rlisp/rsupport.red as
2226  ;; symbolic procedure princ u; prin2 u;
2227  "PRINC(U:id):id eval, spread
2228U must be a single character id such as produced by EXPLODE or
2229read by READCH or the value of !$EOL!$. The effect is the character
2230U displayed upon the currently selected output device. The value of
2231!$EOL!$ causes termination of the current line like a call to TERPRI."
2232  (cond ((eq u $eol$) (terpri))
2233        (t (%prin-string (%princ-to-string u))))
2234  u)
2235
2236(defun print (u)
2237  "PRINT(U:any):any eval, spread
2238Displays U in READ readable format and terminates the print line.
2239The value of U is returned.
2240EXPR PROCEDURE PRINT(U);
2241<< PRIN1 U; TERPRI(); U >>;"
2242  (prin1 u)
2243  (terpri)
2244  u)
2245
2246(defun prin1 (u)
2247  "PRIN1(U:any):any eval, spread
2248U is displayed in a READ readable form. The format of display is the
2249result of EXPLODE expansion; special characters are prefixed with the
2250escape character !, and strings are enclosed in \"...\".  Lists are
2251displayed in list-notation and vectors in vector-notation.  The value
2252of U is returned."
2253  (cond ((symbolp u) (%prin-string (%prin1-id-to-string u)))
2254        ((stringp u) (%prin-string (%prin1-string-to-string u)))
2255        ((floatp u) (%prin-string (%prin-float-to-string u)))
2256        ((vectorp u) (%prin-vector u #'prin1))
2257        ((atom u) (%prin-string (prin1-to-string u)))
2258        ;; ((eq (car u) 'quote) (%prin-string "'") (prin1 (cadr u)))
2259        ;; CSL doesn't treat quote specially
2260        (t (%prin-cons u #'prin1)))
2261  u)
2262
2263(defun prin2 (u)
2264  "PRIN2(U:any):any eval, spread
2265U is displayed upon the currently selected print device but output is
2266not READ readable.  The value of U is returned. Items are displayed as
2267described in the EXPLODE function with the exceptions that the escape
2268character does not prefix special characters and strings are not
2269enclosed in \"...\".  Lists are displayed in list-notation and vectors
2270in vector-notation.  The value of U is returned."
2271  (cond ((symbolp u) (%prin-string (%princ-id-to-string u)))
2272        ((stringp u) (%prin-string u))
2273        ((floatp u) (%prin-string (%prin-float-to-string u)))
2274        ((vectorp u) (%prin-vector u #'prin2))
2275        ((atom u) (%prin-string (princ-to-string u)))
2276        ;; ((eq (car u) 'quote) (%prin-string "'") (prin2 (cadr u)))
2277        ;; CSL doesn't treat quote specially
2278        (t (%prin-cons u #'prin2)))
2279  u)
2280
2281(defun %princ-id-to-string (u)
2282  "Convert identifier U to a string without any escapes."
2283  (declare (symbol u))
2284  (the simple-string (%string-invert-case (cl:symbol-name u))))
2285
2286(defun %prin1-id-to-string (u)
2287  "Convert identifier U to a string including appropriate `!' escapes."
2288  ;; Insert ! before an upper-case letter, leading digit or _, or
2289  ;; special character (except _):
2290  (declare (symbol u))
2291  (the simple-string
2292       (coerce
2293        (loop with s = (cl:symbol-name u) and c
2294           for i below (cl:length s)
2295           do (setq c (aref s i))
2296           unless (or (upper-case-p c)  ; case-inverted!
2297                      (and (not (eql i 0))
2298                           (or (digit-char-p c) (char= c #\_))))
2299           collect #\!
2300           collect (%character-invert-case c))
2301        'string)))
2302
2303(defun %prin1-string-to-string (s)
2304  "Add delimiting \"s and escape internal \"s as \"\" in string S."
2305  (declare (simple-string s))
2306  (the simple-string
2307       (loop with p = 0 and q and v = (list "\"")
2308          ;; v must be a new cons to allow destructive reverse
2309          do
2310            (setq q (position #\" s :start p))
2311            (if q (incf q))
2312            (setq v (cons "\"" (cons (subseq s p q) v))
2313                  p q)
2314          while q
2315          finally (return
2316                    (cl:apply #'concatenate 'string (nreverse v))))))
2317
2318(defparameter *float-print-precision* 12
2319  ;; The choice of 12 is somewhat arbitrary.  Algebraic output seems
2320  ;; to default to 6.  13 or less makes arith.tst agree with its
2321  ;; reference output.  Should perhaps try to compute this; cf. !!nfpd
2322  ;; defined in the REDUCE source file "arith/paraset.red".
2323  "Number of significant decimal digits to include when printing floats, or nil.
2324If nil then floats are printed without any additional rounding.")
2325
2326;; (defun %prin-float-to-string (u)
2327;;   "Print a float to a string rounded to include only significant digits."
2328;;   ;; Rescale u so that the significant digits form the integer part,
2329;;   ;; round that and then undo the rescaling.
2330;;   (princ-to-string
2331;;    (if (and *float-print-precision* (not (zerop u)))
2332;;        (let* ((e (floor (log (abs u) 10d0))) ; decimal exponent
2333;;               ;; |u| = m 10^e, where 0 <= m < 10, so (for e >= 0) the
2334;;               ;; integer part of u contains e+1 digits.  To make u
2335;;               ;; contain d significant digits, multiply by a scale
2336;;               ;; factor s = 10^(d-e-1), round and divide s out again:
2337;;               (s (expt 10d0 (- *float-print-precision* e 1))))
2338;;          (setq u (/ (fround (* u s)) s)))
2339;;        u)))
2340
2341;; Using `format' instead of `princ-to-string' below might be better.
2342;; (format nil "~,,,,,,'ee" 1e10) -> "1.0e+10"
2343;; But deciding between ~f and ~e format to emulate Standard Lisp
2344;; print output might not be so easy.  So, at least for now, use the
2345;; following hack!
2346
2347(defun %prin-float-to-string (u)
2348  "Print a float to a string rounded to include only significant digits."
2349  ;; Rescale u so that the significant digits form the integer part,
2350  ;; round that and then undo the rescaling.
2351  (declare (double-float u))
2352  (let ((s (cl:princ-to-string
2353            (if (and *float-print-precision* (not (zerop u)))
2354                (let* ((e (floor (log (abs u) 10d0))) ; decimal exponent
2355                       ;; |u| = m 10^e, where 0 <= m < 10, so (for e >= 0) the
2356                       ;; integer part of u contains e+1 digits.  To make u
2357                       ;; contain d significant digits, multiply by a scale
2358                       ;; factor s = 10^(d-e-1), round and divide s out again:
2359                       (e1 (- *float-print-precision* e 1))
2360                       (s (expt 10d0 (if (> e1 300) 300 e1)))
2361                       (s1 (if (> e1 300) (expt 10d0 (- e1 300)) 1d0)))
2362                  (if (> e1 300)
2363                   (setq u (/ (/ (fround (* (* u s) s1)) s) s1))
2364                  (setq u (/ (fround (* u s)) s))))
2365                u)))
2366        p)
2367    ;; Lower-case an E if necessary and follow e with + unless there is already a -.
2368    (when (setq p (position #+SBCL #\e #+CLISP #\E #+ABCL #\E s))
2369      #+CLISP (setf (aref s p) #\e)
2370      (incf p)
2371      (unless (char-equal (aref s p) #\-)
2372        (setq s (concatenate 'string (subseq s 0 p) "+" (subseq s p)))))
2373    (the simple-string s)))
2374
2375(defun %prin-vector (u prinfn)
2376  "Print vector U delimited by [ and ] using PRINFN to print each element."
2377  (declare (simple-vector u) (cl:function prinfn))
2378  (loop
2379     initially (%prin-string "[") (funcall prinfn (aref u 0))
2380     for i from 1 below (cl:length u) do
2381       (%prin-space-maybe) (funcall prinfn (aref u i))
2382     finally (%prin-string "]"))
2383  nil)
2384
2385(defun %prin-cons (u prinfn)
2386  "Print cons cell U using PRINFN."
2387  (declare (cons u) (cl:function prinfn))
2388  (%prin-string "(")
2389  (funcall prinfn (car u))
2390  (%prin-cdr (cdr u) prinfn)
2391  (%prin-string ")")
2392  nil)
2393
2394(defun %prin-cdr (u prinfn)
2395  "If U is non-nil then print it or its elements spaced appropriately.
2396U is the cdr of a cons cell: nil, an atom or another cons cell.
2397Cons cell elements are printed using PRINFN."
2398  (declare (cl:function prinfn))
2399  (cond ((null u))                      ; do nothing
2400        ((atom u)
2401         (%prin-space-maybe) (%prin-string ".")
2402         (%prin-space-maybe) (funcall prinfn u))
2403        (t (%prin-space-maybe)
2404           (funcall prinfn (car u))
2405           (%prin-cdr (cdr u) prinfn)))
2406  nil)
2407
2408(defun %default-read-stream ()
2409  "The default read stream using the current value of *standard-input*."
2410  (cons *standard-input* *standard-input*))
2411
2412(defparameter +default-read-stream+ (%default-read-stream)
2413  "The default read stream using the initial value of *standard-input*.
2414This must be re-set when Standard Lisp is started to work in a saved
2415CLISP memory image.")
2416
2417(defvar %read-stream +default-read-stream+
2418  "The current input filehandle: a cons pair of the form
2419\(input-stream . echo-stream).
2420This must be re-set when Standard Lisp is started to work in a saved
2421CLISP memory image.")
2422
2423(defun %read-stream ()
2424  "Return the appropriate input stream depending on the value of *echo."
2425  (the stream
2426       (or (and *echo (cdr %read-stream)) (car %read-stream))))
2427
2428(defun rds (filehandle)
2429  "RDS(FILEHANDLE:any):any eval, spread
2430Input from the currently selected input file is suspended and
2431further input comes from the file named. FILEHANDLE is a system
2432dependent internal name which is a value returned by OPEN. If
2433FILEHANDLE is NIL the standard input device is selected. When end
2434of file is reached on a non-standard input device, the standard
2435input device is reselected. When end of file occurs on the
2436standard input device the Standard LISP reader terminates. RDS
2437returns the internal name of the previously selected input file.
2438***** FILEHANDLE could not be selected for input"
2439  (declare (type filehandle filehandle))
2440  (the filehandle
2441       (prog1
2442           %read-stream
2443         (setq %read-stream
2444               (if (and filehandle (open-stream-p (car filehandle)))
2445                   filehandle
2446                   +default-read-stream+)))))
2447
2448(defparameter *sl-readtable* (copy-readtable)
2449  "Readtable implementing Standard Lisp syntax.
2450% introduces a comment and ! is the single-escape character.")
2451;; Cannot redefine *readtable* directly because it would come into
2452;; effect immediately during a load of the uncompiled file and break
2453;; the syntax below!
2454(set-syntax-from-char #\% #\; *sl-readtable*)
2455(set-syntax-from-char #\; #\A *sl-readtable*)
2456(set-syntax-from-char #\! #\\ *sl-readtable*)
2457(set-syntax-from-char #\\ #\A *sl-readtable*)
2458(set-syntax-from-char #\# #\A *sl-readtable*)
2459(set-syntax-from-char #\| #\A *sl-readtable*)
2460
2461(defparameter *string-readtable* (copy-readtable *sl-readtable*)
2462  "Readtable implementing Standard Lisp string syntax.
2463No escape characters are defined.")
2464(set-syntax-from-char #\! #\A *string-readtable*)
2465
2466(unless (fboundp '%cl-read-string)
2467  (setf (symbol-function '%cl-read-string)
2468        (get-macro-character #\" *sl-readtable*)))
2469
2470(defun %sl-read-string (stream closech)
2471  ;; This accumulates chars until it sees same char that invoked it,
2472  ;; namely closech. See the function read-string in
2473  ;; "sbcl-1.4.14/src/code/reader.lisp".
2474  (declare (stream stream) (character closech))
2475  (let* ((*readtable* *string-readtable*)
2476         (s (%cl-read-string stream closech)))
2477    (loop while ;; following character is "
2478         (char= (peek-char nil stream nil $eof$ t) closech)
2479       do ;; read and ignore it
2480         (read-char stream nil $eof$ t)
2481       ;; then read and concatenate the following string
2482         (setq s (concatenate 'string s (string closech)
2483                              (%cl-read-string stream closech))))
2484    (the simple-string s)))
2485
2486(set-macro-character #\" #'%sl-read-string nil *sl-readtable*)
2487
2488;; The read functions (rather than open or rds) must select the echo
2489;; stream dynamically because REDUCE sets *echo AFTER open and rds
2490;; have been called.  They do this by calling the function
2491;; %read-stream, which returns either the input stream or the echo
2492;; stream depending on the value of *echo.
2493
2494(defun read ()
2495  "READ():any
2496The next expression from the file currently selected for
2497input. Valid input forms are: vector-notation, dot-notation,
2498list-notation, numbers, function-pointers, strings, and
2499identifiers with escape characters. Identifiers are interned on
2500the OBLIST (see the INTERN function in \"Identifiers\"). READ
2501returns the value of !$EOF!$ when the end of the currently
2502selected input file is reached."
2503  (let* ((*readtable* *sl-readtable*))
2504    ;; The case sensitivity mode is one of the symbols :upcase,
2505    ;; :downcase, :preserve, or :invert.
2506    ;; (setf (readtable-case *readtable*)
2507    ;;       (if *raise :downcase :preserve))
2508    ;; Using read-preserving-whitespace rather than read seems to be
2509    ;; more consistent with PSL and CSL: it leaves the EOL to be read
2510    ;; by REDUCE, which counts input lines in each file into the value
2511    ;; of curline* and this is used in rlisp88.tst.
2512    (cl:read-preserving-whitespace (%read-stream) nil $eof$)))
2513
2514(defvar %readch-escape nil
2515  "True if the next character to be read by READCH should be escaped.")
2516
2517(defun readch ()
2518  "READCH():id
2519Returns the next interned character from the file currently selected
2520for input. Two special cases occur. If all the characters in an input
2521record have been read, the value of !$EOL!$ is returned. If the file
2522selected for input has all been read the value of !$EOF!$ is returned.
2523Comments delimited by % and end-of-line are not transparent to READCH."
2524  ;; This function must perform any required case conversion.
2525  (the symbol
2526       (let ((c (read-char (%read-stream) nil $eof$)))
2527         (if (eq c $eof$)
2528             (progn
2529               (setq %readch-escape nil)
2530               $eof$)
2531             (progn
2532               (when *echo              ; track output position
2533                 (setq %posn (if (char= c #\Newline) 0 (1+ %posn))))
2534               (cond ((char= c #\!)
2535                      (setq %readch-escape (not %readch-escape)) '!)
2536                     (%readch-escape    ; preserve case
2537                      (setq %readch-escape nil) (%intern-character-invert-case c))
2538                     (*raise            ; down-case
2539                      (%intern-character-preserve-case (cl:char-upcase c)))
2540                     (t                 ; preserve case
2541                      (%intern-character-invert-case c))))))))
2542
2543(defun terpri ()
2544  "TERPRI():NIL
2545The current print line is terminated."
2546  (setf %posn 0)
2547  (cl:terpri)
2548  nil)
2549
2550(defun %default-write-stream ()
2551  "The default write stream using the current value of *standard-output*."
2552  (the filehandle (list 'file *standard-output*)))
2553
2554(defparameter +default-write-stream+ (%default-write-stream)
2555  "The default write stream using the initial value of *standard-output*.
2556This must be re-set when Standard Lisp is started to work in a saved
2557CLISP memory image.")
2558
2559(defvar %write-stream +default-write-stream+
2560  "The current output filehandle: a dotted-list of the form
2561\('file . output-stream) or ('pipe output-stream . process).
2562This must be re-set when Standard Lisp is started to work in a saved
2563CLISP memory image.")
2564
2565(defun wrs (filehandle)
2566  "WRS(FILEHANDLE:any):any eval, spread
2567Output to the currently active output file is suspended and further
2568output is directed to the file named. FILEHANDLE is an internal
2569name which is returned by OPEN. The file named must have been
2570opened for output. If FILEHANDLE is NIL the standard output
2571device is selected. WRS returns the internal name of the previously
2572selected output file.
2573***** FILEHANDLE could not be selected for output"
2574  (declare (type filehandle filehandle))
2575  (the filehandle
2576       (prog1
2577           %write-stream
2578         ;; This fails to compile (report as SBCL bug?):
2579         ;; (setq *standard-output* (cdr +default-write-stream+)
2580         ;;    %write-stream +default-write-stream+)
2581         ;; But this version compiles OK:
2582         (setq %write-stream +default-write-stream+
2583               *standard-output* (cadr %write-stream))
2584         (when filehandle
2585           (cond
2586             ((eq (car filehandle) 'file)
2587              ;; Output file stream ('file output-stream):
2588              (if (open-stream-p (cadr filehandle))
2589                  (setq *standard-output* (cadr filehandle)
2590                        %write-stream filehandle)))
2591             ((eq (car filehandle) 'pipe)
2592              ;; Output pipe stream ('pipe output-stream . process):
2593              (if (open-stream-p (cadr filehandle))
2594                  (setq *standard-output* (cadr filehandle)
2595                        %write-stream filehandle))))))))
2596
2597(defun pipe-open (command how)
2598  "Run COMMAND asynchronously with input via the pipe returned as a
2599stream by this function."
2600  (declare (simple-string command) (symbol how))
2601  (the filehandle
2602       (cond ((eq how 'output)
2603              #+SBCL
2604              ;; An output filehandle is a dotted-list of the form ('file .
2605              ;; output-stream) or ('pipe output-stream . process):
2606              (let ((p
2607                     #+win32
2608		              (sb-ext:run-program "cmd" (list "/c" command)
2609                                          :wait nil :search t :input :stream
2610                                          :escape-arguments nil)
2611		              #+unix
2612		              (sb-ext:run-program "sh" (list "-c" command)
2613					                      :wait nil :search t :input :stream)))
2614                (cons 'pipe (cons (sb-ext:process-input p) p)))
2615              #+CLISP
2616              ;; An output filehandle is a dotted-list of the form ('file .
2617              ;; output-stream) or ('pipe output-stream . nil):
2618              ;; (list 'pipe (ext:run-shell-command command :input :stream :wait nil)))
2619              (list 'pipe (ext:make-pipe-output-stream command)))
2620             (t (cl:error "~a is not (currently) an option for PIPE-OPEN" how)))))
2621
2622(defun channelflush (filehandle)        ; PSL
2623  (declare (type filehandle filehandle))
2624  "Flush FILEHANDLE if it is a pipe stream."
2625  ;; filehandle = ('pipe output-stream . process)
2626  (if (eq (car filehandle) 'pipe)
2627      (finish-output (cadr filehandle)))
2628  nil)
2629
2630(defun flush ()                         ; CSL
2631  "Flush the current output stream."
2632  (finish-output (cadr %write-stream))
2633  nil)
2634
2635
2636;;; PSL/CSL functions and some other required functions
2637;;; ===================================================
2638
2639;; In the Standard Lisp world, "character" means either a symbol whose
2640;; name is one character long or an ASCII character code.
2641
2642(defconstant +short-day-names+
2643  #("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun")
2644  "A vector of names of the days abbreviated to 3 letters.")
2645
2646(defconstant +short-month-names+
2647  #("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")
2648  "A vector of names of the months abbreviated to 3 letters.")
2649
2650(defun date-and-time ()                 ; CSL
2651  "Return a string of the form \"Fri Feb 01 18:38:36 2019\"."
2652  (the simple-string
2653       (multiple-value-bind
2654             (second minute hour date month year day)
2655           (get-decoded-time)
2656         (format nil "~a ~a ~2,'0d ~2,'0d:~2,'0d:~2,'0d ~d"
2657                 (aref +short-day-names+ day)
2658                 (aref +short-month-names+ (1- month))
2659                 date hour minute second year))))
2660
2661(defun date ()                          ; PSL
2662  "(date): string expr
2663The date in the form \"day-month-year\"
26641 lisp> (date)
2665\"21-Jan-1997\""
2666  (the simple-string
2667       (multiple-value-bind
2668             (second minute hour date month year)
2669           (get-decoded-time)
2670         (declare (ignore second minute hour))
2671         (format nil "~2,'0d-~a-~d"
2672                 date (aref +short-month-names+ (1- month)) year))))
2673
2674(defalias 'datestamp 'get-universal-time
2675  "The number of seconds that have elapsed since some epoch.
2676This version uses the Common Lisp epoch at the beginning of the year
26771900, whereas the CSL version uses the \"Unix time\" epoch at the
2678beginning of the year 1970.  The difference of 70 years is
267970*31,536,000 = 2,207,520,000 seconds.  This function should not be
2680used to determine an absolute date or time!")
2681
2682(defconstant +milliseconds-per-internal-time-unit+
2683  (/ 1000 internal-time-units-per-second)
2684  "Multiplier to convert internal time units to milliseconds.")
2685
2686(defun time ()                          ; PSL
2687  "(time): integer expr
2688Elapsed time from some arbitrary initial point in milliseconds."
2689  ;; This is used for timing computations, so use run time.
2690  (the (integer 0)
2691       (values (round (* (get-internal-run-time)
2692                         +milliseconds-per-internal-time-unit+)))))
2693
2694#+CLISP
2695(defun %nth-room-value (n)
2696  "Return the Nth multiple value provided by CLISP `room' function.
2697Suppress the printed output."
2698  (let ((*standard-output* (make-broadcast-stream)))
2699    (nth-value n (room nil))))
2700
2701(defun gctime ()
2702  "The total time (in milliseconds) spent in garbage collection."
2703  (the (integer 0)
2704       (values (round (* #+SBCL sb-ext:*gc-run-time*
2705                         #+CLISP (%nth-room-value 5)
2706                         +milliseconds-per-internal-time-unit+)))))
2707
2708(defvar gcknt* 0
2709  "gcknt* = [Initially: 0] global
2710Records the number of times that the garbage collector has been
2711invoked.  Gcknt* may be reset to another value to record counts
2712incrementally, as desired.")
2713
2714#+SBCL (progn                           ; use sb-ext:*after-gc-hooks*
2715
2716(defvar *previous-gc-run-time* 0
2717  "Total (internal) GC time up to previous garbage collection.")
2718
2719(defun %gc-reporting ()
2720  "Increment garbage collection count and optionally output a report.
2721A function hung on the garbage collection hook."
2722  (incf gcknt*)
2723  (if *gc
2724      (format t "*** Garbage collection number ~a completed in ~ams.~%"
2725              gcknt*
2726              (round (* (- sb-ext:*gc-run-time* *previous-gc-run-time*)
2727                        +milliseconds-per-internal-time-unit+))))
2728  (setq *previous-gc-run-time* sb-ext:*gc-run-time*)
2729  nil)
2730
2731(push #'%gc-reporting sb-ext:*after-gc-hooks*)
2732
2733;; The file "rlisp/inter.red" defines procedures `with!-timeout` and
2734;; similar that use garbage collection to provide an interrupt by
2735;; assigning a function to the variable `!*gc!-hook!*`:
2736
2737(defvar *gc-hook*)
2738
2739(defun %run-gc-hook ()
2740  "Run the REDUCE procedure (if any) assigned to the variable *gc-hook*."
2741  (if (fboundp *gc-hook*) (funcall *gc-hook* nil))
2742  nil)
2743
2744(push #'%run-gc-hook sb-ext:*after-gc-hooks*)
2745
2746)                                       ; use sb-ext:*after-gc-hooks*
2747
2748(defun gtheap ()
2749  "Size of the free dynamic space in bytes."
2750  (the integer                          ; should be (integer 0) !!!!!
2751       #+SBCL (- (sb-ext:dynamic-space-size)
2752                 (let* ((s (with-output-to-string (*standard-output*)
2753                             (room nil)))
2754                        (p (position-if #'digit-char-p s)))
2755                   (read-from-string
2756                    (remove #\, (subseq s p (position #\Space s :start p))))))
2757       #+CLISP (%nth-room-value 1)
2758       #+ABCL 0))
2759
2760(defun explode2 (u)                     ; PSL
2761  "(explode2 U:atom-vector): id-list expr
2762PRIN2-like version of EXPLODE without escapes or double quotes."
2763  (cl:map 'list #'%intern-character-preserve-case
2764          (if (or (stringp u) (floatp u))
2765              (%string-invert-case (cl:princ-to-string u))
2766              (cl:princ-to-string u))))
2767
2768(defun explode2uc (u)                   ; defined in "pslrend.red"
2769  "Upper-case version of explode2."
2770  ;; NB: downcase because of symbol name case inversion!
2771  (cl:map 'list #'%intern-character-preserve-case
2772          (cl:string-downcase (cl:princ-to-string u))))
2773
2774(defun concat2 (s1 s2)
2775  "Concatenates its two string arguments, returning the newly created string."
2776  (declare (simple-string s1 s2))
2777  (the simple-string (concatenate 'string s1 s2)))
2778
2779(defun concat (&rest s)
2780  "Concatenates all of its string arguments, returning the newly created string."
2781  ;; Flagged variadic in clprolo.
2782  (declare (list s))
2783  (the simple-string (cl:apply #'concatenate 'string s)))
2784
2785;; (defalias 'allocate-string 'cl:make-string ; PSL
2786;;   "(allocate-string SIZE:integer): string expr
2787;; Constructs and returns a string with SIZE characters. The contents of
2788;; the string are not initialized.")
2789
2790(defun string2list (s)                  ; PSL
2791  "(string2list S:string): inum-list expr
2792Creates a list of length (add1 (size S)), converting the ASCII
2793characters into small integers.
2794lisp> (string2list \"STRING\")
2795\(83 84 82 73 78 71)"
2796  (declare (simple-string s))
2797  (cl:map 'list
2798             #'(lambda (x) (cl:char-code x))
2799             s))
2800
2801(defun %character (x)
2802  "Generalize cl:character to accept also a character code."
2803  (declare (type (or (unsigned-byte 8) symbol) x))
2804  (the character
2805       (if (integerp x)
2806           (if (<= 0 x 255)             ; (and (<= 0 x) (<= x 255))
2807               ;; Was 127, but then reading rlisp/tok.red fails!
2808               ;; Should 128 -> nil as specified for PSL?
2809               (code-char x)
2810               (cl:error
2811                "***** SL error in `%character': ~d is not a character code" x))
2812           (%id-to-char-invert-case x))))
2813
2814(defun list2string (l)                  ; PSL
2815  "(list2string L:inum-list): string expr
2816Allocates a string of the same size as L, and converts small integers
2817into characters according to their ASCII code. An integer outside the
2818range of 0 ... 127 will result in an error.
2819lisp> (list2string '(83 84 82 73 78 71))
2820\"STRING\"
2821Identifiers are case-inverted."
2822  (declare (list l))
2823  (cl:map 'string #'%character l))
2824
2825(defun list2widestring (u)
2826  "Take a list U of integers (each in the range 0-0x0010ffff) and turn
2827it into a string encoding those using UTF-8.  It will also support use
2828of identifiers or strings as well as integers, and will use the first
2829character (N.B. not octet) as the code concerned.
2830Identifiers are case-inverted."
2831  ;; This is a re-implementation of the procedure in rlisp/tok.red.
2832  ;; It must be flagged lose in clprolo.
2833  ;; It should make string!-store etc. redundant.
2834  (declare (list u))
2835  (cl:map 'string
2836          #'(lambda (x)
2837              (if (integerp x) (code-char x) (%id-to-char-invert-case x)))
2838          u))
2839
2840(defun widestring2list (u)
2841  "Given a string U that may contain bytes that are over 127, return a
2842list of positive integers corresponding to the characters in it if it
2843is interpreted as being encoded in UTF-8.  The behaviour if the bytes
2844are not valid UTF-8 is to be considered undefined."
2845  ;; This is a re-implementation of the procedure in rlisp/tok.red.
2846  ;; It must be flagged lose in clprolo.
2847  ;; It should make moan!-if!-truncated etc. redundant.
2848  (declare (simple-string u))
2849  (cl:map 'list #'cl:char-code u))
2850
2851;; (defun string-store (s i x)              ; PSL
2852;;   "(string-store S:string I:integer X:char): None Returned expr
2853;; Stores into a PSL string. String indexes start with 0."
2854;;   (setf (aref s i) (%character x)))
2855
2856(defalias 'string-length 'cl:length     ; PSL
2857  "(string-length S:string): integer expr
2858Returns the number of elements in a PSL string. Since indexes start with
2859index 0, the size is one larger than the greatest legal index. Compare this
2860function with string-upper-bound, documented below.")
2861
2862(defun char-downcase (c)                ; CSL
2863  "Convert single-character identifier C to lower case."
2864  ;; NB: upcase because of symbol name case inversion!
2865  (declare (symbol c))
2866  (the symbol
2867       (values (cl:intern (cl:string-upcase (cl:symbol-name c))))))
2868
2869(defalias 'red-char-downcase 'char-downcase) ; PSL
2870
2871(defun char-upcase (c)                  ; CSL
2872  "Convert single-character identifier C to lower case."
2873  ;; NB: downcase because of symbol name case inversion!
2874  (declare (symbol c))
2875  (the symbol
2876       (values (cl:intern (cl:string-downcase (cl:symbol-name c))))))
2877
2878(defun int2id (i)                       ; PSL
2879  "(int2id I:integer): id expr
2880Converts an integer to an id; this refers to the I'th id in the id space. Since
28810 ... 255 correspond to ASCII characters, int2id with an argument in this
2882range converts an ASCII code to the corresponding single character id. The
2883id NIL is always found by (int2id 128)."
2884  ;; Defined in csl.red as
2885  ;; inline procedure int2id x; % Turns 8-bit value into name. Only OK is under 0x80
2886  ;;   intern list2string list x;
2887  ;; (unless (= i 128) (%intern-character (code-char i)))
2888  (declare (type (unsigned-byte 8) i))
2889  (the symbol (%intern-character-invert-case (code-char i))))
2890
2891(defun id2int (d)                       ; PSL
2892  "(id2int D:id): integer expr
2893Returns the id space position of D as a LISP integer."
2894  ;; Defined in csl.red as
2895  ;; inline procedure id2int x; % Gets first octet of UTF-8 form of name
2896  ;;   car string2list x;
2897  ;; (if d (cl:char-code (aref (symbol-name d) 0)) 128)
2898  (declare (symbol d))
2899  (the (unsigned-byte 8)
2900       (cl:char-code (%character-invert-case (aref (cl:symbol-name d) 0)))))
2901
2902(defun char-code (c)                    ; PSL
2903  "Returns the code attribute of C. (In PSL this function is an identity function.)"
2904  (declare (symbol c))
2905  (the (unsigned-byte 8)
2906       (cl:char-code (character c))))
2907
2908(defun id2string (d)                    ; PSL
2909  "(id2string D:id): string expr
2910Get name from id space. Id2string returns the print name of its argument
2911as a string. This is not a copy, so destructive operations should not be performed
2912on the result. PSL uses an escape convention for notating identifiers
2913which contain special characters. Any character which follows the character
2914! is considered to be an alphabetic character. In the example, notice that the
2915character ! does not appear in the result.
29161 lisp> (id2string 'is-!%)
2917\"is-%\""
2918  (declare (symbol d))
2919  (the simple-string (%string-invert-case (cl:symbol-name d))))
2920
2921(defalias 'symbol-name 'id2string)
2922
2923(defun string-downcase (u)
2924  "Convert identifier or string U to a lower-case string."
2925  (declare (type (or symbol simple-string) u))
2926  (the simple-string (cl:string-downcase (if (symbolp u) (cl:symbol-name u) u))))
2927
2928(defalias 'land 'cl:logand           ; PSL
2929  "(land U:integer V:integer): integer expr
2930Bitwise or logical and. Each bit of the result is independently
2931determined from the corresponding bits of the operands.")
2932
2933(defalias 'lshift 'cl:ash            ; PSL
2934  ;; Not quite right for negative integers N!
2935  "(lshift N:integer K:integer): integer expr
2936Shifts N to the left by K bits. The effect is similar to multiplying
2937by 2 to the K power. Negative values are acceptable for K, and cause a
2938right shift (in the usual manner). Lshift is a logical shift, so right
2939shifts do not resemble division by a power of 2.")
2940
2941(defun list2vector (l)                  ; PSL
2942  "(list2vector L:list): vector expr
2943Copy the elements of the list into a vector of the same size.
29441 lisp> (list2vector '(V E C T O R))
2945[V E C T O R]"
2946  (declare (list l))
2947  (the simple-vector (cl:apply #'cl:vector l)))
2948
2949(defalias 'list-to-vector 'list2vector)
2950
2951(defun vector2list (v)                  ; PSL (should be flagged lose!)
2952  "(vector2list V:vector): list expr
2953Create a list of the same size as V, the elements are copied in a left to right
2954order.
29551 lisp> (vector2list [L I S T])
2956\(L I S T)"
2957  (declare (simple-vector v))
2958  (the list (cl:map 'list #'cl:identity v)))
2959
2960(defalias 'copy 'cl:copy-tree        ; PSL
2961  "(copy X:any): any expr
2962This function returns a copy of X. While each pair is copied, atomic
2963elements (for example ids, strings, and vectors) are not.")
2964
2965;; REDUCE needs complexp in various places but also needs to be able
2966;; to overwrite it, as in rlisp88.tst:
2967(defalias 'complexp 'cl:complexp)
2968
2969;; The next three PSL definitions are based on those at the end of
2970;; support/csl.red:
2971
2972(defmacro bothtimes (u)                 ; PSL
2973  "Evaluate the expression U at both compile time and load time."
2974  `(eval-when (:compile-toplevel :load-toplevel :execute) ,u))
2975
2976(defmacro compiletime (u)               ; PSL
2977  "Evaluate the expression U at compile time only."
2978  `(eval-when (:compile-toplevel :execute) ,u))
2979
2980(defmacro loadtime (u)                  ; PSL
2981  "Evaluate the expression U at load time only."
2982  `(eval-when (:load-toplevel :execute) ,u))
2983
2984(defalias 'prop 'cl:symbol-plist)    ; PSL
2985(defalias 'plist 'cl:symbol-plist)   ; CSL
2986
2987(defun setprop (u l)                    ; PSL
2988  "(setprop U:id L:any): L:any expr
2989Store item L as the property list of U."
2990  (declare (symbol u))
2991  (setf (symbol-plist u) l))
2992
2993;; CL union and intersection return different orderings that those in
2994;; the REDUCE source, which leads to different (although probably not
2995;; incorrect) results, so don't use them.  However, union is needed in
2996;; the build process before it is defined in the rlisp module, so
2997;; define an initial version here, which will be replaced when
2998;; building rlisp:
2999
3000(defun union (x y)                      ; PSL
3001  "(union X:list Y:list): list expr
3002Returns the union of sets X and Y."
3003  (declare (list x y))
3004  (the list (cl:union x y :test #'equal)))
3005
3006(defalias 'mod 'cl:mod) ; not just imported because cali redefines mod
3007(defalias 'gcdn 'cl:gcd)
3008(defalias 'lcmn 'cl:lcm)
3009(defalias 'yesp1 'cl:y-or-n-p)
3010
3011(defun orderp (u v)
3012  "This CL-specific definition of ORDERP is designed to work in
3013lexicographical order.  It assumes arguments are truly id's, which
3014should be true with current REDUCE.  Ignore case."
3015  ;; Previously defined in clprolo, but I want to use cl:symbol-name
3016  ;; to avoid unnecessary case inversions.
3017  (declare (symbol u v))
3018  (string-not-greaterp (cl:symbol-name u) (cl:symbol-name v)))
3019
3020
3021;;; Operating system interface
3022;;; ==========================
3023
3024;; (defun system (command)                  ; PSL
3025;;   "(system COMMAND:string):undefined expr
3026;; Run a (system specific) command interpreter synchronously, pass
3027;; COMMAND to the interpreter and return the process exit code."
3028;;   ;; Split off the arguments:
3029;;   (setq command
3030;;      (loop with beg and end = 0
3031;;         while end
3032;;         do (setq beg (position-if #'(lambda (x) (char/= x #\Space))
3033;;                                   command :start end))
3034;;           (unless beg (loop-finish))
3035;;           (setq end (position #\Space command :start beg))
3036;;         collect (subseq command beg end)))
3037;;   (sb-ext:process-exit-code
3038;;    (sb-ext:run-program "cmd" (cons "/c" command)
3039;;                     :search t :output t :escape-arguments nil)))
3040
3041(defun system (command)                 ; PSL
3042  "(system COMMAND:string):undefined expr
3043Run a (system specific) command interpreter synchronously, pass
3044COMMAND to the interpreter and return the process exit code."
3045  (declare (simple-string command))
3046  (the integer
3047       #+SBCL
3048       (sb-ext:process-exit-code
3049        #+win32
3050        (sb-ext:run-program "cmd" (list "/c" command)
3051                            :search t :output t :escape-arguments nil)
3052        #+unix
3053        (sb-ext:run-program "sh" (list "-c" command)
3054                            :search t :output t))
3055       ;; Cygwin CLISP behaves as if running on Unix, not Windows.
3056       ;; ext:shell returns nil for normal exit with status 0!
3057       #+CLISP (or (ext:shell command) 0)))
3058
3059#+SBCL
3060(defun system-to-string (command)       ; experimental - not tested!
3061  (with-output-to-string (*standard-output*)
3062    (system command)))
3063
3064#+CLISP
3065(defun system-to-string (command)       ; experimental - doesn't seem to work
3066  (let ((s (ext:run-shell-command command :output :stream)))
3067    (get-output-stream-string s)))
3068
3069(defun pwd ()                           ; PSL / Unix
3070  "(pwd):STRING expr
3071Return the current working directory in system specific format."
3072  (the simple-string
3073       #+SBCL (sb-ext:native-namestring *default-pathname-defaults*)
3074       #+CLISP (namestring (ext:cd))))
3075
3076#+SBCL
3077(defun cd (&optional dir)               ; PSL / Unix
3078  "(cd DIR:{null,string}):{nil,string} expr
3079Set the current working directory to string DIR (if supplied and
3080non-empty), after substituting environment variables and then
3081expanding \".\" and \"..\".  If successful then return the new current
3082directory; otherwise, return nil."
3083  (declare (type (or null simple-string pathname) dir))
3084  (unless (and dir (string/= dir ""))
3085    (return-from cd
3086      (sb-ext:native-namestring *default-pathname-defaults*)))
3087  ;; SBCL seems to mis-parse ".." to be the same as "." hence this
3088  ;; inelegant hack.  Allow dir not to end with a separator:
3089  (if (pathname-name dir)
3090      (setq dir (concatenate 'string dir "/")))
3091  ;; Substitute environment variables and then expand . and ..:
3092  (setq dir (substitute-in-file-name dir))
3093  (setq dir (expand-file-name dir))
3094  (setq dir (pathname dir))
3095  ;; ;; Allow dir not to end with a separator:
3096  ;; (if (pathname-name dir)
3097  ;;     (setq dir (make-pathname :directory
3098  ;;                              (nconc (or (pathname-directory dir) '(:relative))
3099  ;;                                     (list (pathname-name dir))))))
3100  (setq dir (merge-pathnames dir))
3101  (the (or null pathname)
3102       (and (probe-file dir)
3103            ;; Return the new current working directory:
3104            (sb-ext:native-namestring   ; \ instead of /
3105             (setq *default-pathname-defaults* dir)))))
3106
3107#+CLISP
3108(defun cd (&optional dir)               ; PSL / Unix
3109  "(cd DIR:{null,string}):{nil,string} expr
3110Set the current working directory to string DIR (if supplied and
3111non-empty), after substituting environment variables and then
3112expanding \".\" and \"..\".  If successful then return the new current
3113directory."
3114  ;; In CLISP, MAKE-PATHNAME canonicalizes the PATHNAME directory component.
3115  (declare (type (or null simple-string) dir))
3116  (the simple-string
3117       (namestring
3118        ;; cd crashes with nil or ""!
3119        (cl:apply #'ext:cd (and dir (string/= dir "")
3120                                (list (substitute-in-file-name dir)))))))
3121
3122#+ABCL
3123(defun cd (x)
3124    "Change current directory, as per POSIX chdir(2), to a given pathname object"
3125    (if-let (x (pathname x))
3126      (setf *default-pathname-defaults* (truename x)) ;; d-p-d is canonical!
3127      ))
3128
3129
3130(defalias 'chdir 'cd)                   ; CSL / MS Windows
3131
3132(defalias 'filep 'probe-file)           ; PSL
3133
3134#+ABCL
3135(defun getenv (string)
3136    (java:jstatic "getenv" "java.lang.System" string))
3137
3138
3139#+SBCL (import 'sb-posix:getpid)
3140#+CLISP (defalias 'getpid 'os:process-id)
3141
3142(defun exit (&optional code)
3143  #+SBCL (sb-ext:exit :code code)
3144  #+CLISP (ext:exit code))
3145
3146
3147;;; Compile and load
3148;;; ================
3149
3150(defvar *verboseload nil
3151  "*verboseload = [Initially: nil] switch
3152If non-nil, a message is displayed when a request is made to load a
3153file which has already been loaded, when a file is about to be loaded,
3154and when the loading of a file is complete.  Since *redefmsg is set to
3155the value of *verboseload within `load', a non-nil value will also
3156cause a message to be printed whenever a function is redefined during
3157a load.")
3158
3159(defvar options* nil
3160  "A list of loaded `modules', which are loaded only once.
3161These are files referenced by symbols rather than strings.")
3162
3163(defconstant %fasl-directory-pathname
3164  (make-pathname :directory (cl:append (pathname-directory (truename ""))
3165                                       '(#+SBCL "fasl.sbcl" #+CLISP "fasl.clisp")))
3166  "Absolute pathname of fasl directory.")
3167
3168(defun load (file)             ; currently only supports a single file
3169  "(load [FILE:{string, id}]): nil macro
3170For each argument FILE, an attempt is made to locate a corresponding
3171file.  If a file is found then it will be loaded by a call on an
3172appropriate function.  A full file name is constructed by using the
3173directory specifications in loaddirectories* and the extensions in
3174loadextensions*.  The strings from each list are used in a left to
3175right order, for a given string from loaddirectories* each extension
3176from loadextensions* is used.
3177
3178Load a \".sl\" file using Standard Lisp read syntax."
3179  ;; filename defaults are taken from *default-pathname-defaults*,
3180  ;; which defaults to the directory in which SBCL was started.
3181  (declare (type (or symbol simple-string) file))
3182  (let ((*readtable* (copy-readtable nil)) ; normal CL syntax
3183        (*load-verbose* *verboseload)
3184        (*redefmsg *verboseload) file-pathname)
3185    (if (symbolp file)
3186        (progn
3187          (if (cl:member file options*) (return-from load)) ; already loaded
3188          (push file options*)
3189          (setq file-pathname
3190                (pathname (cl:string-downcase (cl:symbol-name file)))))
3191        (progn
3192          (setq file-pathname (pathname file))
3193          (if (string-equal (pathname-type file-pathname) "sl")
3194              (setq *readtable* *sl-readtable*))))
3195    (if (eqcar (pathname-directory file-pathname) :absolute)
3196        (cl:load file-pathname)
3197        ;; Relative filename -- look in current directory and fasl
3198        ;; directory; if not found then throw an error:
3199        (or (cl:load file-pathname :if-does-not-exist nil)
3200            (cl:load (merge-pathnames file-pathname %fasl-directory-pathname))))))
3201
3202
3203;;; Faslout/faslend interface
3204;;; =========================
3205
3206(defconstant %faslout-header
3207  (concatenate
3208   'string
3209  #-DEBUG "(cl:declaim (cl:optimize cl:speed))"
3210  #+DEBUG "(cl:declaim (cl:optimize cl:debug cl:safety))"
3211   (string #\Newline)
3212   #+SBCL "(cl:declaim (sb-ext:muffle-conditions sb-ext:compiler-note cl:style-warning))"
3213   #+CLISP "(setq custom:*suppress-check-redefinition* t
3214              custom:*compile-warnings* nil)")
3215  "Header string written at the top of every Lisp file generated by `faslout'
3216or nil, meaning no header.")
3217
3218(defvar *writingfaslfile nil
3219  "REDUCE variable set to t by `faslout' and reset to nil by `faslend'.")
3220(defvar *int)
3221
3222(defvar %faslout-name.lisp)
3223(defvar %faslout-stream)
3224
3225(defun prettyprint (u)
3226  "Default prettyprint function, required for bootstrapping.
3227Redefined later as an autoload for the real prettyprinter."
3228  (print u))
3229
3230(defun %faslout-prettyprint (u)
3231  "The prettyprint function used for faslout generation.
3232It prints Common Lisp syntax to %faslout-stream."
3233  (let (*print-gensym* ; inhibit printing #: prefix for uninterned symbols
3234        (*readtable* (copy-readtable nil))) ; needed for CLISP
3235    (cl:print u %faslout-stream)))
3236
3237(defvar %faslout-saved-prettyprint nil
3238  "The saved current global definition of the function prettyprint.
3239It is replaced during faslout.")
3240
3241(defun faslout (name)
3242  "Compile subsequent input into Common Lisp FASL file \"NAME.fasl\".
3243NAME should be an identifier or string.  (The actual extension of fasl
3244files depends on the version of Common Lisp.)"
3245  ;; Output subsequent code as Common Lisp to a temporary file until
3246  ;; FASLEND evaluated.
3247  (declare (type (or symbol simple-string) name))
3248  (setq name (string-downcase name))
3249  (if *int
3250      (format t "FASLOUT ~a: IN files$ or type in expressions.
3251When all done, execute FASLEND;~2%" name))
3252  (unless
3253      (setq %faslout-stream
3254            (cl:open (setq %faslout-name.lisp (concat2 name ".lisp"))
3255                     :direction :output :if-exists :supersede
3256                     :external-format #+SBCL :UTF-8 #+CLISP charset:UTF-8))
3257    (cl:error "Faslout: cannot open ~a" %faslout-name.lisp))
3258  (if %faslout-header
3259      (cl:princ %faslout-header %faslout-stream))
3260  (setf %faslout-saved-prettyprint (symbol-function 'prettyprint)
3261        (symbol-function 'prettyprint) (symbol-function '%faslout-prettyprint))
3262  (setq *defn t
3263        *writingfaslfile t)
3264  nil)
3265
3266(flag '(faslout) 'opfn)
3267(flag '(faslout) 'noval)
3268
3269;; SBCL outputs more detailed and useful messages than those that I
3270;; have therefore temporarily commented out below.  Delete them unless
3271;; they prove useful with other versions of Common Lisp.
3272
3273(defun faslend ()
3274  "Terminate a previous FASLOUT and generate the compiled file."
3275  (unless *writingfaslfile
3276    (cl:error "FASLEND is only allowed after a previous FASLOUT"))
3277  ;; First, tidy up after the call of FASLOUT:
3278  (unless
3279      (cl:close %faslout-stream)
3280    (cl:error "Faslend: cannot close ~a" %faslout-name.lisp))
3281  (setq *writingfaslfile nil
3282        *defn nil) ; necessary here if faslend not input as a statement
3283  (setf (symbol-function 'prettyprint) %faslout-saved-prettyprint)
3284  ;; Now compile the Lisp output generated by FASLOUT:
3285  ;; (format t  "Compiling ~a..." %faslout-name.lisp)
3286  ;; (if
3287  (let ((*readtable* (copy-readtable nil))) ; normal CL syntax
3288    (compile-file %faslout-name.lisp
3289                  :external-format #+SBCL :UTF-8 #+CLISP charset:UTF-8))
3290  ;;      ;; (progn
3291  ;;      ;; (delete-file %faslout-name.lisp) ; keep to aid debugging ???
3292  ;;      (format t "Compiling ~a...done" %faslout-name.lisp)
3293  ;;      ;; nil)
3294  ;;      (cl:error "Error compiling ~a" %faslout-name.lisp))
3295  )
3296
3297(defvar cursym*)
3298
3299(defun faslendstat ()
3300  "Terminate reading faslend and turn defn off."
3301  ;; Modelled on endstat in rlisp/parser.
3302  (let ((x cursym*))
3303    (setq *defn nil)                    ; must do this ASAP!
3304    (comm1 'end)
3305    (list x)))
3306
3307(put 'faslend 'stat 'faslendstat)       ; cf. endstat
3308(flag '(faslendstat) 'endstatfn)        ; ditto
3309
3310(flag '(faslend) 'ignore)               ; to stop it getting compiled!
3311
3312
3313;;; User interface
3314;;; ==============
3315
3316(defun standard-lisp ()
3317  "Switch to STANDARD LISP mode."
3318  (the package
3319       (prog1
3320           (in-package :sl)
3321         (setq *readtable* *sl-readtable*
3322               ;; The REDUCE source code implies that 64-bit IEEE
3323               ;; arithmetic is expected and it seems to be necessary to
3324               ;; read the constant 1.0e300 in arith/paraset.red:
3325               *read-default-float-format* 'double-float
3326               ;; These must be re-set when Standard Lisp is started to
3327               ;; work in a saved CLISP memory image:
3328               +default-read-stream+ (%default-read-stream)
3329               %read-stream +default-read-stream+
3330               +default-write-stream+ (%default-write-stream)
3331               %write-stream +default-write-stream+))))
3332
3333(defun start-reduce ()                  ; Now probably redundant
3334  "Switch to STANDARD LISP mode and start REDUCE."
3335  (standard-lisp)
3336  (begin)
3337  nil)
3338
3339(import '(standard-lisp start-reduce) :cl-user)
3340
3341(defun reset-readtable ()
3342  "Switch to Common Lisp read syntax."
3343  (setq *readtable* (copy-readtable nil))
3344  nil)
3345
3346#+SBCL
3347;; See function `toplevel-repl' in "sbcl-1.4.14/src/code/toplevel.lisp".
3348(defun reduce-init-function ()
3349  "The function executed at startup of the saved REDUCE memory image."
3350  (standard-lisp)
3351  (handler-bind ((sb-impl::step-condition 'invoke-stepper))
3352    (loop
3353       ;; CLHS recommends that there should always be an
3354       ;; ABORT restart; we have this one here, and one per
3355       ;; debugger level.
3356       (with-simple-restart
3357           (abort "~@<Exit debugger, returning to top level.~@:>")
3358         (catch 'toplevel-catcher
3359           ;; In the event of a control-stack-exhausted-error, we
3360           ;; should have unwound enough stack by the time we get
3361           ;; here that this is now possible.
3362           #-win32
3363           (sb-kernel::reset-control-stack-guard-page)
3364           (begin))))))
3365
3366#+CLISP
3367;; See function `main-loop' in
3368;; "clisp-2.49-6.20150312hg15611.src/clisp/src/reploop.lisp".
3369(defun reduce-init-function ()
3370  "The function executed at startup of the saved REDUCE memory image."
3371  (standard-lisp)
3372  (system::driver       ; build driver-frame; do #'lambda "infinitely"
3373   #'(lambda ()
3374       (system::with-abort-restart (:report (system::text "Abort main loop"))
3375         ;; ANSI CL wants an ABORT restart to be available.
3376         (begin))))
3377  (ext:exit))
3378
3379(defun save-reduce-image (name)
3380  "Save a REDUCE memory image with main filename component NAME."
3381  (declare (string name))
3382  #+SBCL
3383  (sb-ext:save-lisp-and-die (concat "fasl.sbcl/" name ".img")
3384                            :toplevel #'reduce-init-function)
3385  #+CLISP
3386  (ext:saveinitmem (concat "fasl.clisp/" name ".mem")
3387                   :init-function #'reduce-init-function
3388                   :quiet t :norc t)
3389  #+ABCL (asdf-jar:package (intern name ) :verbose t)
3390)
3391
3392(pushnew :standard-lisp *features*)
3393
3394(defparameter lispsystem* '(common-lisp)
3395  "Information about the Lisp system supporting REDUCE.
3396A list of identifiers indicating system properties.")
3397
3398#+SBCL (pushnew 'sbcl lispsystem*)
3399#+CLISP (pushnew 'clisp lispsystem*)
3400#+ABCL (pushnew 'abcl lispsystem*)
3401#+win32 (pushnew 'win32 lispsystem*)
3402#+cygwin (pushnew 'cygwin lispsystem*)
3403#+unix (pushnew 'unix lispsystem*)  ; appears together with cygwin
3404
3405#+SBCL
3406(defun compilation (on)
3407  "Set the SBCL evaluation mode to compile if ON is non-nil and to
3408interpret otherwise.  The default is compile."
3409  (the symbol
3410       (setq sb-ext:*evaluator-mode*
3411             (if on :compile :interpret))))
3412
3413;; In SBCL, inhibit printing of package prefixes in the debugger
3414;; (which doesn't seem to work):
3415#+SBCL (setq sb-ext:*debug-print-variable-alist* '((*print-escape* . nil)))
3416
3417#+ABCL (setq *autoload-verbose* t)
3418
3419;; Common Lisp symbols used in REDUCE source code:
3420(import
3421 '(lambda warning
3422   unwind-protect evenp oddp
3423   string-not-greaterp y-or-n-p         ; used in clprolo
3424   force-output                         ; used in clrend
3425   file-write-date                      ; used in remake
3426   catch throw                          ; used in rubi_red
3427   sleep                                ; used in crack
3428   ))
3429
3430;; Cease inheriting the external symbols of :common-lisp except for
3431;; those that have been explicitly imported:
3432;; (unuse-package :common-lisp)
3433
3434;; Unfortunately, the above does not work in reduce.img.  So try
3435;; shadowing all external CL symbols:
3436(do-external-symbols (s :cl)
3437  (multiple-value-bind (symbol status)
3438      (cl:find-symbol (cl:symbol-name s))
3439    ;; (if (eq status :internal) (print symbol))
3440    (if (eq status :inherited) (shadow symbol))))
3441
3442;;; sl-on-cl.lisp ends here
3443
3444;; To do:
3445;; Use pathnames more consistently.
3446;; Revise documentation strings and function order to follow PSL manual more closely.
3447