【问题标题】:Why don't reader macro extensions propagate to runtime (read)?为什么阅读器宏扩展不传播到运行时(读取)?
【发布时间】:2013-09-02 13:53:47
【问题描述】:

为什么以下不起作用?

;;;; 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 #\?

【问题讨论】:

    标签: lisp common-lisp reader-macro


    【解决方案1】:

    因为只有一个读取器,具有全局状态。您正在有效地打开和关闭宏。在这种情况下,读取器宏仅在编译时读取您的 read-and-eval 函数期间启用。

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

    【讨论】:

      【解决方案2】:

      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 语法,则必须将语法放在启用和禁用之间来电。出于后一种目的,国际刑警组织的调用扩展到 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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-07-10
        • 2019-05-20
        • 2012-04-10
        • 2011-09-19
        • 1970-01-01
        • 2017-10-22
        • 1970-01-01
        • 2015-12-03
        相关资源
        最近更新 更多