如何获取在 Common Lisp 中定义函数的文件名?

How to get the filename where a function is defined in Common Lisp?

我想获取上次写入特定函数或宏定义的文件的名称,用于各种文档和测试目的?

在 PJB 对 #CommonLisp (IRC.LIBERA.CHAT) 的帮助下,我发布了一个对我来说效果很好的答案,但我会接受任何提供不依赖于 SWANK 或提供可移植解决方案的答案关于如何在其他 IDE(如 LispWorks、ACL 等)中实现相同目标的更多详细信息

#CommonLisp 上的 PJB (IRC.LIBERA.CHAT) 提供了极大的帮助并提供了以下答案(我已经解释过,所以任何错误都在我这边):

首先,CL 是 lisp-2(实际上是 lisp-∞),因此可能没有附加到符号的单一定义。例如,下面提到的 foo 是哪个?

(deftype foo () (member foo))

(defvar foo 'foo)

(defun foo () foo)

此外,例如,符号可能在 REPL 中定义(没有关联的文件名),因此这不是一个可以在不对意图进行假设的情况下回答的简单问题。

现在,当 M-. 的行为满足您的要求时,您可以查看 slime/swank 具体实现 API 的内容。

这应该指向 swank:find-definition-for-emacs,这可能是您想要的:

(swank:find-definitions-for-emacs "foo") #| -->

(("#'foo"
 (:location (:file "/private/tmp/foo.lisp")
 (:position 50)
 (:snippet "(defun foo () foo)")))
 
 ("(type foo)"
 (:location (:file "/private/tmp/foo.lisp")
 (:position 1)
 (:snippet "(deftype foo () '(member foo))")))
 
 ("(variable foo)"
 (:location (:file "/private/tmp/foo.lisp")
 (:position 32)
 (:snippet "(defvar foo 'foo)")))) 

如果您想依赖上述内容,请确保将 swank 作为依赖项加载到您的 .asd 文件中。

编辑:我还发现下面的内容非常有用(对于大多数实现,swank 都有一个类似的文件,所以只需浏览每个文件以查看它们的等效项):

https://github.com/slime/slime/blob/68c58c0194ff03cd147fcec99f0ee90ba9178875/swank/sbcl.lisp#L811

函数调用(sb-introspect:find-definition-sources-by-name name type)(名称是一个符号,类型是一个关键字,例如:函数 - 参考上面link)returns存储定义的文件,假设您使用的是 SBCL。更多 (SBCL) 详细信息也在:

https://github.com/sbcl/sbcl/blob/master/contrib/sb-introspect/introspect.lisp

如果您寻求的是 可移植 解决方案——一个用可移植 CL 编写的解决方案——那么答案就是定义用于定义表单的包装器,然后使用包装器。

(defvar *flocs* (make-hash-table :test #'equal))

(defgeneric function-location (f/name)
  (:method ((name t))
   (values (gethash name *flocs* nil) t))
  (:method ((f function))
   (multiple-value-bind (le cp nm) (function-lambda-expression f)
     (declare (ignore le cp))
     (if nm
         (function-location nm)
       (values nil nil)))))

(defmacro define-function (f args &body doc/decls/forms)
  (when (or *load-pathname* *compile-file-pathname*)
    ;; Prefer *load-pathname*
    (setf (gethash f *flocs*) (or *load-pathname* *compile-file-pathname*)))
  `(defun ,f ,args ,@doc/decls/forms))

在现实生活中,你当然会调用 define-function defun,类似地调用 define-variable 等,然后为 CL 构造一个管道包,它导出所有 CL 符号,同时用这些替换定义形式。

如果您寻求的是一种可移植的解决方案,因为它导出一些标准接口但具有不同的依赖于实现的后端,那么看看 SLY 或 SWANK 所做的可能是一个好的开始。对于 LW,您可能希望后端使用 DSPEC,这是它处理位置信息的方式:

> (dspec:dspec-definition-locations '(defun foo))
(((defun foo) :listener))

> (dspec:dspec-definition-locations '(defun needs))
(((defmacro needs)
  #P"..."))

> (defclass foo () ())
#<standard-class foo 402000B763>

> (dspec:name-definition-locations dspec:*dspec-classes* 'foo)
(((defclass foo) :listener) ((defun foo) :listener))