Skip to content

Latest commit

 

History

History
1828 lines (1276 loc) · 73.2 KB

README.md

File metadata and controls

1828 lines (1276 loc) · 73.2 KB

DRef Manual

Table of Contents

[in package DREF]

1 Introduction

What if definitions were first-class objects?

Some defining forms do not create first-class objects. For example, DEFUN creates FUNCTION objects, but DEFVAR does not create variable objects as no such thing exists. The main purpose of this library is to fill this gap with the introduction of XREF objects:

(xref '*my-var* 'variable)
==> #<XREF *MY-VAR* VARIABLE>

XREFs just package up a name (*MY-VAR*) and a locative (VARIABLE). They need not denote existing definitions until we actually want to use them:

(docstring (xref '*my-var* 'variable))
.. debugger invoked on LOCATE-ERROR:
..   Could not locate *MY-VAR* VARIABLE.
(defvar *my-var* nil
  "This is my var.")

(docstring (xref '*my-var* 'variable))
=> "This is my var."

Behind the scenes, the DOCSTRING function LOCATEs the definition corresponding to its XREF argument, turning it into a DREF:

(locate (xref '*my-var* 'variable))
==> #<DREF *MY-VAR* VARIABLE>

Within DRef, the DREF Subclasses form the basis of extending DOCSTRING, SOURCE-LOCATION and ARGLIST. Outside DRef, PAX makes PAX:DOCUMENT extensible through PAX:DOCUMENT-OBJECT*, which has methods specialized on DREFs.

Finally, existing definitions can be queried with DEFINITIONS and DREF-APROPOS:

(definitions 'dref-ext:locate*)
==> (#<DREF LOCATE* GENERIC-FUNCTION>
-->  #<DREF LOCATE* (METHOD NIL (GLOSSARY-TERM))>
-->  #<DREF LOCATE* (METHOD NIL (SECTION))>
-->  #<DREF LOCATE* (METHOD NIL (READTABLE))>
-->  #<DREF LOCATE* (METHOD NIL (PACKAGE))>
-->  #<DREF LOCATE* (METHOD NIL (ASDF/SYSTEM:SYSTEM))>
-->  #<DREF LOCATE* (METHOD NIL (CLASS))>
-->  #<DREF LOCATE* (METHOD NIL (METHOD))>
-->  #<DREF LOCATE* (METHOD NIL (GENERIC-FUNCTION))>
-->  #<DREF LOCATE* (METHOD NIL (FUNCTION))>
-->  #<DREF LOCATE* (METHOD (:AROUND) (T))>
-->  #<DREF LOCATE* (METHOD NIL (T))> #<DREF LOCATE* (METHOD NIL (XREF))>
-->  #<DREF LOCATE* (METHOD NIL (DREF))>)
(dref-apropos 'locate-error :package :dref :external-only t)
==> (#<DREF LOCATE-ERROR CONDITION> #<DREF LOCATE-ERROR FUNCTION>)

(dref-apropos "ate-err" :package :dref :external-only t)
==> (#<DREF LOCATE-ERROR CONDITION> #<DREF LOCATE-ERROR FUNCTION>)

2 Locatives and References

After the Introduction, here we get into the details. Of special interest are:

  • The XREF function to construct an arbitrary reference without any checking of validity.

  • LOCATE and DREF to construct a syntactically valid reference (matching the LAMBDA-LIST in the locative type's definition) that refers to an exisiting definition.

  • RESOLVE to find the first-class (non-XREF) object the definition refers to, if any.

Operations (ARGLIST, DOCSTRING, SOURCE-LOCATION) know how to deal with references (discussed in the Extending DRef).

  • [class] XREF

    An XREF (cross-reference) may represent some kind of definition of its name in the context given by its locative. The definition may not exist and the locative may be malformed. The subclass DREF represents definitions that exist.

  • [class] DREF XREF

    DREFs can be thought of as referring to definitions that actually exist, although changes in the system can invalidate them (for example, a DREF to a function definition can be invalidated by FMAKUNBOUND).

    DREFs must be created with LOCATE, and their purpose is to allow easy specialization of other generic functions (see Extending DRef) and to confine locative validation to LOCATE.

  • [function] XREF NAME LOCATIVE

    A shorthand for (MAKE-INSTANCE 'XREF :NAME NAME :LOCATIVE LOCATIVE). It does no error checking: the LOCATIVE-TYPE of LOCATIVE-TYPE need not be defined, and the LOCATIVE-ARGS need not be valid. Use LOCATE or the DREF function to create DREF objects.

  • [function] XREF= XREF1 XREF2

    See if XREF1 and XREF2 have the same XREF-NAME and XREF-LOCATIVE under EQUAL. Comparing like this makes most sense for DREFs. However, two XREFs different under XREF= may denote the same DREFs.

  • [function] LOCATE OBJECT &OPTIONAL (ERRORP T)

    Return a DREF representing the definition given by the arguments. In the same dynamic environment, two DREFs denote the same thing if and only if they are XREF=.

    OBJECT must be a supported first-class object, a DREF, or an XREF:

    (locate #'print)
    ==> #<DREF PRINT FUNCTION>
    (locate (locate #'print))
    ==> #<DREF PRINT FUNCTION>
    (locate (xref 'print 'function))
    ==> #<DREF PRINT FUNCTION>

    LOCATE-ERROR(0 1) is signalled if OBJECT is an XREF with malformed LOCATIVE-ARGS, or if no corresponding definition is found. If ERRORP is NIL, then NIL and the LOCATE-ERROR condition are returned instead.

    (locate (xref 'no-such-function 'function))
    .. debugger invoked on LOCATE-ERROR:
    ..   Could not locate NO-SUCH-FUNCTION FUNCTION.
    ..   NO-SUCH-FUNCTION is not a symbol naming a function.
    (locate (xref 'print '(function xxx)))
    .. debugger invoked on LOCATE-ERROR:
    ..   Could not locate PRINT #'XXX.
    ..   Bad arguments (XXX) for locative FUNCTION with lambda list NIL.
    (locate "xxx")
    .. debugger invoked on LOCATE-ERROR:
    ..   Could not locate "xxx".

    Use the low-level XREF to construct an XREF without error checking.

    Can be extended via LOCATE*.

  • [function] DREF NAME LOCATIVE &OPTIONAL (ERRORP T)

    Shorthand for (LOCATE (XREF NAME LOCATIVE) ERRORP).

  • [function] RESOLVE OBJECT &OPTIONAL (ERRORP T)

    If OBJECT is an XREF, then return the first-class object associated with its definition if any. Return OBJECT if it's not an XREF. Thus, the value returned is never an XREF.

    (resolve (dref 'print 'function))
    ==> #<FUNCTION PRINT>
    (resolve #'print)
    ==> #<FUNCTION PRINT>

    If OBJECT is an XREF, and the definition for it cannot be LOCATEd, then signal a LOCATE-ERROR condition.

    (resolve (xref 'undefined 'variable))
    .. debugger invoked on LOCATE-ERROR:
    ..   Could not locate UNDEFINED VARIABLE.

    If there is a definition, but there is no first-class object corresponding to it, then signal a RESOLVE-ERROR condition or return NIL depending on ERRORP:

    (resolve (dref '*print-length* 'variable))
    .. debugger invoked on RESOLVE-ERROR:
    ..   Could not resolve *PRINT-LENGTH* VARIABLE.
    (resolve (dref '*print-length* 'variable) nil)
    => NIL

    RESOLVE is a partial inverse of LOCATE: if a DREF is RESOLVEable, then LOCATEing the object it resolves to recovers the DREF equivalent to the original (XREF= and of the same type but not EQ).

    Can be extended via RESOLVE*.

  • [condition] LOCATE-ERROR ERROR

    Signalled by LOCATE when the definition cannot be found, and ERRORP is true.

  • [condition] RESOLVE-ERROR ERROR

    Signalled by RESOLVE when the object defined cannot be returned, and ERRORP is true.

3 Listing Definitions

  • [function] DEFINITIONS NAME &KEY (LOCATIVE-TYPES (LISP-LOCATIVE-TYPES))

    Return all definitions of NAME that match LOCATIVE-TYPES as a list of DREFs.

    The DREF-NAMEs may not be the same as NAME, for example, when NAME is a package nickname:

    (definitions 'pax)
    ==> (#<DREF "MGL-PAX" PACKAGE>)

    Can be extended via MAP-DEFINITIONS.

  • [function] DREF-APROPOS NAME &KEY PACKAGE EXTERNAL-ONLY CASE-SENSITIVE (LOCATIVE-TYPES '(:LISP))

    Return a list of DREFs corresponding to existing definitions that match the various arguments. First, (DREF-APROPOS NIL :LOCATIVE-TYPES NIL) lists all definitions in the system. Arguments with non-NIL values filter the list of definitions.

    Roughly speaking, when NAME or PACKAGE is a SYMBOL, they must match the whole name of the definition:

    (dref-apropos 'method :package :dref :external-only t)
    ==> (#<DREF METHOD CLASS> #<DREF METHOD LOCATIVE>)

    On the other hand, when NAME or PACKAGE is a STRING(0 1), they are matched as substrings:

    (dref-apropos "method" :package :dref :external-only t)
    ==> (#<DREF METHOD CLASS> #<DREF METHOD LOCATIVE>
    -->  #<DREF METHOD-COMBINATION CLASS> #<DREF METHOD-COMBINATION LOCATIVE>)

    The list of LOCATIVE-TYPES, if non-NIL, filters out definitions whose locative types are not listed:

    (dref-apropos "method" :package :dref :external-only t
                  :locative-types '(class))
    ==> (#<DREF METHOD CLASS> #<DREF METHOD-COMBINATION CLASS>)

    In the list, the special keywords :ALL, :LISP, :PSEUDO match all LOCATIVE-TYPES, LISP-LOCATIVE-TYPES and PSEUDO-LOCATIVE-TYPES, respectively.

    When PACKAGE is :NONE, only non-symbol names are matched:

    (dref-apropos "dref" :package :none)
    ==> (#<DREF "DREF" PACKAGE> #<DREF "DREF-EXT" PACKAGE>
    -->  #<DREF "DREF-TEST" PACKAGE> #<DREF "dref" ASDF/SYSTEM:SYSTEM>
    -->  #<DREF "dref/full" ASDF/SYSTEM:SYSTEM>
    -->  #<DREF "dref/test" ASDF/SYSTEM:SYSTEM>
    -->  #<DREF "dref/test-autoload" ASDF/SYSTEM:SYSTEM>)
    

    The exact rules of filtering are as follows. Let C be the name of the candidate definition from the list of all definitions that we are matching against the arguments and denote its string representation (PRINC-TO-STRING C) with P. Note that PRINC-TO-STRING does not print the package of symbols. We say that two strings match if CASE-SENSITIVE is NIL and they are EQUALP, or CASE-SENSITIVE is true and they are EQUAL. CASE-SENSITIVE affects substring comparisons too.

    • If NAME is a SYMBOL, then its SYMBOL-NAME must match P.

    • If NAME is a STRING, then it must be a substring of P.

    • If PACKAGE is :NONE, then C must not be a SYMBOL.

    • If PACKAGE is not NIL or :NONE, then C must be a symbol.

    • If PACKAGE is a PACKAGE, it must be EQ to the SYMBOL-PACKAGE of C.

    • If PACKAGE is a SYMBOL other than :NONE, then its SYMBOL-NAME must match the PACKAGE-NAME or one of the PACKAGE-NICKNAMES of SYMBOL-PACKAGE of C.

    • If PACKAGE is a STRING, then it must be a substring of the PACKAGE-NAME of SYMBOL-PACKAGE of C.

    • If EXTERNAL-ONLY and C is a symbol, then C must be external in a matching package.

    • If LOCATIVE-TYPES is NIL, then it matches everything.

    • If LOCATIVE-TYPEs is non-NIL, then the LOCATIVE-TYPE of the candidate definition must be in it (handling :ALL, :LISP, and :PSEUDO as described above).

    Can be extended via MAP-NAMES.

  • [function] LISP-LOCATIVE-TYPES

    Return the locative types that correspond to Lisp definitions except UNKNOWN. These are the ones defined with DEFINE-LOCATIVE-TYPE.

  • [function] PSEUDO-LOCATIVE-TYPES

    Return the locative types that correspond to non-Lisp definitions plus UNKNOWN. These are the ones defined with DEFINE-PSEUDO-LOCATIVE-TYPE.

4 Operations

The following functions take a single object definition as their argument. They may try to LOCATE the definition of the object, which may signal a LOCATE-ERROR condition.

  • [function] ARGLIST OBJECT

    Return the arglist of the definition of OBJECT or NIL if the arglist cannot be determined.

    The second return value indicates whether the arglist has been found. Furthermore, :ORDINARY indicates an ordinary lambda list, :MACRO a macro lambda list, :DEFTYPE a deftype lambda list, and :DESTRUCTURING a destructuring lambda list. Other non-NIL values are also allowed.

    (arglist #'arglist)
    => (OBJECT)
    => :ORDINARY
    (arglist (dref 'define-locative-type 'macro))
    => (LOCATIVE-TYPE LAMBDA-LIST &BODY DOCSTRING)
    => :MACRO
    (arglist (dref 'method 'locative))
    => (METHOD-QUALIFIERS METHOD-SPECIALIZERS)
    => :DESTRUCTURING

    This function supports MACROs, COMPILER-MACROs, SETF functions, FUNCTION(0 1)s, GENERIC-FUNCTIONs, METHODs, TYPEs, LOCATIVEs. Note that ARGLIST depends on the quality of SWANK-BACKEND:ARGLIST. With the exception of SBCL, which has perfect support, all Lisp implementations have minor omissions:

    • DEFTYPE lambda lists on ABCL, AllegroCL, CLISP, CCL, CMUCL, ECL;

    • default values in MACRO lambda lists on AllegroCL; various edge

    • cases involving traced functions.

    Can be extended via ARGLIST*

  • [function] DOCSTRING OBJECT

    Return the docstring from the definition of OBJECT. As the second value, return the *PACKAGE* that was in effect when the docstring was installed or NIL if it cannot be determined (this is used by PAX:DOCUMENT when Parsing the docstring). This function is similar in purpose to CL:DOCUMENTATION.

    Note that some locative types such as ASDF:SYSTEMs and DECLARATIONs have no docstrings, and some Lisp implementations do not record all docstrings. The following are known to be missing:

    Can be extended via DOCSTRING*.

  • [function] SOURCE-LOCATION OBJECT &KEY ERRORP

    Return the Swank source location for the defining form of OBJECT. If no source location was found, then either an ERROR condition is signalled if ERRORP else the ERROR is returned as the second value (with the first being NIL). The returned Swank location object is to be accessed only through the Source Locations API or to be passed to e.g Slime's slime-goto-source-location.

    Note that the availability of source location information varies greatly across Lisp implementations.

    Can be extended via SOURCE-LOCATION*.

5 Locative Types

The following are the locative types supported out of the box. As all locative types, they are named by symbols. When there is a CL type corresponding to the reference's locative type, the references can be RESOLVEd to a unique object as is the case in

(resolve (dref 'print 'function))
==> #<FUNCTION PRINT>

Even if there is no such CL type, the ARGLIST, the DOCSTRING, and the SOURCE-LOCATION of the defining form is usually recorded unless otherwise noted.

5.1 Locatives for Variables

  • [locative] VARIABLE &OPTIONAL INITFORM

    Refers to a global special variable. INITFORM, or if not specified, the global value of the variable is to be used for presentation.

    (dref '*print-length* 'variable)
    ==> #<DREF *PRINT-LENGTH* VARIABLE>

    VARIABLE references do not RESOLVE.

  • [locative] CONSTANT &OPTIONAL INITFORM

    Refers to a constant variable defined with DEFCONSTANT. INITFORM, or if not specified, the value of the constant is included in the documentation. The CONSTANT locative is like the VARIABLE locative, but it also checks that its object is CONSTANTP.

    CONSTANT references do not RESOLVE.

5.2 Locatives for Macros

  • [locative] SYMBOL-MACRO

    Refers to a global symbol macro, defined with DEFINE-SYMBOL-MACRO. Note that since DEFINE-SYMBOL-MACRO does not support docstrings, PAX defines methods on the DOCUMENTATION generic function specialized on (DOC-TYPE (EQL 'SYMBOL-MACRO)).

    (define-symbol-macro my-mac 42)
    (setf (documentation 'my-mac 'symbol-macro)
          "This is MY-MAC.")
    (documentation 'my-mac 'symbol-macro)
    => "This is MY-MAC."
    

    SYMBOL-MACRO references do not RESOLVE.

  • [locative] COMPILER-MACRO

    Refers to a compiler macro, typically defined with DEFINE-COMPILER-MACRO.

    COMPILER-MACRO references do not RESOLVE.

  • [locative] SETF &OPTIONAL METHOD

    Refers to a setf expander (see DEFSETF and DEFINE-SETF-EXPANDER) or a setf function (e.g. (DEFUN (SETF NAME) ...) or the same with DEFGENERIC). The format in DEFSECTION is (<NAME> SETF) in all these cases.

    To refer to methods of a setf generic function, use a METHOD locative inside SETF like this:

      (dref 'documentation '(setf (method () (t symbol (eql function))))
    

    References to setf functions RESOLVE to the function object. Setf expander references do not RESOLVE.

5.3 Locatives for Functions

  • [locative] FUNCTION

    Refers to a global function, typically defined with DEFUN. The name must be a SYMBOL (see the SETF locative for how to reference setf functions). It is also allowed to reference GENERIC-FUNCTIONs as FUNCTIONs:

    (dref 'docstring 'function)
    ==> #<DREF DOCSTRING FUNCTION>

  • [locative] METHOD METHOD-QUALIFIERS METHOD-SPECIALIZERS

    Refers to METHODs named by SYMBOLs (for SETF methods, see the SETF locative). METHOD-QUALIFIERS and METHOD-SPECIALIZERS are similar to the CL:FIND-METHOD's arguments of the same names. For example, the method

    (defgeneric foo-gf (x y z)
      (:method :around (x (y (eql 'xxx)) (z string))
        (values x y z)))

    can be referred to as

    (dref 'foo-gf '(method (:around) (t (eql xxx) string)))
    ==> #<DREF FOO-GF (METHOD (:AROUND) (T (EQL XXX) STRING))>

    METHOD is not EXPORTABLE-LOCATIVE-TYPE-P.

  • [locative] ACCESSOR CLASS-NAME

    Refers to an :ACCESSOR in a DEFCLASS:

    (defclass foo ()
      ((xxx :accessor foo-xxx)))
    
    (dref 'foo-xxx '(accessor foo))
    ==> #<DREF FOO-XXX (ACCESSOR FOO)>

    An :ACCESSOR in DEFCLASS creates a reader and a writer method. Somewhat arbitrarily, ACCESSOR references RESOLVE to the writer method but can be LOCATEd with either.

  • [locative] READER CLASS-NAME

    Like ACCESSOR, but refers to a :READER method in a DEFCLASS.

  • [locative] WRITER CLASS-NAME

    Like ACCESSOR, but refers to a :WRITER method in a DEFCLASS.

  • [locative] STRUCTURE-ACCESSOR &OPTIONAL STRUCTURE-CLASS-NAME

    Refers to an accessor function generated by DEFSTRUCT. A LOCATE-ERROR condition is signalled if the wrong STRUCTURE-CLASS-NAME is provided.

    Note that there is no portable way to detect structure accessors, and on some platforms, (LOCATE #'MY-ACCESSOR), DEFINITIONS and DREF-APROPOS will return FUNCTION(0 1) references instead. On such platforms, STRUCTURE-ACCESSOR references do not RESOLVE.

5.4 Locatives for Types and Declarations

  • [locative] TYPE

    This locative can refer to any Lisp type and compound type specifiers such as AND. For types defined with DEFTYPE, their ARGLIST is available. CLASSes and CONDITIONs may be referred to as TYPEs:

    (dref 'xref 'type)
    ==> #<DREF XREF CLASS>
    (dref 'locate-error 'type)
    ==> #<DREF LOCATE-ERROR CONDITION>

    TYPE references do not RESOLVE.

  • [locative] CLASS

    Naturally, CLASS is the locative type for CLASSes. CONDITIONs may be referred to as CLASSes:

    (dref 'locate-error 'class)
    ==> #<DREF LOCATE-ERROR CONDITION>

  • [locative] DECLARATION

    Refers to a declaration, used in DECLARE, DECLAIM and PROCLAIM.

    User code may also define new declarations with CLTL2 functionality, but there is currently no way to provide a docstring, and their ARGLIST is always NIL.

    (cl-environments:define-declaration my-decl (&rest things)
      (values :declare (cons 'foo things)))
    

    DECLARATION references do not RESOLVE.

    Also, SOURCE-LOCATION on declarations currently only works on SBCL.

5.5 Locatives for the Condition System

  • [locative] CONDITION

    CONDITION is the locative type for CONDITIONs.

  • [locative] RESTART

    A locative to refer to the definition of a restart defined by DEFINE-RESTART.

  • [macro] DEFINE-RESTART SYMBOL LAMBDA-LIST &BODY DOCSTRING

    Associate a definition with the name of a restart, which must be a symbol. LAMBDA-LIST should be what calls like (INVOKE-RESTART '<SYMBOL> ...) must conform to, but this not enforced.

    PAX "defines" standard CL restarts such as USE-VALUE(0 1) with DEFINE-RESTART:

    (first-line (source-location-snippet
                 (source-location (dref 'use-value 'restart))))
    => "(define-restart use-value (value)"

    Note that while there is a CL:RESTART class, its instances have no docstring or source location.

5.6 Locatives for Packages and Readtables

  • [locative] ASDF/SYSTEM:SYSTEM

    Refers to a registered ASDF:SYSTEM. The name may be anything ASDF:FIND-SYSTEM supports.

    ASDF:SYSTEM is not EXPORTABLE-LOCATIVE-TYPE-P.

  • [locative] READTABLE

    Refers to a named READTABLE defined with NAMED-READTABLES:DEFREADTABLE, which associates a global name and a docstring with the readtable object. The name may be anything FIND-READTABLE supports. Unfortunately, SOURCE-LOCATION information is not available.

    READTABLE references RESOLVE to FIND-READTABLE on their name.

5.7 DRef Locatives

  • [locative] UNKNOWN DSPEC

    This pseudo locative type is to allow PAX to work in a limited way with locatives it doesn't know. UNKNOWN definitions come from DEFINITIONS, which uses SWANK/BACKEND:FIND-DEFINITIONS. The following examples show PAX stuffing the Swank dspec (:DEFINE-ALIEN-TYPE DOUBLE-FLOAT) into an UNKNOWN locative on SBCL.

    (definitions 'double-float :locative-types (locative-types))
    ==> (#<DREF DOUBLE-FLOAT CLASS> #<DREF DOUBLE-FLOAT (CLHS TYPE)>
    -->  #<DREF DOUBLE-FLOAT (UNKNOWN (:DEFINE-ALIEN-TYPE DOUBLE-FLOAT))>)
    (dref 'double-float '(unknown (:define-alien-type double-float)))
    ==> #<DREF DOUBLE-FLOAT (UNKNOWN (:DEFINE-ALIEN-TYPE DOUBLE-FLOAT))>

    ARGLIST and DOCSTRING return NIL for UNKNOWNs, but SOURCE-LOCATION works.

  • [locative] LAMBDA &KEY ARGLIST ARGLIST-TYPE DOCSTRING DOCSTRING-PACKAGE FILE FILE-POSITION SNIPPET &ALLOW-OTHER-KEYS

    A pseudo locative type that carries its ARGLIST, DOCSTRING and SOURCE-LOCATION in the locative itself. See MAKE-SOURCE-LOCATION for the description of FILE, FILE-POSITION, and SNIPPET. LAMBDA references do not RESOLVE. The name must be NIL.

    (arglist (dref nil '(lambda :arglist ((x y) z)
                                       :arglist-type :macro)))
    => ((X Y) Z)
    => :MACRO
    (docstring (dref nil '(lambda :docstring "xxx"
                                         :docstring-package :dref)))
    => "xxx"
    ==> #<PACKAGE "DREF">
    (source-location-file
     (source-location (dref nil '(lambda :file "xxx.el"))))
    => "xxx.el"

    See the PAX:INCLUDE locative for an example.

6 Glossary

  • [glossary-term] reference

    A reference is an name plus a locative, and it identifies a possible definition. References are of class XREF. When a reference is a DREF, it may also be called a definition.

7 Extending DRef

7.1 References

  • [reader] XREF-NAME XREF (:NAME)

    The name of the reference.

  • [reader] XREF-LOCATIVE XREF (:LOCATIVE)

    The locative of the reference.

    The locative is normalized by replacing single-element lists with their only element:

    (xref 'print 'function)
    ==> #<XREF PRINT FUNCTION>
    (xref 'print '(function))
    ==> #<XREF PRINT FUNCTION>

  • [reader] DREF-NAME DREF

    The same as XREF-NAME, but only works on DREFs. Use it as a statement of intent.

  • [reader] DREF-LOCATIVE DREF

    The same as XREF-LOCATIVE, but only works on DREFs. Use it as a statement of intent.

  • [reader] DREF-ORIGIN DREF

    The object from which LOCATE constructed this DREF. This is an XREF when the LOCATIVE argument to LOCATE was non-NIL and the value NAME-OR-OBJECT argument otherwise. DREF-ORIGIN may have presentation arguments, which are not included in LOCATIVE-ARGS as is the case with INITFORM argument of the VARIABLE locative:

    (dref '*standard-output* '(variable "see-below"))
    ==> #<DREF *STANDARD-OUTPUT* VARIABLE>
    (dref-origin (dref '*standard-output* '(variable "see-below")))
    ==> #<XREF *STANDARD-OUTPUT* (VARIABLE "see-below")>

    The INITFORM argument overrides the global binding of *STANDARD-OUTPUT* when it's PAX:DOCUMENTed:

    (first-line
     (pax:document (dref '*standard-output* '(variable "see-below"))
                   :stream nil))
    => "- [variable] *STANDARD-OUTPUT* \"see-below\""

  • [function] LOCATIVE-TYPE LOCATIVE

    Return locative type of LOCATIVE (which may be from XREF-LOCATIVE). This is the first element of LOCATIVE if it's a list. If it's a symbol, it's that symbol itself.

  • [function] LOCATIVE-ARGS LOCATIVE

    Return the REST of LOCATIVE (which may be from XREF-LOCATIVE) if it's a list. If it's a symbol, then return NIL. The locative args should match the LAMBDA-LIST of the LOCATIVE-TYPE's definition, but this is guaranteed only for locatives of DREFs and is not checked for plain XREFs.

The following convenience functions are compositions of {LOCATIVE-TYPE, LOCATIVE-ARGS} and {XREF-LOCATIVE, DREF-LOCATIVE}.

  • [function] XREF-LOCATIVE-TYPE XREF

  • [function] XREF-LOCATIVE-ARGS XREF

  • [function] DREF-LOCATIVE-TYPE DREF

  • [function] DREF-LOCATIVE-ARGS DREF

7.2 Adding New Locatives

Let's see how to tell DRef about new kinds of definitions through the example of the implementation of the CLASS locative. Note that this is a verbatim PAX:INCLUDE of the sources. Please ignore any internal machinery. The first step is to define the locative type:

(define-locative-type class ()
  "Naturally, CLASS is the locative type for [CLASS][class]es.
  [CONDITIONs][type] may be referred to as CLASSes:

  ```cl-transcript
  (dref 'locate-error 'class)
  ==> #<DREF LOCATE-ERROR CONDITION>
  ```")

Next, we define a subclass of DREF associated with the CLASS locative type and specialize LOCATE*:

(define-definition-class class class-dref)

(defmethod locate* ((class class))
  (make-instance 'class-dref :name (class-name class) :locative 'class))

(defmethod dref* (symbol (locative-type (eql 'class)) locative-args)
  (check-locative-args class locative-args)
  (unless (and (symbolp symbol)
               (find-class symbol nil))
    (locate-error "~S does not name a class." symbol))
  (make-instance 'class-dref :name symbol :locative 'class))

The first method makes (LOCATE (FIND-CLASS 'DREF)) work, while the second is for (DREF 'DREF 'CLASS). Naturally, for locative types that do not define first-class objects, the first method cannot be defined.

Then, with ADD-DREF-ACTUALIZER, we install a function that that runs whenever a new DREF is about to be returned from LOCATE and turn the locative TYPE into the locative CLASS if the denoted definition is of a class:

(defun actualize-type-to-class (dref)
  (when (eq (dref-locative-type dref) 'type)
    (dref (dref-name dref) 'class nil)))

(add-dref-actualizer 'actualize-type-to-class)

Finally, we define a RESOLVE* method to recover the CLASS object from a CLASS-DREF. We also specialize DOCSTRING* and SOURCE-LOCATION*:

(defmethod resolve* ((dref class-dref))
  (find-class (dref-name dref)))

(defmethod docstring* ((class class))
  (documentation* class t))

(defmethod source-location* ((dref class-dref))
  (swank-source-location* (resolve dref) (dref-name dref) 'class))

We took advantage of having just made the class locative type being RESOLVEable, by specializing DOCSTRING* on the CLASS class. SOURCE-LOCATION* was specialized on CLASS-DREF to demonstrate how this can be done for non-RESOLVEable locative types.

Classes have no arglist, so no ARGLIST* method is needed. In the following, we describe the pieces in detail.

  • [macro] DEFINE-PSEUDO-LOCATIVE-TYPE LOCATIVE-TYPE LAMBDA-LIST &BODY DOCSTRING

    Like DEFINE-LOCATIVE-TYPE, but declare that LOCATIVE-TYPE does not correspond to definitions in the Lisp system. Definitions with pseduo locatives are not listed by default by DEFINITIONS.

    Locative types defined with DEFINE-PSEUDO-LOCATIVE-TYPE can be listed with PSEUDO-LOCATIVE-TYPES.

  • [macro] DEFINE-LOCATIVE-ALIAS ALIAS LOCATIVE-TYPE &BODY DOCSTRING

    Define ALIAS as a locative equivalent to LOCATIVE-TYPE (both SYMBOLs). LOCATIVE-TYPE must exist (i.e. be among LOCATIVE-TYPES). For example, let's define OBJECT as an alias of the CLASS locative:

    (define-locative-alias object class)

    Then, LOCATEing with OBJECT will find the CLASS:

    (dref 'xref 'object)
    ==> #<DREF XREF CLASS>

    The LOCATIVE-ARGS of OBJECT (none in the above) are passed on to CLASS.

    (arglist (dref 'object 'locative))
    => (&REST ARGS)
    => :DESTRUCTURING

    Also, see Locative Aliases in PAX.

  • [macro] DEFINE-DEFINITION-CLASS LOCATIVE-TYPE CLASS-NAME &OPTIONAL (SUPERCLASSES '(DREF)) &BODY BODY

    Define a subclass of DREF(0 1). All definitions with LOCATIVE-TYPE must be of this type. If non-NIL, BODY is DEFCLASS' slot definitions and other options.

  • [generic-function] LOCATE* OBJECT

    Return a definition of OBJECT as a DREF, without actualizing it. If OBJECT is a DREF already, then this function simply returns it. If no definition is found for OBJECT, then LOCATE-ERROR(0 1) is signalled.

    This function is for extending LOCATE. Do not call it directly.

  • [generic-function] DREF* NAME LOCATIVE-TYPE LOCATIVE-ARGS

    LOCATE* calls this for XREFs which are not DREFs.

    An EQL(0 1)-specialized method must be defined for all new locative types. This function is for extending LOCATE. Do not call it directly.

  • [macro] CHECK-LOCATIVE-ARGS LOCATIVE-TYPE LOCATIVE-ARGS

    Signal a LOCATE-ERROR condition if LOCATIVE-ARGS do not match the LAMBDA-LIST argument of LOCATIVE-TYPE (not evaluated).

  • [function] LOCATE-ERROR &REST FORMAT-AND-ARGS

    Call this function to signal a LOCATE-ERROR condition from the dynamic extent of a LOCATE* method (which includes DREF*). It is an error to call LOCATE-ERROR elsewhere.

    FORMAT-AND-ARGS, if non-NIL, is a format string and arguments suitable for FORMAT.

  • [function] ADD-DREF-ACTUALIZER NAME

    Add the global function denoted by the symbol NAME to the list of actualizers. Actualizers are functions of a single DREF argument. They are called within LOCATE when LOCATE* returns a DREF. Their job is to make the DREF more specific.

  • [function] REMOVE-DREF-ACTUALIZER NAME

    Remove the global function denoted by the symbol NAME from the list of actualizers.

  • [generic-function] RESOLVE* DREF

    Return the object defined by the definition DREF refers to. Signal a RESOLVE-ERROR condition by calling the RESOLVE-ERROR function if the lookup fails.

    To keep RESOLVE a partial inverse of LOCATE, a specialized LOCATE* method or an actualizer must be defined for RESOLVEable definitions. This function is for extending RESOLVE. Do not call it directly.

    It is an error for methods of this generic function to return an XREF.

  • [function] RESOLVE-ERROR &REST FORMAT-AND-ARGS

    Call this function to signal a RESOLVE-ERROR condition from the dynamic extent of a RESOLVE* method. It is an error to call RESOLVE-ERROR elsewhere.

    FORMAT-AND-ARGS, if non-NIL, is a format string and arguments suitable for FORMAT.

  • [generic-function] MAP-DEFINITIONS FN NAME LOCATIVE-TYPE

    Call FN with DREFs which have the given NAME and LOCATIVE-TYPE. For most locative types, there is at most one definition, but for METHOD, for example, there may be many. The default method simply does (DREF NAME LOCATIVE-TYPE NIL) and calls FN with result if DREF succeeds.

    This function is for extending DEFINITIONS. Do not call it directly.

  • [generic-function] MAP-NAMES FN LOCATIVE-TYPE

    Call FN with names that form a DREF with some locative with LOCATIVE-TYPE. The default method tries to form DREFs by combining each interned symbol with LOCATIVE-TYPE and no LOCATIVE-ARGS.

    This function is for extending DREF-APROPOS. Do not call it directly.

  • [generic-function] ARGLIST* OBJECT

    To extend ARGLIST, specialize this on a subclass of DREF if that subclass is not RESOLVEable, else on the type of object it resolves to. This function is for extension only. Do not call it directly.

  • [generic-function] DOCSTRING* DREF

    To extend DOCSTRING, specialize this on a subclass of DREF if that subclass is not RESOLVEable, else on the type of object it resolves to. This function is for extension only. Do not call it directly.

  • [generic-function] SOURCE-LOCATION* DREF

    To extend SOURCE-LOCATION, specialize this on a subclass of DREF if that subclass is not RESOLVEable, else on the type of object it resolves to. This function is for extension only. Do not call it directly.

7.3 Symbol Locatives

Let's see how the opaque DEFINE-SYMBOL-LOCATIVE-TYPE and the obscure DEFINE-DEFINER-FOR-SYMBOL-LOCATIVE-TYPE macros work together to simplify the common task of associating definition with a symbol in a certain context.

  • [macro] DEFINE-SYMBOL-LOCATIVE-TYPE LOCATIVE-TYPE LAMBDA-LIST &BODY DOCSTRING

    Similar to DEFINE-LOCATIVE-TYPE, but it assumes that all things LOCATEable with LOCATIVE-TYPE are going to be symbols defined with a definer defined with DEFINE-DEFINER-FOR-SYMBOL-LOCATIVE-TYPE. Symbol locatives are for attaching a definition (along with arglist, documentation and source location) to a symbol in a particular context. An example will make everything clear:

    (define-symbol-locative-type direction ()
      "A direction is a symbol.")
    
    (define-definer-for-symbol-locative-type define-direction direction
      "With DEFINE-DIRECTION, one can document what a symbol means when
      interpreted as a DIRECTION.")
    
    (define-direction up ()
      "UP is equivalent to a coordinate delta of (0, -1).")
    

    After all this, (DREF 'UP 'DIRECTION) refers to the DEFINE-DIRECTION form above.

  • [macro] DEFINE-DEFINER-FOR-SYMBOL-LOCATIVE-TYPE NAME LOCATIVE-TYPE &BODY DOCSTRING

    Define a macro with NAME that can be used to attach a lambda list, documentation, and source location to a symbol in the context of LOCATIVE-TYPE. The defined macro's arglist is (SYMBOL LAMBDA-LIST &OPTIONAL DOCSTRING). LOCATIVE-TYPE is assumed to have been defined with DEFINE-SYMBOL-LOCATIVE-TYPE.

7.4 DREF Subclasses

These are the DREF subclasses corresponding to Locative Types. They are exported to make it possible to go beyond the standard Operations (e.g. PAX:DOCUMENT-OBJECT*) and for subclassing.

for Variables

  • [class] VARIABLE-DREF DREF

for Macros

  • [class] MACRO-DREF DREF

  • [class] SYMBOL-MACRO-DREF DREF

  • [class] COMPILER-MACRO-DREF DREF

  • [class] SETF-DREF DREF

for Functions

  • [class] FUNCTION-DREF DREF

  • [class] METHOD-DREF DREF

  • [class] METHOD-COMBINATION-DREF DREF

  • [class] ACCESSOR-DREF DREF

  • [class] READER-DREF DREF

  • [class] WRITER-DREF DREF

  • [class] STRUCTURE-ACCESSOR-DREF DREF

for Types and Declarations

  • [class] TYPE-DREF DREF

  • [class] CLASS-DREF DREF

  • [class] DECLARATION-DREF DREF

for the Condition System

for Packages and Readtables

  • [class] ASDF-SYSTEM-DREF DREF

  • [class] PACKAGE-DREF DREF

  • [class] READTABLE-DREF DREF

for DRef Locatives

  • [class] LOCATIVE-DREF DREF

  • [class] SYMBOL-LOCATIVE-DREF DREF

  • [class] UNKNOWN-DREF DREF

  • [class] LAMBDA-DREF DREF

7.5 Source Locations

These represent the file or buffer position of a defining form and are returned by the SOURCE-LOCATION function. For the details, see the Elisp function slime-goto-source-location.

  • [function] MAKE-SOURCE-LOCATION &KEY FILE FILE-POSITION BUFFER BUFFER-POSITION SNIPPET

    Make a Swank source location. The ultimate reference is slime.el. When SNIPPET is provided, the match nearest to FILE-POSITION is determined (see the Elisp slime-isearch and SOURCE-LOCATION-ADJUSTED-FILE-POSITION).

  • [function] SOURCE-LOCATION-P OBJECT

    See if OBJECT is a source location object.

  • [function] SOURCE-LOCATION-FILE LOCATION

    Return the name of the file of the defining form. This may be NIL, for example, if LOCATION is of a defining form that was entered at the REPL, or compiled in the *slime-scratch* buffer.

  • [function] SOURCE-LOCATION-FILE-POSITION LOCATION

    Return the file position of the defining form or NIL if it's not available. The first position is 0.

  • [function] SOURCE-LOCATION-BUFFER LOCATION

    Return the name of the Emacs buffer of the defining form or NIL if there is no such Emacs buffer.

  • [function] SOURCE-LOCATION-BUFFER-POSITION LOCATION

    Return the position of the defining form in SOURCE-LOCATION-BUFFER or NIL if it's not available. The first position is 1.

  • [function] SOURCE-LOCATION-SNIPPET LOCATION

    Return the defining form or a prefix of it as a string or NIL if it's not available.

  • [function] SOURCE-LOCATION-ADJUSTED-FILE-POSITION LOCATION

    Return the actual file position LOCATION points to allowing for some deviation from the raw SOURCE-LOCATION-FILE-POSITION, which is adjusted by searching for the nearest occurrence of SOURCE-LOCATION-SNIPPET in the file. Needless to say, this can be a very expensive operation.

    If SOURCE-LOCATION-FILE is NIL, NIL is returned. If there is no snippet, or it doesn't match, then SOURCE-LOCATION-FILE-POSITION (or 0 if that's NIL) is returned.

    This is a non-interactive companion to the Elisp function slime-location-offset, supporting only file positions and non-partial matching of snippets.


[generated by MGL-PAX]