2017-07-29 30 views
3

unread-char的Common Lisp的HyperSpec页面 - 见 here - 它说以下两个方面:偏离规范的未读字符行为?

  1. “未读炭旨在成为一个有效的机制,以允许Lisp的读卡器等 解析器在输入流中执行单字符预览。“

  2. “这是一个错误在相同流连续两次调用未读炭没有 居间调用读取炭(或隐含地读取字符某种其它输入动作)在该流 ”。

我研究如何增加对多字符先行支持CL的 解析器我打算写的,只是为了确认上述溪流,我跑到下面的代码:

(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" 

它不会抛出一个错误(在SBCL或CCL上,我还没有在其他实现上测试过),但我没有看到如何可能发生任何读取 操作(隐式或显式)发生在连续呼叫 到unread-char之间的流上。

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

+3

我假设开发人员在字符串处于内存时未读取字符串输入流上的多个字符时发出信号错误,并且unreading实际上只是递减当前索引。在CLISP中为 – jkiiski

+1

,这是一个错误 –

回答

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。如果正确,这也支持这样的假设,即当从内容为内存的流中读取时,典型实现中不会抛出规范中描述的错误。