Clisp 错误消息:对象不能以#\) 开头。这是什么意思?

Clisp error message: an object cannot start with #\). What does this mean?

请看这个例子。我正在使用 GNU CLISP 2.49。

(defparameter *pudding-eater* 'henry')
;; output: 
READ from
#<INPUT CONCATENATED-STREAM #<INPUT STRING-INPUT-STREAM> #<IO TERMINAL-STREAM>>: an
  object cannot start with #\)

(defparameter *pudding-eater* 'henry)
;; output: 
*PUDDING-EATER*

我明白是双引号引起了问题。我明白的是,an object cannot start with #\)是什么意思?我从哪里开始 #\)?我期待一些错误消息,例如 umatched parenthesis.

您在 'henry 之后的额外引号字符是另一个对象的开始,这在以下上下文中有意义:

(defparameter *pudding-eater* 'henry '(a b c))

(如果 defparameter 有那么多参数的话)

但是,您引述后的下一个字符是 close-paren。用于显示字符(而不是符号、字符串等)的 Common Lisp 表示法是 #\ 前缀,后跟字符。

因此,错误消息并未说明 \# 字符,仅说明 ),它告诉您您有一个需要更多表达式而不是当前表达式的末尾(因为您通过添加 ' 字符开始表达式)。

正如其他答案所指出的,#\( 是表示 ( 字符的语法。请参阅 2.4.8 Sharpsign 部分。首先 reader 看到 #,然后调度下一个字符,\(反斜杠)用于读取字符对象。因此,您的错误消息是说 ) 在您的输入中是意外的。

错误信息

I was expecting some error message like umatched parenthesis.

这基本上就是错误的意思,SBCL 等其他实现会产生您期望的消息。我在 Emacs 中工作:

CL-USER> )

... 使用 unmatched close parenthesis 和回溯进入调试器。

让我们看看为什么。使用 GET-MACRO-CHARACTER,我们要求我们的 Common Lisp 实现 return 与 #\) 字符关联的函数:

CL-USER> (get-macro-character #\))
SB-IMPL::READ-RIGHT-PAREN
NIL

在 Emacs 中,结果值显示为 表示,这使我们可以检查它。您还可以指向函数的名称,如果您的 SBCL 安装允许它(例如,您从源安装),您可以使用 M-.:[=37 转到定义=]

(defun read-right-paren (stream ignore)
  (declare (ignore ignore))
  (simple-reader-error stream "unmatched close parenthesis"))

与 CLISP 相同会产生您看到的错误。我们还可以这样做:

CL-USER> (get-macro-character #\))
#<SYSTEM-FUNCTION SYSTEM::RPAR-READER>
NIL

不过那个系统函数的源码定义不是很清楚(如果你好奇的话可以看non-official mirror),但是显示另一个错误信息也就不足为奇了。

read-table 以及 Lisp 如何读取表达式列表

行为取决于当前 readtable 中哪些函数绑定到正在读取的字符。 在这种情况下,所有实现都会发出错误信号,因为当您到达必须在可读表中查找右括号的位置时,输入的格式肯定很糟糕。这是像 READ-DELIMITED-LIST 这样的函数的工作,从与 左括号 、a.k.a 关联的函数调用。 #\(,一直读到找到对应的右括号为止。 请注意,如果您在 SBCL 中 look-up reader for #\(,它不会使用 read-delimited-list,而是使用不同的专用版本。 post这里有点太长了.

报价和递归阅读

在你的情况下,你也有报价。报价如何表现?

(defun read-quote (stream ignore)
  (declare (ignore ignore))
  (list 'quote (read stream t nil t)))

它构建一个以quote 开头并包含通过递归调用READ 获得的sub-expression 的列表。这与上面一样,当我在 REPL 提示符下输入右括号时。由于我们当前不在左括号的上下文中,我们遇到 #\) 字符,在可读表中查找它并发出错误信号。

你写

'henry')

' 是 Common Lisp 中的终止宏字符

终止表示:

if it appears while parsing a token, it terminates that token.

所以第二个引号字符终止了之前的标记,这里是henry

我们有:quote, henry, quote, 右括号。​​

引用字符是一个宏字符,它专门导致另一个对象被读取。 'some-object 读作 (QUOTE some-object)some-object 可以是任何数据:数字、字符串、列表、符号...

因此正在读取一个新对象,并且对象的文本表示不能以右括号开头。右括号用于结束列表或 cons 单元格。

如果您想在符号中使用右括号,则需要将其转义:

CL-USER 3 > '\)
\)

CL-USER 4 > '|)|
\)

CL-USER 5 > '|)woaaah(|
|)woaaah(|