X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=ecmalisp.lisp;h=d8806141993b254c4e1f0070e8e3d34d91dbc051;hb=2d5ef7b50ce3a117e48eaa5bf04e6735a746b3a8;hp=17dd213314d996a354fc048ff49e88d36a696926;hpb=a258adba32ebbf9bec1646eace200558ea80ade0;p=jscl.git diff --git a/ecmalisp.lisp b/ecmalisp.lisp index 17dd213..d880614 100644 --- a/ecmalisp.lisp +++ b/ecmalisp.lisp @@ -23,23 +23,20 @@ ;;; language to the compiler to be able to run. #+ecmalisp -(js-eval "function pv (x) { return x; }") -#+ecmalisp -(js-eval "var values = pv;") - -#+ecmalisp (progn (eval-when-compile (%compile-defmacro 'defmacro - '(lambda (name args &rest body) - `(eval-when-compile - (%compile-defmacro ',name - '(lambda ,(mapcar (lambda (x) - (if (eq x '&body) - '&rest - x)) - args) - ,@body)))))) + '(function + (lambda (name args &rest body) + `(eval-when-compile + (%compile-defmacro ',name + '(function + (lambda ,(mapcar #'(lambda (x) + (if (eq x '&body) + '&rest + x)) + args) + ,@body)))))))) (defmacro declaim (&rest decls) `(eval-when-compile @@ -47,8 +44,12 @@ (declaim (constant nil t) (special t nil)) (setq nil 'nil) + (js-vset "nil" nil) (setq t 't) + (defmacro lambda (args &body body) + `(function (lambda ,args ,@body))) + (defmacro when (condition &body body) `(if ,condition (progn ,@body) nil)) @@ -76,7 +77,6 @@ (defmacro defun (name args &rest body) `(progn - (declaim (non-overridable ,name)) (fset ',name (named-lambda ,(symbol-name name) ,args ,@(if (and (stringp (car body)) (not (null (cdr body)))) @@ -683,7 +683,15 @@ (values-array (list-to-vector list))) (defun values (&rest args) - (values-list args))) + (values-list args)) + + (defmacro multiple-value-bind (variables value-from &body body) + `(multiple-value-call (lambda (&optional ,@variables &rest ,(gensym)) + ,@body) + ,value-from)) + + (defmacro multiple-value-list (value-from) + `(multiple-value-call #'list ,value-from))) ;;; Like CONCAT, but prefix each line with four spaces. Two versions @@ -984,9 +992,14 @@ ;;; too. The respective real functions are defined in the target (see ;;; the beginning of this file) as well as some primitive functions. +;;; 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 +;;; `*multiple-value-p*' is NIL, is granted that only the primary +;;; value will be used, so we can optimize to avoid the VALUES +;;; function call. (defvar *multiple-value-p* nil) -(defvar *compilation-unit-checks* '()) (defun make-binding (name type value &optional declarations) (list name type value declarations)) @@ -1060,7 +1073,8 @@ (defun %compile-defmacro (name lambda) (toplevel-compilation (ls-compile `',name)) - (push-to-lexenv (make-binding name 'macro lambda) *environment* 'function)) + (push-to-lexenv (make-binding name 'macro lambda) *environment* 'function) + name) (defun global-binding (name type namespace) (or (lookup-in-lexenv name *environment* namespace) @@ -1085,11 +1099,7 @@ (constant (dolist (name (cdr decl)) (let ((b (global-binding name 'variable 'variable))) - (push-binding-declaration 'constant b)))) - (non-overridable - (dolist (name (cdr decl)) - (let ((b (global-binding name 'function 'function))) - (push-binding-declaration 'non-overridable b)))))) + (push-binding-declaration 'constant b)))))) #+ecmalisp (fset 'proclaim #'!proclaim) @@ -1107,8 +1117,8 @@ (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)) @@ -1133,7 +1143,6 @@ (error "Bad lambda-list")) (car rest))) - (defun lambda-docstring-wrapper (docstring &rest strs) (if docstring (js!selfcall @@ -1142,8 +1151,26 @@ "return func;" *newline*) (join strs))) - -(define-compilation lambda (lambda-list &rest body) +(defun lambda-check-argument-count + (n-required-arguments n-optional-arguments rest-p) + ;; Note: Remember that we assume that the number of arguments of a + ;; call is at least 1 (the values argument). + (let ((min (1+ n-required-arguments)) + (max (if rest-p 'n/a (+ 1 n-required-arguments n-optional-arguments)))) + (block nil + ;; Special case: a positive exact number of arguments. + (when (and (< 1 min) (eql min max)) + (return (concat "checkArgs(arguments, " (integer-to-string min) ");" *newline*))) + ;; General case: + (concat + (if (< 1 min) + (concat "checkArgsAtLeast(arguments, " (integer-to-string min) ");" *newline*) + "") + (if (numberp max) + (concat "checkArgsAtMost(arguments, " (integer-to-string max) ");" *newline*) + ""))))) + +(defun compile-lambda (lambda-list 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)) @@ -1167,17 +1194,11 @@ (append required-arguments optional-arguments))) ",") "){" *newline* - ;; Check number of arguments (indent - (if 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 (+ 1 n-required-arguments n-optional-arguments)) - ") throw 'too many arguments';" *newline*) - "") + ;; Check number of arguments + (lambda-check-argument-count n-required-arguments + n-optional-arguments + rest-argument) ;; Optional arguments (if optional-arguments (concat "switch(arguments.length-1){" *newline* @@ -1212,7 +1233,7 @@ *newline*)) "") ;; Body - (ls-compile-block body t)) *newline* + (let ((*multiple-value-p* t)) (ls-compile-block body t))) "})")))) @@ -1285,13 +1306,19 @@ (toplevel-compilation (concat "var " v " = " s)) v))) ((consp sexp) - (let ((c (concat "{car: " (literal (car sexp) t) ", " - "cdr: " (literal (cdr sexp) t) "}"))) + (let* ((head (butlast sexp)) + (tail (last sexp)) + (c (concat "QIList(" + (join-trailing (mapcar (lambda (x) (literal x t)) head) ",") + (literal (car tail) t) + "," + (literal (cdr tail) t) + ")"))) (if recursive c (let ((v (genlit))) - (toplevel-compilation (concat "var " v " = " c)) - v)))) + (toplevel-compilation (concat "var " v " = " c)) + v)))) ((arrayp sexp) (let ((elements (vector-to-list sexp))) (let ((c (concat "[" (join (mapcar #'literal elements) ", ") "]"))) @@ -1314,20 +1341,26 @@ (define-compilation function (x) (cond ((and (listp x) (eq (car x) 'lambda)) - (ls-compile x)) + (compile-lambda (cadr x) (cddr x))) ((symbolp x) (ls-compile `(symbol-function ',x))))) +(defvar *compiling-file* nil) (define-compilation eval-when-compile (&rest body) - (eval (cons 'progn body)) - nil) + (if *compiling-file* + (progn + (eval (cons 'progn body)) + nil) + (ls-compile `(progn ,@body)))) (defmacro define-transformation (name args form) `(define-compilation ,name ,args (ls-compile ,form))) (define-compilation progn (&rest body) - (js!selfcall (ls-compile-block body t))) + (if (null (cdr body)) + (ls-compile (car body) *multiple-value-p*) + (js!selfcall (ls-compile-block body t)))) (defun special-variable-p (x) (and (claimp x 'variable 'special) t)) @@ -1418,7 +1451,6 @@ store)) "}" *newline*))) - (define-compilation let* (bindings &rest body) (let ((bindings (mapcar #'ensure-list bindings)) (*environment* (copy-lexenv *environment*))) @@ -1432,53 +1464,68 @@ (defvar *block-counter* 0) (define-compilation block (name &rest body) - (let ((tr (integer-to-string (incf *block-counter*)))) - (let ((b (make-binding name 'block tr))) - (js!selfcall - "try {" *newline* - (let ((*environment* (extend-lexenv (list b) *environment* 'block))) - (indent "return " (ls-compile `(progn ,@body)) ";" *newline*)) - "}" *newline* - "catch (cf){" *newline* - " if (cf.type == 'block' && cf.id == " tr ")" *newline* - " return cf.value;" *newline* - " else" *newline* - " throw cf;" *newline* - "}" *newline*)))) + (let* ((tr (integer-to-string (incf *block-counter*))) + (b (make-binding name 'block tr))) + (when *multiple-value-p* + (push-binding-declaration 'multiple-value b)) + (let* ((*environment* (extend-lexenv (list b) *environment* 'block)) + (cbody (ls-compile-block body t))) + (if (member 'used (binding-declarations b)) + (js!selfcall + "try {" *newline* + (indent cbody) + "}" *newline* + "catch (cf){" *newline* + " if (cf.type == 'block' && cf.id == " tr ")" *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))))) (define-compilation return-from (name &optional value) - (let ((b (lookup-in-lexenv name *environment* 'block))) - (if b - (js!selfcall - "throw ({" - "type: 'block', " - "id: " (binding-value b) ", " - "value: " (ls-compile value) ", " - "message: 'Return from unknown block " (symbol-name name) ".'" - "})") - (error (concat "Unknown block `" (symbol-name name) "'."))))) - + (let* ((b (lookup-in-lexenv name *environment* 'block)) + (multiple-value-p (member 'multiple-value (binding-declarations b)))) + (when (null b) + (error (concat "Unknown block `" (symbol-name name) "'."))) + (push-binding-declaration 'used b) + (js!selfcall + (if multiple-value-p + (concat "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) ".'" + "})"))) (define-compilation catch (id &rest body) (js!selfcall "var id = " (ls-compile id) ";" *newline* "try {" *newline* - (indent "return " (ls-compile `(progn ,@body)) - ";" *newline*) + (indent (ls-compile-block body t)) *newline* "}" *newline* "catch (cf){" *newline* " if (cf.type == 'catch' && cf.id == id)" *newline* - " return cf.value;" *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*)) (define-compilation throw (id value) (js!selfcall + "var values = mv;" *newline* "throw ({" "type: 'catch', " "id: " (ls-compile id) ", " - "value: " (ls-compile value) ", " + "values: " (ls-compile value t) ", " "message: 'Throw uncatched.'" "})")) @@ -1565,18 +1612,27 @@ "return ret;" *newline*)) (define-compilation multiple-value-call (func-form &rest forms) - (let ((func (ls-compile func-form))) + (js!selfcall + "var func = " (ls-compile func-form) ";" *newline* + "var args = [" (if *multiple-value-p* "values" "pv") "];" *newline* + "return " (js!selfcall - "var args = [values];" *newline* - "function values(){" *newline* - (indent "var result = [];" *newline* - "for (var i=0; i >= and append apply aref arrayp aset assoc atom block boundp boundp butlast caar cadddr caddr cadr car car case catch cdar cdddr cddr cdr cdr char char-code char= code-char cond cons consp copy-list - decf declaim defparameter defun defmacro defvar digit-char-p disassemble - documentation dolist dotimes ecase eq eql equal error eval every - export fdefinition find-package find-symbol first fourth fset funcall - function functionp gensym get-universal-time go identity if in-package - incf integerp integerp intern keywordp lambda last length let let* - list-all-packages list listp make-array make-package make-symbol - mapcar member minusp mod multiple-value-call nil not nth nthcdr null - numberp or package-name package-use-list packagep plusp prin1-to-string - print proclaim prog1 prog2 progn psetq push quote remove remove-if - remove-if-not return return-from revappend reverse second set setq - some string-upcase string string= stringp subseq symbol-function - symbol-name symbol-package symbol-plist symbol-value symbolp t tagbody - third throw truncate unless unwind-protect values values-list variable - warn when write-line write-string zerop)) + decf declaim defparameter defun defmacro defvar digit-char-p + disassemble documentation dolist dotimes ecase eq eql equal error eval + every export fdefinition find-package find-symbol first fourth fset + funcall function functionp gensym get-universal-time go identity if + in-package incf integerp integerp intern keywordp lambda last length + let let* list-all-packages list listp make-array make-package + make-symbol mapcar member minusp mod multiple-value-bind + multiple-value-call multiple-value-list multiple-value-prog1 nil not + nth nthcdr null numberp or package-name package-use-list packagep + plusp prin1-to-string print proclaim prog1 prog2 progn psetq push + quote remove remove-if remove-if-not return return-from revappend + reverse second set setq some string-upcase string string= stringp + subseq symbol-function symbol-name symbol-package symbol-plist + symbol-value symbolp t tagbody third throw truncate unless + unwind-protect values values-list variable warn when write-line + write-string zerop)) (setq *package* *user-package*) @@ -2083,9 +2146,9 @@ (js-vset "lisp.read" #'ls-read-from-string) (js-vset "lisp.print" #'prin1-to-string) (js-vset "lisp.eval" #'eval) - (js-vset "lisp.compile" #'ls-compile-toplevel) + (js-vset "lisp.compile" (lambda (s) (ls-compile-toplevel s t))) (js-vset "lisp.evalString" (lambda (str) (eval (ls-read-from-string str)))) - (js-vset "lisp.compileString" (lambda (str) (ls-compile-toplevel (ls-read-from-string str)))) + (js-vset "lisp.compileString" (lambda (str) (ls-compile-toplevel (ls-read-from-string str) t))) ;; Set the initial global environment to be equal to the host global ;; environment at this point of the compilation. @@ -2119,19 +2182,17 @@ seq))) (defun ls-compile-file (filename output) - (setq *compilation-unit-checks* nil) - (with-open-file (out output :direction :output :if-exists :supersede) - (let* ((source (read-whole-file filename)) - (in (make-string-stream source))) - (loop - for x = (ls-read in) - until (eq x *eof*) - for compilation = (ls-compile-toplevel x) - when (plusp (length compilation)) - do (write-string compilation out)) - (dolist (check *compilation-unit-checks*) - (funcall check)) - (setq *compilation-unit-checks* nil)))) + (let ((*compiling-file* t)) + (with-open-file (out output :direction :output :if-exists :supersede) + (write-string (read-whole-file "prelude.js") out) + (let* ((source (read-whole-file filename)) + (in (make-string-stream source))) + (loop + for x = (ls-read in) + until (eq x *eof*) + for compilation = (ls-compile-toplevel x) + when (plusp (length compilation)) + do (write-string compilation out)))))) (defun bootstrap () (setq *environment* (make-lexenv))