Rename LS-COMPILE => CONVERT and LS-COMPILE-TOPLEVEL to COMPILE-TOPLEVEL
[jscl.git] / src / compiler.lisp
index b8623e1..2c8269c 100644 (file)
@@ -1,6 +1,6 @@
-;;; compiler.lisp --- 
+;;; compiler.lisp ---
 
 
-;; copyright (C) 2012, 2013 David Vazquez
+;; Copyright (C) 2012, 2013 David Vazquez
 ;; Copyright (C) 2012 Raimon Grau
 
 ;; JSCL is free software: you can redistribute it and/or
 ;; Copyright (C) 2012 Raimon Grau
 
 ;; JSCL is free software: you can redistribute it and/or
 
 ;;;; Compiler
 
 
 ;;;; Compiler
 
+(/debug "loading compiler.lisp!")
+
+(define-js-macro selfcall (&body body)
+  `(call (function () ,@body)))
+
+(define-js-macro bool (expr)
+  `(if ,expr ,(convert t) ,(convert nil)))
+
 ;;; Translate the Lisp code to Javascript. It will compile the special
 ;;; forms. Some primitive functions are compiled as special forms
 ;;; too. The respective real functions are defined in the target (see
 ;;; the beginning of this file) as well as some primitive functions.
 
 ;;; Translate the Lisp code to Javascript. It will compile the special
 ;;; forms. Some primitive functions are compiled as special forms
 ;;; too. The respective real functions are defined in the target (see
 ;;; the beginning of this file) as well as some primitive functions.
 
-(defun code (&rest args)
-  (mapconcat (lambda (arg)
-               (cond
-                 ((null arg) "")
-                 ((integerp arg) (integer-to-string arg))
-                 ((floatp arg) (float-to-string arg))
-                 ((stringp arg) arg)
-                 (t (error "Unknown argument `~S'." arg))))
-             args))
-
-;;; Wrap X with a Javascript code to convert the result from
-;;; Javascript generalized booleans to T or NIL.
-(defun js!bool (x)
-  (code "(" x "?" (ls-compile t) ": " (ls-compile nil) ")"))
-
-;;; Concatenate the arguments and wrap them with a self-calling
-;;; Javascript anonymous function. It is used to make some Javascript
-;;; statements valid expressions and provide a private scope as well.
-;;; It could be defined as function, but we could do some
-;;; preprocessing in the future.
-(defmacro js!selfcall (&body body)
-  `(code "(function(){" *newline* (indent ,@body) "})()"))
+(defun interleave (list element &optional after-last-p)
+  (unless (null list)
+    (with-collect
+      (collect (car list))
+      (dolist (x (cdr list))
+        (collect element)
+        (collect x))
+      (when after-last-p
+        (collect element)))))
 
 ;;; Like CODE, 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 CODE, 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.
 
-#+jscl
-(defun indent (&rest string)
-  (let ((input (apply #'code string)))
-    (let ((output "")
-          (index 0)
-          (size (length input)))
-      (when (plusp (length input)) (concatf output "    "))
-      (while (< index size)
-        (let ((str
-               (if (and (char= (char input index) #\newline)
-                        (< index (1- size))
-                        (not (char= (char input (1+ index)) #\newline)))
-                   (concat (string #\newline) "    ")
-                   (string (char input index)))))
-          (concatf output str))
-        (incf index))
-      output)))
-
-#+common-lisp
-(defun indent (&rest string)
-  (with-output-to-string (*standard-output*)
-    (with-input-from-string (input (apply #'code string))
-      (loop
-         for line = (read-line input nil)
-         while line
-         do (write-string "    ")
-         do (write-line line)))))
-
-
 ;;; A Form can return a multiple values object calling VALUES, like
 ;;; values(arg1, arg2, ...). It will work in any context, as well as
 ;;; returning an individual object. However, if the special variable
 ;;; A Form can return a multiple values object calling VALUES, like
 ;;; values(arg1, arg2, ...). It will work in any context, as well as
 ;;; returning an individual object. However, if the special variable
 ;;; function call.
 (defvar *multiple-value-p* nil)
 
 ;;; function call.
 (defvar *multiple-value-p* nil)
 
-;; A very simple defstruct built on lists. It supports just slot with
-;; an optional default initform, and it will create a constructor,
-;; predicate and accessors for you.
-(defmacro def!struct (name &rest slots)
-  (unless (symbolp name)
-    (error "It is not a full defstruct implementation."))
-  (let* ((name-string (symbol-name name))
-         (slot-descriptions
-          (mapcar (lambda (sd)
-                    (cond
-                      ((symbolp sd)
-                       (list sd))
-                      ((and (listp sd) (car sd) (cddr sd))
-                       sd)
-                      (t
-                       (error "Bad slot description `~S'." sd))))
-                  slots))
-         (predicate (intern (concat name-string "-P"))))
-    `(progn
-       ;; Constructor
-       (defun ,(intern (concat "MAKE-" name-string)) (&key ,@slot-descriptions)
-         (list ',name ,@(mapcar #'car slot-descriptions)))
-       ;; Predicate
-       (defun ,predicate (x)
-         (and (consp x) (eq (car x) ',name)))
-       ;; Copier
-       (defun ,(intern (concat "COPY-" name-string)) (x)
-         (copy-list x))
-       ;; Slot accessors
-       ,@(with-collect
-          (let ((index 1))
-            (dolist (slot slot-descriptions)
-              (let* ((name (car slot))
-                     (accessor-name (intern (concat name-string "-" (string name)))))
-                (collect
-                    `(defun ,accessor-name (x)
-                       (unless (,predicate x)
-                         (error "The object `~S' is not of type `~S'" x ,name-string))
-                       (nth ,index x)))
-                ;; TODO: Implement this with a higher level
-                ;; abstraction like defsetf or (defun (setf ..))
-                (collect
-                    `(define-setf-expander ,accessor-name (x)
-                       (let ((object (gensym))
-                             (new-value (gensym)))
-                         (values (list object)
-                                 (list x)
-                                 (list new-value)
-                                 `(progn
-                                    (rplaca (nthcdr ,',index ,object) ,new-value) 
-                                    ,new-value)
-                                 `(,',accessor-name ,object)))))
-                (incf index)))))
-       ',name)))
-
-
 ;;; Environment
 
 (def!struct binding
 ;;; Environment
 
 (def!struct binding
 
 
 (defvar *environment* (make-lexenv))
 
 
 (defvar *environment* (make-lexenv))
-
 (defvar *variable-counter* 0)
 
 (defun gvarname (symbol)
   (declare (ignore symbol))
 (defvar *variable-counter* 0)
 
 (defun gvarname (symbol)
   (declare (ignore symbol))
-  (code "v" (incf *variable-counter*)))
+  (incf *variable-counter*)
+  (concat "v" (integer-to-string *variable-counter*)))
 
 (defun translate-variable (symbol)
   (awhen (lookup-in-lexenv symbol *environment* 'variable)
 
 (defun translate-variable (symbol)
   (awhen (lookup-in-lexenv symbol *environment* 'variable)
 (defun toplevel-compilation (string)
   (push string *toplevel-compilations*))
 
 (defun toplevel-compilation (string)
   (push string *toplevel-compilations*))
 
-(defun null-or-empty-p (x)
-  (zerop (length x)))
-
 (defun get-toplevel-compilations ()
 (defun get-toplevel-compilations ()
-  (reverse (remove-if #'null-or-empty-p *toplevel-compilations*)))
+  (reverse *toplevel-compilations*))
 
 (defun %compile-defmacro (name lambda)
 
 (defun %compile-defmacro (name lambda)
-  (toplevel-compilation (ls-compile `',name))
+  (toplevel-compilation (convert `',name))
   (let ((binding (make-binding :name name :type 'macro :value lambda)))
     (push-to-lexenv binding  *environment* 'function))
   name)
   (let ((binding (make-binding :name name :type 'macro :value lambda)))
     (push-to-lexenv binding  *environment* 'function))
   name)
   `(push (list ',name (lambda ,args (block ,name ,@body)))
          *compilations*))
 
   `(push (list ',name (lambda ,args (block ,name ,@body)))
          *compilations*))
 
-(define-compilation if (condition true false)
-  (code "(" (ls-compile condition) " !== " (ls-compile nil)
-        " ? " (ls-compile true *multiple-value-p*)
-        " : " (ls-compile false *multiple-value-p*)
-        ")"))
+(define-compilation if (condition true &optional false)
+  `(if (!== ,(convert condition) ,(convert nil))
+       ,(convert true *multiple-value-p*)
+       ,(convert false *multiple-value-p*)))
 
 (defvar *ll-keywords* '(&optional &rest &key))
 
 
 (defvar *ll-keywords* '(&optional &rest &key))
 
   (flet ((canonicalize (keyarg)
           ;; Build a canonical keyword argument descriptor, filling
           ;; the optional fields. The result is a list of the form
   (flet ((canonicalize (keyarg)
           ;; Build a canonical keyword argument descriptor, filling
           ;; the optional fields. The result is a list of the form
-          ;; ((keyword-name var) init-form).
+          ;; ((keyword-name var) init-form svar).
            (let ((arg (ensure-list keyarg)))
              (cons (if (listp (car arg))
                        (car arg)
            (let ((arg (ensure-list keyarg)))
              (cons (if (listp (car arg))
                        (car arg)
           (ll-optional-arguments-canonical lambda-list))))
     (remove nil (mapcar #'third args))))
 
           (ll-optional-arguments-canonical lambda-list))))
     (remove nil (mapcar #'third args))))
 
-(defun lambda-name/docstring-wrapper (name docstring &rest strs)
+(defun lambda-name/docstring-wrapper (name docstring code)
   (if (or name docstring)
   (if (or name docstring)
-      (js!selfcall
-        "var func = " (join strs) ";" *newline*
-        (when name
-          (code "func.fname = '" (escape-string name) "';" *newline*))
-        (when docstring
-          (code "func.docstring = '" (escape-string docstring) "';" *newline*))
-        "return func;" *newline*)
-      (apply #'code strs)))
+      `(selfcall
+        (var (func ,code))
+        ,(when name `(= (get func "fname") ,name))
+        ,(when docstring `(= (get func "docstring") ,docstring))
+        (return func))
+      code))
 
 (defun lambda-check-argument-count
     (n-required-arguments n-optional-arguments rest-p)
 
 (defun lambda-check-argument-count
     (n-required-arguments n-optional-arguments rest-p)
     (block nil
       ;; Special case: a positive exact number of arguments.
       (when (and (< 0 min) (eql min max))
     (block nil
       ;; Special case: a positive exact number of arguments.
       (when (and (< 0 min) (eql min max))
-        (return (code "checkArgs(nargs, " min ");" *newline*)))
+        (return `(call |checkArgs| |nargs| ,min)))
       ;; General case:
       ;; General case:
-      (code
-       (when (< 0 min)
-         (code "checkArgsAtLeast(nargs, " min ");" *newline*))
-       (when (numberp max)
-         (code "checkArgsAtMost(nargs, " max ");" *newline*))))))
+      `(progn
+         ,(when (< 0 min)     `(call |checkArgsAtLeast| |nargs| ,min))
+         ,(when (numberp max) `(call |checkArgsAtMost|  |nargs| ,max))))))
 
 (defun compile-lambda-optional (ll)
   (let* ((optional-arguments (ll-optional-arguments-canonical ll))
         (n-required-arguments (length (ll-required-arguments ll)))
         (n-optional-arguments (length optional-arguments)))
     (when optional-arguments
 
 (defun compile-lambda-optional (ll)
   (let* ((optional-arguments (ll-optional-arguments-canonical ll))
         (n-required-arguments (length (ll-required-arguments ll)))
         (n-optional-arguments (length optional-arguments)))
     (when optional-arguments
-      (code "switch(nargs){" *newline*
-            (let ((cases nil)
-                  (idx 0))
-              (progn
-                (while (< idx n-optional-arguments)
-                  (let ((arg (nth idx optional-arguments)))
-                    (push (code "case " (+ idx n-required-arguments) ":" *newline*
-                                (indent (translate-variable (car arg))
-                                        "="
-                                        (ls-compile (cadr arg)) ";" *newline*)
-                                (when (third arg)
-                                  (indent (translate-variable (third arg))
-                                          "="
-                                          (ls-compile nil)
-                                          ";" *newline*)))
-                          cases)
-                    (incf idx)))
-                (push (code "default: break;" *newline*) cases)
-                (join (reverse cases))))
-            "}" *newline*))))
+      `(switch |nargs|
+               ,@(with-collect
+                  (dotimes (idx n-optional-arguments)
+                    (let ((arg (nth idx optional-arguments)))
+                      (collect `(case ,(+ idx n-required-arguments)))
+                      (collect `(= ,(make-symbol (translate-variable (car arg)))
+                                   ,(convert (cadr arg))))
+                      (collect (when (third arg)
+                                 `(= ,(make-symbol (translate-variable (third arg)))
+                                     ,(convert nil))))))
+                  (collect 'default)
+                  (collect '(break)))))))
 
 (defun compile-lambda-rest (ll)
   (let ((n-required-arguments (length (ll-required-arguments ll)))
        (n-optional-arguments (length (ll-optional-arguments ll)))
        (rest-argument (ll-rest-argument ll)))
     (when rest-argument
 
 (defun compile-lambda-rest (ll)
   (let ((n-required-arguments (length (ll-required-arguments ll)))
        (n-optional-arguments (length (ll-optional-arguments ll)))
        (rest-argument (ll-rest-argument ll)))
     (when rest-argument
-      (let ((js!rest (translate-variable rest-argument)))
-        (code "var " js!rest "= " (ls-compile nil) ";" *newline*
-              "for (var i = nargs-1; i>=" (+ n-required-arguments n-optional-arguments)
-              "; i--)" *newline*
-              (indent js!rest " = {car: arguments[i+2], cdr: " js!rest "};" *newline*))))))
+      (let ((js!rest (make-symbol (translate-variable rest-argument))))
+        `(progn
+           (var (,js!rest ,(convert nil)))
+           (var i)
+           (for ((= i (- |nargs| 1))
+                 (>= i ,(+ n-required-arguments n-optional-arguments))
+                 (post-- i))
+                (= ,js!rest (object "car" (property |arguments| (+ i 2))
+                                    "cdr" ,js!rest))))))))
 
 (defun compile-lambda-parse-keywords (ll)
   (let ((n-required-arguments
 
 (defun compile-lambda-parse-keywords (ll)
   (let ((n-required-arguments
         (length (ll-optional-arguments ll)))
        (keyword-arguments
         (ll-keyword-arguments-canonical ll)))
         (length (ll-optional-arguments ll)))
        (keyword-arguments
         (ll-keyword-arguments-canonical ll)))
-    (code
-     ;; Declare variables
-     (mapconcat (lambda (arg)
-                 (let ((var (second (car arg))))
-                   (code "var " (translate-variable var) "; " *newline*
-                          (when (third arg)
-                            (code "var " (translate-variable (third arg))
-                                  " = " (ls-compile nil)
-                                  ";" *newline*)))))
-               keyword-arguments)
-     ;; Parse keywords
-     (flet ((parse-keyword (keyarg)
-             ;; ((keyword-name var) init-form)
-             (code "for (i=" (+ n-required-arguments n-optional-arguments)
-                    "; i<nargs; i+=2){" *newline*
-                    (indent
-                     "if (arguments[i+2] === " (ls-compile (caar keyarg)) "){" *newline*
-                     (indent (translate-variable (cadr (car keyarg)))
-                             " = arguments[i+3];"
-                             *newline*
-                             (let ((svar (third keyarg)))
-                               (when svar
-                                 (code (translate-variable svar) " = " (ls-compile t) ";" *newline*)))
-                             "break;" *newline*)
-                     "}" *newline*)
-                    "}" *newline*
-                    ;; Default value
-                    "if (i == nargs){" *newline*
-                    (indent (translate-variable (cadr (car keyarg))) " = " (ls-compile (cadr keyarg)) ";" *newline*)
-                    "}" *newline*)))
-       (when keyword-arguments
-         (code "var i;" *newline*
-               (mapconcat #'parse-keyword keyword-arguments))))
-     ;; Check for unknown keywords
-     (when keyword-arguments
-       (code "for (i=" (+ n-required-arguments n-optional-arguments)
-             "; i<nargs; i+=2){" *newline*
-             (indent "if ("
-                     (join (mapcar (lambda (x)
-                                     (concat "arguments[i+2] !== " (ls-compile (caar x))))
-                                   keyword-arguments)
-                           " && ")
-                     ")" *newline*
-                     (indent
-                      "throw 'Unknown keyword argument ' + xstring(arguments[i].name);" *newline*))
-             "}" *newline*)))))
+    `(progn
+       ;; Declare variables
+       ,@(with-collect
+          (dolist (keyword-argument keyword-arguments)
+            (destructuring-bind ((keyword-name var) &optional initform svar)
+                keyword-argument
+              (declare (ignore keyword-name initform))
+              (collect `(var ,(make-symbol (translate-variable var))))
+              (when svar
+                (collect
+                    `(var (,(make-symbol (translate-variable svar))
+                            ,(convert nil))))))))
+       
+       ;; Parse keywords
+       ,(flet ((parse-keyword (keyarg)
+                (destructuring-bind ((keyword-name var) &optional initform svar) keyarg
+                  ;; ((keyword-name var) init-form svar)
+                  `(progn
+                     (for ((= i ,(+ n-required-arguments n-optional-arguments))
+                           (< i |nargs|)
+                           (+= i 2))
+                          ;; ....
+                          (if (=== (property |arguments| (+ i 2))
+                                   ,(convert keyword-name))
+                              (progn
+                                (= ,(make-symbol (translate-variable var))
+                                   (property |arguments| (+ i 3)))
+                                ,(when svar `(= ,(make-symbol (translate-variable svar))
+                                                ,(convert t)))
+                                (break))))
+                     (if (== i |nargs|)
+                         (= ,(make-symbol (translate-variable var))
+                            ,(convert initform)))))))
+         (when keyword-arguments
+           `(progn
+              (var i)
+              ,@(mapcar #'parse-keyword keyword-arguments))))
+       
+       ;; Check for unknown keywords
+       ,(when keyword-arguments
+         `(progn
+            (var (start ,(+ n-required-arguments n-optional-arguments)))
+            (if (== (% (- |nargs| start) 2) 1)
+                (throw "Odd number of keyword arguments."))
+            (for ((= i start) (< i |nargs|) (+= i 2))
+                 (if (and ,@(mapcar (lambda (keyword-argument)
+                                 (destructuring-bind ((keyword-name var) &optional initform svar)
+                                     keyword-argument
+                                   (declare (ignore var initform svar))
+                                   `(!== (property |arguments| (+ i 2)) ,(convert keyword-name))))
+                               keyword-arguments))
+                     (throw (+ "Unknown keyword argument "
+                               (call |xstring|
+                                     (property
+                                      (property |arguments| (+ i 2))
+                                      "name")))))))))))
 
 (defun parse-lambda-list (ll)
   (values (ll-required-arguments ll)
 
 (defun parse-lambda-list (ll)
   (values (ll-required-arguments ll)
                                     keyword-arguments
                                     (ll-svars ll)))))
         (lambda-name/docstring-wrapper name documentation
                                     keyword-arguments
                                     (ll-svars ll)))))
         (lambda-name/docstring-wrapper name documentation
-         "(function ("
-         (join (list* "values"
-                      "nargs"
-                      (mapcar #'translate-variable
-                              (append required-arguments optional-arguments)))
-               ",")
-         "){" *newline*
-         (indent
-          ;; Check number of arguments
-          (lambda-check-argument-count n-required-arguments
-                                       n-optional-arguments
-                                       (or rest-argument keyword-arguments))
-                                        (compile-lambda-optional ll)
-                                        (compile-lambda-rest ll)
-                                        (compile-lambda-parse-keywords ll)
-                                        (let ((*multiple-value-p* t))
-                                          (if block
-                                              (ls-compile-block `((block ,block ,@body)) t)
-                                              (ls-compile-block body t))))
-         "})")))))
+         `(function (|values| |nargs| ,@(mapcar (lambda (x)
+                                                  (make-symbol (translate-variable x)))
+                                                (append required-arguments optional-arguments)))
+                     ;; Check number of arguments
+                    ,(lambda-check-argument-count n-required-arguments
+                                                  n-optional-arguments
+                                                  (or rest-argument keyword-arguments))
+                    ,(compile-lambda-optional ll)
+                    ,(compile-lambda-rest ll)
+                    ,(compile-lambda-parse-keywords ll)
+
+                    ,(let ((*multiple-value-p* t))
+                          (if block
+                              (convert-block `((block ,block ,@body)) t)
+                              (convert-block body t)))))))))
 
 
 (defun setq-pair (var val)
 
 
 (defun setq-pair (var val)
             (eq (binding-type b) 'variable)
             (not (member 'special (binding-declarations b)))
             (not (member 'constant (binding-declarations b))))
             (eq (binding-type b) 'variable)
             (not (member 'special (binding-declarations b)))
             (not (member 'constant (binding-declarations b))))
-       (code (binding-value b) " = " (ls-compile val)))
+       ;; TODO: Unnecesary make-symbol when codegen migration is
+       ;; finished.
+       `(= ,(make-symbol (binding-value b)) ,(convert val)))
       ((and b (eq (binding-type b) 'macro))
       ((and b (eq (binding-type b) 'macro))
-       (ls-compile `(setf ,var ,val)))
+       (convert `(setf ,var ,val)))
       (t
       (t
-       (ls-compile `(set ',var ,val))))))
+       (convert `(set ',var ,val))))))
 
 
 (define-compilation setq (&rest pairs)
 
 
 (define-compilation setq (&rest pairs)
-  (let ((result ""))
+  (let ((result nil))
+    (when (null pairs)
+      (return-from setq (convert nil)))
     (while t
       (cond
     (while t
       (cond
-       ((null pairs) (return))
+       ((null pairs)
+         (return))
        ((null (cdr pairs))
         (error "Odd pairs in SETQ"))
        (t
        ((null (cdr pairs))
         (error "Odd pairs in SETQ"))
        (t
-        (concatf result
-          (concat (setq-pair (car pairs) (cadr pairs))
-                  (if (null (cddr pairs)) "" ", ")))
+         (push `,(setq-pair (car pairs) (cadr pairs)) result)
         (setq pairs (cddr pairs)))))
         (setq pairs (cddr pairs)))))
-    (code "(" result ")")))
-
-
-;;; Literals
-(defun escape-string (string)
-  (let ((output "")
-        (index 0)
-        (size (length string)))
-    (while (< index size)
-      (let ((ch (char string index)))
-        (when (or (char= ch #\") (char= ch #\\))
-          (setq output (concat output "\\")))
-        (when (or (char= ch #\newline))
-          (setq output (concat output "\\"))
-          (setq ch #\n))
-        (setq output (concat output (string ch))))
-      (incf index))
-    output))
+    `(progn ,@(reverse result))))
+
 
 
+;;; Compilation of literals an object dumping
+
+;;; BOOTSTRAP MAGIC: We record the macro definitions as lists during
+;;; the bootstrap. Once everything is compiled, we want to dump the
+;;; whole global environment to the output file to reproduce it in the
+;;; run-time. However, the environment must contain expander functions
+;;; rather than lists. We do not know how to dump function objects
+;;; itself, so we mark the list definitions with this object and the
+;;; compiler will be called when this object has to be dumped.
+;;; Backquote/unquote does a similar magic, but this use is exclusive.
+;;;
+;;; Indeed, perhaps to compile the object other macros need to be
+;;; evaluated. For this reason we define a valid macro-function for
+;;; this symbol.
+(defvar *magic-unquote-marker* (gensym "MAGIC-UNQUOTE"))
+
+#-jscl
+(setf (macro-function *magic-unquote-marker*)
+      (lambda (form &optional environment)
+        (declare (ignore environment))
+        (second form)))
 
 (defvar *literal-table* nil)
 (defvar *literal-counter* 0)
 
 (defun genlit ()
 
 (defvar *literal-table* nil)
 (defvar *literal-counter* 0)
 
 (defun genlit ()
-  (code "l" (incf *literal-counter*)))
+  (incf *literal-counter*)
+  (concat "l" (integer-to-string *literal-counter*)))
 
 (defun dump-symbol (symbol)
 
 (defun dump-symbol (symbol)
-  #+common-lisp
+  #-jscl
   (let ((package (symbol-package symbol)))
     (if (eq package (find-package "KEYWORD"))
   (let ((package (symbol-package symbol)))
     (if (eq package (find-package "KEYWORD"))
-        (code "{name: " (dump-string (symbol-name symbol))
-              ", 'package': " (dump-string (package-name package)) "}")
-        (code "{name: " (dump-string (symbol-name symbol)) "}")))
+        `(new (call |Symbol| ,(dump-string (symbol-name symbol)) ,(dump-string (package-name package))))
+        `(new (call |Symbol| ,(dump-string (symbol-name symbol))))))
   #+jscl
   (let ((package (symbol-package symbol)))
     (if (null package)
   #+jscl
   (let ((package (symbol-package symbol)))
     (if (null package)
-        (code "{name: " (dump-symbol (symbol-name symbol)) "}")
-        (ls-compile `(intern ,(symbol-name symbol) ,(package-name package))))))
+        `(new (call |Symbol| ,(dump-string (symbol-name symbol))))
+        (convert `(intern ,(symbol-name symbol) ,(package-name package))))))
 
 (defun dump-cons (cons)
   (let ((head (butlast cons))
         (tail (last cons)))
 
 (defun dump-cons (cons)
   (let ((head (butlast cons))
         (tail (last cons)))
-    (code "QIList("
-          (join-trailing (mapcar (lambda (x) (literal x t)) head) ",")
-          (literal (car tail) t)
-          ","
-          (literal (cdr tail) t)
-          ")")))
+    `(call |QIList|
+           ,@(mapcar (lambda (x) (literal x t)) head)
+           ,(literal (car tail) t)
+           ,(literal (cdr tail) t))))
 
 (defun dump-array (array)
   (let ((elements (vector-to-list array)))
 
 (defun dump-array (array)
   (let ((elements (vector-to-list array)))
-    (concat "[" (join (mapcar #'literal elements) ", ") "]")))
+    (list-to-vector (mapcar #'literal elements))))
 
 (defun dump-string (string)
 
 (defun dump-string (string)
-  (code "make_lisp_string(\"" (escape-string string) "\")"))
+  `(call |make_lisp_string| ,string))
 
 (defun literal (sexp &optional recursive)
   (cond
 
 (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)) "\""))
+    ((integerp sexp) sexp)
+    ((floatp sexp) sexp)
+    ((characterp sexp)
+     ;; TODO: Remove selfcall after migration
+     `(selfcall (return ,(string sexp))))
     (t
     (t
-     (or (cdr (assoc sexp *literal-table* :test #'equal))
+     (or (cdr (assoc sexp *literal-table* :test #'eql))
          (let ((dumped (typecase sexp
                          (symbol (dump-symbol sexp))
          (let ((dumped (typecase sexp
                          (symbol (dump-symbol sexp))
-                         (cons (dump-cons sexp))
                          (string (dump-string sexp))
                          (string (dump-string sexp))
+                         (cons
+                          ;; BOOTSTRAP MAGIC: See the root file
+                          ;; jscl.lisp and the function
+                          ;; `dump-global-environment' for futher
+                          ;; information.
+                          (if (eq (car sexp) *magic-unquote-marker*)
+                              (convert (second sexp))
+                              (dump-cons sexp)))
                          (array (dump-array sexp)))))
            (if (and recursive (not (symbolp sexp)))
                dumped
                (let ((jsvar (genlit)))
                          (array (dump-array sexp)))))
            (if (and recursive (not (symbolp sexp)))
                dumped
                (let ((jsvar (genlit)))
-                 (push (cons sexp jsvar) *literal-table*)
-                 (toplevel-compilation (code "var " jsvar " = " dumped))
-                 jsvar)))))))
+                 (push (cons sexp (make-symbol jsvar)) *literal-table*)
+                 (toplevel-compilation `(var (,(make-symbol jsvar) ,dumped)))
+                 (when (keywordp sexp)
+                   (toplevel-compilation `(= ,(get (make-symbol jsvar) "value") ,(make-symbol jsvar))))
+                 (make-symbol jsvar))))))))
+
 
 (define-compilation quote (sexp)
   (literal sexp))
 
 (define-compilation %while (pred &rest body)
 
 (define-compilation quote (sexp)
   (literal sexp))
 
 (define-compilation %while (pred &rest body)
-  (js!selfcall
-    "while(" (ls-compile pred) " !== " (ls-compile nil) "){" *newline*
-    (indent (ls-compile-block body))
-    "}"
-    "return " (ls-compile nil) ";" *newline*))
+  `(selfcall
+    (while (!== ,(convert pred) ,(convert nil))
+      0                                 ; TODO: Force
+                                        ; braces. Unnecesary when code
+                                        ; is gone
+      ,(convert-block body))
+    (return ,(convert nil))))
 
 (define-compilation function (x)
   (cond
     ((and (listp x) (eq (car x) 'lambda))
      (compile-lambda (cadr x) (cddr x)))
     ((and (listp x) (eq (car x) 'named-lambda))
 
 (define-compilation function (x)
   (cond
     ((and (listp x) (eq (car x) 'lambda))
      (compile-lambda (cadr x) (cddr x)))
     ((and (listp x) (eq (car x) 'named-lambda))
-     ;; TODO: destructuring-bind now! Do error checking manually is
-     ;; very annoying.
-     (let ((name (cadr x))
-           (ll (caddr x))
-           (body (cdddr x)))
+     (destructuring-bind (name ll &rest body) (cdr x)
        (compile-lambda ll body
                        :name (symbol-name name)
                        :block name)))
     ((symbolp x)
      (let ((b (lookup-in-lexenv x *environment* 'function)))
        (if b
        (compile-lambda ll body
                        :name (symbol-name name)
                        :block name)))
     ((symbolp x)
      (let ((b (lookup-in-lexenv x *environment* 'function)))
        (if b
-          (binding-value b)
-          (ls-compile `(symbol-function ',x)))))))
-
+          (make-symbol (binding-value b))
+          (convert `(symbol-function ',x)))))))
 
 (defun make-function-binding (fname)
   (make-binding :name fname :type 'function :value (gvarname fname)))
 
 (defun make-function-binding (fname)
   (make-binding :name fname :type 'function :value (gvarname fname)))
           (extend-lexenv (mapcar #'make-function-binding fnames)
                          *environment*
                          'function)))
           (extend-lexenv (mapcar #'make-function-binding fnames)
                          *environment*
                          'function)))
-    (code "(function("
-          (join (mapcar #'translate-function fnames) ",")
-          "){" *newline*
-          (let ((body (ls-compile-block body t)))
-            (indent body))
-          "})(" (join cfuncs ",") ")")))
+    `(call (function ,(mapcar #'make-symbol (mapcar #'translate-function fnames))
+                ,(convert-block body t))
+           ,@cfuncs)))
 
 (define-compilation labels (definitions &rest body)
   (let* ((fnames (mapcar #'car definitions))
 
 (define-compilation labels (definitions &rest body)
   (let* ((fnames (mapcar #'car definitions))
           (extend-lexenv (mapcar #'make-function-binding fnames)
                          *environment*
                          'function)))
           (extend-lexenv (mapcar #'make-function-binding fnames)
                          *environment*
                          'function)))
-    (js!selfcall
-      (mapconcat (lambda (func)
-                  (code "var " (translate-function (car func))
-                         " = " (compile-lambda (cadr func)
-                                               `((block ,(car func) ,@(cddr func))))
-                         ";" *newline*))
-                definitions)
-      (ls-compile-block body t))))
+    `(selfcall
+      ,@(mapcar (lambda (func)
+                  `(var (,(make-symbol (translate-function (car func)))
+                          ,(compile-lambda (cadr func)
+                                           `((block ,(car func) ,@(cddr func)))))))
+                definitions)
+      ,(convert-block body t))))
 
 
 (defvar *compiling-file* nil)
 
 
 (defvar *compiling-file* nil)
   (if *compiling-file*
       (progn
         (eval (cons 'progn body))
   (if *compiling-file*
       (progn
         (eval (cons 'progn body))
-        nil)
-      (ls-compile `(progn ,@body))))
+        (convert 0))
+      (convert `(progn ,@body))))
 
 (defmacro define-transformation (name args form)
   `(define-compilation ,name ,args
 
 (defmacro define-transformation (name args form)
   `(define-compilation ,name ,args
-     (ls-compile ,form)))
+     (convert ,form)))
 
 (define-compilation progn (&rest body)
   (if (null (cdr body))
 
 (define-compilation progn (&rest body)
   (if (null (cdr body))
-      (ls-compile (car body) *multiple-value-p*)
-      (js!selfcall (ls-compile-block body t))))
+      (convert (car body) *multiple-value-p*)
+      `(progn
+         ,@(append (mapcar #'convert (butlast body))
+                   (list (convert (car (last body)) t))))))
+
+(define-compilation macrolet (definitions &rest body)
+  (let ((*environment* (copy-lexenv *environment*)))
+    (dolist (def definitions)
+      (destructuring-bind (name lambda-list &body body) def
+        (let ((binding (make-binding :name name :type 'macro :value
+                                     (let ((g!form (gensym)))
+                                       `(lambda (,g!form)
+                                          (destructuring-bind ,lambda-list ,g!form
+                                            ,@body))))))
+          (push-to-lexenv binding  *environment* 'function))))
+    (convert `(progn ,@body) *multiple-value-p*)))
+
 
 (defun special-variable-p (x)
   (and (claimp x 'variable 'special) t))
 
 (defun special-variable-p (x)
   (and (claimp x 'variable 'special) t))
 (defun let-binding-wrapper (bindings body)
   (when (null bindings)
     (return-from let-binding-wrapper body))
 (defun let-binding-wrapper (bindings body)
   (when (null bindings)
     (return-from let-binding-wrapper body))
-  (code
-   "try {" *newline*
-   (indent "var tmp;" *newline*
-           (mapconcat
-            (lambda (b)
-              (let ((s (ls-compile `(quote ,(car b)))))
-                (code "tmp = " s ".value;" *newline*
-                      s ".value = " (cdr b) ";" *newline*
-                      (cdr b) " = tmp;" *newline*)))
-            bindings)
-           body *newline*)
-   "}" *newline*
-   "finally {"  *newline*
-   (indent
-    (mapconcat (lambda (b)
-                 (let ((s (ls-compile `(quote ,(car b)))))
-                   (code s ".value" " = " (cdr b) ";" *newline*)))
-               bindings))
-   "}" *newline*))
+  `(progn
+     (try (var tmp)
+          ,@(with-collect
+             (dolist (b bindings)
+               (let ((s (convert `',(car b))))
+                 (collect `(= tmp (get ,s "value")))
+                 (collect `(= (get ,s "value") ,(cdr b)))
+                 (collect `(= ,(cdr b) tmp)))))
+          ,body)
+     (finally
+      ,@(with-collect
+         (dolist (b bindings)
+           (let ((s (convert `(quote ,(car b)))))
+             (collect `(= (get ,s "value") ,(cdr b)))))))))
 
 (define-compilation let (bindings &rest body)
   (let* ((bindings (mapcar #'ensure-list bindings))
          (variables (mapcar #'first bindings))
 
 (define-compilation let (bindings &rest body)
   (let* ((bindings (mapcar #'ensure-list bindings))
          (variables (mapcar #'first bindings))
-         (cvalues (mapcar #'ls-compile (mapcar #'second bindings)))
+         (cvalues (mapcar #'convert (mapcar #'second bindings)))
          (*environment* (extend-local-env (remove-if #'special-variable-p variables)))
          (dynamic-bindings))
          (*environment* (extend-local-env (remove-if #'special-variable-p variables)))
          (dynamic-bindings))
-    (code "(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 ",") ")")))
+    `(call (function ,(mapcar (lambda (x)
+                                (if (special-variable-p x)
+                                    (let ((v (gvarname x)))
+                                      (push (cons x (make-symbol v)) dynamic-bindings)
+                                      (make-symbol v))
+                                    (make-symbol (translate-variable x))))
+                              variables)
+                     ,(let ((body (convert-block body t t)))
+                           `,(let-binding-wrapper dynamic-bindings body)))
+           ,@cvalues)))
 
 
 ;;; Return the code to initialize BINDING, and push it extending the
 
 
 ;;; Return the code to initialize BINDING, and push it extending the
   (let ((var (first binding))
         (value (second binding)))
     (if (special-variable-p var)
   (let ((var (first binding))
         (value (second binding)))
     (if (special-variable-p var)
-        (code (ls-compile `(setq ,var ,value)) ";" *newline*)
+        (convert `(setq ,var ,value))
         (let* ((v (gvarname var))
                (b (make-binding :name var :type 'variable :value v)))
         (let* ((v (gvarname var))
                (b (make-binding :name var :type 'variable :value v)))
-          (prog1 (code "var " v " = " (ls-compile value) ";" *newline*)
+          (prog1 `(var (,(make-symbol v) ,(convert value)))
             (push-to-lexenv b *environment* 'variable))))))
 
 ;;; Wrap BODY to restore the symbol values of SYMBOLS after body. It
             (push-to-lexenv b *environment* 'variable))))))
 
 ;;; Wrap BODY to restore the symbol values of SYMBOLS after body. It
     (return-from let*-binding-wrapper body))
   (let ((store (mapcar (lambda (s) (cons s (gvarname s)))
                        (remove-if-not #'special-variable-p symbols))))
     (return-from let*-binding-wrapper body))
   (let ((store (mapcar (lambda (s) (cons s (gvarname s)))
                        (remove-if-not #'special-variable-p symbols))))
-    (code
-     "try {" *newline*
-     (indent
-      (mapconcat (lambda (b)
-                   (let ((s (ls-compile `(quote ,(car b)))))
-                     (code "var " (cdr b) " = " s ".value;" *newline*)))
-                 store)
-      body)
-     "}" *newline*
-     "finally {" *newline*
-     (indent
-      (mapconcat (lambda (b)
-                   (let ((s (ls-compile `(quote ,(car b)))))
-                     (code s ".value" " = " (cdr b) ";" *newline*)))
-                 store))
-     "}" *newline*)))
+    `(progn
+       (try
+        ,@(mapcar (lambda (b)
+                    (let ((s (convert `(quote ,(car b)))))
+                      `(var (,(make-symbol (cdr b)) (get ,s "value")))))
+                  store)
+        ,body)
+       (finally
+        ,@(mapcar (lambda (b)
+                    (let ((s (convert `(quote ,(car b)))))
+                      `(= (get ,s "value") ,(make-symbol (cdr b)))))
+                  store)))))
 
 (define-compilation let* (bindings &rest body)
   (let ((bindings (mapcar #'ensure-list bindings))
         (*environment* (copy-lexenv *environment*)))
 
 (define-compilation let* (bindings &rest body)
   (let ((bindings (mapcar #'ensure-list bindings))
         (*environment* (copy-lexenv *environment*)))
-    (js!selfcall
-      (let ((specials (remove-if-not #'special-variable-p (mapcar #'first bindings)))
-            (body (concat (mapconcat #'let*-initialize-value bindings)
-                          (ls-compile-block body t))))
-        (let*-binding-wrapper specials body)))))
+    (let ((specials (remove-if-not #'special-variable-p (mapcar #'first bindings)))
+          (body `(progn
+                   ,@(mapcar #'let*-initialize-value bindings)
+                   ,(convert-block body t t))))
+      `(selfcall ,(let*-binding-wrapper specials body)))))
 
 
 (define-compilation block (name &rest body)
 
 
 (define-compilation block (name &rest body)
     (when *multiple-value-p*
       (push 'multiple-value (binding-declarations b)))
     (let* ((*environment* (extend-lexenv (list b) *environment* 'block))
     (when *multiple-value-p*
       (push 'multiple-value (binding-declarations b)))
     (let* ((*environment* (extend-lexenv (list b) *environment* 'block))
-           (cbody (ls-compile-block body t)))
+           (cbody (convert-block body t)))
       (if (member 'used (binding-declarations b))
       (if (member 'used (binding-declarations b))
-          (js!selfcall
-            "try {" *newline*
-            "var " idvar " = [];" *newline*
-            (indent cbody)
-            "}" *newline*
-            "catch (cf){" *newline*
-            "    if (cf.type == 'block' && cf.id == " idvar ")" *newline*
-            (if *multiple-value-p*
-                "        return values.apply(this, forcemv(cf.values));"
-                "        return cf.values;")
-            *newline*
-            "    else" *newline*
-            "        throw cf;" *newline*
-            "}" *newline*)
-          (js!selfcall cbody)))))
+          `(selfcall
+            (try
+             (var (,(make-symbol idvar) #()))
+             ,cbody)
+            (catch (cf)
+              (if (and (== (get cf "type") "block")
+                       (== (get cf "id") ,(make-symbol idvar)))
+                  ,(if *multiple-value-p*
+                       `(return (call (get |values| "apply") this (call |forcemv| (get cf "values"))))
+                       `(return (get cf "values")))
+                  (throw cf))))
+          ;; TODO: is selfcall necessary here?
+          `(selfcall ,cbody)))))
 
 (define-compilation return-from (name &optional value)
   (let* ((b (lookup-in-lexenv name *environment* 'block))
 
 (define-compilation return-from (name &optional value)
   (let* ((b (lookup-in-lexenv name *environment* 'block))
     ;; unique identifier of the block as exception. We can't use the
     ;; variable name itself, because it could not to be unique, so we
     ;; capture it in a closure.
     ;; unique identifier of the block as exception. We can't use the
     ;; variable name itself, because it could not to be unique, so we
     ;; capture it in a closure.
-    (js!selfcall
-      (when multiple-value-p (code "var values = mv;" *newline*))
-      "throw ({"
-      "type: 'block', "
-      "id: " (binding-value b) ", "
-      "values: " (ls-compile value multiple-value-p) ", "
-      "message: 'Return from unknown block " (symbol-name name) ".'"
-      "})")))
+    `(selfcall
+      ,(when multiple-value-p `(var (|values| |mv|)))
+      (throw
+          (object
+           "type" "block"
+           "id" ,(make-symbol (binding-value b))
+           "values" ,(convert value multiple-value-p)
+           "message" ,(concat "Return from unknown block '" (symbol-name name) "'."))))))
 
 (define-compilation catch (id &rest body)
 
 (define-compilation catch (id &rest body)
-  (js!selfcall
-    "var id = " (ls-compile id) ";" *newline*
-    "try {" *newline*
-    (indent (ls-compile-block body t)) *newline*
-    "}" *newline*
-    "catch (cf){" *newline*
-    "    if (cf.type == 'catch' && cf.id == id)" *newline*
-    (if *multiple-value-p*
-        "        return values.apply(this, forcemv(cf.values));"
-        "        return pv.apply(this, forcemv(cf.values));")
-    *newline*
-    "    else" *newline*
-    "        throw cf;" *newline*
-    "}" *newline*))
+  `(selfcall
+    (var (|id| ,(convert id)))
+    (try
+     ,(convert-block body t))
+    (catch (|cf|)
+      (if (and (== (get |cf| "type") "catch")
+               (== (get |cf| "id") |id|))
+          ,(if *multiple-value-p*
+               `(return (call (get |values| "apply")
+                              this
+                              (call |forcemv| (get |cf| "values"))))
+               `(return (call (get |pv| "apply")
+                              this
+                              (call |forcemv| (get |cf| "values")))))
+          (throw |cf|)))))
 
 (define-compilation throw (id value)
 
 (define-compilation throw (id value)
-  (js!selfcall
-    "var values = mv;" *newline*
-    "throw ({"
-    "type: 'catch', "
-    "id: " (ls-compile id) ", "
-    "values: " (ls-compile value t) ", "
-    "message: 'Throw uncatched.'"
-    "})"))
+  `(selfcall
+    (var (|values| |mv|))
+    (throw (object
+            |type| "catch"
+            |id| ,(convert id)
+            |values| ,(convert value t)
+            |message| "Throw uncatched."))))
 
 (defun go-tag-p (x)
   (or (integerp x) (symbolp x)))
 
 (defun go-tag-p (x)
   (or (integerp x) (symbolp x)))
   (let* ((go-tag-counter 0)
          (bindings
           (mapcar (lambda (label)
   (let* ((go-tag-counter 0)
          (bindings
           (mapcar (lambda (label)
-                    (let ((tagidx (integer-to-string (incf go-tag-counter))))
+                    (let ((tagidx (incf go-tag-counter)))
                       (make-binding :name label :type 'gotag :value (list tbidx tagidx))))
                   (remove-if-not #'go-tag-p body))))
     (extend-lexenv bindings *environment* 'gotag)))
                       (make-binding :name label :type 'gotag :value (list tbidx tagidx))))
                   (remove-if-not #'go-tag-p body))))
     (extend-lexenv bindings *environment* 'gotag)))
   ;; because 1) it is easy and 2) many built-in forms expand to a
   ;; implicit tagbody, so we save some space.
   (unless (some #'go-tag-p body)
   ;; because 1) it is easy and 2) many built-in forms expand to a
   ;; implicit tagbody, so we save some space.
   (unless (some #'go-tag-p body)
-    (return-from tagbody (ls-compile `(progn ,@body nil))))
+    (return-from tagbody (convert `(progn ,@body nil))))
   ;; The translation assumes the first form in BODY is a label
   (unless (go-tag-p (car body))
     (push (gensym "START") body))
   ;; The translation assumes the first form in BODY is a label
   (unless (go-tag-p (car body))
     (push (gensym "START") body))
           initag)
       (let ((b (lookup-in-lexenv (first body) *environment* 'gotag)))
         (setq initag (second (binding-value b))))
           initag)
       (let ((b (lookup-in-lexenv (first body) *environment* 'gotag)))
         (setq initag (second (binding-value b))))
-      (js!selfcall
+      `(selfcall
         ;; TAGBODY branch to take
         ;; TAGBODY branch to take
-        "var " branch " = " initag ";" *newline*
-        "var " tbidx " = [];" *newline*
-        "tbloop:" *newline*
-        "while (true) {" *newline*
-        (indent "try {" *newline*
-                (indent (let ((content ""))
-                          (code "switch(" branch "){" *newline*
-                                "case " initag ":" *newline*
-                                (dolist (form (cdr body) content)
-                                  (concatf content
-                                    (if (not (go-tag-p form))
-                                        (indent (ls-compile form) ";" *newline*)
-                                        (let ((b (lookup-in-lexenv form *environment* 'gotag)))
-                                          (code "case " (second (binding-value b)) ":" *newline*)))))
-                                "default:" *newline*
-                                "    break tbloop;" *newline*
-                                "}" *newline*)))
-                "}" *newline*
-                "catch (jump) {" *newline*
-                "    if (jump.type == 'tagbody' && jump.id == " tbidx ")" *newline*
-                "        " branch " = jump.label;" *newline*
-                "    else" *newline*
-                "        throw(jump);" *newline*
-                "}" *newline*)
-        "}" *newline*
-        "return " (ls-compile nil) ";" *newline*))))
+        (var (,(make-symbol branch) ,initag))
+        (var (,(make-symbol tbidx) #()))
+        (label tbloop
+               (while true
+                 (try
+                  (switch ,(make-symbol branch)
+                          ,@(with-collect
+                             (collect `(case ,initag))
+                             (dolist (form (cdr body))
+                               (if (go-tag-p form)
+                                   (let ((b (lookup-in-lexenv form *environment* 'gotag)))
+                                     (collect `(case ,(second (binding-value b)))))
+                                   (collect (convert form)))))
+                          default
+                          (break tbloop)))
+                 (catch (jump)
+                   (if (and (== (get jump "type") "tagbody")
+                            (== (get jump "id") ,(make-symbol tbidx)))
+                       (= ,(make-symbol branch) (get jump "label"))
+                       (throw jump)))))
+        (return ,(convert nil))))))
 
 (define-compilation go (label)
   (let ((b (lookup-in-lexenv label *environment* 'gotag))
 
 (define-compilation go (label)
   (let ((b (lookup-in-lexenv label *environment* 'gotag))
              ((integerp label) (integer-to-string label)))))
     (when (null b)
       (error "Unknown tag `~S'" label))
              ((integerp label) (integer-to-string label)))))
     (when (null b)
       (error "Unknown tag `~S'" label))
-    (js!selfcall
-      "throw ({"
-      "type: 'tagbody', "
-      "id: " (first (binding-value b)) ", "
-      "label: " (second (binding-value b)) ", "
-      "message: 'Attempt to GO to non-existing tag " n "'"
-      "})" *newline*)))
+    `(selfcall
+      (throw
+          (object
+           "type" "tagbody"
+           "id" ,(make-symbol (first (binding-value b)))
+           "label" ,(second (binding-value b))
+           "message" ,(concat "Attempt to GO to non-existing tag " n))))))
 
 (define-compilation unwind-protect (form &rest clean-up)
 
 (define-compilation unwind-protect (form &rest clean-up)
-  (js!selfcall
-    "var ret = " (ls-compile nil) ";" *newline*
-    "try {" *newline*
-    (indent "ret = " (ls-compile form) ";" *newline*)
-    "} finally {" *newline*
-    (indent (ls-compile-block clean-up))
-    "}" *newline*
-    "return ret;" *newline*))
+  `(selfcall
+    (var (|ret| ,(convert nil)))
+    (try
+     (= |ret| ,(convert form)))
+    (finally
+     ,(convert-block clean-up))
+    (return |ret|)))
 
 (define-compilation multiple-value-call (func-form &rest forms)
 
 (define-compilation multiple-value-call (func-form &rest forms)
-  (js!selfcall
-    "var func = " (ls-compile func-form) ";" *newline*
-    "var args = [" (if *multiple-value-p* "values" "pv") ", 0];" *newline*
-    "return "
-    (js!selfcall
-      "var values = mv;" *newline*
-      "var vs;" *newline*
-      (mapconcat (lambda (form)
-                   (code "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)
-      "args[1] = args.length-2;" *newline*
-      "return func.apply(window, args);" *newline*) ";" *newline*))
+  `(selfcall
+    (var (func ,(convert func-form)))
+    (var (args ,(vector (if *multiple-value-p* '|values| '|pv|) 0)))
+    (return
+      (selfcall
+       (var (|values| |mv|))
+       (var vs)
+       (progn
+         ,@(with-collect
+            (dolist (form forms)
+              (collect `(= vs ,(convert form t)))
+              (collect `(if (and (=== (typeof vs) "object")
+                                 (in "multiple-value" vs))
+                            (= args (call (get args "concat") vs))
+                            (call (get args "push") vs))))))
+       (= (property args 1) (- (property args "length") 2))
+       (return (call (get func "apply") |window| args))))))
 
 (define-compilation multiple-value-prog1 (first-form &rest forms)
 
 (define-compilation multiple-value-prog1 (first-form &rest forms)
-  (js!selfcall
-    "var args = " (ls-compile first-form *multiple-value-p*) ";" *newline*
-    (ls-compile-block forms)
-    "return args;" *newline*))
-
-
-;;; Javascript FFI
-
-(define-compilation %js-vref (var) var)
-
-(define-compilation %js-vset (var val)
-  (code "(" var " = " (ls-compile val) ")"))
-
-(define-setf-expander %js-vref (var)
-  (let ((new-value (gensym)))
-    (unless (stringp var)
-      (error "`~S' is not a string." var))
-    (values nil
-            (list var)
-            (list new-value)
-            `(%js-vset ,var ,new-value)
-            `(%js-vref ,var))))
-
-
-;;; Backquote implementation.
-;;;
-;;;    Author: Guy L. Steele Jr.     Date: 27 December 1985
-;;;    Tested under Symbolics Common Lisp and Lucid Common Lisp.
-;;;    This software is in the public domain.
-
-;;;    The following are unique tokens used during processing.
-;;;    They need not be symbols; they need not even be atoms.
-(defvar *comma* 'unquote)
-(defvar *comma-atsign* 'unquote-splicing)
-
-(defvar *bq-list* (make-symbol "BQ-LIST"))
-(defvar *bq-append* (make-symbol "BQ-APPEND"))
-(defvar *bq-list** (make-symbol "BQ-LIST*"))
-(defvar *bq-nconc* (make-symbol "BQ-NCONC"))
-(defvar *bq-clobberable* (make-symbol "BQ-CLOBBERABLE"))
-(defvar *bq-quote* (make-symbol "BQ-QUOTE"))
-(defvar *bq-quote-nil* (list *bq-quote* nil))
-
-;;; BACKQUOTE is an ordinary macro (not a read-macro) that processes
-;;; the expression foo, looking for occurrences of #:COMMA,
-;;; #:COMMA-ATSIGN, and #:COMMA-DOT.  It constructs code in strict
-;;; accordance with the rules on pages 349-350 of the first edition
-;;; (pages 528-529 of this second edition).  It then optionally
-;;; applies a code simplifier.
-
-;;; If the value of *BQ-SIMPLIFY* is non-NIL, then BACKQUOTE
-;;; processing applies the code simplifier.  If the value is NIL,
-;;; then the code resulting from BACKQUOTE is exactly that
-;;; specified by the official rules.
-(defparameter *bq-simplify* t)
-
-(defmacro backquote (x)
-  (bq-completely-process x))
-
-;;; Backquote processing proceeds in three stages:
-;;;
-;;; (1) BQ-PROCESS applies the rules to remove occurrences of
-;;; #:COMMA, #:COMMA-ATSIGN, and #:COMMA-DOT corresponding to
-;;; this level of BACKQUOTE.  (It also causes embedded calls to
-;;; BACKQUOTE to be expanded so that nesting is properly handled.)
-;;; Code is produced that is expressed in terms of functions
-;;; #:BQ-LIST, #:BQ-APPEND, and #:BQ-CLOBBERABLE.  This is done
-;;; so that the simplifier will simplify only list construction
-;;; functions actually generated by BACKQUOTE and will not involve
-;;; any user code in the simplification.  #:BQ-LIST means LIST,
-;;; #:BQ-APPEND means APPEND, and #:BQ-CLOBBERABLE means IDENTITY
-;;; but indicates places where "%." was used and where NCONC may
-;;; therefore be introduced by the simplifier for efficiency.
-;;;
-;;; (2) BQ-SIMPLIFY, if used, rewrites the code produced by
-;;; BQ-PROCESS to produce equivalent but faster code.  The
-;;; additional functions #:BQ-LIST* and #:BQ-NCONC may be
-;;; introduced into the code.
-;;;
-;;; (3) BQ-REMOVE-TOKENS goes through the code and replaces
-;;; #:BQ-LIST with LIST, #:BQ-APPEND with APPEND, and so on.
-;;; #:BQ-CLOBBERABLE is simply eliminated (a call to it being
-;;; replaced by its argument).  #:BQ-LIST* is replaced by either
-;;; LIST* or CONS (the latter is used in the two-argument case,
-;;; purely to make the resulting code a tad more readable).
-
-(defun bq-completely-process (x)
-  (let ((raw-result (bq-process x)))
-    (bq-remove-tokens (if *bq-simplify*
-                          (bq-simplify raw-result)
-                          raw-result))))
-
-(defun bq-process (x)
-  (cond ((atom x)
-         (list *bq-quote* x))
-        ((eq (car x) 'backquote)
-         (bq-process (bq-completely-process (cadr x))))
-        ((eq (car x) *comma*) (cadr x))
-        ((eq (car x) *comma-atsign*)
-         (error ",@~S after `" (cadr x)))
-        ;; ((eq (car x) *comma-dot*)
-        ;;  ;; (error ",.~S after `" (cadr x))
-        ;;  (error "ill-formed"))
-        (t (do ((p x (cdr p))
-                (q '() (cons (bracket (car p)) q)))
-               ((atom p)
-                (cons *bq-append*
-                      (nreconc q (list (list *bq-quote* p)))))
-             (when (eq (car p) *comma*)
-               (unless (null (cddr p))
-                 (error "Malformed ,~S" p))
-               (return (cons *bq-append*
-                             (nreconc q (list (cadr p))))))
-             (when (eq (car p) *comma-atsign*)
-               (error "Dotted ,@~S" p))
-             ;; (when (eq (car p) *comma-dot*)
-             ;;   ;; (error "Dotted ,.~S" p)
-             ;;   (error "Dotted"))
-             ))))
-
-;;; This implements the bracket operator of the formal rules.
-(defun bracket (x)
-  (cond ((atom x)
-         (list *bq-list* (bq-process x)))
-        ((eq (car x) *comma*)
-         (list *bq-list* (cadr x)))
-        ((eq (car x) *comma-atsign*)
-         (cadr x))
-        ;; ((eq (car x) *comma-dot*)
-        ;;  (list *bq-clobberable* (cadr x)))
-        (t (list *bq-list* (bq-process x)))))
-
-;;; This auxiliary function is like MAPCAR but has two extra
-;;; purposes: (1) it handles dotted lists; (2) it tries to make
-;;; the result share with the argument x as much as possible.
-(defun maptree (fn x)
-  (if (atom x)
-      (funcall fn x)
-      (let ((a (funcall fn (car x)))
-            (d (maptree fn (cdr x))))
-        (if (and (eql a (car x)) (eql d (cdr x)))
-            x
-            (cons a d)))))
-
-;;; This predicate is true of a form that when read looked
-;;; like %@foo or %.foo.
-(defun bq-splicing-frob (x)
-  (and (consp x)
-       (or (eq (car x) *comma-atsign*)
-           ;; (eq (car x) *comma-dot*)
-           )))
-
-;;; This predicate is true of a form that when read
-;;; looked like %@foo or %.foo or just plain %foo.
-(defun bq-frob (x)
-  (and (consp x)
-       (or (eq (car x) *comma*)
-           (eq (car x) *comma-atsign*)
-           ;; (eq (car x) *comma-dot*)
-           )))
-
-;;; The simplifier essentially looks for calls to #:BQ-APPEND and
-;;; tries to simplify them.  The arguments to #:BQ-APPEND are
-;;; processed from right to left, building up a replacement form.
-;;; At each step a number of special cases are handled that,
-;;; loosely speaking, look like this:
-;;;
-;;;  (APPEND (LIST a b c) foo) => (LIST* a b c foo)
-;;;       provided a, b, c are not splicing frobs
-;;;  (APPEND (LIST* a b c) foo) => (LIST* a b (APPEND c foo))
-;;;       provided a, b, c are not splicing frobs
-;;;  (APPEND (QUOTE (x)) foo) => (LIST* (QUOTE x) foo)
-;;;  (APPEND (CLOBBERABLE x) foo) => (NCONC x foo)
-(defun bq-simplify (x)
-  (if (atom x)
-      x
-      (let ((x (if (eq (car x) *bq-quote*)
-                   x
-                   (maptree #'bq-simplify x))))
-        (if (not (eq (car x) *bq-append*))
-            x
-            (bq-simplify-args x)))))
-
-(defun bq-simplify-args (x)
-  (do ((args (reverse (cdr x)) (cdr args))
-       (result
-         nil
-         (cond ((atom (car args))
-                (bq-attach-append *bq-append* (car args) result))
-               ((and (eq (caar args) *bq-list*)
-                     (notany #'bq-splicing-frob (cdar args)))
-                (bq-attach-conses (cdar args) result))
-               ((and (eq (caar args) *bq-list**)
-                     (notany #'bq-splicing-frob (cdar args)))
-                (bq-attach-conses
-                  (reverse (cdr (reverse (cdar args))))
-                  (bq-attach-append *bq-append*
-                                    (car (last (car args)))
-                                    result)))
-               ((and (eq (caar args) *bq-quote*)
-                     (consp (cadar args))
-                     (not (bq-frob (cadar args)))
-                     (null (cddar args)))
-                (bq-attach-conses (list (list *bq-quote*
-                                              (caadar args)))
-                                  result))
-               ((eq (caar args) *bq-clobberable*)
-                (bq-attach-append *bq-nconc* (cadar args) result))
-               (t (bq-attach-append *bq-append*
-                                    (car args)
-                                    result)))))
-      ((null args) result)))
-
-(defun null-or-quoted (x)
-  (or (null x) (and (consp x) (eq (car x) *bq-quote*))))
-
-;;; When BQ-ATTACH-APPEND is called, the OP should be #:BQ-APPEND
-;;; or #:BQ-NCONC.  This produces a form (op item result) but
-;;; some simplifications are done on the fly:
-;;;
-;;;  (op '(a b c) '(d e f g)) => '(a b c d e f g)
-;;;  (op item 'nil) => item, provided item is not a splicable frob
-;;;  (op item 'nil) => (op item), if item is a splicable frob
-;;;  (op item (op a b c)) => (op item a b c)
-(defun bq-attach-append (op item result)
-  (cond ((and (null-or-quoted item) (null-or-quoted result))
-         (list *bq-quote* (append (cadr item) (cadr result))))
-        ((or (null result) (equal result *bq-quote-nil*))
-         (if (bq-splicing-frob item) (list op item) item))
-        ((and (consp result) (eq (car result) op))
-         (list* (car result) item (cdr result)))
-        (t (list op item result))))
-
-;;; The effect of BQ-ATTACH-CONSES is to produce a form as if by
-;;; `(LIST* ,@items ,result) but some simplifications are done
-;;; on the fly.
-;;;
-;;;  (LIST* 'a 'b 'c 'd) => '(a b c . d)
-;;;  (LIST* a b c 'nil) => (LIST a b c)
-;;;  (LIST* a b c (LIST* d e f g)) => (LIST* a b c d e f g)
-;;;  (LIST* a b c (LIST d e f g)) => (LIST a b c d e f g)
-(defun bq-attach-conses (items result)
-  (cond ((and (every #'null-or-quoted items)
-              (null-or-quoted result))
-         (list *bq-quote*
-               (append (mapcar #'cadr items) (cadr result))))
-        ((or (null result) (equal result *bq-quote-nil*))
-         (cons *bq-list* items))
-        ((and (consp result)
-              (or (eq (car result) *bq-list*)
-                  (eq (car result) *bq-list**)))
-         (cons (car result) (append items (cdr result))))
-        (t (cons *bq-list** (append items (list result))))))
-
-;;; Removes funny tokens and changes (#:BQ-LIST* a b) into
-;;; (CONS a b) instead of (LIST* a b), purely for readability.
-(defun bq-remove-tokens (x)
-  (cond ((eq x *bq-list*) 'list)
-        ((eq x *bq-append*) 'append)
-        ((eq x *bq-nconc*) 'nconc)
-        ((eq x *bq-list**) 'list*)
-        ((eq x *bq-quote*) 'quote)
-        ((atom x) x)
-        ((eq (car x) *bq-clobberable*)
-         (bq-remove-tokens (cadr x)))
-        ((and (eq (car x) *bq-list**)
-              (consp (cddr x))
-              (null (cdddr x)))
-         (cons 'cons (maptree #'bq-remove-tokens (cdr x))))
-        (t (maptree #'bq-remove-tokens x))))
+  `(selfcall
+    (var (args ,(convert first-form *multiple-value-p*)))
+    ;; TODO: Interleave is temporal
+    (progn ,@(mapcar #'convert forms))
+    (return args)))
 
 (define-transformation backquote (form)
   (bq-completely-process form))
 
 (define-transformation backquote (form)
   (bq-completely-process form))
 
 (defmacro define-builtin (name args &body body)
   `(define-raw-builtin ,name ,args
 
 (defmacro define-builtin (name args &body body)
   `(define-raw-builtin ,name ,args
-     (let ,(mapcar (lambda (arg) `(,arg (ls-compile ,arg))) args)
+     (let ,(mapcar (lambda (arg) `(,arg (convert ,arg))) args)
        ,@body)))
 
        ,@body)))
 
-;;; DECLS is a list of (JSVARNAME TYPE LISPFORM) declarations.
-(defmacro type-check (decls &body body)
-  `(js!selfcall
-     ,@(mapcar (lambda (decl)
-                 `(code "var " ,(first decl) " = " ,(third decl) ";" *newline*))
-               decls)
-     ,@(mapcar (lambda (decl)
-                 `(code "if (typeof " ,(first decl) " != '" ,(second decl) "')" *newline*
-                        (indent "throw 'The value ' + "
-                                ,(first decl)
-                                " + ' is not a type "
-                                ,(second decl)
-                                ".';"
-                                *newline*)))
-               decls)
-     (code "return " (progn ,@body) ";" *newline*)))
-
 ;;; VARIABLE-ARITY compiles variable arity operations. ARGS stands for
 ;;; a variable which holds a list of forms. It will compile them and
 ;;; store the result in some Javascript variables. BODY is evaluated
 ;;; with ARGS bound to the list of these variables to generate the
 ;;; code which performs the transformation on these variables.
 ;;; VARIABLE-ARITY compiles variable arity operations. ARGS stands for
 ;;; a variable which holds a list of forms. It will compile them and
 ;;; store the result in some Javascript variables. BODY is evaluated
 ;;; with ARGS bound to the list of these variables to generate the
 ;;; code which performs the transformation on these variables.
-
 (defun variable-arity-call (args function)
   (unless (consp args)
     (error "ARGS must be a non-empty list"))
   (let ((counter 0)
         (fargs '())
 (defun variable-arity-call (args function)
   (unless (consp args)
     (error "ARGS must be a non-empty list"))
   (let ((counter 0)
         (fargs '())
-        (prelude ""))
+        (prelude '()))
     (dolist (x args)
     (dolist (x args)
-      (cond
-        ((floatp x) (push (float-to-string x) fargs))
-        ((numberp x) (push (integer-to-string x) fargs))
-        (t (let ((v (code "x" (incf counter))))
-             (push v fargs)
-             (concatf prelude
-               (code "var " v " = " (ls-compile x) ";" *newline*
-                     "if (typeof " v " !== 'number') throw 'Not a number!';"
-                     *newline*))))))
-    (js!selfcall prelude (funcall function (reverse fargs)))))
+      (if (or (floatp x) (numberp x))
+          (push x fargs)
+          (let ((v (make-symbol (concat "x" (integer-to-string (incf counter))))))
+            (push v fargs)
+            (push `(var (,v ,(convert x)))
+                  prelude)
+            (push `(if (!= (typeof ,v) "number")
+                       (throw "Not a number!"))
+                  prelude))))
+    `(selfcall
+      (progn ,@(reverse prelude))
+      ,(funcall function (reverse fargs)))))
 
 
 (defmacro variable-arity (args &body body)
   (unless (symbolp args)
     (error "`~S' is not a symbol." args))
 
 
 (defmacro variable-arity (args &body body)
   (unless (symbolp args)
     (error "`~S' is not a symbol." args))
-  `(variable-arity-call ,args
-                        (lambda (,args)
-                          (code "return " ,@body ";" *newline*))))
-
-(defun num-op-num (x op y)
-  (type-check (("x" "number" x) ("y" "number" y))
-    (code "x" op "y")))
+  `(variable-arity-call ,args (lambda (,args) `(return  ,,@body))))
 
 (define-raw-builtin + (&rest numbers)
   (if (null numbers)
 
 (define-raw-builtin + (&rest numbers)
   (if (null numbers)
-      "0"
+      0
       (variable-arity numbers
       (variable-arity numbers
-       (join numbers "+"))))
+        `(+ ,@numbers))))
 
 (define-raw-builtin - (x &rest others)
   (let ((args (cons x others)))
 
 (define-raw-builtin - (x &rest others)
   (let ((args (cons x others)))
-    (variable-arity args
-      (if (null others)
-         (concat "-" (car args))
-         (join args "-")))))
+    (variable-arity args `(- ,@args))))
 
 (define-raw-builtin * (&rest numbers)
   (if (null numbers)
 
 (define-raw-builtin * (&rest numbers)
   (if (null numbers)
-      "1"
-      (variable-arity numbers
-       (join numbers "*"))))
+      1
+      (variable-arity numbers `(* ,@numbers))))
 
 (define-raw-builtin / (x &rest others)
   (let ((args (cons x others)))
     (variable-arity args
       (if (null others)
 
 (define-raw-builtin / (x &rest others)
   (let ((args (cons x others)))
     (variable-arity args
       (if (null others)
-         (concat "1 /" (car args))
-         (join args "/")))))
+          `(/ 1 ,(car args))
+          (reduce (lambda (x y) `(/ ,x ,y))
+                  args)))))
 
 
-(define-builtin mod (x y) (num-op-num x "%" y))
+(define-builtin mod (x y)
+  `(% ,x ,y))
 
 
 (defun comparison-conjuntion (vars op)
   (cond
     ((null (cdr vars))
 
 
 (defun comparison-conjuntion (vars op)
   (cond
     ((null (cdr vars))
-     "true")
+     'true)
     ((null (cddr vars))
     ((null (cddr vars))
-     (concat (car vars) op (cadr vars)))
+     `(,op ,(car vars) ,(cadr vars)))
     (t
     (t
-     (concat (car vars) op (cadr vars)
-            " && "
-            (comparison-conjuntion (cdr vars) op)))))
+     `(and (,op ,(car vars) ,(cadr vars))
+           ,(comparison-conjuntion (cdr vars) op)))))
 
 (defmacro define-builtin-comparison (op sym)
   `(define-raw-builtin ,op (x &rest args)
      (let ((args (cons x args)))
        (variable-arity args
 
 (defmacro define-builtin-comparison (op sym)
   `(define-raw-builtin ,op (x &rest args)
      (let ((args (cons x args)))
        (variable-arity args
-        (js!bool (comparison-conjuntion args ,sym))))))
+        `(bool ,(comparison-conjuntion args ',sym))))))
 
 
-(define-builtin-comparison > ">")
-(define-builtin-comparison < "<")
-(define-builtin-comparison >= ">=")
-(define-builtin-comparison <= "<=")
-(define-builtin-comparison = "==")
+(define-builtin-comparison > >)
+(define-builtin-comparison < <)
+(define-builtin-comparison >= >=)
+(define-builtin-comparison <= <=)
+(define-builtin-comparison = ==)
+(define-builtin-comparison /= !=)
 
 (define-builtin numberp (x)
 
 (define-builtin numberp (x)
-  (js!bool (code "(typeof (" x ") == \"number\")")))
+  `(bool (== (typeof ,x) "number")))
 
 (define-builtin floor (x)
 
 (define-builtin floor (x)
-  (type-check (("x" "number" x))
-    "Math.floor(x)"))
+  `(call (get |Math| |floor|) ,x))
 
 (define-builtin expt (x y)
 
 (define-builtin expt (x y)
-  (type-check (("x" "number" x)
-               ("y" "number" y))
-    "Math.pow(x, y)"))
+  `(call (get |Math| |pow|) ,x ,y))
 
 (define-builtin float-to-string (x)
 
 (define-builtin float-to-string (x)
-  (type-check (("x" "number" x))
-    "make_lisp_string(x.toString())"))
+  `(call |make_lisp_string| (call (get ,x |toString|))))
 
 (define-builtin cons (x y)
 
 (define-builtin cons (x y)
-  (code "({car: " x ", cdr: " y "})"))
+  `(object "car" ,x "cdr" ,y))
 
 (define-builtin consp (x)
 
 (define-builtin consp (x)
-  (js!bool
-   (js!selfcall
-     "var tmp = " x ";" *newline*
-     "return (typeof tmp == 'object' && 'car' in tmp);" *newline*)))
+  `(selfcall
+    (var (tmp ,x))
+    (return (bool (and (== (typeof tmp) "object")
+                       (in "car" tmp))))))
 
 (define-builtin car (x)
 
 (define-builtin car (x)
-  (js!selfcall
-    "var tmp = " x ";" *newline*
-    "return tmp === " (ls-compile nil)
-    "? " (ls-compile nil)
-    ": tmp.car;" *newline*))
+  `(selfcall
+    (var (tmp ,x))
+    (return (if (=== tmp ,(convert nil))
+                ,(convert nil)
+                (get tmp "car")))))
 
 (define-builtin cdr (x)
 
 (define-builtin cdr (x)
-  (js!selfcall
-    "var tmp = " x ";" *newline*
-    "return tmp === " (ls-compile nil) "? "
-    (ls-compile nil)
-    ": tmp.cdr;" *newline*))
+  `(selfcall
+    (var (tmp ,x))
+    (return (if (=== tmp ,(convert nil))
+                ,(convert nil)
+                (get tmp "cdr")))))
 
 (define-builtin rplaca (x new)
 
 (define-builtin rplaca (x new)
-  (type-check (("x" "object" x))
-    (code "(x.car = " new ", x)")))
+  `(= (get ,x "car") ,new))
 
 (define-builtin rplacd (x new)
 
 (define-builtin rplacd (x new)
-  (type-check (("x" "object" x))
-    (code "(x.cdr = " new ", x)")))
+  `(= (get ,x "cdr") ,new))
 
 (define-builtin symbolp (x)
 
 (define-builtin symbolp (x)
-  (js!bool
-   (js!selfcall
-     "var tmp = " x ";" *newline*
-     "return (typeof tmp == 'object' && 'name' in tmp);" *newline*)))
+  `(bool (instanceof ,x |Symbol|)))
 
 (define-builtin make-symbol (name)
 
 (define-builtin make-symbol (name)
-  (code "({name: " name "})"))
+  `(new (call |Symbol| ,name)))
 
 (define-builtin symbol-name (x)
 
 (define-builtin symbol-name (x)
-  (code "(" x ").name"))
+  `(get ,x "name"))
 
 (define-builtin set (symbol value)
 
 (define-builtin set (symbol value)
-  (code "(" symbol ").value = " value))
+  `(= (get ,symbol "value") ,value))
 
 (define-builtin fset (symbol value)
 
 (define-builtin fset (symbol value)
-  (code "(" symbol ").fvalue = " value))
+  `(= (get ,symbol "fvalue") ,value))
 
 (define-builtin boundp (x)
 
 (define-builtin boundp (x)
-  (js!bool (code "(" x ".value !== undefined)")))
+  `(bool (!== (get ,x "value") undefined)))
+
+(define-builtin fboundp (x)
+  `(bool (!== (get ,x "fvalue") undefined)))
 
 (define-builtin symbol-value (x)
 
 (define-builtin symbol-value (x)
-  (js!selfcall
-    "var symbol = " x ";" *newline*
-    "var value = symbol.value;" *newline*
-    "if (value === undefined) throw \"Variable `\" + xstring(symbol.name) + \"' is unbound.\";" *newline*
-    "return value;" *newline*))
+  `(selfcall
+    (var (symbol ,x)
+         (value (get symbol "value")))
+    (if (=== value undefined)
+        (throw (+ "Variable `" (call |xstring| (get symbol "name")) "' is unbound.")))
+    (return value)))
 
 (define-builtin symbol-function (x)
 
 (define-builtin symbol-function (x)
-  (js!selfcall
-    "var symbol = " x ";" *newline*
-    "var func = symbol.fvalue;" *newline*
-    "if (func === undefined) throw \"Function `\" + xstring(symbol.name) + \"' is undefined.\";" *newline*
-    "return func;" *newline*))
+  `(selfcall
+    (var (symbol ,x)
+         (func (get symbol "fvalue")))
+    (if (=== func undefined)
+        (throw (+ "Function `" (call |xstring| (get symbol "name")) "' is undefined.")))
+    (return func)))
 
 (define-builtin symbol-plist (x)
 
 (define-builtin symbol-plist (x)
-  (code "((" x ").plist || " (ls-compile nil) ")"))
+  `(or (get ,x "plist") ,(convert nil)))
 
 (define-builtin lambda-code (x)
 
 (define-builtin lambda-code (x)
-  (code "make_lisp_string((" x ").toString())"))
+  `(call |make_lisp_string| (call (get ,x "toString"))))
 
 (define-builtin eq (x y)
 
 (define-builtin eq (x y)
-  (js!bool (code "(" x " === " y ")")))
+  `(bool (=== ,x ,y)))
 
 (define-builtin char-code (x)
 
 (define-builtin char-code (x)
-  (type-check (("x" "string" x))
-    "x.charCodeAt(0)"))
+  `(call |char_to_codepoint| ,x))
 
 (define-builtin code-char (x)
 
 (define-builtin code-char (x)
-  (type-check (("x" "number" x))
-    "String.fromCharCode(x)"))
+  `(call |char_from_codepoint| ,x))
 
 (define-builtin characterp (x)
 
 (define-builtin characterp (x)
-  (js!bool
-   (js!selfcall
-     "var x = " x ";" *newline*
-     "return (typeof(" x ") == \"string\") && x.length == 1;")))
+  `(selfcall
+    (var (x ,x))
+    (return (bool
+             (and (== (typeof x) "string")
+                  (or (== (get x "length") 1)
+                      (== (get x "length") 2)))))))
+
+(define-builtin char-upcase (x)
+  `(call |safe_char_upcase| ,x))
 
 
-(define-builtin char-to-string (x)
-  (js!selfcall
-    "var r = [" x "];" *newline*
-    "r.type = 'character';"
-    "return r"))
+(define-builtin char-downcase (x)
+  `(call |safe_char_downcase| ,x))
 
 (define-builtin stringp (x)
 
 (define-builtin stringp (x)
-  (js!bool
-   (js!selfcall
-     "var x = " x ";" *newline*
-     "return typeof(x) == 'object' && 'length' in x && x.type == 'character';")))
-
-(define-builtin string-upcase (x)
-  (code "make_lisp_string(xstring(" x ").toUpperCase())"))
-
-(define-builtin string-length (x)
-  (code x ".length"))
-
-(define-raw-builtin slice (vector a &optional b)
-  (js!selfcall
-    "var vector = " (ls-compile vector) ";" *newline*
-    "var a = " (ls-compile a) ";" *newline*
-    "var b;" *newline*
-    (when b (code "b = " (ls-compile b) ";" *newline*))
-    "return vector.slice(a,b);" *newline*))
-
-(define-builtin char (string index)
-  (code string "[" index "]"))
-
-(define-builtin concat-two (string1 string2)
-  (js!selfcall
-    "var r = " string1 ".concat(" string2 ");" *newline*
-    "r.type = 'character';"
-    "return r;" *newline*))
+  `(selfcall
+    (var (x ,x))
+    (return (bool
+             (and (and (===(typeof x) "object")
+                       (in "length" x))
+                  (== (get x "stringp") 1))))))
 
 (define-raw-builtin funcall (func &rest args)
 
 (define-raw-builtin funcall (func &rest args)
-  (js!selfcall
-    "var f = " (ls-compile func) ";" *newline*
-    "return (typeof f === 'function'? f: f.fvalue)("
-    (join (list* (if *multiple-value-p* "values" "pv")
-                 (integer-to-string (length args))
-                 (mapcar #'ls-compile args))
-          ", ")
-    ")"))
+  `(selfcall
+    (var (f ,(convert func)))
+    (return (call (if (=== (typeof f) "function")
+                      f
+                      (get f "fvalue"))
+                  ,@(list* (if *multiple-value-p* '|values| '|pv|)
+                           (length args)
+                           (mapcar #'convert args))))))
 
 (define-raw-builtin apply (func &rest args)
   (if (null args)
 
 (define-raw-builtin apply (func &rest args)
   (if (null args)
-      (code "(" (ls-compile func) ")()")
+      (convert func)
       (let ((args (butlast args))
             (last (car (last args))))
       (let ((args (butlast args))
             (last (car (last args))))
-        (js!selfcall
-          "var f = " (ls-compile func) ";" *newline*
-          "var args = [" (join (list* (if *multiple-value-p* "values" "pv")
-                                      (integer-to-string (length args))
-                                      (mapcar #'ls-compile args))
-                               ", ")
-          "];" *newline*
-          "var tail = (" (ls-compile last) ");" *newline*
-          "while (tail != " (ls-compile nil) "){" *newline*
-          "    args.push(tail.car);" *newline*
-          "    args[1] += 1;" *newline*
-          "    tail = tail.cdr;" *newline*
-          "}" *newline*
-          "return (typeof f === 'function'? f : f.fvalue).apply(this, args);" *newline*))))
+        `(selfcall
+           (var (f ,(convert func)))
+           (var (args ,(list-to-vector
+                        (list* (if *multiple-value-p* '|values| '|pv|)
+                               (length args)
+                               (mapcar #'convert args)))))
+           (var (tail ,(convert last)))
+           (while (!= tail ,(convert nil))
+             (call (get args "push") (get tail "car"))
+             (post++ (property args 1))
+             (= tail (get tail "cdr")))
+           (return (call (get (if (=== (typeof f) "function")
+                                  f
+                                  (get f "fvalue"))
+                              "apply")
+                         this
+                         args))))))
 
 (define-builtin js-eval (string)
   (if *multiple-value-p*
 
 (define-builtin js-eval (string)
   (if *multiple-value-p*
-      (js!selfcall
-        "var v = globalEval(xstring(" string "));" *newline*
-        "return values.apply(this, forcemv(v));" *newline*)
-      (code "globalEval(xstring(" string "))")))
+      `(selfcall
+        (var (v (call |globalEval| (call |xstring| ,string))))
+        (return (call (get |values| "apply") this (call |forcemv| v))))
+      `(call |globalEval| (call |xstring| ,string))))
 
 (define-builtin %throw (string)
 
 (define-builtin %throw (string)
-  (js!selfcall "throw " string ";" *newline*))
+  `(selfcall (throw ,string)))
 
 
-(define-builtin new () "{}")
+(define-builtin functionp (x)
+  `(bool (=== (typeof ,x) "function")))
 
 
-(define-builtin objectp (x)
-  (js!bool (code "(typeof (" x ") === 'object')")))
+(define-builtin %write-string (x)
+  `(call (get |lisp| "write") ,x))
 
 
-(define-builtin oget (object key)
-  (js!selfcall
-    "var tmp = " "(" object ")[xstring(" key ")];" *newline*
-    "return tmp == undefined? " (ls-compile nil) ": tmp ;" *newline*))
+(define-builtin /debug (x)
+  `(call (get |console| "log") (call |xstring| ,x)))
 
 
-(define-builtin oset (object key value)
-  (code "((" object ")[xstring(" key ")] = " value ")"))
 
 
-(define-builtin in (key object)
-  (js!bool (code "(xstring(" key ") in (" object "))")))
+;;; Storage vectors. They are used to implement arrays and (in the
+;;; future) structures.
 
 
-(define-builtin functionp (x)
-  (js!bool (code "(typeof " x " == 'function')")))
-
-(define-builtin write-string (x)
-  (code "lisp.write(xstring(" 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 storage-vector-p (x)
+  `(selfcall
+    (var (x ,x))
+    (return (bool (and (=== (typeof x) "object") (in "length" x))))))
+
+(define-builtin make-storage-vector (n)
+  `(selfcall
+    (var (r #()))
+    (= (get r "length") ,n)
+    (return r)))
+
+(define-builtin storage-vector-size (x)
+  `(get ,x "length"))
+
+(define-builtin resize-storage-vector (vector new-size)
+  `(= (get ,vector "length") ,new-size))
+
+(define-builtin storage-vector-ref (vector n)
+  `(selfcall
+    (var (x (property ,vector ,n)))
+    (if (=== x undefined) (throw "Out of range."))
+    (return x)))
+
+(define-builtin storage-vector-set (vector n value)
+  `(selfcall
+    (var (x ,vector))
+    (var (i ,n))
+    (if (or (< i 0) (>= i (get x "length")))
+        (throw "Out of range."))
+    (return (= (property x i) ,value))))
+
+(define-builtin concatenate-storage-vector (sv1 sv2)
+  `(selfcall
+     (var (sv1 ,sv1))
+     (var (r (call (get sv1 "concat") ,sv2)))
+     (= (get r "type") (get sv1 "type"))
+     (= (get r "stringp") (get sv1 "stringp"))
+     (return r)))
 
 (define-builtin get-internal-real-time ()
 
 (define-builtin get-internal-real-time ()
-  "(new Date()).getTime()")
+  `(call (get (new (call |Date|)) "getTime")))
 
 (define-builtin values-array (array)
   (if *multiple-value-p*
 
 (define-builtin values-array (array)
   (if *multiple-value-p*
-      (code "values.apply(this, " array ")")
-      (code "pv.apply(this, " array ")")))
+      `(call (get |values| "apply") this ,array)
+      `(call (get |pv| "apply") this ,array)))
 
 (define-raw-builtin values (&rest args)
   (if *multiple-value-p*
 
 (define-raw-builtin values (&rest args)
   (if *multiple-value-p*
-      (code "values(" (join (mapcar #'ls-compile args) ", ") ")")
-      (code "pv(" (join (mapcar #'ls-compile args) ", ") ")")))
-
-;; Receives the JS function as first argument as a literal string. The
-;; second argument is compiled and should evaluate to a vector of
-;; values to apply to the the function. The result returned.
-(define-builtin %js-call (fun args)
-  (code fun ".apply(this, " args ")"))
-
-(defun macro (x)
-  (and (symbolp x)
-       (let ((b (lookup-in-lexenv x *environment* 'function)))
-         (if (and b (eq (binding-type b) 'macro))
-             b
-             nil))))
-
-#+common-lisp
+      `(call |values| ,@(mapcar #'convert args))
+      `(call |pv| ,@(mapcar #'convert args))))
+
+;;; Javascript FFI
+
+(define-builtin new ()
+  '(object))
+
+(define-raw-builtin oget* (object key &rest keys)
+  `(selfcall
+    (progn
+      (var (tmp (property ,(convert object) (call |xstring| ,(convert key)))))
+      ,@(mapcar (lambda (key)
+                  `(progn
+                     (if (=== tmp undefined) (return ,(convert nil)))
+                     (= tmp (property tmp (call |xstring| ,(convert key))))))
+                keys))
+    (return (if (=== tmp undefined) ,(convert nil) tmp))))
+
+(define-raw-builtin oset* (value object key &rest keys)
+  (let ((keys (cons key keys)))
+    `(selfcall
+      (progn
+        (var (obj ,(convert object)))
+        ,@(mapcar (lambda (key)
+                    `(progn
+                       (= obj (property obj (call |xstring| ,(convert key))))
+                       (if (=== object undefined)
+                           (throw "Impossible to set object property."))))
+                  (butlast keys))
+        (var (tmp
+              (= (property obj (call |xstring| ,(convert (car (last keys)))))
+                 ,(convert value))))
+        (return (if (=== tmp undefined)
+                    ,(convert nil)
+                    tmp))))))
+
+(define-raw-builtin oget (object key &rest keys)
+  `(call |js_to_lisp| ,(convert `(oget* ,object ,key ,@keys))))
+
+(define-raw-builtin oset (value object key &rest keys)
+  (convert `(oset* (lisp-to-js ,value) ,object ,key ,@keys)))
+
+(define-builtin objectp (x)
+  `(bool (=== (typeof ,x) "object")))
+
+(define-builtin lisp-to-js (x) `(call |lisp_to_js| ,x))
+(define-builtin js-to-lisp (x) `(call |js_to_lisp| ,x))
+
+
+(define-builtin in (key object)
+  `(bool (in (call |xstring| ,key) ,object)))
+
+(define-builtin map-for-in (function object)
+  `(selfcall
+    (var (f ,function)
+         (g (if (=== (typeof f) "function") f (get f "fvalue")))
+         (o ,object))
+    (for-in (key o)
+            (call g ,(if *multiple-value-p* '|values| '|pv|) 1 (get o "key")))
+    (return ,(convert nil))))
+
+(define-compilation %js-vref (var)
+  `(call |js_to_lisp| ,(make-symbol var)))
+
+(define-compilation %js-vset (var val)
+  `(= ,(make-symbol var) (call |lisp_to_js| ,(convert val))))
+
+(define-setf-expander %js-vref (var)
+  (let ((new-value (gensym)))
+    (unless (stringp var)
+      (error "`~S' is not a string." var))
+    (values nil
+            (list var)
+            (list new-value)
+            `(%js-vset ,var ,new-value)
+            `(%js-vref ,var))))
+
+
+#-jscl
 (defvar *macroexpander-cache*
   (make-hash-table :test #'eq))
 
 (defvar *macroexpander-cache*
   (make-hash-table :test #'eq))
 
-(defun ls-macroexpand-1 (form)
+(defun !macro-function (symbol)
+  (unless (symbolp symbol)
+    (error "`~S' is not a symbol." symbol))
+  (let ((b (lookup-in-lexenv symbol *environment* 'function)))
+    (if (and b (eq (binding-type b) 'macro))
+        (let ((expander (binding-value b)))
+          (cond
+            #-jscl
+            ((gethash b *macroexpander-cache*)
+             (setq expander (gethash b *macroexpander-cache*)))
+            ((listp expander)
+             (let ((compiled (eval expander)))
+               ;; The list representation are useful while
+               ;; bootstrapping, as we can dump the definition of the
+               ;; macros easily, but they are slow because we have to
+               ;; evaluate them and compile them now and again. So, let
+               ;; us replace the list representation version of the
+               ;; function with the compiled one.
+               ;;
+               #+jscl (setf (binding-value b) compiled)
+               #-jscl (setf (gethash b *macroexpander-cache*) compiled)
+               (setq expander compiled))))
+          expander)
+        nil)))
+
+(defun !macroexpand-1 (form)
   (cond
     ((symbolp form)
      (let ((b (lookup-in-lexenv form *environment* 'variable)))
        (if (and b (eq (binding-type b) 'macro))
            (values (binding-value b) t)
            (values form nil))))
   (cond
     ((symbolp form)
      (let ((b (lookup-in-lexenv form *environment* 'variable)))
        (if (and b (eq (binding-type b) 'macro))
            (values (binding-value b) t)
            (values form nil))))
-    ((consp form)
-     (let ((macro-binding (macro (car form))))
-       (if macro-binding
-           (let ((expander (binding-value macro-binding)))
-             (cond
-               #+common-lisp
-               ((gethash macro-binding *macroexpander-cache*)
-                (setq expander (gethash macro-binding *macroexpander-cache*)))
-               ((listp expander)
-                (let ((compiled (eval expander)))
-                  ;; The list representation are useful while
-                  ;; bootstrapping, as we can dump the definition of the
-                  ;; macros easily, but they are slow because we have to
-                  ;; evaluate them and compile them now and again. So, let
-                  ;; us replace the list representation version of the
-                  ;; function with the compiled one.
-                  ;;
-                  #+jscl (setf (binding-value macro-binding) compiled)
-                  #+common-lisp (setf (gethash macro-binding *macroexpander-cache*) compiled)
-                  (setq expander compiled))))
-             (values (apply expander (cdr form)) t))
+    ((and (consp form) (symbolp (car form)))
+     (let ((macrofun (!macro-function (car form))))
+       (if macrofun
+           (values (funcall macrofun (cdr form)) t)
            (values form nil))))
     (t
      (values form nil))))
 
 (defun compile-funcall (function args)
            (values form nil))))
     (t
      (values form nil))))
 
 (defun compile-funcall (function args)
-  (let* ((values-funcs (if *multiple-value-p* "values" "pv"))
-         (arglist (concat "(" (join (list* values-funcs
-                                           (integer-to-string (length args))
-                                           (mapcar #'ls-compile args)) ", ") ")")))
+  (let* ((arglist (list* (if *multiple-value-p* '|values| '|pv|)
+                         (length args)
+                         (mapcar #'convert args))))
     (unless (or (symbolp function)
                 (and (consp function)
     (unless (or (symbolp function)
                 (and (consp function)
-                     (eq (car function) 'lambda)))
+                     (member (car function) '(lambda oget))))
       (error "Bad function designator `~S'" function))
     (cond
       ((translate-function function)
       (error "Bad function designator `~S'" function))
     (cond
       ((translate-function function)
-       (concat (translate-function function) arglist))
+       `(call ,(make-symbol (translate-function function)) ,@arglist))
       ((and (symbolp function)
             #+jscl (eq (symbol-package function) (find-package "COMMON-LISP"))
       ((and (symbolp function)
             #+jscl (eq (symbol-package function) (find-package "COMMON-LISP"))
-            #+common-lisp t)
-       (code (ls-compile `',function) ".fvalue" arglist))
+            #-jscl t)
+       `(call (get ,(convert `',function) "fvalue") ,@arglist))
+      #+jscl((symbolp function)
+             `(call ,(convert `#',function) ,@arglist))
+      ((and (consp function) (eq (car function) 'lambda))
+       `(call ,(convert `#',function) ,@arglist))
+      ((and (consp function) (eq (car function) 'oget))
+       `(call ,(convert function) ,@arglist))
       (t
       (t
-       (code (ls-compile `#',function) arglist)))))
-
-(defun ls-compile-block (sexps &optional return-last-p)
-  (if return-last-p
-      (code (ls-compile-block (butlast sexps))
-            "return " (ls-compile (car (last sexps)) *multiple-value-p*) ";")
-      (join-trailing
-       (remove-if #'null-or-empty-p (mapcar #'ls-compile sexps))
-       (concat ";" *newline*))))
-
-(defun ls-compile (sexp &optional multiple-value-p)
-  (multiple-value-bind (sexp expandedp) (ls-macroexpand-1 sexp)
+       (error "Bad function descriptor")))))
+
+(defun convert-block (sexps &optional return-last-p decls-allowed-p)
+  (multiple-value-bind (sexps decls)
+      (parse-body sexps :declarations decls-allowed-p)
+    (declare (ignore decls))
+    (if return-last-p
+        `(progn
+           ,@(mapcar #'convert (butlast sexps))
+           (return ,(convert (car (last sexps)) *multiple-value-p*)))
+        `(progn ,@(mapcar #'convert sexps)))))
+
+(defun convert* (sexp &optional multiple-value-p)
+  (multiple-value-bind (sexp expandedp) (!macroexpand-1 sexp)
     (when expandedp
     (when expandedp
-      (return-from ls-compile (ls-compile sexp multiple-value-p)))
+      (return-from convert* (convert sexp multiple-value-p)))
     ;; The expression has been macroexpanded. Now compile it!
     (let ((*multiple-value-p* multiple-value-p))
       (cond
     ;; The expression has been macroexpanded. Now compile it!
     (let ((*multiple-value-p* multiple-value-p))
       (cond
          (let ((b (lookup-in-lexenv sexp *environment* 'variable)))
            (cond
              ((and b (not (member 'special (binding-declarations b))))
          (let ((b (lookup-in-lexenv sexp *environment* 'variable)))
            (cond
              ((and b (not (member 'special (binding-declarations b))))
-              (binding-value b))
+              (make-symbol (binding-value b)))
              ((or (keywordp sexp)
                   (and b (member 'constant (binding-declarations b))))
              ((or (keywordp sexp)
                   (and b (member 'constant (binding-declarations b))))
-              (code (ls-compile `',sexp) ".value"))
+              `(get ,(convert `',sexp) "value"))
              (t
              (t
-              (ls-compile `(symbol-value ',sexp))))))
+              (convert `(symbol-value ',sexp))))))
         ((or (integerp sexp) (floatp sexp) (characterp sexp) (stringp sexp) (arrayp sexp))
          (literal sexp))
         ((listp sexp)
         ((or (integerp sexp) (floatp sexp) (characterp sexp) (stringp sexp) (arrayp sexp))
          (literal sexp))
         ((listp sexp)
         (t
          (error "How should I compile `~S'?" sexp))))))
 
         (t
          (error "How should I compile `~S'?" sexp))))))
 
+(defun convert (sexp &optional multiple-value-p)
+  (convert* sexp multiple-value-p))
+
 
 (defvar *compile-print-toplevels* nil)
 
 
 (defvar *compile-print-toplevels* nil)
 
                (min width (length string)))))
     (subseq string 0 n)))
 
                (min width (length string)))))
     (subseq string 0 n)))
 
-(defun ls-compile-toplevel (sexp &optional multiple-value-p)
+(defun convert-toplevel (sexp &optional multiple-value-p)
   (let ((*toplevel-compilations* nil))
     (cond
   (let ((*toplevel-compilations* nil))
     (cond
-      ((and (consp sexp) (eq (car sexp) 'progn))
-       (let ((subs (mapcar (lambda (s)
-                             (ls-compile-toplevel s t))
-                           (cdr sexp))))
-         (join (remove-if #'null-or-empty-p subs))))
+      ;; Non-empty toplevel progn
+      ((and (consp sexp)
+            (eq (car sexp) 'progn)
+            (cdr sexp))
+       `(progn
+          ,@(mapcar (lambda (s) (convert-toplevel s t))
+                    (cdr sexp))))
       (t
        (when *compile-print-toplevels*
          (let ((form-string (prin1-to-string sexp)))
            (format t "Compiling ~a..." (truncate-string form-string))))
       (t
        (when *compile-print-toplevels*
          (let ((form-string (prin1-to-string sexp)))
            (format t "Compiling ~a..." (truncate-string form-string))))
-       (let ((code (ls-compile sexp multiple-value-p)))
-         (code (join-trailing (get-toplevel-compilations)
-                              (code ";" *newline*))
-               (when code
-                 (code code ";" *newline*))))))))
+       (let ((code (convert sexp multiple-value-p)))
+         `(progn
+            ,@(get-toplevel-compilations)
+            ,code))))))
+
+(defun compile-toplevel (sexp &optional multiple-value-p)
+  (with-output-to-string (*standard-output*)
+    (js (convert-toplevel sexp multiple-value-p))))