;;; -*- Mode:LISP; Package:USER; Syntax:COMMON-LISP; Base:10 -*- ;;; Copyright (c) 1988 by Robert W. Kerns. ;;; Permission is granted to distribute this freely, or to ;;; incorporate this in any commercial product, so long as this ;;; copyright notice remains intact. ;;; This is where the handlers live. (defvar *definition-spec-handlers* (make-hash-table :test 'equal)) ;;; This is used by the expansion of the FUNCTION-SPEC-INVOKE macro. (defun lookup-spec-handler (spec) (multiple-value-bind (spec-type spec) (etypecase spec (symbol (values 'symbol `(symbol ,spec))) (list (values (first spec) spec))) (let ((spec-handler (gethash spec-type *definition-spec-handlers*))) (unless spec-handler (error "Definition spec type ~S not recognized." spec-type)) (values spec-handler spec)))) ;;; This is here just to avoid having to redefine the DEFUN in the working system, ;;; which cannot be done portably. When you install this for real in your system, ;;; replace the usage of this in DEF-DEFINITION-SPEC-HANDLER with DEFUN, and modify ;;; whatever DEFUN uses to store the definition (compiled and interpreted) to use ;;; (SETF (FDEFINITION ) ) ;;; Note that this kludging of a temporary name is not satisfactory for use as a substitute ;;; for this mechanism. Consider, for example, what happens if you export a symbol after defining ;;; the function. (defmacro defun-dummy (spec arglist &body body) (let ((temp-name (make-symbol (let ((*package* (find-package "LISP"))) (format nil "~S" spec))))) (if (symbolp spec) `(defun ,spec ,arglist ,@body) `(progn (defun ,temp-name ,arglist ,@body) (setf (fdefinition ',spec) #',temp-name))))) ;;; Run-time subroutine: Verify that the list of the required length. ;;; Does so carefully, so as to not blow out on dotted lists; otherwise we'd ;;; just use LENGTH. (defun verify-list-of-length (list length) (do ((list list (rest list)) (n length (1- n))) ((zerop n) (return t)) (when (atom list) (return nil)))) ;;; Run-time subroutine: Verify that the list is at least of the required length. ;;; Does so carefully, so as to not blow out on dotted lists; otherwise we'd ;;; just use LENGTH. (defun verify-list-over-length (arglist-var length) (do ((n 0 (1+ n)) (list arglist-var (rest list))) ((>= n length) (return t)) (when (atom list) (return nil)))) ;;; Run-time subroutine: Verify that only valid keywords are supplied. (defun verify-keyword-arguments (arglist valid-keys) (do ((key (car arglist) (car arglist)) (arglist arglist (cddr arglist))) ((endp arglist)) (unless (find key valid-keys :key #'key-arg-keyword) (return-from verify-keyword-arguments nil))) (return-from verify-keyword-arguments t)) ;;; Default place to put properties. (defvar *definition-spec-properties* (make-hash-table :test #'equal)) ;;; This one is separate so nobody things DOCUMENTATION is a ;;; "standard property". (defvar *definition-spec-documentation* (make-hash-table :test #'equal)) (defun fdefinition (spec) (function-spec-invoke fdefinition spec)) (defsetf fdefinition set-fdefinition) (defun set-fdefinition (spec new-definition) (etypecase spec (symbol (setf (symbol-function spec) new-definition)) (list (function-spec-invoke (setf fdefinition) spec new-definition) new-definition))) ;;; This gets called when the user's code doesn't handle something. (defun default-definition-spec-handler (spec operation &rest arglist) (case operation (documentation (gethash spec *definition-spec-documentation*)) (setf-documentation (let ((new-doc (first arglist))) (setf (gethash spec *definition-spec-documentation*) new-doc))) (otherwise (error "Unimplemented operation on definition-spec ~S: ~S." spec (or (first (find operation *definition-spec-messages* :key #'second)) operation)))))