为什么读取器宏扩展不会传播到运行时(读取)?

nbt*_*rap 6 lisp common-lisp reader-macro

为什么以下不起作用?

;;;; 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)
Run Code Online (Sandbox Code Playgroud)

然后:

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

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

Wil*_*ung 10

因为只有一个读者,具有全球状态.您可以有效地打开和关闭宏.在这种情况下,读取器宏仅read-and-eval在编译时读取函数的持续时间内启用.

在这种情况下,您需要在read-and-eval函数内设置宏以确保阅读器在您需要时处于正确状态.


m-n*_*m-n 8

CL:READ调度基于绑定到CL的可读表:*READTABLE*在调用READ运行时.在引擎盖下,ENABLE-INTERPOL-SYNTAX正在创建一个新的可读表,设置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))))
Run Code Online (Sandbox Code Playgroud)

在defvar和read-and-eval仍然可以工作之后,可以将禁用语法的调用放在任何位置,但是如果要在文件中直接输入interpol语法,则必须在启用和禁用调用之间放置语法.对于后一个目的,国际刑警组织的呼叫扩展到EVAL-WHENs是很重要的,原因是你要求REQUIRE在EVAL-WHEN范围内; 也就是说,当后者的形式是READ时,效果需要已经发生.

CL-INTERPOL的界面抽象了正在发生的事情,因此我将向您展示如何手动创建和更改可读表:

;; 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)
Run Code Online (Sandbox Code Playgroud)