X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=ecmalisp.lisp;h=80a5479aec96c3b479b9012073ffef875bb8324d;hb=295b18fb108eec92698b6b3629e159c26ab91f3c;hp=fa5dcc5f983122a9a68b7afbd887842481027578;hpb=5a05b2053cf76ea53701c11072073994dbd31de8;p=jscl.git diff --git a/ecmalisp.lisp b/ecmalisp.lisp index fa5dcc5..80a5479 100644 --- a/ecmalisp.lisp +++ b/ecmalisp.lisp @@ -21,52 +21,48 @@ ;;; as well as funcalls and macroexpansion, but no functions. So, we ;;; define the Lisp world from scratch. This code has to define enough ;;; language to the compiler to be able to run. + #+ecmalisp (progn + + 'defmacro (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)))))) - - (defmacro %defvar (name value) + `(progn + (eval-when-compile + (%compile-defmacro ',name + '(lambda ,(mapcar (lambda (x) + (if (eq x '&body) + '&rest + x)) + args) + ,@body))) + ',name)))) + + (defmacro defvar (name value) `(progn - (eval-when-compile - (%compile-defvar ',name)) - (setq ,name ,value))) - - (defmacro defvar (name &optional value) - `(%defvar ,name ,value)) + (setq ,name ,value) + ',name)) - (defmacro named-lambda (name args &rest body) + (defmacro named-lambda (name args &body body) (let ((x (gensym "FN"))) `(let ((,x (lambda ,args ,@body))) (oset ,x "fname" ,name) ,x))) - (defmacro %defun (name args &rest body) + (defmacro defun (name args &body body) `(progn (eval-when-compile (%compile-defun ',name)) (fsetq ,name (named-lambda ,(symbol-name name) ,args - (block ,name ,@body))))) - - (defmacro defun (name args &rest body) - `(%defun ,name ,args ,@body)) + (block ,name ,@body))) + ',name)) (defvar *package* (new)) - (defvar nil (make-symbol "NIL")) - (oset *package* "NIL" nil) - - (defvar t (make-symbol "T")) - (oset *package* "T" t) + (defvar nil 'nil) + (defvar t 't) (defun null (x) (eq x nil)) @@ -226,12 +222,10 @@ ,value))) (defmacro prog2 (form1 result &body body) - `(prog1 (progn ,form1 ,result) ,@body)) + `(prog1 (progn ,form1 ,result) ,@body))) -) - ;;; This couple of helper functions will be defined in both Common ;;; Lisp and in Ecmalisp. (defun ensure-list (x) @@ -251,16 +245,6 @@ ;;; constructions. #+ecmalisp (progn - (defmacro defun (name args &body body) - `(progn - (%defun ,name ,args ,@body) - ',name)) - - (defmacro defvar (name &optional value) - `(progn - (%defvar ,name ,value) - ',name)) - (defun append-two (list1 list2) (if (null list1) list2 @@ -489,6 +473,8 @@ "" (concat (car list) separator (join-trailing (cdr list) separator)))) +(defun mapconcat (func list) + (join (mapcar func list))) ;;; Like CONCAT, but prefix each line with four spaces. Two versions ;;; of this function are available, because the Ecmalisp version is @@ -580,6 +566,10 @@ (write-string *newline*) x) + (defun warn (string) + (write-string "WARNING: ") + (write-line string)) + (defun print (x) (write-line (prin1-to-string x)) x)) @@ -788,21 +778,8 @@ (defun gvarname (symbol) (concat "v" (integer-to-string (incf *variable-counter*)))) -(defun lookup-variable (symbol env) - (or (lookup-in-lexenv symbol env 'variable) - (lookup-in-lexenv symbol *environment* 'variable) - (let ((name (symbol-name symbol)) - (binding (make-binding symbol 'special-variable (gvarname symbol) nil))) - (push-to-lexenv binding *environment* 'variable) - (push (lambda () - (let ((b (lookup-in-lexenv symbol *environment* 'variable))) - (unless (binding-declared b) - (error (concat "Undefined variable `" name "'"))))) - *compilation-unit-checks*) - binding))) - -(defun lookup-variable-translation (symbol env) - (binding-translation (lookup-variable symbol env))) +(defun translate-variable (symbol env) + (binding-translation (lookup-in-lexenv symbol env 'variable))) (defun extend-local-env (args env) (let ((new (copy-lexenv env))) @@ -831,17 +808,22 @@ (defun lookup-function-translation (symbol env) (binding-translation (lookup-function symbol env))) +;;; Toplevel compilations (defvar *toplevel-compilations* nil) -(defun %compile-defvar (name) - (let ((b (lookup-variable name *environment*))) - (mark-binding-as-declared b) - (push (concat "var " (binding-translation b)) *toplevel-compilations*))) +(defun toplevel-compilation (string) + (push string *toplevel-compilations*)) + +(defun null-or-empty-p (x) + (zerop (length x))) + +(defun get-toplevel-compilations () + (reverse (remove-if #'null-or-empty-p *toplevel-compilations*))) (defun %compile-defun (name) (let ((b (lookup-function name *environment*))) (mark-binding-as-declared b) - (push (concat "var " (binding-translation b)) *toplevel-compilations*))) + (toplevel-compilation (concat "var " (binding-translation b))))) (defun %compile-defmacro (name lambda) (push-to-lexenv (make-binding name 'macro lambda t) *environment* 'function)) @@ -850,10 +832,7 @@ (defun ls-compile-block (sexps env) (join-trailing - (remove-if (lambda (x) - (or (null x) - (and (stringp x) - (zerop (length x))))) + (remove-if #'null (mapcar (lambda (x) (ls-compile x env)) sexps)) (concat ";" *newline*))) @@ -909,7 +888,7 @@ env))) (concat "(function (" (join (mapcar (lambda (x) - (lookup-variable-translation x new-env)) + (translate-variable x new-env)) (append required-arguments optional-arguments)) ",") "){" *newline* @@ -936,7 +915,7 @@ (let ((arg (nth idx optional-and-defaults))) (push (concat "case " (integer-to-string (+ idx n-required-arguments)) ":" *newline* - (lookup-variable-translation (car arg) new-env) + (translate-variable (car arg) new-env) "=" (ls-compile (cadr arg) new-env) ";" *newline*) @@ -948,7 +927,7 @@ "") ;; &rest/&body argument (if rest-argument - (let ((js!rest (lookup-variable-translation rest-argument new-env))) + (let ((js!rest (translate-variable rest-argument new-env))) (concat "var " js!rest "= " (ls-compile nil) ";" *newline* "for (var i = arguments.length-1; i>=" (integer-to-string (+ n-required-arguments n-optional-arguments)) @@ -968,10 +947,17 @@ (ls-compile val env))) (define-compilation setq (var val) - (let ((b (lookup-variable var env))) - (ecase (binding-type b) - (lexical-variable (concat (binding-translation b) " = " (ls-compile val env))) - (special-variable (ls-compile `(set ',var ,val) env))))) + (let ((b (lookup-in-lexenv var env 'variable))) + (if (eq (binding-type b) 'lexical-variable) + (concat (binding-translation b) " = " (ls-compile val env)) + (ls-compile `(set ',var ,val) env)))) + +;;; FFI Variable accessors +(define-compilation js-vref (var) + var) + +(define-compilation js-vset (var val) + (concat "(" var " = " (ls-compile val env) ")")) ;;; Literals @@ -990,25 +976,43 @@ (incf index)) output)) -(defun literal->js (sexp) + +(defvar *literal-symbols* nil) +(defvar *literal-counter* 0) + +(defun genlit () + (concat "l" (integer-to-string (incf *literal-counter*)))) + +(defun literal (sexp &optional recursive) (cond ((integerp sexp) (integer-to-string sexp)) ((stringp sexp) (concat "\"" (escape-string sexp) "\"")) - ((symbolp sexp) (ls-compile `(intern ,(escape-string (symbol-name sexp))) *environment*)) - ((consp sexp) (concat "{car: " - (literal->js (car sexp)) - ", cdr: " - (literal->js (cdr sexp)) "}")))) - -(defvar *literal-counter* 0) -(defun literal (form) - (let ((var (concat "l" (integer-to-string (incf *literal-counter*))))) - (push (concat "var " var " = " (literal->js form)) *toplevel-compilations*) - var)) + ((symbolp sexp) + #+common-lisp + (or (cdr (assoc sexp *literal-symbols*)) + (let ((v (genlit)) + (s (concat "{name: \"" (escape-string (symbol-name sexp)) "\"}"))) + (push (cons sexp v) *literal-symbols*) + (toplevel-compilation (concat "var " v " = " s)) + v)) + #+ecmalisp + (let ((v (genlit)) + (s (ls-compile `(intern ,(symbol-name sexp))))) + (toplevel-compilation (concat "var " v " = " s)) + v)) + ((consp sexp) + (let ((c (concat "{car: " (literal (car sexp) t) ", " + "cdr: " (literal (cdr sexp) t) "}"))) + (if recursive + c + (let ((v (genlit))) + (toplevel-compilation (concat "var " v " = " c)) + v)))))) (define-compilation quote (sexp) (literal sexp)) + (define-compilation %while (pred &rest body) (js!selfcall "while(" (ls-compile pred env) " !== " (ls-compile nil) "){" *newline* @@ -1025,7 +1029,7 @@ (define-compilation eval-when-compile (&rest body) (eval (cons 'progn body)) - "") + nil) (defmacro define-transformation (name args form) `(define-compilation ,name ,args @@ -1043,7 +1047,7 @@ (let ((new-env (extend-local-env variables env))) (concat "(function(" (join (mapcar (lambda (x) - (lookup-variable-translation x new-env)) + (translate-variable x new-env)) variables) ",") "){" *newline* @@ -1277,7 +1281,9 @@ (type-check (("x" "number" x)) "Math.floor(x)")) -(define-builtin cons (x y) (concat "({car: " x ", cdr: " y "})")) +(define-builtin cons (x y) + (concat "({car: " x ", cdr: " y "})")) + (define-builtin consp (x) (js!bool (js!selfcall @@ -1323,7 +1329,11 @@ (concat "(" symbol ").value =" value)) (define-builtin symbol-value (x) - (concat "(" x ").value")) + (js!selfcall + "var symbol = " x ";" *newline* + "var value = symbol.value;" *newline* + "if (value === undefined) throw \"Variable `\" + symbol.name + \"' is unbound.\";" *newline* + "return value;" *newline*)) (define-builtin symbol-function (x) (concat "(" x ").function")) @@ -1450,12 +1460,10 @@ (defun ls-compile (sexp &optional (env (make-lexenv))) (cond ((symbolp sexp) - (let ((b (lookup-variable sexp env))) - (ecase (binding-type b) - (lexical-variable - (lookup-variable-translation sexp env)) - (special-variable - (ls-compile `(symbol-value ',sexp) env))))) + (let ((b (lookup-in-lexenv sexp env 'variable))) + (if (eq (binding-type b) 'lexical-variable) + (binding-translation b) + (ls-compile `(symbol-value ',sexp) env)))) ((integerp sexp) (integer-to-string sexp)) ((stringp sexp) (concat "\"" (escape-string sexp) "\"")) ((listp sexp) @@ -1468,12 +1476,18 @@ (defun ls-compile-toplevel (sexp) (setq *toplevel-compilations* nil) - (let ((code (ls-compile sexp))) - (prog1 - (concat (join (mapcar (lambda (x) (concat x ";" *newline*)) - *toplevel-compilations*)) - code) - (setq *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))) + (prog1 + (concat (join-trailing (get-toplevel-compilations) (concat ";" *newline*)) + (if code + (concat code ";" *newline*) + "")) + (setq *toplevel-compilations* nil)))))) ;;; Once we have the compiler, we define the runtime environment and @@ -1497,32 +1511,34 @@ (ls-compile-toplevel x)))) (js-eval code))) + (js-eval "var lisp") + (js-vset "lisp" (new)) + (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.evalString" (lambda (str) (eval (ls-read-from-string str)))) + (js-vset "lisp.compileString" (lambda (str) (ls-compile-toplevel (ls-read-from-string str)))) + ;; Set the initial global environment to be equal to the host global ;; environment at this point of the compilation. (eval-when-compile - (let ((tmp (ls-compile - `(progn - (setq *environment* ',*environment*) - (setq *variable-counter* ',*variable-counter*) - (setq *function-counter* ',*function-counter*) - (setq *literal-counter* ',*literal-counter*) - (setq *gensym-counter* ',*gensym-counter*) - (setq *block-counter* ',*block-counter*))))) - (setq *toplevel-compilations* - (append *toplevel-compilations* (list tmp))))) - - (js-eval - (concat "var lisp = {};" - "lisp.read = " (lookup-function-translation 'ls-read-from-string nil) ";" *newline* - "lisp.print = " (lookup-function-translation 'prin1-to-string nil) ";" *newline* - "lisp.eval = " (lookup-function-translation 'eval nil) ";" *newline* - "lisp.compile = " (lookup-function-translation 'ls-compile-toplevel nil) ";" *newline* - "lisp.evalString = function(str){" *newline* - " return lisp.eval(lisp.read(str));" *newline* - "}" *newline* - "lisp.compileString = function(str){" *newline* - " return lisp.compile(lisp.read(str));" *newline* - "}" *newline*))) + (toplevel-compilation + (ls-compile + `(progn + ,@(mapcar (lambda (s) + `(oset *package* ,(symbol-name (car s)) + (js-vref ,(cdr s)))) + *literal-symbols*) + (setq *environment* ',*environment*) + (setq *variable-counter* ,*variable-counter*) + (setq *function-counter* ,*function-counter*) + (setq *gensym-counter* ,*gensym-counter*) + (setq *block-counter* ,*block-counter*))))) + + (eval-when-compile + (toplevel-compilation + (ls-compile `(setq *literal-counter* ,*literal-counter*))))) ;;; Finally, we provide a couple of functions to easily bootstrap @@ -1546,13 +1562,14 @@ until (eq x *eof*) for compilation = (ls-compile-toplevel x) when (plusp (length compilation)) - do (write-line (concat compilation "; ") out)) + do (write-string compilation out)) (dolist (check *compilation-unit-checks*) (funcall check)) (setq *compilation-unit-checks* nil)))) (defun bootstrap () (setq *environment* (make-lexenv)) + (setq *literal-symbols* nil) (setq *variable-counter* 0 *gensym-counter* 0 *function-counter* 0