#+ecmalisp
(js-eval "function pv (x) { return x; }")
+
+#+ecmalisp
+(js-eval "function mv(){ var r = []; r['multiple-value'] = true; for (var i=0; i<arguments.length; i++) r.push(arguments[i]); return r; }")
+
+;;; NOTE: Define VALUES to be MV for toplevel forms. It is because
+;;; `eval' compiles the forms and execute the Javascript code at
+;;; toplevel with `js-eval', so it is necessary to return multiple
+;;; values from the eval function.
#+ecmalisp
-(js-eval "var values = pv;")
+(js-eval "var values = mv;")
#+ecmalisp
(progn
;;; too. The respective real functions are defined in the target (see
;;; the beginning of this file) as well as some primitive functions.
+;;; If the special variable `*multiple-value-p*' is NON-NIL, then the
+;;; compilation of the current form is allowed to return multiple
+;;; values, using the VALUES variable.
(defvar *multiple-value-p* nil)
(defvar *compilation-unit-checks* '())
(define-compilation if (condition true false)
(concat "(" (ls-compile condition) " !== " (ls-compile nil)
- " ? " (ls-compile true)
- " : " (ls-compile false)
+ " ? " (ls-compile true *multiple-value-p*)
+ " : " (ls-compile false *multiple-value-p*)
")"))
(defvar *lambda-list-keywords* '(&optional &rest))
(error "Bad lambda-list"))
(car rest)))
-
(defun lambda-docstring-wrapper (docstring &rest strs)
(if docstring
(js!selfcall
"return func;" *newline*)
(join strs)))
-
(define-compilation lambda (lambda-list &rest body)
(let ((required-arguments (lambda-list-required-arguments lambda-list))
(optional-arguments (lambda-list-optional-arguments lambda-list))
*newline*))
"")
;; Body
- (ls-compile-block body t)) *newline*
+ (let ((*multiple-value-p* t)) (ls-compile-block body t)))
+ *newline*
"})"))))
store))
"}" *newline*)))
-
(define-compilation let* (bindings &rest body)
(let ((bindings (mapcar #'ensure-list bindings))
(*environment* (copy-lexenv *environment*)))
(js!selfcall
"try {" *newline*
(let ((*environment* (extend-lexenv (list b) *environment* 'block)))
- (indent "return " (ls-compile `(progn ,@body)) ";" *newline*))
+ (indent "return " (ls-compile `(progn ,@body) *multiple-value-p*) ";" *newline*))
"}" *newline*
"catch (cf){" *newline*
" if (cf.type == 'block' && cf.id == " tr ")" *newline*
"})")
(error (concat "Unknown block `" (symbol-name name) "'.")))))
-
(define-compilation catch (id &rest body)
(js!selfcall
"var id = " (ls-compile id) ";" *newline*
(let ((func (ls-compile func-form)))
(js!selfcall
"var args = [values];" *newline*
- "values = function(){" *newline*
- (indent "var result = [];" *newline*
- "result['multiple-value'] = true;" *newline*
- "for (var i=0; i<arguments.length; i++)" *newline*
- (indent "result.push(arguments[i]);" *newline*)
- "return result;" *newline*)
- "}" *newline*
+ "var values = mv;" *newline*
"var vs;" *newline*
(mapconcat (lambda (form)
(concat "vs = " (ls-compile form t) ";" *newline*
(define-builtin js-eval (string)
(type-check (("string" "string" string))
- "eval.apply(window, [string])"))
+ (if *multiple-value-p*
+ (js!selfcall
+ "var v = eval.apply(window, [string]);" *newline*
+ "if (typeof v !== 'object' || !('multiple-value' in v)){" *newline*
+ (indent "v = [v];" *newline*
+ "v['multiple-value'] = true;" *newline*)
+ "}" *newline*
+ "return values.apply(this, v);" *newline*)
+ "eval.apply(window, [string])")))
(define-builtin error (string)
(js!selfcall "throw " string ";" *newline*))
(concat "(Math.round(new Date() / 1000))"))
(define-builtin values-array (array)
- (concat "values.apply(this, " array ")"))
+ (if *multiple-value-p*
+ (concat "values.apply(this, " array ")")
+ (concat "pv.apply(this, " array ")")))
(define-raw-builtin values (&rest args)
- (concat "values(" (join (mapcar #'ls-compile args) ", ") ")"))
-
+ (if *multiple-value-p*
+ (concat "values(" (join (mapcar #'ls-compile args) ", ") ")")
+ (concat "pv(" (join (mapcar #'ls-compile args) ", ") ")")))
(defun macro (x)
(and (symbolp x)
(defun ls-compile-block (sexps &optional return-last-p)
(if return-last-p
(concat (ls-compile-block (butlast sexps))
- "return " (ls-compile (car (last sexps))) ";")
+ "return "(ls-compile (car (last sexps)) *multiple-value-p*) ";")
(join-trailing
(remove-if #'null-or-empty-p (mapcar #'ls-compile sexps))
(concat ";" *newline*))))
(t
(error "How should I compile this?")))))
-(defun ls-compile-toplevel (sexp)
+(defun ls-compile-toplevel (sexp &optional multiple-value-p)
(let ((*toplevel-compilations* nil))
(cond
((and (consp sexp) (eq (car sexp) 'progn))
(let ((subs (mapcar #'ls-compile-toplevel (cdr sexp))))
(join (remove-if #'null-or-empty-p subs))))
(t
- (let ((code (ls-compile sexp)))
+ (let ((code (ls-compile sexp multiple-value-p)))
(concat (join-trailing (get-toplevel-compilations)
(concat ";" *newline*))
(if code
#+ecmalisp
(progn
- (defmacro with-compilation-unit (&body body)
- `(prog1
- (progn
- (setq *compilation-unit-checks* nil)
- ,@body)
- (dolist (check *compilation-unit-checks*)
- (funcall check))))
-
(defun eval (x)
- (let ((code
- (with-compilation-unit
- (ls-compile-toplevel x))))
- (js-eval code)))
+ (js-eval (ls-compile-toplevel x t)))
(export '(&rest &optional &body * *gensym-counter* *package* + - / 1+ 1- < <= =
= > >= and append apply aref arrayp aset assoc atom block boundp