我想要宏展开发生的文件位置和字符位置,以便在 GUI 中突出显示宏展开。
为此,我希望能够从宏本身引用发生扩展的宏的当前位置。
例如,如果我有以下代码:
(defun mve ()
(magic-macro :maybe :args))
我希望能够将它扩展成类似的东西
(defun mve ()
(progn
(macro-body-stuff)
"This expansion took place at #P(myfile.lisp) between chars 16 and 40"))
如果存在这样的函数,一个最小的示例宏可能类似于
(defmacro maybe-macro (&rest r)
`(progn
(macro-body-stuff)
,(format nil "This expansion took place at ~S between chars ~D and ~D"
(??:get-macroexpansion-pathname)
(??:get-macroexpansion-char-start)
(??:get-macroexpansion-char-end))))
我也将它标记为阅读器宏,因为我不知道应该在哪里发生。
最佳答案
你不能用普通的宏来移植
"This expansion took place at #P(myfile.lisp) between chars 16 and 40"
一般情况下,您将无法获得那种东西,因为一旦阅读了表格,它就不再可用了。例如,。如果您有包含此内容的文件:
;; line 0
(some-form arg1)
以及包含此内容的文件:
;; line 0
;; line 1
;; line 2
(
some-form
arg1
)
从概念上讲,编译器将获得相同的输入。原则上,reader 首先从文件中读取一个表单,然后将其传递给编译器。在这两种情况下,编译器都会得到 (some-form arg1) 形式。这就是保证您编写的宏也可以访问的内容。一个单独的实现实际上可能使编译器更可用,但它将以一种依赖于实现的方式,并且不一定以可移植的方式向您公开。
不过,文件加载器在加载文件时会绑定(bind)一些标准内容,这些内容有助于提供一些此类信息。例如,load函数将特殊变量与文件的路径名和真实名称绑定(bind):
*load-truename* is bound by load to hold the truename of the pathname of the file being loaded.
*load-pathname* is bound by load to hold a pathname that represents filespec merged against the defaults. That is,
(pathname (merge-pathnames filespec))
.
提供行号和列号(如果有)等内容的依赖于实现的扩展可能可以通过相同的方式访问。
但有时您可以使用阅读器宏来做到这一点
您不能通过可移植的普通宏来执行此操作,因为您没有可移植的机制来确定从文件中的哪个位置读取表单。但是,读取器宏调用一个函数,该函数通过从中读取表单的流调用,并且有一些函数用于调查流中的位置。例如,这是一个文件:
(defparameter *begin* nil
"File position before reading a form prefixed with #@.")
(defparameter *end* nil
"File position after reading a form prefixed with #@.")
(eval-when (:compile-toplevel :load-toplevel :execute)
(set-dispatch-macro-character
#\# #\@
(lambda (stream char infix-parameter)
(declare (ignore char infix-parameter))
(let ((begin (file-position stream))
(form (read stream t nil t))
(end (file-position stream)))
`(let ((*begin* ,begin)
(*end* ,end))
,form)))))
(defun foo ()
#@(format nil "form began at ~a and ended at ~a."
*begin* *end*))
现在,加载后,我们可以调用foo:
CL-USER> (load ".../reader-macro-for-position.lisp")
T
CL-USER> (foo)
"form began at 576 and ended at 650."
这当然有点脆弱,因为可以以一种方式调用读取器宏,其中流不是 file-position 具有很大意义的流,因此您我想对此进行一些检查,您仍然需要一种方法来根据行号和列来解释这些文件位置,但我认为这是一个很好的开端。
关于common-lisp - 我可以在他们的扩展站点中获取宏的边界吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37541578/