3

我想知道宏扩展发生的文件位置和字符位置,以便在 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))))

我也将它标记为 reader-macro,因为我真的不知道这应该发生在哪里。

4

1 回答 1

2

你不能用普通的宏便携地做到这一点

“此扩展发生在 #P(myfile.lisp) 字符 16 和 40 之间”

通常,您将无法获得那种东西,因为一旦阅读了表格,它就无法使用。例如,。如果您有包含此内容的文件:

;; line 0
(some-form arg1)

以及包含以下内容的文件:

;; line 0
;; line 1
;; line 2
(


some-form



arg1
                )

从概念上讲,编译器将获得相同的输入。原则上,阅读器首先从文件中读取一个表单,然后将其传递给编译器。在这两种情况下,编译器都会得到形式(some-form arg1)。这就是保证您编写的宏也可以访问的原因。单独的实现实际上可能使编译器可以使用更多,但它将以实现依赖的方式,并且不一定以可移植的方式向您公开。

不过,文件加载器在加载文件时会绑定一些标准的东西,这些东西可以帮助提供一些此类信息例如,加载函数将特殊变量与文件的路径名和真实名绑定:

*load-truename*由 load 绑定以保存正在加载的文件的路径名的真实名称。

*load-pathname*受 load 约束,以保存表示与默认值合并的文件规范的路径名。即,(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."

当然,这有点脆弱,因为可以以一种方式调用读取器宏,其中流不是文件位置对它有很大意义的流,因此您需要对此进行一些检查,并且您仍然需要一种方法来根据行号和列来解释这些文件位置,但我认为这是一个很好的第一次尝试。

于 2016-06-01T15:05:04.303 回答