CL:READ 根据调用 READ 时绑定到 CL:*READTABLE* 的 readtable 调度。在引擎盖下 ENABLE-INTERPOL-SYNTAX 正在创建一个新的 readtable,设置 CL:*READTABLE* 来保存它,并存储 CL:*READTABLE* 的旧值。DISABLE-INTERPOL-SYNTAX 正在取消存储以前的 readtable 并设置 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 语法,则必须将语法放在启用和禁用调用之间。出于后一种目的,interpol 调用扩展到 EVAL-WHEN 是很重要的,原因与您对 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)