(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
+ (skip-whitespaces-and-comments stream)
+ (let ((ch (%peek-char stream)))
+ (if (or (null ch) (char= #\) ch))
+ (discard-char stream #\))
+ (error "Multiple objects following . in a list"))))
+ (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))))
+ (#\: (make-symbol
+ (unescape-token
+ (string-upcase-noescaped
+ (read-escaped-until stream #'terminalp)))))
(#\\
(let ((cname
(concat (string (%read-char stream))
((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))
+ (:nil
+ (ls-read stream)
+ (ls-read stream eof-error-p eof-value)))))))
-(defun unescape (x)
+(defun unescape-token (x)
(let ((result ""))
(dotimes (i (length x))
(unless (char= (char x i) #\\)
(setq result (concat result (string (char x i))))))
result))
-(defun escape-all (x)
- (let ((result ""))
- (dotimes (i (length x))
- (setq result (concat result "\\"))
- (setq result (concat result (string (char x i)))))
- result))
-
(defun string-upcase-noescaped (s)
(let ((result "")
(last-escape nil))
(setq name (subseq string index))))
;; Canonalize symbol name and package
(setq name (if (equal package "JS")
- (setq name (unescape name))
+ (setq name (unescape-token name))
(setq name (string-upcase-noescaped name))))
(setq package (find-package package))
(if (or internalp
(find-symbol name package)
(if (eq external :external)
symbol
- (error (concat "The symbol '" name "' is not external")))))))
+ (error "The symbol `~S' is not external in the package ~S." name package))))))
(defun read-integer (string)
(let ((sign 1)
;; Optional exponent part
(when (< index size)
;; Exponent-marker
- (unless (member (string-upcase (string (char string index)))
- '("E" "S" "F" "D" "L"))
+ (unless (find (char-upcase (char string index)) "ESFDL")
(return))
(incf index)
(unless (< index size) (return))
(unless (= index size) (return))
;; Everything went ok, we have a float
;; XXX: Use FLOAT when implemented.
- (/ (* sign (expt 10.0 (* exponent-sign exponent)) number) divisor))))
+ (/ (* sign (expt 10.0 (* exponent-sign exponent)) number) divisor 1.0))))
(defun !parse-integer (string junk-allow)
(block nil
(!parse-integer string junk-allowed)
(if num
(values num index)
- (error "junk detected."))))
+ (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 "EOF") 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))