;;; This is a -*-Lisp-*- file.
;;; **********************************************************************
;;; This code was written as part of the Spice Lisp project at
;;; Carnegie-Mellon University, and has been placed in the public domain.
;;; Spice Lisp is currently incomplete and under active development.
;;; If you want to use this code or any part of Spice Lisp, please contact
;;; Scott Fahlman (FAHLMAN@CMUC).
;;; **********************************************************************
;;; Assorted miscellaneous functions for Spice Lisp.
;;; Written and maintained by Scott Fahlman.
;;; Contributions made by Dan Aronson, Skef Wholey and Rob MacLachlan.
;;; *******************************************************************
(defun documentation (symbol doc-type)
"Returns the documentation string of Doc-Type for the Symbol, or NIL if
none exists. Legal doc-types are VARIABLE, FUNCTION, STRUCTURE, TYPE,
and SETF."
(or (get symbol 'documentation) ; Temporary kludge.
(case doc-type
(variable (get symbol '%var-documentation))
(function (get symbol '%fun-documentation))
(structure (get symbol '%struct-documentation))
(type (get symbol '%type-documentation))
(setf (get symbol '%setf-documentation)))))
(defun %set-documentation (symbol doc-type string)
(case doc-type
(variable (%put symbol '%var-documentation string))
(function (%put symbol '%fun-documentation string))
(structure (%put symbol '%struct-documentation string))
(type (%put symbol '%type-documentation string))
(setf (%put symbol '%setf-documentation string))))
(declare (special *features*))
(defun featurep (x)
"If X is an atom, see if it is present in *FEATURES*. Also
handle arbitrary combinations of atoms using NOT, AND, OR."
(cond ((atom x) (memq x *features*))
((eq (car x) 'not) (not (featurep (cadr x))))
((eq (car x) 'and)
(every #'featurep (cdr x)))
((eq (car x) 'or)
(some #'featurep (cdr x)))
(t nil)))
;;;; Room and GC.
(defvar alloctable-address (%primitive make-immediate-type #x10000 0)
"A system area pointer that addresses the the alloctable.")
(declare (special *already-maybe-gcing*))
(defun alloc-ref (index)
(logior (%primitive 16bit-system-ref alloctable-address index)
(ash (logand #x1FF (%primitive 16bit-system-ref alloctable-address
(1+ index)))
16)))
(defun space-usage (type)
(let ((base (ash type 4)))
(values (alloc-ref (+ base (ash (%sp-get-newspace-bit) 2)))
(alloc-ref (+ base 8))
(alloc-ref (+ base 12)))))
(defconstant type-space-names
'("Misc" "Bit-Vector" "Integer-Vector" "String" "Bignum" "Long-Float"
"Complex" "Ratio" "General-Vector" "Function" "Array" "Symbol" "List"
"Unused-13" "Unused-14" "Unused-15"))
(defconstant first-used-type 1)
(defconstant first-unused-type 13)
(defun room-header ()
(fresh-line)
(princ " Type | Dynamic | Static | Read-Only | Total")
(terpri)
(princ "-------------------|-----------|-----------|-----------|-----------")
(terpri))
(defun room-summary (dynamic static read-only)
(princ "-------------------|-----------|-----------|-----------|-----------")
(format t "~% Totals: |~10D |~10D |~10D =~10D~%"
dynamic static read-only (+ static dynamic read-only)))
(defun describe-one-type (type dynamic static read-only)
(format t "~18A |~10D |~10D |~10D |~10D~%" (elt type-space-names type)
dynamic static read-only (+ static dynamic read-only)))
(defun room (&optional (x t) (object nil argp))
"Displays information about storage allocation.
If X is true then information is displayed broken down by types.
If Object is supplied then just display information for objects of
that type."
(when x
(let ((type (%primitive get-type object)))
(when (>= type 16)
(error "Objects of type ~S have no allocated storage."
(type-of object)))
(room-header)
(cond
(argp
(multiple-value-bind (dyn stat ro)
(space-usage type)
(describe-one-type type dyn stat ro)))
(t
(let ((cum-dyn 0)
(cum-stat 0)
(cum-ro 0))
(do ((type first-used-type (1+ type)))
((= type first-unused-type))
(multiple-value-bind (dyn stat ro)
(space-usage type)
(describe-one-type type dyn stat ro)
(incf cum-dyn dyn) (incf cum-stat stat) (incf cum-ro ro)))
(room-summary cum-dyn cum-stat cum-ro))))))
(format t "~%There are approximately ~D words of swap space left."
(ash (available-vm) -1)))
(defvar gc-flip-ratio 2
"When the amount of stuff in dynamic space exceeds the amount of paging space
by this factor, a GC is done.")
(defun maybe-gc ()
"Does a GC if we're low on paging space, otherwise does nothing. This
function is called by the microcode now and then."
(let ((cum-dyn 0))
(do ((type first-used-type (1+ type)))
((= type first-unused-type))
(incf cum-dyn (space-usage type)))
(when (> (/ cum-dyn (ash (available-vm) -1)) gc-flip-ratio)
(let ((*standard-output* *terminal-io*))
(beep)
(terpri)
(write-line "[GC-Flip-Ratio exceeded. Commencing GC.]")
(let ((freed (gc nil)))
(write-string "[GC completed. Approximately ")
(princ freed)
(write-string " words were freed.]"))
(beep)
(terpri)))))
(defun gc (&optional (blah-p t))
"Initiates a garbage collection. If Blah-P is T, the amount of storage
reclaimed is printed, otherwise it is returned."
(let ((free-bytes-at-start (available-vm))
(oldspace-base (ash (%sp-get-newspace-bit) 25)))
(%sp-collect-garbage)
(do* ((i first-used-type (1+ i))
(this-space (logior oldspace-base (ash i 27))
(logior oldspace-base (ash i 27))))
((= i first-unused-type))
(invalidate-memory this-space #x4000000)
(validate-memory this-space #x4000000 -1))
(cond (blah-p
(princ "Approximately ")
(princ (ash (- (available-vm) free-bytes-at-start) -1))
(princ " words have been reclaimed.")
(values))
(t
(ash (- (available-vm) free-bytes-at-start) -1)))))
;;; Describe:
;;; Desc-Doc prints the specified kind of documentation about the given Symbol.
(defun desc-doc (symbol name string)
(let ((doc (documentation symbol name)))
(when doc
(format t "~%~A~% ~A" string doc))))
(defun primep (x)
"Returns T if X is prime, otherwise Nil."
(setq x (abs x))
(or (= x 1)
(= x 2)
(= x 3)
(do ((i 3 (+ i 2))
(terminus (sqrt x)))
((> i terminus) t)
(if (zerop (rem x i))
(return nil)))))
(defun desc-arglist (function)
(cond ((compiled-function-p function)
(%primitive header-ref function %function-arg-names-slot))
((and (listp function)
(eq (car function) 'macro))
'(&rest **macroarg**))
((and (listp function)
(eq (car function) 'lambda))
(cadr function))))
(defun describe (x)
"Prints a description of the object X."
(format t "~%~S is a ~S." x (type-of x))
(typecase x
(integer
(cond ((= x 2)
(format t "~%It is the only even prime number."))
((oddp x)
(if (primep x)
(format t "~%It is a prime number.")
(format t "~%It is a composite odd number.")))
(t
(format t "~%It is a composite even number."))))
(number
(if (primep x)
(format t "~%It is a prime number.")))
(symbol
(if (boundp X)
(format t "~%Its value is ~S." (symbol-value x)))
(desc-doc x 'variable "Documentation on the variable:")
(if (fboundp x)
(format t
"~%It can be called as a function in the following way:~% ~S"
(cons x (desc-arglist (symbol-function x)))))
(desc-doc x 'function "Documentation on the function:")
(desc-doc x 'structure "Documentation on the structure:")
(desc-doc x 'type "Documentation on the type:")
(desc-doc x 'setf "Documentation on the SETF form:")
(do ((plist (symbol-plist X) (cddr plist)))
((null plist) ())
(unless (member (car plist) '(%var-documentation %fun-documentation
%struct-documentation %type-documentation
%setf-documentation))
(format t "~%Its ~S property is ~S." (car plist) (cadr plist)))))
(function
(format t "~%It can be called in the following way:~% ~S."
(cons (%primitive header-ref x %function-name-slot)
(desc-arglist x)))
(desc-doc (%primitive header-ref x %function-name-slot) 'function
"Documentation:"))
(hash-table
(format t "~%It currently has ~S entries and ~S buckets."
(hash-table-count x) (hash-table-size x))))
(terpri)
x)
;;; Other Environment Inquiries.
(defun lisp-implementation-type ()
"Spice Lisp")
(defun lisp-implementation-version ()
*lisp-implementation-version*)
(defun machine-type ()
"Perq-1A or Perq-2")
(defun machine-version ()
"16K WCS CPU")
(defun machine-instance ()
*machine-instance*)
(defun software-type ()
"Spice")
(defun software-version ()
"Accent system S4")
(defun short-site-name ()
"CMU-CSD")
(defun long-site-name ()
"Carnegie-Mellon University Computer Science Department")
;;; DIRECTORY stuff
;;; Does a directory call on NAME.
(defun directory (name)
"Prints out a list of files that matches NAME. NAME may be a string,
a pathname of a file stream."
(if (not (or (stringp name) (file-stream-p name) (pathnamep name)))
(error "The argument ~s is not a legal argument to this function."))
(let ((filename (namestring (truename name))))
(multiple-value-bind (address count)
(ses-scan-names (concatenate 'string filename "*"))
(do* ((i 0 (1+ i))
entries
(list (%sp-make-immediate-type address 0))
(base 0 (+ 90 base))
(len (%primitive 8bit-system-ref list base)
(%primitive 8bit-system-ref list base))
(str (make-string len) (make-string len)))
((= i count) entries)
(%sp-byte-blt list (1+ base) str 0 len)
(push (pathname (concatenate 'string filename str)) entries)))))
;;; A timing macro.
(defmacro time (form)
"Evaluates the Form and outputs timing statistics on *Trace-Output*."
(let ((old-time (gensym))
(new-time (gensym)))
`(let ((,old-time)
(,new-time))
(prog2
(setq ,old-time (%sp-io-get-time))
,form
(setq ,new-time (%sp-io-get-time))
(format *trace-output*
"~&Evaluation took ~S second~:P (~S jiff~:@P) of real time.~%"
(/ (- ,new-time ,old-time) 60.0) (- ,new-time ,old-time))))))