如何获取在 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))
我想获取上次写入特定函数或宏定义的文件的名称,用于各种文档和测试目的?
在 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))