Fix dotted notation and incomplete input error reporting
authorDavid Vázquez <davazp@gmail.com>
Sat, 4 May 2013 00:54:23 +0000 (01:54 +0100)
committerDavid Vázquez <davazp@gmail.com>
Sat, 4 May 2013 00:54:23 +0000 (01:54 +0100)
Fixes #72

src/read.lisp

index bcc5bac..4116bb6 100644 (file)
       (skip-whitespaces stream)
       (setq ch (%peek-char stream)))))
 
+(defun discard-char (stream expected)
+  (let ((ch (%read-char stream)))
+    (when (null ch)
+      (error "End of file when character ~S was expected." expected))
+    (unless (char= ch expected)
+      (error "Character ~S was found but ~S was expected." ch expected))))
+
 (defun %read-list (stream)
   (skip-whitespaces-and-comments stream)
   (let ((ch (%peek-char stream)))
       ((null ch)
        (error "Unspected EOF"))
       ((char= ch #\))
-       (%read-char stream)
+       (discard-char stream #\))
        nil)
       (t
-       (let ((car (ls-read-1 stream)))
+       (let* ((eof (gensym))
+              (next (ls-read stream nil eof)))
          (skip-whitespaces-and-comments stream)
-         (cons car
-               (if (char= (%peek-char stream) #\.)
-                   (progn
-                     (%read-char stream)
-                     (if (terminalp (%peek-char stream))
-                         (ls-read-1 stream) ; Dotted pair notation
-                         (cons (let ((string (concat "." (read-escaped-until stream #'terminalp))))
-                                 (or (values (!parse-integer string nil))
-                                     (read-float string)
-                                     (read-symbol string)))
-                               (%read-list stream))))
-                   (%read-list stream))))))))
+         (cond
+           ((eq next eof)
+            (discard-char stream #\)))
+           (t
+            (cons next
+                  (if (char= (%peek-char stream) #\.)
+                      (progn
+                        (discard-char stream #\.)
+                        (if (terminalp (%peek-char stream))
+                            (prog1 (ls-read stream) ; Dotted pair notation
+                              (discard-char stream #\)))
+                            (let ((token (concat "." (read-escaped-until stream #'terminalp))))
+                              (cons (interpret-token token)
+                                    (%read-list stream)))))
+                      (%read-list stream))))))))))
 
 (defun read-string (stream)
   (let ((string "")
       (setq ch (%read-char stream)))
     string))
 
-(defun read-sharp (stream)
+(defun read-sharp (stream &optional eof-error-p eof-value)
   (%read-char stream)
   (ecase (%read-char stream)
     (#\'
-     (list 'function (ls-read-1 stream)))
+     (list 'function (ls-read stream)))
     (#\( (list-to-vector (%read-list stream)))
     (#\: (make-symbol (string-upcase (read-until stream #'terminalp))))
     (#\\
          ((string= cname "newline") #\newline)
          (t (char cname 0)))))
     (#\+
-     (let ((feature (read-until stream #'terminalp)))
-       (cond
-         ((string= feature "common-lisp")
-          (ls-read-1 stream)              ;ignore
-          (ls-read-1 stream))
-         ((string= feature "jscl")
-          (ls-read-1 stream))
-         (t
-          (error "Unknown reader form.")))))))
+     (let ((feature (let ((symbol (ls-read stream)))
+                      (unless (symbolp symbol)
+                        (error "Invalid feature ~S" symbol))
+                      (intern (string symbol) "KEYWORD"))))
+       (ecase feature
+         (:common-lisp
+          (ls-read stream)
+          (ls-read stream eof-error-p eof-value))
+         (:jscl
+          (ls-read stream eof-error-p eof-value)))))))
 
 (defun unescape (x)
   (let ((result ""))
         (values num index)
         (error "Junk detected."))))
 
-(defvar *eof* (gensym))
-(defun ls-read-1 (stream)
+
+(defun interpret-token (string)
+  (or (read-integer string)
+      (read-float string)
+      (read-symbol string)))
+
+(defun ls-read (stream  &optional (eof-error-p t) eof-value)
   (skip-whitespaces-and-comments stream)
   (let ((ch (%peek-char stream)))
     (cond
       ((or (null ch) (char= ch #\)))
-       *eof*)
+       (if eof-error-p
+           (error "End of file")
+           eof-value))
       ((char= ch #\()
        (%read-char stream)
        (%read-list stream))
       ((char= ch #\')
        (%read-char stream)
-       (list 'quote (ls-read-1 stream)))
+       (list 'quote (ls-read stream)))
       ((char= ch #\`)
        (%read-char stream)
-       (list 'backquote (ls-read-1 stream)))
+       (list 'backquote (ls-read stream)))
       ((char= ch #\")
        (%read-char stream)
        (read-string stream))
       ((char= ch #\,)
        (%read-char stream)
        (if (eql (%peek-char stream) #\@)
-           (progn (%read-char stream) (list 'unquote-splicing (ls-read-1 stream)))
-           (list 'unquote (ls-read-1 stream))))
+           (progn (%read-char stream) (list 'unquote-splicing (ls-read stream)))
+           (list 'unquote (ls-read stream))))
       ((char= ch #\#)
        (read-sharp stream))
       (t
        (let ((string (read-escaped-until stream #'terminalp)))
-         (or (read-integer string)
-             (read-float string)
-             (read-symbol string)))))))
-
-(defun ls-read (stream &optional (eof-error-p t) eof-value)
-  (let ((x (ls-read-1 stream)))
-    (if (eq x *eof*)
-        (if eof-error-p
-            (error "End of file")
-            eof-value)
-        x)))
+         (interpret-token string))))))
 
 (defun ls-read-from-string (string &optional (eof-error-p t) eof-value)
   (ls-read (make-string-stream string) eof-error-p eof-value))