为什么下面的方法不起作用?

;;;; foo.lisp
(in-package :cl-user)

(eval-when (:compile-toplevel :load-toplevel :execute)
  (require :cl-interpol))

(cl-interpol:enable-interpol-syntax)

(defun read-and-eval (s)
  (eval (read-from-string s)))

(cl-interpol:disable-interpol-syntax)

然后:
LISP> (load (compile-file "foo.lisp"))
=> T

LISP> (read-and-eval
        "(let ((a \"foo\")) (princ #?\"${a}\"))")
=> no dispatch function defined for #\?

最佳答案

CL:READ会根据对READ的调用运行时绑定(bind)到CL:* READTABLE *的readtable进行调度。 ENABLE-INTERPOL-SYNTAX在后台创建一个新的readtable,将CL:* READTABLE *设置为保存它,并存储CL:* READTABLE *的旧值。 DISABLE-INTERPOL-SYNTAX正在取消存储先前的可读表,并将CL:* READTABLE *设置为再次保留它。最小地更改原始设置,您可以通过以下方式安排所需的行为:

(in-package :cl-user)

(eval-when (:compile-toplevel :load-toplevel :execute)
  (require :cl-interpol))

(cl-interpol:enable-interpol-syntax)

(defvar *interpol-reader* *readtable*)

(cl-interpol:disable-interpol-syntax)

(defun read-and-eval (s)
  (let ((*readtable* *interpol-reader*))
    (eval (read-from-string s))))

在defvar和read-and-eval仍然可以使用之后,可以将禁用语法的调用放在任何位置,但是如果您想直接在文件中输入interpol语法,则必须在enable和disable调用之间放置语法。对于后一个目的,interpol调用扩展为EVAL-WHEN很重要,因为同样的原因,您的REQUIRE调用必须在EVAL-WHEN范围内;也就是说,当后一种形式为READ时,效果必须已经发生。

CL-INTERPOL的界面抽象了正在发生的事情,因此,我将向您展示如何手动创建和更改readtable:
;; Create a fresh readtable with standard syntax
(defvar *not-readtable* (copy-readtable nil))

;; A simple reader function
(defun not-reader (stream char &optional count)
  "Like ' but for (not ...) instead of (quote ...)"
  (declare (ignore count char))
  `(not ,(read stream t nil t)))

;; Mutate that readtable so that the dispatch character you want
;; calls the function you want
(set-macro-character #\! 'not-reader nil *not-readtable*)

;; Try it out
(let ((*readtable* *not-readtable*))
  (read-from-string "(if !foo bar baz)"))

=>
(IF (NOT FOO)
    BAR
    BAZ)

关于lisp - 为什么阅读器宏扩展不能传播到运行时(读取)?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18574918/

10-09 02:29