unread-char行为偏离规范?

时间:2017-07-29 13:21:18

标签: io stream common-lisp sbcl ccl

lastName='Oracle'的Common Lisp HyperSpec页面上 - 请参阅 here - 它说明了以下两点:

  1. " unread-char旨在成为允许Lisp阅读器和其他阅读器的有效机制 解析器在输入流中执行单字符前瞻。"

  2. "在同一个流上连续两次调用unread-char是错误的 介入调用read-char(或其他隐式读取字符的输入操作) 在那条小溪上。"

  3. 我正在调查如何添加对CL流的多字符前瞻的支持 解析器我打算写,并且为了确认上述内容,我运行了以下代码:

    unread-char

    它没有抛出错误(在SBCL或CCL上,我还没有在其他实现上测试它)但我不知道怎么可能有任何读取 在连续调用之间的流上发生的操作(隐式或显式) 到(defun unread-char-test (data) (with-input-from-string (stream data) (let ((stack nil)) (loop for c = (read-char stream nil) while c do (push c stack)) (loop for c = (pop stack) while c do (unread-char c stream))) (coerce (loop for c = (read-char stream nil) while c collect c) 'string))) (unread-char-test "hello") ==> "hello"

    这种行为对于多字符前瞻来说是个好消息,只要它是一致的,但为什么 是不是会抛出错误?

1 个答案:

答案 0 :(得分:5)

在回应用户jkiiski的评论时,我做了一些挖掘工作。我定义了一个类似于上面的函数但是将流作为参数(为了更容易重用):

(defun unread-char-test (stream)
  (let ((stack nil))
    (loop
       for c = (read-char stream nil)
       while c
       do (push c stack))
    (loop
       for c = (pop stack)
       while c
       do (unread-char c stream)))
  (coerce
   (loop
      for c = (read-char stream nil)
      while c
      collect c)
   'string))

然后我在第二个REPL中运行了以下内容:

(defun create-server (port)
  (usocket:with-socket-listener (listener "127.0.0.1" port)
    (usocket:with-server-socket (connection (usocket:socket-accept listener))
      (let ((stream (usocket:socket-stream connection)))
        (print "hello" stream)))))

(create-server 4000)

以下第一个REPL:

(defun create-client (port)
  (usocket:with-client-socket (connection stream "127.0.0.1" port)
    (unread-char-test stream)))

(create-client 4000)

它确实抛出了我预期的错误:

Two UNREAD-CHARs without intervening READ-CHAR on #<BASIC-TCP-STREAM ISO-8859-1 (SOCKET/4) #x302001813E2D>
   [Condition of type SIMPLE-ERROR]

这表明jkiiski的假设是正确的。从文本文件中读取输入时也会观察到原始行为,如下所示:

(with-open-file (stream "test.txt" :direction :output)
  (princ "hello" stream))

(with-open-file (stream "test.txt")
  (unread-char-test stream)))
==> "hello"

我想,在处理本地文件I / O时,实现会将大块文件读入内存,然后read-char从缓冲区读取。如果正确的话,这也支持这样的假设:当从内容在内存中的流中读取时,典型实现不会抛出规范中描述的错误。