symbol quoting support in reader
authorAndrea Griffini <agriff@tin.it>
Wed, 1 May 2013 23:10:12 +0000 (01:10 +0200)
committerAndrea Griffini <agriff@tin.it>
Wed, 1 May 2013 23:10:12 +0000 (01:10 +0200)
src/read.lisp
tests/read.lisp

index b33bee6..3cfd0fe 100644 (file)
 
 (defun read-escaped-until (stream func)
   (let ((string "")
-        (ch))
-    (setq ch (%peek-char stream))
-    (while (and ch (not (funcall func ch)))
-      (setq string (concat string (string ch)))
+        (ch (%peek-char stream))
+        (multi-escape nil))
+    (while (and ch (or multi-escape (not (funcall func ch))))
+      (cond
+        ((char= ch #\|)
+         (if multi-escape
+             (setf multi-escape nil)
+             (setf multi-escape t)))
+        ((char= ch #\\)
+         (%read-char stream)
+         (setf ch (%peek-char stream))
+         (setf string (concat string "\\" (string ch))))
+        (t
+         (if multi-escape
+             (setf string (concat string "\\" (string ch)))
+             (setf string (concat string (string ch))))))
       (%read-char stream)
-      (when (char= ch #\\)
-        ;; Note... escape char has been left in the string!
-        (setq string (concat string (string (%read-char stream)))))
-      (setq ch (%peek-char stream)))
+      (setf ch (%peek-char stream)))
     string))
 
 (defun skip-whitespaces-and-comments (stream)
         (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))
+    (dotimes (i (length s))
+      (let ((ch (char s i)))
+        (if last-escape
+            (progn
+              (setf last-escape nil)
+              (setf result (concat result (string ch))))
+            (if (char= ch #\\)
+                (setf last-escape t)
+                (setf result (concat result (string-upcase (string ch))))))))
+    result))
+
 ;;; Parse a string of the form NAME, PACKAGE:NAME or
 ;;; PACKAGE::NAME and return the name. If the string is of the
 ;;; form 1) or 3), but the symbol does not exist, it will be created
     (cond
       ;; No package prefix
       ((= index size)
-       (setq name (unescape string))
+       (setq name string)
        (setq package *package*)
        (setq internalp t))
       (t
        ;; Package prefix
        (if (zerop index)
            (setq package "KEYWORD")
-           (setq package (string-upcase (unescape (subseq string 0 index)))))
+           (setq package (string-upcase-noescaped (subseq string 0 index))))
        (incf index)
        (when (char= (char string index) #\:)
          (setq internalp t)
          (incf index))
-       (setq name (unescape (subseq string index)))))
+       (setq name (subseq string index))))
     ;; Canonalize symbol name and package
-    (when (not (eq package "JS"))
-      (setq name (string-upcase name)))
+    (setq name (if (equal package "JS")
+                   (setq name (unescape name))
+                   (setq name (string-upcase-noescaped name))))
     (setq package (find-package package))
-    ;; TODO: PACKAGE:SYMBOL should signal error if SYMBOL is not an
-    ;; external symbol from PACKAGE.
     (if (or internalp
             (eq package (find-package "KEYWORD"))
             (eq package (find-package "JS")))
         (intern name package)
-        (find-symbol name package))))
+        (multiple-value-bind (symbol external)
+            (find-symbol name package)
+          (if (eq external :external)
+              symbol
+              (error (concat "The symbol '" name "' is not external")))))))
 
 (defun read-integer (string)
   (let ((sign 1)
            (case elt
              (#\+ nil)
              (#\- (setq sign -1))
-             (otherwise (return-from read-integer))))
+             (t (return-from read-integer))))
           ((and (= i (1- size)) (char= elt #\.)) nil)
           (t (return-from read-integer)))))
     (and number (* sign number))))
            (list 'unquote (ls-read-1 stream))))
       ((char= ch #\#)
        (read-sharp stream))
-      ((char= ch #\|)
-       (%read-char stream)
-       (let ((string (read-escaped-until stream (lambda (x) (char= x #\|)))))
-         (%read-char stream)
-         (read-symbol string)))
       (t
        (let ((string (read-escaped-until stream #'terminalp)))
          (or (read-integer string)
index c7ac66b..d8cadf4 100644 (file)
@@ -5,6 +5,10 @@
  (equal (multiple-value-list (read-from-string "(a b c)"))
         '((A B C) 7)))
 
+(test (equal (symbol-name (read-from-string "js:alert")) "alert"))
+(test (equal (symbol-name (read-from-string "cl:cond")) "COND"))
+(test (equal (symbol-name (read-from-string "co|N|d")) "COND"))
+(test (equal (symbol-name (read-from-string "abc\\def")) "ABCdEF"))
 (test (equal (symbol-name (read-from-string "|.|")) "."))
 (test (equal (read-from-string "(1 .25)") '(1 0.25)))
 (test (equal (read-from-string ".25") 0.25))