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