在Common Lisp中更改默认阅读器

4

我编写了一些函数,可以替换 Common Lisp 中的 read 函数。

(defun my-read (stream &rest args)
  (declare (ignore args))
  (funcall (my-get-macro-character (read-char stream))))

有没有办法将这个功能设置为默认阅读器?

1
简短回答:不行,至少不能做到可移植。例如 loadcompile-file 可能会将读取器内联,或者它们可能会引用内部读取器定义,例如绕过可选/关键参数处理开销。 - acelent
2个回答

5
您不能重新定义内置函数1,但您可以定义一个包来隐藏cl:read并定义一个新函数my:read,这样当您使用该包时,它看起来像是默认的read函数。例如,像这样的东西:
CL-USER> (defpackage #:my-package 
           (:use "COMMON-LISP")
           (:shadow #:read)
           (:export #:read))
;=> #<PACKAGE "MY-PACKAGE">

CL-USER> (defun my-package:read (&rest args)
           (declare (ignore args))
           42)
;=> MY-PACKAGE:READ

CL-USER> (defpackage #:another-package
           (:use #:my-package "COMMON-LISP")
           (:shadowing-import-from #:my-package #:read))
;=> #<PACKAGE "ANOTHER-PACKAGE">

CL-USER> (in-package #:another-package)
;=> #<PACKAGE "ANOTHER-PACKAGE">

ANOTHER-PACKAGE> (read)
;=> 42

  1. 事实上,正如Rainer Joswig在评论中指出,虽然这是未定义行为(请参见11.1.2.1.2 COMMON-LISP软件包对符合条件的程序的限制),但通常还是有方法重新定义Common Lisp函数的。例如,在SBCL中,您可以使用unlock-package,如重新定义内置函数所示。 CLISP具有软件包锁定。其他实现可能具有类似的功能。

3
请注意,标准不允许重新定义标准函数,但是许多/大多数实际实现有一种特定于实现的方式来实现此操作。 - Rainer Joswig
更改默认阅读器以阅读主文件并不实用。 - cl-porky11
@cl-porky11,我不确定你所说的“主文件”具体是什么意思。如果你在某个地方有这样的定义,那么你只需要在该文件顶部添加(in-package #:my-package)即可。 - Joshua Taylor

4
一种方法是在读表中对所有“有效”的输入字符使用`set-macro-character`。(如果只接受ASCII输入,则可以这样做,但我不知道对于完整的Unicode是否实用。)
类似于这样的代码:
(defun replace-default-read-behavior (rt fn)
  (loop for c across 
        " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
        do (set-macro-character c fn t rt)))

(defun my-parser (stream char)
  (format t "custom read: ~A~A" char (read-line stream)))

(defun my-get-macro-character (char)
  (declare (ignore char))
  #'my-parser)

(defun my-read (stream char)
  (funcall (my-get-macro-character char) stream char))

(defvar *my-readtable* (copy-readtable ()))

(replace-default-read-behavior *my-readtable* #'my-read)

(let ((*readtable* *my-readtable*))
  (read-from-string "foo"))
custom read: foo  ; printed
NIL               ; returned
3

我也考虑过这个解决方案,但对于非标准字符可能不起作用,在某些情况下可能会有问题。(你在字符串中忘记了 #\Newline) - cl-porky11
@cl-porky11同意。最糟糕的情况是,意外字符可能会导致您的程序默默地出现问题。如果您调整my-read以摄取整个输入,则可以放宽要求,只需说输入的第一个字符必须是您期望的字符之一。这仍然不太令人满意,但我不知道有更好的方法来解决这个问题。 - m-n

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接