Some refactoring
[jscl.git] / src / compiler.lisp
index 2c8269c..1c0bad1 100644 (file)
 
 (/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.
 
-(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)))))
+(define-js-macro selfcall (&body body)
+  `(call (function () ,@body)))
+
+(define-js-macro bool (expr)
+  `(if ,expr ,(convert t) ,(convert nil)))
 
-;;; 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.
+(define-js-macro method-call (x method &rest args)
+  `(call (get ,x ,method) ,@args))
 
 ;;; A Form can return a multiple values object calling VALUES, like
 ;;; values(arg1, arg2, ...). It will work in any context, as well as
@@ -94,7 +83,7 @@
 (defun gvarname (symbol)
   (declare (ignore symbol))
   (incf *variable-counter*)
-  (concat "v" (integer-to-string *variable-counter*)))
+  (make-symbol (concat "v" (integer-to-string *variable-counter*))))
 
 (defun translate-variable (symbol)
   (awhen (lookup-in-lexenv symbol *environment* 'variable)
                   (dotimes (idx n-optional-arguments)
                     (let ((arg (nth idx optional-arguments)))
                       (collect `(case ,(+ idx n-required-arguments)))
-                      (collect `(= ,(make-symbol (translate-variable (car arg)))
+                      (collect `(= ,(translate-variable (car arg))
                                    ,(convert (cadr arg))))
                       (collect (when (third arg)
-                                 `(= ,(make-symbol (translate-variable (third arg)))
+                                 `(= ,(translate-variable (third arg))
                                      ,(convert nil))))))
                   (collect 'default)
                   (collect '(break)))))))
        (n-optional-arguments (length (ll-optional-arguments ll)))
        (rest-argument (ll-rest-argument ll)))
     (when rest-argument
-      (let ((js!rest (make-symbol (translate-variable rest-argument))))
+      (let ((js!rest (translate-variable rest-argument)))
         `(progn
            (var (,js!rest ,(convert nil)))
            (var i)
             (destructuring-bind ((keyword-name var) &optional initform svar)
                 keyword-argument
               (declare (ignore keyword-name initform))
-              (collect `(var ,(make-symbol (translate-variable var))))
+              (collect `(var ,(translate-variable var)))
               (when svar
                 (collect
-                    `(var (,(make-symbol (translate-variable svar))
+                    `(var (,(translate-variable svar)
                             ,(convert nil))))))))
        
        ;; Parse keywords
                           (if (=== (property |arguments| (+ i 2))
                                    ,(convert keyword-name))
                               (progn
-                                (= ,(make-symbol (translate-variable var))
+                                (= ,(translate-variable var)
                                    (property |arguments| (+ i 3)))
-                                ,(when svar `(= ,(make-symbol (translate-variable svar))
+                                ,(when svar `(= ,(translate-variable svar)
                                                 ,(convert t)))
                                 (break))))
                      (if (== i |nargs|)
-                         (= ,(make-symbol (translate-variable var))
-                            ,(convert initform)))))))
+                         (= ,(translate-variable var) ,(convert initform)))))))
          (when keyword-arguments
            `(progn
               (var i)
                                     (ll-svars ll)))))
         (lambda-name/docstring-wrapper name documentation
          `(function (|values| |nargs| ,@(mapcar (lambda (x)
-                                                  (make-symbol (translate-variable x)))
+                                                  (translate-variable x))
                                                 (append required-arguments optional-arguments)))
                      ;; Check number of arguments
                     ,(lambda-check-argument-count n-required-arguments
             (eq (binding-type b) 'variable)
             (not (member 'special (binding-declarations b)))
             (not (member 'constant (binding-declarations b))))
-       ;; TODO: Unnecesary make-symbol when codegen migration is
-       ;; finished.
-       `(= ,(make-symbol (binding-value b)) ,(convert val)))
+       `(= ,(binding-value b) ,(convert val)))
       ((and b (eq (binding-type b) 'macro))
        (convert `(setf ,var ,val)))
       (t
 
 (defun genlit ()
   (incf *literal-counter*)
-  (concat "l" (integer-to-string *literal-counter*)))
+  (make-symbol (concat "l" (integer-to-string *literal-counter*))))
 
 (defun dump-symbol (symbol)
   #-jscl
   (cond
     ((integerp sexp) sexp)
     ((floatp sexp) sexp)
-    ((characterp sexp)
-     ;; TODO: Remove selfcall after migration
-     `(selfcall (return ,(string sexp))))
+    ((characterp sexp) (string sexp))
     (t
      (or (cdr (assoc sexp *literal-table* :test #'eql))
          (let ((dumped (typecase sexp
            (if (and recursive (not (symbolp sexp)))
                dumped
                (let ((jsvar (genlit)))
-                 (push (cons sexp (make-symbol jsvar)) *literal-table*)
-                 (toplevel-compilation `(var (,(make-symbol jsvar) ,dumped)))
+                 (push (cons sexp jsvar) *literal-table*)
+                 (toplevel-compilation `(var (,jsvar ,dumped)))
                  (when (keywordp sexp)
-                   (toplevel-compilation `(= ,(get (make-symbol jsvar) "value") ,(make-symbol jsvar))))
-                 (make-symbol jsvar))))))))
+                   (toplevel-compilation `(= ,(get jsvar "value") ,jsvar)))
+                 jsvar)))))))
 
 
 (define-compilation quote (sexp)
 (define-compilation %while (pred &rest body)
   `(selfcall
     (while (!== ,(convert pred) ,(convert nil))
-      0                                 ; TODO: Force
-                                        ; braces. Unnecesary when code
-                                        ; is gone
       ,(convert-block body))
     (return ,(convert nil))))
 
     ((symbolp x)
      (let ((b (lookup-in-lexenv x *environment* 'function)))
        (if b
-          (make-symbol (binding-value b))
+          (binding-value b)
           (convert `(symbol-function ',x)))))))
 
 (defun make-function-binding (fname)
           (extend-lexenv (mapcar #'make-function-binding fnames)
                          *environment*
                          'function)))
-    `(call (function ,(mapcar #'make-symbol (mapcar #'translate-function fnames))
+    `(call (function ,(mapcar #'translate-function fnames)
                 ,(convert-block body t))
            ,@cfuncs)))
 
                          'function)))
     `(selfcall
       ,@(mapcar (lambda (func)
-                  `(var (,(make-symbol (translate-function (car func)))
+                  `(var (,(translate-function (car func))
                           ,(compile-lambda (cadr func)
                                            `((block ,(car func) ,@(cddr func)))))))
                 definitions)
     `(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))))
+                                      (push (cons x v) dynamic-bindings)
+                                      v)
+                                    (translate-variable x)))
                               variables)
                      ,(let ((body (convert-block body t t)))
                            `,(let-binding-wrapper dynamic-bindings body)))
         (convert `(setq ,var ,value))
         (let* ((v (gvarname var))
                (b (make-binding :name var :type 'variable :value v)))
-          (prog1 `(var (,(make-symbol v) ,(convert value)))
+          (prog1 `(var (,v ,(convert value)))
             (push-to-lexenv b *environment* 'variable))))))
 
 ;;; Wrap BODY to restore the symbol values of SYMBOLS after body. It
        (try
         ,@(mapcar (lambda (b)
                     (let ((s (convert `(quote ,(car b)))))
-                      `(var (,(make-symbol (cdr b)) (get ,s "value")))))
+                      `(var (,(cdr b) (get ,s "value")))))
                   store)
         ,body)
        (finally
         ,@(mapcar (lambda (b)
                     (let ((s (convert `(quote ,(car b)))))
-                      `(= (get ,s "value") ,(make-symbol (cdr b)))))
+                      `(= (get ,s "value") ,(cdr b))))
                   store)))))
 
 (define-compilation let* (bindings &rest body)
       (if (member 'used (binding-declarations b))
           `(selfcall
             (try
-             (var (,(make-symbol idvar) #()))
+             (var (,idvar #()))
              ,cbody)
             (catch (cf)
               (if (and (== (get cf "type") "block")
-                       (== (get cf "id") ,(make-symbol idvar)))
+                       (== (get cf "id") ,idvar))
                   ,(if *multiple-value-p*
-                       `(return (call (get |values| "apply") this (call |forcemv| (get cf "values"))))
+                       `(return (method-call |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)
       (throw
           (object
            "type" "block"
-           "id" ,(make-symbol (binding-value b))
+           "id" ,(binding-value b)
            "values" ,(convert value multiple-value-p)
            "message" ,(concat "Return from unknown block '" (symbol-name name) "'."))))))
 
 (define-compilation catch (id &rest body)
   `(selfcall
-    (var (|id| ,(convert id)))
+    (var (id ,(convert id)))
     (try
      ,(convert-block body t))
     (catch (|cf|)
       (if (and (== (get |cf| "type") "catch")
-               (== (get |cf| "id") |id|))
+               (== (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")))))
+               `(return (method-call |values| "apply" this (call |forcemv| (get |cf| "values"))))
+               `(return (method-call |pv|     "apply" this (call |forcemv| (get |cf| "values")))))
           (throw |cf|)))))
 
 (define-compilation throw (id value)
   `(selfcall
     (var (|values| |mv|))
     (throw (object
-            |type| "catch"
-            |id| ,(convert id)
-            |values| ,(convert value t)
-            |message| "Throw uncatched."))))
+            "type" "catch"
+            "id" ,(convert id)
+            "values" ,(convert value t)
+            "message" "Throw uncatched."))))
 
 (defun go-tag-p (x)
   (or (integerp x) (symbolp x)))
         (setq initag (second (binding-value b))))
       `(selfcall
         ;; TAGBODY branch to take
-        (var (,(make-symbol branch) ,initag))
-        (var (,(make-symbol tbidx) #()))
+        (var (,branch ,initag))
+        (var (,tbidx #()))
         (label tbloop
                (while true
                  (try
-                  (switch ,(make-symbol branch)
+                  (switch ,branch
                           ,@(with-collect
                              (collect `(case ,initag))
                              (dolist (form (cdr body))
                           (break tbloop)))
                  (catch (jump)
                    (if (and (== (get jump "type") "tagbody")
-                            (== (get jump "id") ,(make-symbol tbidx)))
-                       (= ,(make-symbol branch) (get jump "label"))
+                            (== (get jump "id") ,tbidx))
+                       (= ,branch (get jump "label"))
                        (throw jump)))))
         (return ,(convert nil))))))
 
       (throw
           (object
            "type" "tagbody"
-           "id" ,(make-symbol (first (binding-value b)))
+           "id" ,(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)
   `(selfcall
-    (var (|ret| ,(convert nil)))
+    (var (ret ,(convert nil)))
     (try
-     (= |ret| ,(convert form)))
+     (= ret ,(convert form)))
     (finally
      ,(convert-block clean-up))
-    (return |ret|)))
+    (return ret)))
 
 (define-compilation multiple-value-call (func-form &rest forms)
   `(selfcall
               (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))))))
+                            (= args (method-call args "concat" vs))
+                            (method-call args "push" vs))))))
        (= (property args 1) (- (property args "length") 2))
-       (return (call (get func "apply") |window| args))))))
+       (return (method-call func "apply" |window| args))))))
 
 (define-compilation multiple-value-prog1 (first-form &rest forms)
   `(selfcall
     (var (args ,(convert first-form *multiple-value-p*)))
-    ;; TODO: Interleave is temporal
     (progn ,@(mapcar #'convert forms))
     (return args)))
 
   `(bool (== (typeof ,x) "number")))
 
 (define-builtin floor (x)
-  `(call (get |Math| |floor|) ,x))
+  `(method-call |Math| "floor" ,x))
 
 (define-builtin expt (x y)
-  `(call (get |Math| |pow|) ,x ,y))
+  `(method-call |Math| "pow" ,x ,y))
 
 (define-builtin float-to-string (x)
-  `(call |make_lisp_string| (call (get ,x |toString|))))
+  `(call |make_lisp_string| (method-call ,x |toString|)))
 
 (define-builtin cons (x y)
   `(object "car" ,x "cdr" ,y))
   `(or (get ,x "plist") ,(convert nil)))
 
 (define-builtin lambda-code (x)
-  `(call |make_lisp_string| (call (get ,x "toString"))))
+  `(call |make_lisp_string| (method-call ,x "toString")))
 
 (define-builtin eq (x y)
   `(bool (=== ,x ,y)))
                                (mapcar #'convert args)))))
            (var (tail ,(convert last)))
            (while (!= tail ,(convert nil))
-             (call (get args "push") (get tail "car"))
+             (method-call 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))))))
+           (return (method-call (if (=== (typeof f) "function")
+                                    f
+                                    (get f "fvalue"))
+                                "apply"
+                                this
+                                args))))))
 
 (define-builtin js-eval (string)
   (if *multiple-value-p*
       `(selfcall
         (var (v (call |globalEval| (call |xstring| ,string))))
-        (return (call (get |values| "apply") this (call |forcemv| v))))
+        (return (method-call |values| "apply" this (call |forcemv| v))))
       `(call |globalEval| (call |xstring| ,string))))
 
 (define-builtin %throw (string)
   `(bool (=== (typeof ,x) "function")))
 
 (define-builtin %write-string (x)
-  `(call (get |lisp| "write") ,x))
+  `(method-call |lisp| "write" ,x))
 
 (define-builtin /debug (x)
-  `(call (get |console| "log") (call |xstring| ,x)))
+  `(method-call |console| "log" (call |xstring| ,x)))
 
 
 ;;; Storage vectors. They are used to implement arrays and (in the
 (define-builtin concatenate-storage-vector (sv1 sv2)
   `(selfcall
      (var (sv1 ,sv1))
-     (var (r (call (get sv1 "concat") ,sv2)))
+     (var (r (method-call sv1 "concat" ,sv2)))
      (= (get r "type") (get sv1 "type"))
      (= (get r "stringp") (get sv1 "stringp"))
      (return r)))
 
 (define-builtin get-internal-real-time ()
-  `(call (get (new (call |Date|)) "getTime")))
+  `(method-call (new (call |Date|)) "getTime"))
 
 (define-builtin values-array (array)
   (if *multiple-value-p*
-      `(call (get |values| "apply") this ,array)
-      `(call (get |pv| "apply") this ,array)))
+      `(method-call |values| "apply" this ,array)
+      `(method-call |pv| "apply" this ,array)))
 
 (define-raw-builtin values (&rest args)
   (if *multiple-value-p*
       (error "Bad function designator `~S'" function))
     (cond
       ((translate-function function)
-       `(call ,(make-symbol (translate-function function)) ,@arglist))
+       `(call ,(translate-function function) ,@arglist))
       ((and (symbolp function)
             #+jscl (eq (symbol-package function) (find-package "COMMON-LISP"))
             #-jscl t)
-       `(call (get ,(convert `',function) "fvalue") ,@arglist))
+       `(method-call ,(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))
+       `(call |js_to_lisp|
+              (call ,(reduce (lambda (obj p)
+                               `(property ,obj (call |xstring| ,p)))
+                             (mapcar #'convert (cdr function)))
+                    ,@(mapcar (lambda (s)
+                                `(call |lisp_to_js| ,s))
+                              args))))
       (t
        (error "Bad function descriptor")))))
 
            (return ,(convert (car (last sexps)) *multiple-value-p*)))
         `(progn ,@(mapcar #'convert sexps)))))
 
-(defun convert* (sexp &optional multiple-value-p)
+(defun convert (sexp &optional multiple-value-p)
   (multiple-value-bind (sexp expandedp) (!macroexpand-1 sexp)
     (when expandedp
-      (return-from convert* (convert 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
          (let ((b (lookup-in-lexenv sexp *environment* 'variable)))
            (cond
              ((and b (not (member 'special (binding-declarations b))))
-              (make-symbol (binding-value b)))
+              (binding-value b))
              ((or (keywordp sexp)
                   (and b (member 'constant (binding-declarations b))))
               `(get ,(convert `',sexp) "value"))
         (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)