[slime-devel] advanced source location recording

Helmut Eller heller at common-lisp.net
Wed Nov 10 21:22:48 UTC 2010


* Mark H. David [2010-11-10 19:49] writes:

> I have a fancy def type of macro -- lets call it DEFOPERATION, which
> takes a function name, args, and body, and eventually emits a
> corresponding DEFUN form to get the function defined.  It gets
> sprinkled throughout the sources, but doesn't immediately invoke
> DEFUN. Instead, at some later point, another macro form is invoked --
> lets say it's (DEFINE-DEFS-NOW), and that actually emits all the
> defuns.
>
>
> The problem is that when I do meta-. on any so-defined function, it
> takes me to the line in the source containing (DEFINE-DEFS-NOW).  Not
> too interesting a place to go.  What I really want is for it to take
> me to the corresponding DEFOPERATION.
>
>
> How can I make this work better in SLIME?

The only portable way handle this is a TAGS file or grep.

> Or, if it's an
> implementation thing, or partially so, what can I do specifically in
> my Lisps, specifically CCL and/or SBCL, to make this work better?

With implementation specific tricks we can get a bit further but it's
not easy.  For example in CCL:

(eval-when (:compile-toplevel)
  (defparameter *compile-time-stuff* nil)

  (defmacro defop (&whole whole name args &body body)
    (push (list name args body (ccl::fcomp-source-note whole))
	  *compile-time-stuff*)
    nil)
  
  (defmacro defnow ()
    (prog1
	`(progn
	   . ,(loop for (name args body srcloc) in *compile-time-stuff*
		    collect `(progn
			       (defun ,name ,args . ,body)
			       (ccl::record-definition-source 
				ccl::*function-definition-type*
				',name
				',srcloc))))
      (setq *compile-time-stuff* nil)))
    
  )

(defop foo () (break) 12)
(defop bar () (break) 34)

(defnow)


CCL's reader has a special hack which inserts source-notes[0] in a hash
table.  You can retrieve them whith ccl::fcomp-source-note.  In the
defnow macro we generate code that associates the name with that
source-note.

Note this only specifies the source location for the name and doesn't
deal with the more interesting problem of how to preserve source
locations of all subforms of body.  That would be needed for
pc-to-source mapping, but is not necessary for M-.


The same idea for SBCL could look like so:

(eval-when (:compile-toplevel)
  (defparameter *compile-time-stuff* nil)

  (defmacro defop (&whole whole name args &body body)
    (push (list name args body (cddr (sb-c::get-source-path whole)))
	  *compile-time-stuff*)
    nil)
  
  (defmacro defnow (&whole whole)
    (prog1
	`(progn
	   . ,(loop for (name args body srcloc) in *compile-time-stuff*
		    for defun = `(defun ,name ,args . ,body)
		    do (setf (gethash defun sb-c::*source-paths*)
			     (append (sb-c::get-source-path whole)
				     srcloc))
		    collect defun))
      (setq *compile-time-stuff* nil)))
    
  )

(defop foo () (break) 12)
(defop bar () (break) 34)

(defnow)

SBCL numbers the cons in a sexp and uses those numbers to specify the
source location.  Our stuff will end up in the debug info of function
objects.

All those are obviously just hacks.  Ask the compiler guys for the
proper way to do it.

Helmut

[0] http://trac.clozure.com/ccl/changeset/11373





More information about the slime-devel mailing list