Merge branch 'master' into mutable-strings
authorDavid Vázquez <davazp@gmail.com>
Fri, 3 May 2013 16:02:10 +0000 (17:02 +0100)
committerDavid Vázquez <davazp@gmail.com>
Fri, 3 May 2013 16:02:10 +0000 (17:02 +0100)
jscl.html
jscl.lisp
src/boot.lisp
src/compiler.lisp
src/prelude.js
src/string.lisp [new file with mode: 0644]
src/utils.lisp
tests/strings.lisp [new file with mode: 0644]

index 3cd1330..782db20 100644 (file)
--- a/jscl.html
+++ b/jscl.html
         jqconsole.RegisterMatching('(', ')', 'parents');
 
         lisp.write = function(str){
-           jqconsole.Write(str, 'jqconsole-output', false);
+           jqconsole.Write(xstring(str), 'jqconsole-output', false);
            return str;
         }
 
         var startPrompt = function () {
           // Start the prompt with history enabled.
-          jqconsole.Write(lisp.evalString(pv, 1, '(CL:PACKAGE-NAME CL:*PACKAGE*)') + '> ', 'jqconsole-prompt');
+          jqconsole.Write(xstring(lisp.evalString(pv, 1, make_lisp_string('(CL:PACKAGE-NAME CL:*PACKAGE*)'))) + '> ', 'jqconsole-prompt');
           jqconsole.Prompt(true, function (input) {
             // Output input with the class jqconsole-return.
             if (input[0] != ','){
                 try {
-                    var vs = lisp.evalInput(mv, 1, input);
+                    var vs = lisp.evalInput(mv, 1, make_lisp_string(input));
                     for (var i=0; i<vs.length; i++){
-                       jqconsole.Write(lisp.print(pv, 1, vs[i]) + '\n', 'jqconsole-return');
+                       jqconsole.Write(xstring(lisp.print(pv, 1, vs[i])) + '\n', 'jqconsole-return');
                     }
                 } catch(error) {
-                    jqconsole.Write('ERROR: ' + (error.message || error) + '\n', 'jqconsole-error');
+                    var msg = error.message || error || 'Unknown error';
+                    if (typeof(msg) != 'string') msg = xstring(msg);
+                    jqconsole.Write('ERROR: ' + msg + '\n', 'jqconsole-error');
                 }
             } else {
-                jqconsole.Write(lisp.compileString(pv, 1, input.slice(1)) + '\n', 'jqconsole-return');
+                jqconsole.Write(xstring(lisp.compileString(pv, 1, make_lisp_string(input.slice(1)))) + '\n', 'jqconsole-return');
             }
             // Restart the prompt.
             startPrompt();
           }, function(input){
             try {
-                lisp.read(pv, 1, input[0]==','? input.slice(1): input);
+                lisp.read(pv, 1, make_lisp_string(input[0]==','? input.slice(1): input));
             } catch(error) {
                 return 0;
             }
index fb2045f..e49e696 100644 (file)
--- a/jscl.lisp
+++ b/jscl.lisp
@@ -21,6 +21,7 @@
     ("compat"    :host)
     ("utils"     :both)
     ("list"      :target)
+    ("string"    :target)
     ("print"     :target)
     ("package"   :target)
     ("read"      :both)
index 9378a8a..14d0ab9 100644 (file)
             (append (cdr list1) list2))))
 
 (defun append (&rest lists)
-  (!reduce #'append-two lists))
+  (!reduce #'append-two lists nil))
 
 (defun revappend (list1 list2)
   (while list1
     (setq assignments (reverse assignments))
     ;;
     `(let ,(mapcar #'cdr assignments)
-       (setq ,@(!reduce #'append (mapcar #'butlast assignments))))))
+       (setq ,@(!reduce #'append (mapcar #'butlast assignments) nil)))))
 
 (defmacro do (varlist endlist &body body)
   `(block nil
 (defun atom (x)
   (not (consp x)))
 
-(defun find (item list &key key (test #'eql))
+(defun find (item list &key (key #'identity) (test #'eql))
   (dolist (x list)
     (when (funcall test (funcall key x) item)
       (return x))))
        (char "0123456789" weight)))
 
 (defun subseq (seq a &optional b)
-  (cond
-    ((stringp seq)
-     (if b
-         (slice seq a b)
-         (slice seq a)))
-    (t
-     (error "Unsupported argument."))))
+  (if b
+      (slice seq a b)
+      (slice seq a)))
 
 (defmacro do-sequence (iteration &body body)
   (let ((seq (gensym))
       (incf pos))
     pos))
 
-(defun string (x)
-  (cond ((stringp x) x)
-        ((symbolp x) (symbol-name x))
-        (t (char-to-string x))))
-
 (defun equal (x y)
   (cond
     ((eql x y) t)
      (and (consp y)
           (equal (car x) (car y))
           (equal (cdr x) (cdr y))))
-    ((arrayp x)
-     (and (arrayp y)
-          (let ((n (length x)))
-            (when (= (length y) n)
-              (dotimes (i n)
-                (unless (equal (aref x i) (aref y i))
-                  (return-from equal nil)))
-              t))))
+    ((stringp x)
+     (and (stringp y) (string= x y)))
     (t nil)))
 
-(defun string= (s1 s2)
-  (equal s1 s2))
-
 (defun fdefinition (x)
   (cond
     ((functionp x)
   (+ (get-unix-time) 2208988800))
 
 (defun concat (&rest strs)
-  (!reduce #'concat-two strs :initial-value ""))
+  (!reduce #'concat-two strs ""))
 
 (defun values-list (list)
   (values-array (list-to-vector list)))
 
 (defun error (fmt &rest args)
   (%throw (apply #'format nil fmt args)))
+
index e109404..aa2f821 100644 (file)
                            " && ")
                      ")" *newline*
                      (indent
-                      "throw 'Unknown keyword argument ' + arguments[i].name;" *newline*))
+                      "throw 'Unknown keyword argument ' + xstring(arguments[i].name);" *newline*))
              "}" *newline*)))))
 
 (defun parse-lambda-list (ll)
   #+common-lisp
   (let ((package (symbol-package symbol)))
     (if (eq package (find-package "KEYWORD"))
-        (code "{name: \"" (escape-string (symbol-name symbol))
-              "\", 'package': '" (package-name package) "'}")
-        (code "{name: \"" (escape-string (symbol-name symbol)) "\"}")))
+        (code "{name: " (dump-string (symbol-name symbol))
+              ", 'package': " (dump-string (package-name package)) "}")
+        (code "{name: " (dump-string (symbol-name symbol)) "}")))
   #+jscl
   (let ((package (symbol-package symbol)))
     (if (null package)
-        (code "{name: \"" (escape-string (symbol-name symbol)) "\"}")
+        (code "{name: " (dump-symbol (symbol-name symbol)) "}")
         (ls-compile `(intern ,(symbol-name symbol) ,(package-name package))))))
 
 (defun dump-cons (cons)
   (let ((elements (vector-to-list array)))
     (concat "[" (join (mapcar #'literal elements) ", ") "]")))
 
+(defun dump-string (string)
+  (code "make_lisp_string(\"" (escape-string string) "\")"))
+
 (defun literal (sexp &optional recursive)
   (cond
     ((integerp sexp) (integer-to-string sexp))
     ((floatp sexp) (float-to-string sexp))
     ((characterp sexp) (code "\"" (escape-string (string sexp)) "\""))
-    ((stringp sexp) (code "\"" (escape-string sexp) "\""))
     (t
-     (or (cdr (assoc sexp *literal-table*))
+     (or (cdr (assoc sexp *literal-table* :test #'equal))
          (let ((dumped (typecase sexp
                          (symbol (dump-symbol sexp))
+                         (string (dump-string sexp))
                          (cons
                           (if (eq (car sexp) *magic-unquote-marker*)
                               (ls-compile (cdr sexp))
 
 (define-builtin float-to-string (x)
   (type-check (("x" "number" x))
-    "x.toString()"))
+    "make_lisp_string(x.toString())"))
 
 (define-builtin cons (x y)
   (code "({car: " x ", cdr: " y "})"))
      "return (typeof tmp == 'object' && 'name' in tmp);" *newline*)))
 
 (define-builtin make-symbol (name)
-  (type-check (("name" "string" name))
-    "({name: name})"))
+  (code "({name: " name "})"))
 
 (define-builtin symbol-name (x)
   (code "(" x ").name"))
   (js!selfcall
     "var symbol = " x ";" *newline*
     "var value = symbol.value;" *newline*
-    "if (value === undefined) throw \"Variable `\" + symbol.name + \"' is unbound.\";" *newline*
+    "if (value === undefined) throw \"Variable `\" + xstring(symbol.name) + \"' is unbound.\";" *newline*
     "return value;" *newline*))
 
 (define-builtin symbol-function (x)
   (js!selfcall
     "var symbol = " x ";" *newline*
     "var func = symbol.fvalue;" *newline*
-    "if (func === undefined) throw \"Function `\" + symbol.name + \"' is undefined.\";" *newline*
+    "if (func === undefined) throw \"Function `\" + xstring(symbol.name) + \"' is undefined.\";" *newline*
     "return func;" *newline*))
 
 (define-builtin symbol-plist (x)
   (code "((" x ").plist || " (ls-compile nil) ")"))
 
 (define-builtin lambda-code (x)
-  (code "(" x ").toString()"))
+  (code "make_lisp_string((" x ").toString())"))
 
 (define-builtin eq (x y)
   (js!bool (code "(" x " === " y ")")))
      "return (typeof(" x ") == \"string\") && x.length == 1;")))
 
 (define-builtin char-to-string (x)
-  (type-check (("x" "string" x))
-    "(x)"))
+  (js!selfcall
+    "var r = [" x "];" *newline*
+    "r.type = 'character';"
+    "return r"))
 
 (define-builtin stringp (x)
-  (js!bool (code "(typeof(" x ") == \"string\")")))
+  (js!bool
+   (js!selfcall
+     "var x = " x ";" *newline*
+     "return typeof(x) == 'object' && 'length' in x && x.type == 'character';")))
 
 (define-builtin string-upcase (x)
-  (type-check (("x" "string" x))
-    "x.toUpperCase()"))
+  (code "make_lisp_string(xstring(" x ").toUpperCase())"))
 
 (define-builtin string-length (x)
-  (type-check (("x" "string" x))
-    "x.length"))
+  (code x ".length"))
 
-(define-raw-builtin slice (string a &optional b)
+(define-raw-builtin slice (vector a &optional b)
   (js!selfcall
-    "var str = " (ls-compile string) ";" *newline*
+    "var vector = " (ls-compile vector) ";" *newline*
     "var a = " (ls-compile a) ";" *newline*
     "var b;" *newline*
     (when b (code "b = " (ls-compile b) ";" *newline*))
-    "return str.slice(a,b);" *newline*))
+    "return vector.slice(a,b);" *newline*))
 
 (define-builtin char (string index)
-  (type-check (("string" "string" string)
-               ("index" "number" index))
-    "string.charAt(index)"))
+  (code string "[" index "]"))
 
 (define-builtin concat-two (string1 string2)
-  (type-check (("string1" "string" string1)
-               ("string2" "string" string2))
-    "string1.concat(string2)"))
+  (js!selfcall
+    "var r = " string1 ".concat(" string2 ");" *newline*
+    "r.type = 'character';"
+    "return r;" *newline*))
 
 (define-raw-builtin funcall (func &rest args)
   (js!selfcall
           "return (typeof f === 'function'? f : f.fvalue).apply(this, args);" *newline*))))
 
 (define-builtin js-eval (string)
-  (type-check (("string" "string" string))
-    (if *multiple-value-p*
-        (js!selfcall
-          "var v = globalEval(string);" *newline*
-          "return values.apply(this, forcemv(v));" *newline*)
-        "globalEval(string)")))
+  (if *multiple-value-p*
+      (js!selfcall
+        "var v = globalEval(xstring(" string "));" *newline*
+        "return values.apply(this, forcemv(v));" *newline*)
+      (code "globalEval(xstring(" string "))")))
 
 (define-builtin %throw (string)
   (js!selfcall "throw " string ";" *newline*))
 
 (define-builtin oget (object key)
   (js!selfcall
-    "var tmp = " "(" object ")[" key "];" *newline*
+    "var tmp = " "(" object ")[xstring(" key ")];" *newline*
     "return tmp == undefined? " (ls-compile nil) ": tmp ;" *newline*))
 
 (define-builtin oset (object key value)
-  (code "((" object ")[" key "] = " value ")"))
+  (code "((" object ")[xstring(" key ")] = " value ")"))
 
 (define-builtin in (key object)
-  (js!bool (code "((" key ") in (" object "))")))
+  (js!bool (code "(xstring(" key ") in (" object "))")))
 
 (define-builtin functionp (x)
   (js!bool (code "(typeof " x " == 'function')")))
 
 (define-builtin write-string (x)
-  (type-check (("x" "string" x))
-    "lisp.write(x)"))
+  (code "lisp.write(" x ")"))
 
 (define-builtin make-array (n)
   (js!selfcall
               (code (ls-compile `',sexp) ".value"))
              (t
               (ls-compile `(symbol-value ',sexp))))))
-        ((integerp sexp) (integer-to-string sexp))
-        ((floatp sexp) (float-to-string sexp))
-        ((characterp sexp) (code "\"" (escape-string (string sexp)) "\""))
-        ((stringp sexp) (code "\"" (escape-string sexp) "\""))
-        ((arrayp sexp) (literal sexp))
+        ((or (integerp sexp) (floatp sexp) (characterp sexp) (stringp sexp) (arrayp sexp))
+         (literal sexp))
         ((listp sexp)
          (let ((name (car sexp))
                (args (cdr sexp)))
       (t
        (when *compile-print-toplevels*
          (let ((form-string (prin1-to-string sexp)))
-           (write-string "Compiling ")
-           (write-string (truncate-string form-string))
-           (write-line "...")))
-
+           (format t "Compiling ~a..." (truncate-string form-string))))
        (let ((code (ls-compile sexp multiple-value-p)))
          (code (join-trailing (get-toplevel-compilations)
                               (code ";" *newline*))
index 67b0427..ca5cfdb 100644 (file)
@@ -50,3 +50,14 @@ function QIList(){
         return r;
     }
 }
+
+
+// Create and return a lisp string for the Javascript string STRING.
+function make_lisp_string (string){
+    var array = string.split("");
+    array.type = 'character'
+    return array;
+}
+
+function xstring(x){ return x.join(''); }
+
diff --git a/src/string.lisp b/src/string.lisp
new file mode 100644 (file)
index 0000000..04aa7f1
--- /dev/null
@@ -0,0 +1,36 @@
+;;; string.lisp
+
+;; JSCL is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation, either version 3 of the
+;; License, or (at your option) any later version.
+;;
+;; JSCL is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with JSCL.  If not, see <http://www.gnu.org/licenses/>.
+
+(defun string (x)
+  (cond ((stringp x) x)
+        ((symbolp x) (symbol-name x))
+        (t (char-to-string x))))
+
+(defun string= (s1 s2)
+  (let ((n (length s1)))
+    (when (= (length s2) n)
+      (dotimes (i n t)
+        (unless (char= (char s1 i) (char s2 i))
+          (return-from string= nil))))))
+
+(define-setf-expander char (string index)
+  (let ((g!string (gensym))
+        (g!index (gensym))
+        (g!value (gensym)))
+    (values (list g!string g!index)
+            (list string index)
+            (list g!value)
+            `(aset ,g!string ,g!index ,g!value)
+            `(char ,g!string ,g!index))))
index 20aa855..d586cdd 100644 (file)
@@ -16,7 +16,8 @@
 ;; You should have received a copy of the GNU General Public License
 ;; along with JSCL.  If not, see <http://www.gnu.org/licenses/>.
 
-(defvar *newline* (string (code-char 10)))
+(defvar *newline* "
+")
 
 (defmacro concatf (variable &body form)
   `(setq ,variable (concat ,variable (progn ,@form))))
       x
       (list x)))
 
-(defun !reduce (func list &key initial-value)
-  (if (null list)
-      initial-value
-      (!reduce func
-               (cdr list)
-               :initial-value (funcall func initial-value (car list)))))
+(defun !reduce (func list initial-value)
+  (let ((result initial-value))
+    (dolist (element list result)
+      (setq result (funcall func result element)))))
 
 ;;; Concatenate a list of strings, with a separator
 (defun join (list &optional (separator ""))
   (if (null list)
       ""
-      (!reduce (lambda (s o) (concat s separator o))  
-               (cdr list) 
-               :initial-value (car list)))) 
+      (!reduce (lambda (s o) (concat s separator o))
+               (cdr list)
+               (car list))))
 
 (defun join-trailing (list &optional (separator ""))
   (if (null list)
diff --git a/tests/strings.lisp b/tests/strings.lisp
new file mode 100644 (file)
index 0000000..e955d34
--- /dev/null
@@ -0,0 +1,23 @@
+(defvar *str* "hello world")
+(defvar *str2* "h")
+
+(test (stringp *str*))
+(test (not (characterp *str*)))
+(test (not (integerp *str*)))
+
+(test (stringp *str2*))
+(test (not (characterp *str2*)))
+(test (not (integerp *str2*)))
+
+(test (= (length "hello world") 11))
+(test (arrayp "hello world"))
+
+(test (string= "h" (string #\h)))
+(test (string= "foo" "foo"))
+(test (not (string= "Foo" "foo")))
+(test (not (string= "foo" "foox")))
+
+(let ((str "hello"))
+  (setf (char str 0) #\X)
+  (setf (char str 4) #\X)
+  (test (string= str "XellX")))