;;; -*- Mode:LISP; Package:MFS; Base:10; Readtable:CL -*- #|| Copyright LISP Machine, Inc. 1985, 1986 See filename "Copyright.Text" for licensing and release information. ********************************************************* ********************************************************* *** NOTE: This is an EXAMPLE, not LMI supported code. *** *** information contained in this example is subject *** *** to change without notice. The ways of doing *** *** the things contained in the example may change *** *** between system releases. Some techniques which *** *** are mere examples in one release may become built *** *** in system features in the next release. Use good *** *** judgement when copying these techniques. Most *** *** examples have been motivated by specific customer *** *** requests, and may not be the best engineered *** *** or most efficient solution for someone else. *** ********************************************************* ********************************************************* ||# ;;; We need a structure examiner that can be called from any program ;;; and that can examine circular structures effectively. ;;; 11/06/85 19:14:36 -George Carrette. (defvar *exhibit-help* "~%~ H ....... history~%~ L ....... last object~%~ 0...9 ... get that slot~%~ G ....... get slot, read index number~%~ R ....... replace a slot, reading index and value~%~ D ....... describe current object E ....... evaluate something~% M ....... MOBY-DDT on current object~%") (defun moby-exhibit (object) "A simple structure examiner/editor" (do ((command #\CONTROL-L (progn (format t "~&->") (read-char))) (object-history nil) (current-object object) (nslots nil) (selection-command nil nil) (replace-command nil nil) (punting-stream (make-punting-stream))) (nil) (selector (char-upcase command) char= ((#\? #\HELP) (format t *exhibit-help*)) ((#\E) (let ((* current-object)) (eval (prompt-and-read :read "~&evaluate>")))) ((#\END #\Q) (RETURN CURRENT-OBJECT)) ((#\control-L #\PAGE) (setq nslots (display-object current-object punting-stream))) (#\H (setq current-object (prog1 object-history (push current-object object-history))) (setq nslots (display-object current-object punting-stream))) ((#\P #\L) (setq current-object (prog1 (pop object-history) (setq object-history (append object-history (list current-object))))) (setq nslots (display-object current-object punting-stream))) ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (setq selection-command (digit-char-p command))) (#\G (format t "Get Index->") (setq selection-command (read-index))) (#\D (DESCRIBE CURRENT-OBJECT)) (#\M (moby-ddt nil current-object)) (#\R (format t "Replace Index->") (setq replace-command (read-index)))) (cond (selection-command (cond ((< selection-command nslots) (push current-object object-history) (multiple-value-bind (new errorp) (select-element current-object selection-command) (cond (errorp (format t "~&Cant select because ~A~%" new)) ('else (setq current-object new) (setq nslots (display-object current-object punting-stream)))))) ('else (format t "~&Index must be < ~D: ~D" nslots selection-command)))) (replace-command (cond ((< replace-command nslots) (let ((errorp (replace-element current-object replace-command (let ((* current-object)) (prompt-and-read :eval-read "~&New Value for slot ~D: " replace-command))))) (cond (errorp (format t "~&Cant replace because ~A~%" errorp)) ('else (setq nslots (display-object current-object punting-stream)))))) ('else (format t "~&Index must be < ~D: ~D" nslots replace-command))))))) ;; An object display routine should print elements of the object and index numbers. ;; The number of slots in the object is returned. (defun display-object (x s) (send s :clear-display) (cond ((instancep x) (cond ((send x :get-handler-for :number-of-display-slots) (prog1 (send x :number-of-display-slots) (catch 'output-punt (send x :display-slots)))) ('else (prog1 (number-of-instance-display-slots x) (catch 'output-punt (display-instance-slots x s)))))) ((get (type-of x) 'number-of-display-slots) (prog1 (funcall (get (type-of x) 'number-of-display-slots) x) (catch 'output-punt (funcall (get (type-of x) 'display-object) x s)))) ((and (named-structure-p x) (get (named-structure-p x) 'si:defstruct-description)) (prog1 (number-of-defstruct-display-slots x) (catch 'output-punt (display-defstruct x s)))) ((arrayp x) (prog1 (+ (if (array-has-leader-p x) (array-leader-length x) 0) (array-length x)) (catch 'output-punt (display-array x s)))) ('else (catch 'output-punt (display-random x s)) 0))) (defun display-random (x s) (format s "Object of type ~S~%~S" (type-of x) x)) (defun number-of-instance-display-slots (x) (1+ (length (SI:FLAVOR-ALL-INSTANCE-VARIABLES (SI:INSTANCE-FLAVOR X))))) (defun display-instance-slots (x s) (FORMAT S "Instance of type ~S~%" (type-of x)) (format s "[0] ~S~%" (si:INSTANCE-FLAVOR x)) (DO ((IVARS (SI:FLAVOR-ALL-INSTANCE-VARIABLES (SI:INSTANCE-FLAVOR X)) (CDR IVARS)) (I 1 (1+ I))) ((NULL IVARS)) (FORMAT s "[~D] ~S:~27T " I (CAR IVARS)) (IF (= (SI:%P-LDB-OFFSET SI:%%Q-DATA-TYPE X I) DTP-NULL) (FORMAT s "void~%") (FORMAT s "~S~%" (SI:%INSTANCE-REF X I))))) (defun display-array (x s) (let ((leader-slots (if (array-has-leader-p x) (array-leader-length x) 0))) (format s "Array of type ~S, ~:[no leader~; leader ~D long ~:]~%" (nth (%p-ldb si:%%array-type-field x) si:array-types) (array-has-leader-p x) leader-slots) (do ((slot 0 (1+ slot))) ((= slot (+ leader-slots (array-length x)))) (cond ((< slot leader-slots) (format s "[~D] leader ~D: ~s~%" slot slot (array-leader x slot))) (t (format s "[~D] index ~D: ~s~%" slot (- slot leader-slots) (aref x (- slot leader-slots)))))))) (defun number-of-defstruct-display-slots (x) (length (SI:DEFSTRUCT-DESCRIPTION-SLOT-ALIST (GET (IF (CONSP X) (CAR X) (NAMED-STRUCTURE-P X)) 'SI:DEFSTRUCT-DESCRIPTION)))) (defun display-defstruct (x s) ;; this is not very carefull, since the defstruct description can many times ;; be out of whack with objects. (let ((DESCRIPTION (GET (IF (CONSP X) (CAR X) (NAMED-STRUCTURE-P X)) 'SI:DEFSTRUCT-DESCRIPTION))) (format s "[0] Slots of a ~S ~S~%" (type-of x) (SI:DEFSTRUCT-DESCRIPTION-NAME DESCRIPTION)) (DO ((I 1 (1+ I)) (L (SI:DEFSTRUCT-DESCRIPTION-SLOT-ALIST DESCRIPTION) (CDR L))) ((NULL L)) (FORMAT S "[~D] ~30A ~S~%" I (CAAR L) (EVAL `(,(SI:DEFSTRUCT-SLOT-DESCRIPTION-REF-MACRO-NAME (CDAR L)) ',X)))))) ;; a selection routine receives the object and slot number. it should select ;; that element if possible, return it. If not then return two values, ;; and error message and T. (defun select-element (x slot) (cond ((instancep x) (cond ((send x :get-handler-for :select-element) (send x :select-element slot)) ('else (select-instance-element x slot)))) ((get (type-of x) 'select-element) (funcall (get (type-of x) 'select-element) x slot)) ((and (named-structure-p x) (get (named-structure-p x) 'si:defstruct-description)) (select-element-defstruct x slot)) ((arrayp x) (select-element-array x slot)) ('else (values "dont know how to select an element from this" t)))) (defun select-instance-element (x slot) (cond ((zerop slot) (si:instance-flavor x)) ((= (SI:%P-LDB-OFFSET SI:%%Q-DATA-TYPE X SLOT) DTP-NULL) (values "instance cell is unbound" t)) ('else (SI:%INSTANCE-REF X SLOT)))) (defun select-element-array (x slot) (let ((leader-slots (if (array-has-leader-p x) (array-leader-length x) 0))) (if (< slot leader-slots) (array-leader x slot) (aref x (- slot leader-slots))))) (defun select-element-defstruct (x slot) (let ((DESCRIPTION (GET (IF (CONSP X) (CAR X) (NAMED-STRUCTURE-P X)) 'SI:DEFSTRUCT-DESCRIPTION))) (cond ((= slot 0) (SI:DEFSTRUCT-DESCRIPTION-NAME DESCRIPTION)) ('else (DO ((I 1 (1+ I)) (L (SI:DEFSTRUCT-DESCRIPTION-SLOT-ALIST DESCRIPTION) (CDR L))) ((OR (= I SLOT) (NULL L)) (COND ((NULL L) (values "no such slot?" t)) ('else (EVAL `(,(SI:DEFSTRUCT-SLOT-DESCRIPTION-REF-MACRO-NAME (CDAR L)) ',X)))))))))) ;; a replacement routine receives three arguments, object, slot, value. ;; It should do the replacement, return NIL if ok, otherwise an error message. (defun replace-element (x slot value) (cond ((instancep x) (cond ((send x :get-handler-for :replace-element) (send x :replace-element slot value)) ('else (replace-instance-element x slot value)))) ((get (type-of x) 'replace-element) (funcall (get (type-of x) 'replace-element) x slot value)) ((and (named-structure-p x) (get (named-structure-p x) 'si:defstruct-description)) (replace-element-defstruct x slot value)) ((arrayp x) (replace-element-array x slot value)) ('else "no routine for replacing parts of this structure"))) (defun replace-instance-element (x slot value) (cond ((zerop slot) "Dangerous! Unimplemented dangerous mode") ('else (setf (si:%instance-ref x slot) value) nil))) (defun replace-element-defstruct (x slot value) (let ((DESCRIPTION (GET (IF (CONSP X) (CAR X) (NAMED-STRUCTURE-P X)) 'SI:DEFSTRUCT-DESCRIPTION))) (cond ((= slot 0) "can't change name of struct") ('else (DO ((I 1 (1+ I)) (L (SI:DEFSTRUCT-DESCRIPTION-SLOT-ALIST DESCRIPTION) (CDR L))) ((OR (= I SLOT) (NULL L)) (COND ((NULL L) "no such slot?") ('else (EVAL `(SETF (,(SI:DEFSTRUCT-SLOT-DESCRIPTION-REF-MACRO-NAME (CDAR L)) ',X) ',VALUE)) nil)))))))) (defun replace-element-array (x slot value) (let ((leader-slots (if (array-has-leader-p x) (array-leader-length x) 0))) (cond ((< slot leader-slots) (setf (array-leader x slot) value)) (t (setf (aref x (- slot leader-slots)) value))))) ;; handlers for our favorite objects. (defun (symbol number-of-display-slots) (x) (+ 4 (quotient (length (plist x)) 2))) (defun (symbol display-object) (x s) (format s "This is a symbol~%") (format s "[0] SYMBOL-NAME: ~S~%" (symbol-name x)) (format s "[1] ~:[unbound~;Value: ~S~]~%" (boundp x) (and (boundp x) (symbol-value x))) (format s "[2] ~:[no function~;function: ~S~]~%" (fboundp x) (and (fboundp x) (symbol-function x))) (format s "[3] PACKAGE: ~S~%" (symbol-package x)) (do ((l (plist x) (cddr l)) (i 4 (1+ i))) ((null l)) (format s "[~D] ~S: ~S~%" i (car l) (cadr l)))) (defun (symbol select-element) (x i) (print (list x i)) (cond ((= i 0) (symbol-name x)) ((= i 1) (if (boundp x) (symbol-value x) (values "symbol is unbound" t))) ((= i 2) (if (fboundp x) (symbol-function x) (values "symbol has no function" t))) ((= i 3) (symbol-package x)) ('else (do ((j 4 (1+ j)) (l (plist x) (cddr l))) ((= i j) (cadr l)))))) (defun (symbol replace-element) (x i v) (cond ((= i 0) "you can't change the name of a symbol") ((= i 1) (setf (symbol-value x) v) nil) ((= i 2) (setf (symbol-function x) v) nil) ((= i 3) (setf (symbol-package x) v) nil) ('else (do ((j 4 (1+ j)) (l (plist x) (cddr l))) ((= i j) (cond ((null l) "plist changed suddenly?") ('else (setf (cadr l) v) nil))))))) ;; to do: cons displatch to defstruct functions if car of form ;; is a symbol with an si:defstruct-description (defvar *defstruct-consp* t) (defun defstruct-consp (x) (and *defstruct-consp* (symbolp (car x)) (get (car x) 'si:defstruct-description))) (defun (cons number-of-display-slots) (x) (if (defstruct-consp x) (number-of-defstruct-display-slots x) (cons-number-of-display-slots x))) (defun cons-number-of-display-slots (x) (+ (length x) (if (null (cdr (last x))) 0 1))) (defun (cons display-object) (x s) (if (defstruct-consp x) (display-defstruct x s) (cons-display-object x s))) (defun cons-display-object (x s) (cond ((null (cdr (last x))) (format s "Elements of a proper list ~D long~%" (length x)) (do ((l x (cdr l)) (i 0 (1+ i))) ((null l)) (format s "[~D] ~S~%" i (car l)))) ((atom (cdr x)) (format s "A cons cell~%[0] CAR: ~S~%[1] CDR: ~S~%" (car x) (cdr x))) ('else (format s "~D Elements of an improper list (non-null cdr)~%:" (1+ (length x))) (do ((l x (cdr l)) (i 0 (1+ i))) ((atom l) (format s "[~D] ~S~%" i l)) (format s "[~D] ~S~%" i (car l)))))) (defun (cons select-element) (x i) (if (defstruct-consp x) (select-element-defstruct x i) (cons-select-element x i))) (defun cons-select-element (x i) (do ((l x (cdr l)) (j 0 (1+ j))) ((or (= i j) (atom l)) (cond ((and (= i j) (atom l)) l) ((= i j) (car l)))))) (defun (cons replace-element) (x i v) (if (defstruct-consp x) (replace-element-defstruct x i v) (cons-replace-element x i v))) (defun cons-replace-element (x i v) (do ((l x (cdr l)) (j 0 (1+ j))) ((or (= i j) (atom (cdr l))) (cond ((= j i) (setf (car l) v)) ((= (1+ j) i) (setf (cdr l) v))) nil))) ;; (DEFVAR *SAFE-PUNT* NIL) (defun make-punting-stream (&optional (forward-to standard-output) (listen-on standard-input)) (let ((stream) (limit (or (send-if-handles forward-to :size-in-characters) 70)) (index 0)) (setq stream #'(lambda (operation &optional arg1 &rest args) (si:selectq-with-which-operations operation (:tyo (cond ((char= arg1 #\return) (send forward-to :tyo #\return) (setq index 0) (if (send listen-on :listen) (throw 'output-punt nil))) ((not (graphic-char-p arg1)) (princ "#\\" stream) (princ (or (char-name arg1) (char-code arg1)) stream)) ((< index limit) (send forward-to :tyo arg1) (incf index)) (*safe-punt* (if (send listen-on :listen) (throw 'output-punt nil)) (throw 'punt t)))) (:clear-display (send forward-to :clear-screen) (setq index 0)) (:print (let ((*safe-punt* t)) (catch 'punt (si:print-object arg1 (car args) stream))) t) (:string-out (let ((*safe-punt* t)) (catch 'punt (do ((j (or (car args) 0) (1+ j)) (end (or (cadr args) (length arg1)))) ((= j end)) (send stream :tyo (aref arg1 j)))))) (:fresh-line (send forward-to :fresh-line) (setq index 0)) (:operation-handled-p (memq arg1 (send stream :which-operations)))))))) (defun read-index () (do ((c) (index 0 (+ (* index 10) (digit-char-p c)))) ((not (digit-char-p (setq c (read-char)))) index) (write-char c)))