Lisp 如何从CL REPL获取函数/宏定义?

Lisp 如何从CL REPL获取函数/宏定义?,lisp,common-lisp,sbcl,self-reference,Lisp,Common Lisp,Sbcl,Self Reference,我还有一个问题涉及到公共Lisp中的自我引用。我发现了一个问题,即编写最短的程序来打印程序源代码中不存在的所有可打印ASCII字符。这让我想到了如何用CommonLisp解决这个问题。我遇到了两个问题——一个可能很琐碎,另一个更棘手: 首先是编写CL脚本的情况,例如,以#开头/usr/bin/env sbcl--脚本。我认为通过*posix argv*我可以访问所有命令行参数,包括被调用脚本的名称。我还查找了Bash$0的等价物,但没有找到。最终对我有效的是这个丑陋的Bash-ified SB

我还有一个问题涉及到公共Lisp中的自我引用。我发现了一个问题,即编写最短的程序来打印程序源代码中不存在的所有可打印ASCII字符。这让我想到了如何用CommonLisp解决这个问题。我遇到了两个问题——一个可能很琐碎,另一个更棘手:

  • 首先是编写CL脚本的情况,例如,以
    #开头/usr/bin/env sbcl--脚本
    。我认为通过
    *posix argv*
    我可以访问所有命令行参数,包括被调用脚本的名称。我还查找了Bash
    $0的等价物,但没有找到。最终对我有效的是这个丑陋的Bash-ified SBCL脚本,它显式地将
    $0
    传递给SBCL,并从中继续:

    #!/bin/bash
    #|
    sbcl --script $0 $0
    exit
    |#
    (defun file-string (path)
      (with-open-file (stream path)
        (let ((data (make-string (file-length stream))))
          (read-sequence data stream)
          data)))
    
    (let* ((printable (mapcar #'code-char (loop for i from #x20 to #x7e collect i)))
           (absent (set-difference 
            printable 
            (coerce (file-string (cadr *posix-argv*)) 'list))))
      (print (coerce absent 'string)))
    
    关于这一点,我的问题是:你能想出一种不依赖Bash提供相关参数的方法吗?或者,更简单地说:是否存在相当于
    $0
    的CL(特别是SBCL)

  • 现在是我完全困惑的部分。在使用上面的脚本方法之前,我试图以一种更面向REPL的方式实现这个目标。基于
    defmacro
    中的
    &whole
    说明符和中的考虑因素,我试图从
    &whole
    参数中获取宏的名称,并以某种方式“读入”其源。我完全不知道怎么做。因此,简而言之:给定宏的名称,我能否以某种方式获得定义它的
    defmacro
    表单?我说的是一个通用的解决方案,而不是解析REPL历史

    编辑:关于mbratch关于使用
    macroexpand-1
    的问题,我是这样做的:

    (defmacro self-refer (&whole body)
      (macroexpand-1 `',body))
    
    通过此调用,我可以通过调用
    (自引用)
    获得
    (自引用)
    。这不是一个很好的解决方案


我希望有人能给我指出正确的方向。谢谢

在Common Lisp中未定义获取宏的源

这可能会起作用(来自LispWorks的示例):

更深奥的方法是修改现有的
DEFMACRO
,以记录其源代码。 许多Lisp实现都有一个称为建议的非标准特性。例如,LispWorks可以建议宏:

CL-USER 31 > (defadvice (defmacro source-record-defmacro :after)
                 (&rest args)
               (setf (get (second (first args)) :macro-source) (first args)))
T
上面将代码添加到标准的
DEFMACRO
宏,该宏将源记录在宏名称的符号属性列表中
defmacro
是要建议的内容的名称
source record defmacro
是此建议的选定名称<代码>:after
然后指定代码应在正常的
defmacro
代码之后运行

CL-USER 32 > (defmacro foo (a b) `(* (+ ,a ,b) (+ ,a ,a)))
FOO

CL-USER 33 > (pprint (get 'foo :macro-source))

(DEFMACRO FOO (A B) `(* (+ ,A ,B) (+ ,A ,A)))

同样,这是完全非标准的——我不确定SBCL是否存在类似的机制,尽管它有一种叫做“封装”的机制。

这是对Rainer Joswig的LispWorks解决方案的一个非常晚的后续行动。我最近一直在使用Allegro CL,发现了这个设施。从概念上讲,它与上面的
defadvice
非常相似,而且更加详细。以下是Rainer在ACL 10.0中示例的重新迭代:

(def-fwrapper source-record-defmacro (&rest args)
  (setf (get (second (first args)) :macro-source) (first args))
  (call-next-fwrapper))
定义了一个
fwrapper
之后,您需要明确地“将其付诸实施”:

(fwrap 'defmacro 'srd 'source-record-defmacro)
在这之后,就像雷纳的例子:

CL-USER> (defmacro foo (a b) `(* (+ ,a ,b) (+ ,a ,a)))
FOO
CL-USER> (pprint (get 'foo :macro-source))

(DEFMACRO FOO (A B) `(* (+ ,A ,B) (+ ,A ,A)))
; No value

对于(1)您可以使用posix argv作为参数,加载路径名作为被调用脚本的实际名称。谢谢,mbratch。这使事情简化了很多。对于(2)你看过
macroexpand-1
了吗?是的,我看过,但不幸的是,它只做了所需的一部分。如果我计算如下内容:
(defmacro-self-reference(&whole-body)`(macroexpand-1',body))
我得到的响应是
(macroexpand-1'(self-reference))
,它是
self-reference
宏的主体,但不是整个定义。这很难满足程序访问其整个源代码的要求。您希望/试图如何使用
自引用
?也许第二个项目中的试用代码示例有助于澄清问题陈述。Rainer,您的第一个示例在SBCL和Clozure CL中返回了
nil
。至于第二个,我没有找到多少关于在SBCL中使用
enclosure
的建议,但CMUCL手册包含一些信息。我会尝试调整你的解决方案。无论如何,你的回答只能让我相信,LispWorks比我想象的要酷得多。
CL-USER> (defmacro foo (a b) `(* (+ ,a ,b) (+ ,a ,a)))
FOO
CL-USER> (pprint (get 'foo :macro-source))

(DEFMACRO FOO (A B) `(* (+ ,A ,B) (+ ,A ,A)))
; No value