IF can return multiple values
[jscl.git] / ecmalisp.lisp
index 0258d41..0d091a6 100644 (file)
 ;;; language to the compiler to be able to run.
 
 #+ecmalisp
 ;;; language to the compiler to be able to run.
 
 #+ecmalisp
+(js-eval "function pv (x) { return x; }")
+#+ecmalisp
+(js-eval "var values = pv;")
+
+#+ecmalisp
 (progn
   (eval-when-compile
     (%compile-defmacro 'defmacro
 (progn
   (eval-when-compile
     (%compile-defmacro 'defmacro
   (defun reverse (list)
     (revappend list '()))
 
   (defun reverse (list)
     (revappend list '()))
 
+  (defmacro psetq (&rest pairs)
+    (let ( ;; For each pair, we store here a list of the form
+         ;; (VARIABLE GENSYM VALUE).
+         (assignments '()))
+      (while t
+       (cond
+         ((null pairs) (return))
+         ((null (cdr pairs))
+          (error "Odd paris in PSETQ"))
+         (t
+          (let ((variable (car pairs))
+                (value (cadr pairs)))
+            (push `(,variable ,(gensym) ,value)  assignments)
+            (setq pairs (cddr pairs))))))
+      (setq assignments (reverse assignments))
+      ;;
+      `(let ,(mapcar #'cdr assignments)
+        (setq ,@(!reduce #'append (mapcar #'butlast assignments) '())))))
+
   (defun list-length (list)
     (let ((l 0))
       (while (not (null list))
   (defun list-length (list)
     (let ((l 0))
       (while (not (null list))
       l))
 
   (defun length (seq)
       l))
 
   (defun length (seq)
-    (if (stringp seq)
-        (string-length seq)
-        (list-length seq)))
+    (cond
+      ((stringp seq)
+       (string-length seq))
+      ((arrayp seq)
+       (oget seq "length"))
+      ((listp seq)
+       (list-length seq))))
 
   (defun concat-two (s1 s2)
     (concat-two s1 s2))
 
   (defun concat-two (s1 s2)
     (concat-two s1 s2))
   (defun export (symbols &optional (package *package*))
     (let ((exports (%package-external-symbols package)))
       (dolist (symb symbols t)
   (defun export (symbols &optional (package *package*))
     (let ((exports (%package-external-symbols package)))
       (dolist (symb symbols t)
-        (oset exports (symbol-name symb) symb)))))
+        (oset exports (symbol-name symb) symb))))
+
+  (defun get-universal-time ()
+    (+ (get-unix-time) 2208988800)))
 
 
 ;;; The compiler offers some primitives and special forms which are
 
 
 ;;; The compiler offers some primitives and special forms which are
   (defun setcar (cons new)
     (setf (car cons) new))
   (defun setcdr (cons new)
   (defun setcar (cons new)
     (setf (car cons) new))
   (defun setcdr (cons new)
-    (setf (cdr cons) new)))
+    (setf (cdr cons) new))
+
+  (defun aset (array idx value)
+    (setf (aref array idx) value)))
 
 ;;; At this point, no matter if Common Lisp or ecmalisp is compiling
 ;;; from here, this code will compile on both. We define some helper
 
 ;;; At this point, no matter if Common Lisp or ecmalisp is compiling
 ;;; from here, this code will compile on both. We define some helper
 (defun mapconcat (func list)
   (join (mapcar func list)))
 
 (defun mapconcat (func list)
   (join (mapcar func list)))
 
+(defun vector-to-list (vector)
+  (let ((list nil)
+       (size (length vector)))
+    (dotimes (i size (reverse list))
+      (push (aref vector i) list))))
+
+(defun list-to-vector (list)
+  (let ((v (make-array (length list)))
+       (i 0))
+    (dolist (x list v)
+      (aset v i x)
+      (incf i))))
+
+#+ecmalisp
+(progn
+  (defun values-list (list)
+    (values-array (list-to-vector list)))
+
+  (defun values (&rest args)
+    (values-list args)))
+
+
 ;;; Like CONCAT, but prefix each line with four spaces. Two versions
 ;;; of this function are available, because the Ecmalisp version is
 ;;; very slow and bootstraping was annoying.
 ;;; Like CONCAT, but prefix each line with four spaces. Two versions
 ;;; of this function are available, because the Ecmalisp version is
 ;;; very slow and bootstraping was annoying.
            (symbol-name form)
            (let ((package (symbol-package form))
                  (name (symbol-name form)))
            (symbol-name form)
            (let ((package (symbol-package form))
                  (name (symbol-name form)))
-             (concat (if (eq package (find-package "KEYWORD"))
-                         ""
-                         (package-name package))
+             (concat (cond
+                       ((null package) "#")
+                       ((eq package (find-package "KEYWORD")) "")
+                       (t (package-name package)))
                      ":" name))))
       ((integerp form) (integer-to-string form))
       ((stringp form) (concat "\"" (escape-string form) "\""))
                      ":" name))))
       ((integerp form) (integer-to-string form))
       ((stringp form) (concat "\"" (escape-string form) "\""))
                      (prin1-to-string (car last))
                      (concat (prin1-to-string (car last)) " . " (prin1-to-string (cdr last)))))
                ")"))
                      (prin1-to-string (car last))
                      (concat (prin1-to-string (car last)) " . " (prin1-to-string (cdr last)))))
                ")"))
+      ((arrayp form)
+       (concat "#" (prin1-to-string (vector-to-list form))))
       ((packagep form)
        (concat "#<PACKAGE " (package-name form) ">"))))
 
       ((packagep form)
        (concat "#<PACKAGE " (package-name form) ">"))))
 
   (ecase (%read-char stream)
     (#\'
      (list 'function (ls-read stream)))
   (ecase (%read-char stream)
     (#\'
      (list 'function (ls-read stream)))
+    (#\( (list-to-vector (%read-list stream)))
+    (#\: (make-symbol (string-upcase (read-until stream #'terminalp))))
     (#\\
      (let ((cname
             (concat (string (%read-char stream))
     (#\\
      (let ((cname
             (concat (string (%read-char stream))
 ;;; too. The respective real functions are defined in the target (see
 ;;; the beginning of this file) as well as some primitive functions.
 
 ;;; too. The respective real functions are defined in the target (see
 ;;; the beginning of this file) as well as some primitive functions.
 
+(defvar *multiple-value-p* nil)
+
 (defvar *compilation-unit-checks* '())
 
 (defun make-binding (name type value &optional declarations)
 (defvar *compilation-unit-checks* '())
 
 (defun make-binding (name type value &optional declarations)
 
 (define-compilation if (condition true false)
   (concat "(" (ls-compile condition) " !== " (ls-compile nil)
 
 (define-compilation if (condition true false)
   (concat "(" (ls-compile condition) " !== " (ls-compile nil)
-          " ? " (ls-compile true)
-          " : " (ls-compile false)
+          " ? " (ls-compile true *multiple-value-p*)
+          " : " (ls-compile false *multiple-value-p*)
           ")"))
 
 (defvar *lambda-list-keywords* '(&optional &rest))
           ")"))
 
 (defvar *lambda-list-keywords* '(&optional &rest))
         "return func;" *newline*)
       (join strs)))
 
         "return func;" *newline*)
       (join strs)))
 
+
 (define-compilation lambda (lambda-list &rest body)
   (let ((required-arguments (lambda-list-required-arguments lambda-list))
         (optional-arguments (lambda-list-optional-arguments lambda-list))
 (define-compilation lambda (lambda-list &rest body)
   (let ((required-arguments (lambda-list-required-arguments lambda-list))
         (optional-arguments (lambda-list-optional-arguments lambda-list))
       (lambda-docstring-wrapper
        documentation
        "(function ("
       (lambda-docstring-wrapper
        documentation
        "(function ("
-       (join (mapcar #'translate-variable
-                     (append required-arguments optional-arguments))
+       (join (cons "values"
+                   (mapcar #'translate-variable
+                           (append required-arguments optional-arguments)))
              ",")
        "){" *newline*
        ;; Check number of arguments
        (indent
         (if required-arguments
              ",")
        "){" *newline*
        ;; Check number of arguments
        (indent
         (if required-arguments
-            (concat "if (arguments.length < " (integer-to-string n-required-arguments)
+            (concat "if (arguments.length < " (integer-to-string (1+ n-required-arguments))
                     ") throw 'too few arguments';" *newline*)
             "")
         (if (not rest-argument)
             (concat "if (arguments.length > "
                     ") throw 'too few arguments';" *newline*)
             "")
         (if (not rest-argument)
             (concat "if (arguments.length > "
-                    (integer-to-string (+ n-required-arguments n-optional-arguments))
+                    (integer-to-string (+ 1 n-required-arguments n-optional-arguments))
                     ") throw 'too many arguments';" *newline*)
             "")
         ;; Optional arguments
         (if optional-arguments
                     ") throw 'too many arguments';" *newline*)
             "")
         ;; Optional arguments
         (if optional-arguments
-            (concat "switch(arguments.length){" *newline*
+            (concat "switch(arguments.length-1){" *newline*
                     (let ((optional-and-defaults
                            (lambda-list-optional-arguments-with-default lambda-list))
                           (cases nil)
                     (let ((optional-and-defaults
                            (lambda-list-optional-arguments-with-default lambda-list))
                           (cases nil)
             (let ((js!rest (translate-variable rest-argument)))
               (concat "var " js!rest "= " (ls-compile nil) ";" *newline*
                       "for (var i = arguments.length-1; i>="
             (let ((js!rest (translate-variable rest-argument)))
               (concat "var " js!rest "= " (ls-compile nil) ";" *newline*
                       "for (var i = arguments.length-1; i>="
-                      (integer-to-string (+ n-required-arguments n-optional-arguments))
+                      (integer-to-string (+ 1 n-required-arguments n-optional-arguments))
                       "; i--)" *newline*
                       (indent js!rest " = "
                               "{car: arguments[i], cdr: ") js!rest "};"
                       "; i--)" *newline*
                       (indent js!rest " = "
                               "{car: arguments[i], cdr: ") js!rest "};"
         (ls-compile-block body t)) *newline*
        "})"))))
 
         (ls-compile-block body t)) *newline*
        "})"))))
 
-(define-compilation setq (var val)
+
+(defun setq-pair (var val)
   (let ((b (lookup-in-lexenv var *environment* 'variable)))
     (if (eq (binding-type b) 'lexical-variable)
         (concat (binding-value b) " = " (ls-compile val))
         (ls-compile `(set ',var ,val)))))
 
   (let ((b (lookup-in-lexenv var *environment* 'variable)))
     (if (eq (binding-type b) 'lexical-variable)
         (concat (binding-value b) " = " (ls-compile val))
         (ls-compile `(set ',var ,val)))))
 
+(define-compilation setq (&rest pairs)
+  (let ((result ""))
+    (while t
+      (cond
+       ((null pairs) (return))
+       ((null (cdr pairs))
+        (error "Odd paris in SETQ"))
+       (t
+        (concatf result
+          (concat (setq-pair (car pairs) (cadr pairs))
+                  (if (null (cddr pairs)) "" ", ")))
+        (setq pairs (cddr pairs)))))
+    (concat "(" result ")")))
+
 ;;; FFI Variable accessors
 (define-compilation js-vref (var)
   var)
 ;;; FFI Variable accessors
 (define-compilation js-vref (var)
   var)
   (concat "(" var " = " (ls-compile val) ")"))
 
 
   (concat "(" var " = " (ls-compile val) ")"))
 
 
+
 ;;; Literals
 (defun escape-string (string)
   (let ((output "")
 ;;; Literals
 (defun escape-string (string)
   (let ((output "")
      (or (cdr (assoc sexp *literal-symbols*))
         (let ((v (genlit))
               (s #+common-lisp (concat "{name: \"" (escape-string (symbol-name sexp)) "\"}")
      (or (cdr (assoc sexp *literal-symbols*))
         (let ((v (genlit))
               (s #+common-lisp (concat "{name: \"" (escape-string (symbol-name sexp)) "\"}")
-                 #+ecmalisp (ls-compile
-                              `(intern ,(symbol-name sexp)
-                                       ,(package-name (symbol-package sexp))))))
+                 #+ecmalisp
+                  (let ((package (symbol-package sexp)))
+                    (if (null package)
+                        (concat "{name: \"" (escape-string (symbol-name sexp)) "\"}")
+                        (ls-compile `(intern ,(symbol-name sexp) ,(package-name package)))))))
           (push (cons sexp v) *literal-symbols*)
           (toplevel-compilation (concat "var " v " = " s))
           v)))
           (push (cons sexp v) *literal-symbols*)
           (toplevel-compilation (concat "var " v " = " s))
           v)))
           c
           (let ((v (genlit)))
             (toplevel-compilation (concat "var " v " = " c))
           c
           (let ((v (genlit)))
             (toplevel-compilation (concat "var " v " = " c))
-            v))))))
+            v))))
+    ((arrayp sexp)
+     (let ((elements (vector-to-list sexp)))
+       (let ((c (concat "[" (join (mapcar #'literal elements) ", ") "]")))
+        (if recursive
+            c
+            (let ((v (genlit)))
+              (toplevel-compilation (concat "var " v " = " c))
+              v)))))))
 
 (define-compilation quote (sexp)
   (literal sexp))
 
 (define-compilation quote (sexp)
   (literal sexp))
   (js!selfcall (ls-compile-block body t)))
 
 (defun special-variable-p (x)
   (js!selfcall (ls-compile-block body t)))
 
 (defun special-variable-p (x)
-  (claimp x 'variable 'special))
+  (and (claimp x 'variable 'special) t))
 
 ;;; Wrap CODE to restore the symbol values of the dynamic
 ;;; bindings. BINDINGS is a list of pairs of the form
 
 ;;; Wrap CODE to restore the symbol values of the dynamic
 ;;; bindings. BINDINGS is a list of pairs of the form
    "}" *newline*))
 
 (define-compilation let (bindings &rest body)
    "}" *newline*))
 
 (define-compilation let (bindings &rest body)
-  (let ((bindings (mapcar #'ensure-list bindings)))
-    (let ((variables (mapcar #'first bindings)))
-      (let ((cvalues (mapcar #'ls-compile (mapcar #'second bindings)))
-            (*environment* (extend-local-env (remove-if #'special-variable-p variables)))
-            (dynamic-bindings))
-        (concat "(function("
-                (join (mapcar (lambda (x)
-                                (if (special-variable-p x)
-                                    (let ((v (gvarname x)))
-                                      (push (cons x v) dynamic-bindings)
-                                      v)
-                                    (translate-variable x)))
-                              variables)
-                      ",")
-                "){" *newline*
-                (let ((body (ls-compile-block body t)))
-                  (indent (let-binding-wrapper dynamic-bindings body)))
-                "})(" (join cvalues ",") ")")))))
+  (let* ((bindings (mapcar #'ensure-list bindings))
+         (variables (mapcar #'first bindings))
+         (cvalues (mapcar #'ls-compile (mapcar #'second bindings)))
+         (*environment* (extend-local-env (remove-if #'special-variable-p variables)))
+         (dynamic-bindings))
+    (concat "(function("
+            (join (mapcar (lambda (x)
+                            (if (special-variable-p x)
+                                (let ((v (gvarname x)))
+                                  (push (cons x v) dynamic-bindings)
+                                  v)
+                                (translate-variable x)))
+                          variables)
+                  ",")
+            "){" *newline*
+            (let ((body (ls-compile-block body t)))
+              (indent (let-binding-wrapper dynamic-bindings body)))
+            "})(" (join cvalues ",") ")")))
 
 
 ;;; Return the code to initialize BINDING, and push it extending the
 
 
 ;;; Return the code to initialize BINDING, and push it extending the
         (value (second binding)))
     (if (special-variable-p var)
         (concat (ls-compile `(setq ,var ,value)) ";" *newline*)
         (value (second binding)))
     (if (special-variable-p var)
         (concat (ls-compile `(setq ,var ,value)) ";" *newline*)
-        (let ((v (gvarname var)))
-          (let ((b (make-binding var 'variable v)))
-            (prog1 (concat "var " v " = " (ls-compile value) ";" *newline*)
-              (push-to-lexenv b *environment* 'variable)))))))
+        (let* ((v (gvarname var))
+               (b (make-binding var 'variable v)))
+          (prog1 (concat "var " v " = " (ls-compile value) ";" *newline*)
+            (push-to-lexenv b *environment* 'variable))))))
 
 ;;; Wrap BODY to restore the symbol values of SYMBOLS after body. It
 ;;; DOES NOT generate code to initialize the value of the symbols,
 
 ;;; Wrap BODY to restore the symbol values of SYMBOLS after body. It
 ;;; DOES NOT generate code to initialize the value of the symbols,
           "})" *newline*)
         (error (concat "Unknown tag `" n "'.")))))
 
           "})" *newline*)
         (error (concat "Unknown tag `" n "'.")))))
 
-
 (define-compilation unwind-protect (form &rest clean-up)
   (js!selfcall
     "var ret = " (ls-compile nil) ";" *newline*
 (define-compilation unwind-protect (form &rest clean-up)
   (js!selfcall
     "var ret = " (ls-compile nil) ";" *newline*
     "}" *newline*
     "return ret;" *newline*))
 
     "}" *newline*
     "return ret;" *newline*))
 
+(define-compilation multiple-value-call (func-form &rest forms)
+  (let ((func (ls-compile func-form)))
+    (js!selfcall
+      "var args = [values];" *newline*
+      "values = function(){" *newline*
+      (indent "var result = [];" *newline*
+              "result['multiple-value'] = true;" *newline*
+              "for (var i=0; i<arguments.length; i++)" *newline*
+              (indent "result.push(arguments[i]);" *newline*)
+              "return result;" *newline*)
+      "}" *newline*
+      "var vs;" *newline*
+      (mapconcat (lambda (form)
+                   (concat "vs = " (ls-compile form t) ";" *newline*
+                           "if (typeof vs === 'object' && 'multiple-value' in vs)" *newline*
+                           (indent "args = args.concat(vs);" *newline*)
+                           "else" *newline*
+                           (indent "args.push(vs);" *newline*)))
+                 forms)
+      "return (" func ").apply(window, args);" *newline*)))
+
+
 
 ;;; A little backquote implementation without optimizations of any
 ;;; kind for ecmalisp.
 
 ;;; A little backquote implementation without optimizations of any
 ;;; kind for ecmalisp.
   (let ((args (cons x others)))
     (variable-arity args
       (if (null others)
   (let ((args (cons x others)))
     (variable-arity args
       (if (null others)
-         (concat "/" (car args))
+         (concat "1 /" (car args))
          (join args "/")))))
 
 (define-builtin mod (x y) (num-op-num x "%" y))
 
          (join args "/")))))
 
 (define-builtin mod (x y) (num-op-num x "%" y))
 
+
+(defun comparison-conjuntion (vars op)
+  (cond
+    ((null (cdr vars))
+     "true")
+    ((null (cddr vars))
+     (concat (car vars) op (cadr vars)))
+    (t
+     (concat (car vars) op (cadr vars)
+            " && "
+            (comparison-conjuntion (cdr vars) op)))))
+
 (defmacro define-builtin-comparison (op sym)
 (defmacro define-builtin-comparison (op sym)
-  `(define-raw-builtin ,op (&rest args)
-     (js!bool
-      (let ((x (car args))
-           (res "true"))
-       (dolist (y (cdr args))
-         (setq res (concat "("
-                    (ls-compile x) " " ,sym " " (ls-compile y) ")" " && " res))
-         (setq x y))
-       res))))
+  `(define-raw-builtin ,op (x &rest args)
+     (let ((args (cons x args)))
+       (variable-arity args
+        (js!bool (comparison-conjuntion args ,sym))))))
 
 (define-builtin-comparison > ">")
 (define-builtin-comparison < "<")
 
 (define-builtin-comparison > ">")
 (define-builtin-comparison < "<")
   (concat "(" symbol ").value = " value))
 
 (define-builtin fset (symbol value)
   (concat "(" symbol ").value = " value))
 
 (define-builtin fset (symbol value)
-  (concat "(" symbol ").function = " value))
+  (concat "(" symbol ").fvalue = " value))
 
 (define-builtin boundp (x)
   (js!bool (concat "(" x ".value !== undefined)")))
 
 (define-builtin boundp (x)
   (js!bool (concat "(" x ".value !== undefined)")))
 (define-builtin symbol-function (x)
   (js!selfcall
     "var symbol = " x ";" *newline*
 (define-builtin symbol-function (x)
   (js!selfcall
     "var symbol = " x ";" *newline*
-    "var func = symbol.function;" *newline*
+    "var func = symbol.fvalue;" *newline*
     "if (func === undefined) throw \"Function `\" + symbol.name + \"' is undefined.\";" *newline*
     "return func;" *newline*))
 
     "if (func === undefined) throw \"Function `\" + symbol.name + \"' is undefined.\";" *newline*
     "return func;" *newline*))
 
 
 (define-raw-builtin funcall (func &rest args)
   (concat "(" (ls-compile func) ")("
 
 (define-raw-builtin funcall (func &rest args)
   (concat "(" (ls-compile func) ")("
-          (join (mapcar #'ls-compile args)
+          (join (cons "pv" (mapcar #'ls-compile args))
                 ", ")
           ")"))
 
                 ", ")
           ")"))
 
             (last (car (last args))))
         (js!selfcall
           "var f = " (ls-compile func) ";" *newline*
             (last (car (last args))))
         (js!selfcall
           "var f = " (ls-compile func) ";" *newline*
-          "var args = [" (join (mapcar #'ls-compile args)
+          "var args = [" (join (cons "pv" (mapcar #'ls-compile args))
                                ", ")
           "];" *newline*
           "var tail = (" (ls-compile last) ");" *newline*
                                ", ")
           "];" *newline*
           "var tail = (" (ls-compile last) ");" *newline*
   (type-check (("x" "string" x))
     "lisp.write(x)"))
 
   (type-check (("x" "string" x))
     "lisp.write(x)"))
 
+(define-builtin make-array (n)
+  (js!selfcall
+    "var r = [];" *newline*
+    "for (var i = 0; i < " n "; i++)" *newline*
+    (indent "r.push(" (ls-compile nil) ");" *newline*)
+    "return r;" *newline*))
+
+(define-builtin arrayp (x)
+  (js!bool
+   (js!selfcall
+     "var x = " x ";" *newline*
+     "return typeof x === 'object' && 'length' in x;")))
+
+(define-builtin aref (array n)
+  (js!selfcall
+    "var x = " "(" array ")[" n "];" *newline*
+    "if (x === undefined) throw 'Out of range';" *newline*
+    "return x;" *newline*))
+
+(define-builtin aset (array n value)
+  (js!selfcall
+    "var x = " array ";" *newline*
+    "var i = " n ";" *newline*
+    "if (i < 0 || i >= x.length) throw 'Out of range';" *newline*
+    "return x[i] = " value ";" *newline*))
+
+(define-builtin get-unix-time ()
+  (concat "(Math.round(new Date() / 1000))"))
+
+(define-builtin values-array (array)
+  (concat "values.apply(this, " array ")"))
+
+(define-raw-builtin values (&rest args)
+  (concat "values(" (join (mapcar #'ls-compile args) ", ") ")"))
+
+
 (defun macro (x)
   (and (symbolp x)
        (let ((b (lookup-in-lexenv x *environment* 'function)))
 (defun macro (x)
   (and (symbolp x)
        (let ((b (lookup-in-lexenv x *environment* 'function)))
         form)))
 
 (defun compile-funcall (function args)
         form)))
 
 (defun compile-funcall (function args)
-  (if (and (symbolp function)
-           (claimp function 'function 'non-overridable))
-      (concat (ls-compile `',function) ".function("
-              (join (mapcar #'ls-compile args)
-                    ", ")
-              ")")
-      (concat (ls-compile `#',function) "("
-              (join (mapcar #'ls-compile args)
-                    ", ")
-              ")")))
+  (let ((values-funcs (if *multiple-value-p* "values" "pv")))
+    (if (and (symbolp function)
+             (claimp function 'function 'non-overridable))
+        (concat (ls-compile `',function) ".fvalue("
+                (join (cons values-funcs (mapcar #'ls-compile args))
+                      ", ")
+                ")")
+        (concat (ls-compile `#',function) "("
+                (join (cons values-funcs (mapcar #'ls-compile args))
+                      ", ")
+                ")"))))
 
 (defun ls-compile-block (sexps &optional return-last-p)
   (if return-last-p
 
 (defun ls-compile-block (sexps &optional return-last-p)
   (if return-last-p
        (remove-if #'null-or-empty-p (mapcar #'ls-compile sexps))
        (concat ";" *newline*))))
 
        (remove-if #'null-or-empty-p (mapcar #'ls-compile sexps))
        (concat ";" *newline*))))
 
-(defun ls-compile (sexp)
-  (cond
-    ((symbolp sexp)
-     (let ((b (lookup-in-lexenv sexp *environment* 'variable)))
-       (cond
-         ((and b (not (member 'special (binding-declarations b))))
-          (binding-value b))
-         ((or (keywordp sexp)
-              (member 'constant (binding-declarations b)))
-          (concat (ls-compile `',sexp) ".value"))
-         (t
-          (ls-compile `(symbol-value ',sexp))))))
-    ((integerp sexp) (integer-to-string sexp))
-    ((stringp sexp) (concat "\"" (escape-string sexp) "\""))
-    ((listp sexp)
-     (let ((name (car sexp))
-           (args (cdr sexp)))
-       (cond
-         ;; Special forms
-         ((assoc name *compilations*)
-          (let ((comp (second (assoc name *compilations*))))
-            (apply comp args)))
-         ;; Built-in functions
-         ((and (assoc name *builtins*)
-               (not (claimp name 'function 'notinline)))
-          (let ((comp (second (assoc name *builtins*))))
-            (apply comp args)))
-         (t
-          (if (macro name)
-              (ls-compile (ls-macroexpand-1 sexp))
-              (compile-funcall name args))))))))
+(defun ls-compile (sexp &optional multiple-value-p)
+  (let ((*multiple-value-p* multiple-value-p))
+    (cond
+      ((symbolp sexp)
+       (let ((b (lookup-in-lexenv sexp *environment* 'variable)))
+         (cond
+           ((and b (not (member 'special (binding-declarations b))))
+            (binding-value b))
+           ((or (keywordp sexp)
+                (member 'constant (binding-declarations b)))
+            (concat (ls-compile `',sexp) ".value"))
+           (t
+            (ls-compile `(symbol-value ',sexp))))))
+      ((integerp sexp) (integer-to-string sexp))
+      ((stringp sexp) (concat "\"" (escape-string sexp) "\""))
+      ((arrayp sexp) (literal sexp))
+      ((listp sexp)
+       (let ((name (car sexp))
+             (args (cdr sexp)))
+         (cond
+           ;; Special forms
+           ((assoc name *compilations*)
+            (let ((comp (second (assoc name *compilations*))))
+              (apply comp args)))
+           ;; Built-in functions
+           ((and (assoc name *builtins*)
+                 (not (claimp name 'function 'notinline)))
+            (let ((comp (second (assoc name *builtins*))))
+              (apply comp args)))
+           (t
+            (if (macro name)
+                (ls-compile (ls-macroexpand-1 sexp))
+                (compile-funcall name args))))))
+      (t
+       (error "How should I compile this?")))))
 
 (defun ls-compile-toplevel (sexp)
   (let ((*toplevel-compilations* nil))
 
 (defun ls-compile-toplevel (sexp)
   (let ((*toplevel-compilations* nil))
       (js-eval code)))
 
   (export '(&rest &optional &body * *gensym-counter* *package* + - / 1+ 1- < <= =
       (js-eval code)))
 
   (export '(&rest &optional &body * *gensym-counter* *package* + - / 1+ 1- < <= =
-= > >= and append apply assoc atom block boundp boundp butlast caar
-cadddr caddr cadr car car case catch cdar cdddr cddr cdr cdr char
-char-code char= code-char cond cons consp copy-list decf declaim
-defparameter defun defvar digit-char-p disassemble documentation
-dolist dotimes ecase eq eql equal error eval every export fdefinition
-find-package find-symbol first fourth fset funcall function functionp
-gensym go identity if in-package incf integerp integerp intern
-keywordp lambda last length let let* list-all-packages list listp
-make-package make-symbol mapcar member minusp mod nil not nth nthcdr
-null numberp or package-name package-use-list packagep plusp
-prin1-to-string print proclaim prog1 prog2 pron push quote remove
-remove-if remove-if-not return return-from revappend reverse second
-set setq some string-upcase string string= stringp subseq
-symbol-function symbol-name symbol-package symbol-plist symbol-value
-symbolp t tagbody third throw truncate unless unwind-protect variable
-warn when write-line write-string zerop))
+            = > >= and append apply aref arrayp aset assoc atom block boundp
+            boundp butlast caar cadddr caddr cadr car car case catch cdar cdddr
+            cddr cdr cdr char char-code char= code-char cond cons consp copy-list
+            decf declaim defparameter defun defmacro defvar digit-char-p disassemble
+            documentation dolist dotimes ecase eq eql equal error eval every
+            export fdefinition find-package find-symbol first fourth fset funcall
+            function functionp gensym get-universal-time go identity if in-package
+            incf integerp integerp intern keywordp lambda last length let let*
+            list-all-packages list listp make-array make-package make-symbol
+            mapcar member minusp mod multiple-value-call  nil not nth nthcdr null
+            numberp or package-name package-use-list packagep plusp prin1-to-string
+            print proclaim prog1 prog2 progn psetq push quote remove remove-if
+            remove-if-not return return-from revappend reverse second set setq
+            some string-upcase string string= stringp subseq symbol-function
+            symbol-name symbol-package symbol-plist symbol-value symbolp t tagbody
+            third throw truncate unless unwind-protect values values-list variable
+            warn when write-line write-string zerop))
 
   (setq *package* *user-package*)
 
 
   (setq *package* *user-package*)