Do not inline builtin funcionts out of a lambda
[jscl.git] / ecmalisp.lisp
index 577619b..02aa16e 100644 (file)
@@ -23,6 +23,9 @@
 ;;; language to the compiler to be able to run.
 
 #+ecmalisp
+(js-eval "function pv (x) { return typeof x === 'object' && 'car' in x ? x.car : x; }")
+
+#+ecmalisp
 (progn
   (eval-when-compile
     (%compile-defmacro 'defmacro
     (revappend list '()))
 
   (defmacro psetq (&rest pairs)
-    (let (;; For each pair, we store here a list of the form
+    (let ( ;; For each pair, we store here a list of the form
          ;; (VARIABLE GENSYM VALUE).
          (assignments '()))
       (while t
         (oset exports (symbol-name symb) symb))))
 
   (defun get-universal-time ()
-    (+ (get-unix-time) 2208988800)))
+    (+ (get-unix-time) 2208988800))
+
+  (defun values-list (list)
+    (values-list list))
+
+  (defun values (&rest args)
+    (values-list args)))
 
 
 ;;; The compiler offers some primitives and special forms which are
            (symbol-name form)
            (let ((package (symbol-package form))
                  (name (symbol-name form)))
-             (concat (if (eq package (find-package "KEYWORD"))
-                         ""
-                         (package-name package))
+             (concat (cond
+                       ((null package) "#")
+                       ((eq package (find-package "KEYWORD")) "")
+                       (t (package-name package)))
                      ":" name))))
       ((integerp form) (integer-to-string form))
       ((stringp form) (concat "\"" (escape-string form) "\""))
     (#\'
      (list 'function (ls-read stream)))
     (#\( (list-to-vector (%read-list stream)))
+    (#\: (make-symbol (string-upcase (read-until stream #'terminalp))))
     (#\\
      (let ((cname
             (concat (string (%read-char stream))
         "return func;" *newline*)
       (join strs)))
 
+
+(defvar *compiling-lambda-p* nil)
+
 (define-compilation lambda (lambda-list &rest body)
   (let ((required-arguments (lambda-list-required-arguments lambda-list))
         (optional-arguments (lambda-list-optional-arguments lambda-list))
         (rest-argument (lambda-list-rest-argument lambda-list))
+        (*compiling-lambda-p* t)
         documentation)
     ;; Get the documentation string for the lambda function
     (when (and (stringp (car body))
       (lambda-docstring-wrapper
        documentation
        "(function ("
-       (join (mapcar #'translate-variable
-                     (append required-arguments optional-arguments))
+       (join (cons "values"
+                   (mapcar #'translate-variable
+                           (append required-arguments optional-arguments)))
              ",")
        "){" *newline*
        ;; Check number of arguments
        (indent
         (if required-arguments
-            (concat "if (arguments.length < " (integer-to-string n-required-arguments)
+            (concat "if (arguments.length < " (integer-to-string (1+ n-required-arguments))
                     ") throw 'too few arguments';" *newline*)
             "")
         (if (not rest-argument)
             (concat "if (arguments.length > "
-                    (integer-to-string (+ n-required-arguments n-optional-arguments))
+                    (integer-to-string (+ 1 n-required-arguments n-optional-arguments))
                     ") throw 'too many arguments';" *newline*)
             "")
         ;; Optional arguments
         (if optional-arguments
-            (concat "switch(arguments.length){" *newline*
+            (concat "switch(arguments.length-1){" *newline*
                     (let ((optional-and-defaults
                            (lambda-list-optional-arguments-with-default lambda-list))
                           (cases nil)
             (let ((js!rest (translate-variable rest-argument)))
               (concat "var " js!rest "= " (ls-compile nil) ";" *newline*
                       "for (var i = arguments.length-1; i>="
-                      (integer-to-string (+ n-required-arguments n-optional-arguments))
+                      (integer-to-string (+ 1 n-required-arguments n-optional-arguments))
                       "; i--)" *newline*
                       (indent js!rest " = "
                               "{car: arguments[i], cdr: ") js!rest "};"
   (concat "(" var " = " (ls-compile val) ")"))
 
 
+
 ;;; Literals
 (defun escape-string (string)
   (let ((output "")
      (or (cdr (assoc sexp *literal-symbols*))
         (let ((v (genlit))
               (s #+common-lisp (concat "{name: \"" (escape-string (symbol-name sexp)) "\"}")
-                 #+ecmalisp (ls-compile
-                              `(intern ,(symbol-name sexp)
-                                       ,(package-name (symbol-package sexp))))))
+                 #+ecmalisp
+                  (let ((package (symbol-package sexp)))
+                    (if (null package)
+                        (concat "{name: \"" (escape-string (symbol-name sexp)) "\"}")
+                        (ls-compile `(intern ,(symbol-name sexp) ,(package-name package)))))))
           (push (cons sexp v) *literal-symbols*)
           (toplevel-compilation (concat "var " v " = " s))
           v)))
   (js!selfcall (ls-compile-block body t)))
 
 (defun special-variable-p (x)
-  (claimp x 'variable 'special))
+  (and (claimp x 'variable 'special) t))
 
 ;;; Wrap CODE to restore the symbol values of the dynamic
 ;;; bindings. BINDINGS is a list of pairs of the form
           "})" *newline*)
         (error (concat "Unknown tag `" n "'.")))))
 
-
 (define-compilation unwind-protect (form &rest clean-up)
   (js!selfcall
     "var ret = " (ls-compile nil) ";" *newline*
     "}" *newline*
     "return ret;" *newline*))
 
+(define-compilation multiple-value-call (func-form &rest forms)
+  (let ((func (ls-compile func-form)))
+    (js!selfcall
+      "var args = [values];" *newline*
+      "function values(){" *newline*
+      (indent "var result = [];" *newline*
+              "for (var i=0; i<arguments.length; i++)" *newline*
+              (indent "result.push(arguments[i]);"))
+      "}" *newline*
+      (mapconcat (lambda (form)
+                   (ls-compile form))
+                 forms)
+      "return (" func ").apply(window, [args]);")))
+
+
 
 ;;; A little backquote implementation without optimizations of any
 ;;; kind for ecmalisp.
   (concat "(" symbol ").value = " value))
 
 (define-builtin fset (symbol value)
-  (concat "(" symbol ").function = " value))
+  (concat "(" symbol ").fvalue = " value))
 
 (define-builtin boundp (x)
   (js!bool (concat "(" x ".value !== undefined)")))
 (define-builtin symbol-function (x)
   (js!selfcall
     "var symbol = " x ";" *newline*
-    "var func = symbol.function;" *newline*
+    "var func = symbol.fvalue;" *newline*
     "if (func === undefined) throw \"Function `\" + symbol.name + \"' is undefined.\";" *newline*
     "return func;" *newline*))
 
 
 (define-raw-builtin funcall (func &rest args)
   (concat "(" (ls-compile func) ")("
-          (join (mapcar #'ls-compile args)
+          (join (cons "pv" (mapcar #'ls-compile args))
                 ", ")
           ")"))
 
             (last (car (last args))))
         (js!selfcall
           "var f = " (ls-compile func) ";" *newline*
-          "var args = [" (join (mapcar #'ls-compile args)
+          "var args = [" (join (cons "pv" (mapcar #'ls-compile args))
                                ", ")
           "];" *newline*
           "var tail = (" (ls-compile last) ");" *newline*
 (define-builtin get-unix-time ()
   (concat "(Math.round(new Date() / 1000))"))
 
+(define-builtin values-list (list)
+  (concat "values(" list ")"))
 
 (defun macro (x)
   (and (symbolp x)
 (defun compile-funcall (function args)
   (if (and (symbolp function)
            (claimp function 'function 'non-overridable))
-      (concat (ls-compile `',function) ".function("
-              (join (mapcar #'ls-compile args)
+      (concat (ls-compile `',function) ".fvalue("
+              (join (cons "pv" (mapcar #'ls-compile args))
                     ", ")
               ")")
       (concat (ls-compile `#',function) "("
-              (join (mapcar #'ls-compile args)
+              (join (cons "pv" (mapcar #'ls-compile args))
                     ", ")
               ")")))
 
             (apply comp args)))
          ;; Built-in functions
          ((and (assoc name *builtins*)
-               (not (claimp name 'function 'notinline)))
+               (not (claimp name 'function 'notinline))
+               *compiling-lambda-p*)
           (let ((comp (second (assoc name *builtins*))))
             (apply comp args)))
          (t