X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=ecmalisp.lisp;h=eea0584a8d32d316296ef7a645639eda971ad573;hb=f008590b4b2dbf551c2ce9ee4c173219161e289b;hp=d6a5aceb3e7895d5cc0c7f21a1b61dc94dc3ee5f;hpb=12fe1f382f11debc4ff374e8cb07703e1d88b6c0;p=jscl.git diff --git a/ecmalisp.lisp b/ecmalisp.lisp index d6a5ace..eea0584 100644 --- a/ecmalisp.lisp +++ b/ecmalisp.lisp @@ -21,6 +21,7 @@ ;;; 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 (eval-when-compile @@ -28,7 +29,7 @@ '(lambda (name args &rest body) `(eval-when-compile (%compile-defmacro ',name - '(lambda ,(mapcar (lambda (x) + '(lambda ,(mapcar (lambda (x) (if (eq x '&body) '&rest x)) @@ -47,7 +48,7 @@ (defmacro named-lambda (name args &rest body) (let ((x (gensym "FN"))) `(let ((,x (lambda ,args ,@body))) - (set ,x "fname" ,name) + (oset ,x "fname" ,name) ,x))) (defmacro %defun (name args &rest body) @@ -63,10 +64,10 @@ (defvar *package* (new)) (defvar nil (make-symbol "NIL")) - (set *package* "NIL" nil) + (oset *package* "NIL" nil) (defvar t (make-symbol "T")) - (set *package* "T" t) + (oset *package* "T" t) (defun null (x) (eq x nil)) @@ -82,11 +83,11 @@ (defun intern (name) (if (internp name) - (get *package* name) - (set *package* name (make-symbol name)))) + (oget *package* name) + (oset *package* name (make-symbol name)))) (defun find-symbol (name) - (get *package* name)) + (oget *package* name)) (defvar *gensym-counter* 0) (defun gensym (&optional (prefix "G")) @@ -153,7 +154,7 @@ (,var nil)) (%while ,g!list (setq ,var (car ,g!list)) - ,@body + (tagbody ,@body) (setq ,g!list (cdr ,g!list))) ,(third iter))))) @@ -166,7 +167,7 @@ (let ((,var 0) (,g!to ,to)) (%while (< ,var ,g!to) - ,@body + (tagbody ,@body) (incf ,var)) ,result)))) @@ -223,7 +224,14 @@ (let ((value (gensym))) `(let ((,value ,form)) ,@body - ,value)))) + ,value))) + + (defmacro prog2 (form1 result &body body) + `(prog1 (progn ,form1 ,result) ,@body)) + + + +) ;;; This couple of helper functions will be defined in both Common ;;; Lisp and in Ecmalisp. @@ -263,13 +271,14 @@ (defun append (&rest lists) (!reduce #'append-two lists '())) - (defun reverse-aux (list acc) - (if (null list) - acc - (reverse-aux (cdr list) (cons (car list) acc)))) + (defun revappend (list1 list2) + (while list1 + (push (car list1) list2) + (setq list1 (cdr list1))) + list2) (defun reverse (list) - (reverse-aux list '())) + (revappend list '())) (defun list-length (list) (let ((l 0)) @@ -310,29 +319,29 @@ (defun listp (x) (or (consp x) (null x))) + (defun nthcdr (n list) + (while (and (plusp n) list) + (setq n (1- n)) + (setq list (cdr list))) + list) + (defun nth (n list) - (cond - ((null list) list) - ((zerop n) (car list)) - (t (nth (1- n) (cdr list))))) + (car (nthcdr n list))) (defun last (x) - (if (consp (cdr x)) - (last (cdr x)) - x)) + (while (consp (cdr x)) + (setq x (cdr x))) + x) (defun butlast (x) (and (consp (cdr x)) (cons (car x) (butlast (cdr x))))) (defun member (x list) - (cond - ((null list) - nil) - ((eql x (car list)) - list) - (t - (member x (cdr list))))) + (while list + (when (eql x (car list)) + (return list)) + (setq list (cdr list)))) (defun remove (x list) (cond @@ -464,6 +473,9 @@ (defmacro concatf (variable &body form) `(setq ,variable (concat ,variable (progn ,@form)))) +(defun mapconcat (func list) + (join (mapcar func list))) + ;;; Concatenate a list of strings, with a separator (defun join (list &optional (separator "")) (cond @@ -481,25 +493,40 @@ "" (concat (car list) separator (join-trailing (cdr list) separator)))) -;;; Like CONCAT, but prefix each line with four spaces. + +;;; Like CONCAT, 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. + +#+ecmalisp (defun indent (&rest string) (let ((input (join string))) (let ((output "") (index 0) (size (length input))) - (when (plusp size) - (setq output " ")) + (when (plusp (length input)) (concatf output " ")) (while (< index size) - (setq output - (concat output - (if (and (char= (char input index) #\newline) - (< index (1- size)) - (not (char= (char input (1+ index)) #\newline))) - (concat (string #\newline) " ") - (subseq input index (1+ index))))) + (let ((str + (if (and (char= (char input index) #\newline) + (< index (1- size)) + (not (char= (char input (1+ index)) #\newline))) + (concat (string #\newline) " ") + (string (char input index))))) + (concatf output str)) (incf index)) output))) +#+common-lisp +(defun indent (&rest string) + (with-output-to-string (*standard-output*) + (with-input-from-string (input (join string)) + (loop + for line = (read-line input nil) + while line + do (write-string " ") + do (write-line line))))) + + (defun integer-to-string (x) (cond ((zerop x) @@ -515,8 +542,18 @@ digits)))))) -(defun js!selfcall (&rest args) - (concat "(function(){" *newline* (apply #'indent args) "})()")) +;;; Wrap X with a Javascript code to convert the result from +;;; Javascript generalized booleans to T or NIL. +(defun js!bool (x) + (concat "(" x "?" (ls-compile t) ": " (ls-compile nil) ")")) + +;;; Concatenate the arguments and wrap them with a self-calling +;;; Javascript anonymous function. It is used to make some Javascript +;;; statements valid expressions and provide a private scope as well. +;;; It could be defined as function, but we could do some +;;; preprocessing in the future. +(defmacro js!selfcall (&body body) + `(concat "(function(){" *newline* (indent ,@body) "})()")) ;;; Printer @@ -529,7 +566,7 @@ ((integerp form) (integer-to-string form)) ((stringp form) (concat "\"" (escape-string form) "\"")) ((functionp form) - (let ((name (get form "fname"))) + (let ((name (oget form "fname"))) (if name (concat "#") (concat "#")))) @@ -745,9 +782,10 @@ (defvar *environment* (make-lexenv)) (defun clear-undeclared-global-bindings () - (let ((variables (first *environment*)) - (functions (second *environment*))) - (setq *environment* (list variables functions (third *environment*))))) + (setq *environment* + (mapcar (lambda (namespace) + (remove-if-not #'binding-declared namespace)) + *environment*))) (defvar *variable-counter* 0) @@ -758,11 +796,12 @@ (or (lookup-in-lexenv symbol env 'variable) (lookup-in-lexenv symbol *environment* 'variable) (let ((name (symbol-name symbol)) - (binding (make-binding symbol 'variable (gvarname symbol) nil))) + (binding (make-binding symbol 'special-variable (gvarname symbol) nil))) (push-to-lexenv binding *environment* 'variable) (push (lambda () - (unless (lookup-in-lexenv symbol *environment* 'variable) - (error (concat "Undefined variable `" name "'")))) + (let ((b (lookup-in-lexenv symbol *environment* 'variable))) + (unless (binding-declared b) + (error (concat "Undefined variable `" name "'"))))) *compilation-unit-checks*) binding))) @@ -772,7 +811,7 @@ (defun extend-local-env (args env) (let ((new (copy-lexenv env))) (dolist (symbol args new) - (let ((b (make-binding symbol 'variable (gvarname symbol) t))) + (let ((b (make-binding symbol 'lexical-variable (gvarname symbol) t))) (push-to-lexenv b new 'variable))))) (defvar *function-counter* 0) @@ -787,8 +826,9 @@ nil))) (push-to-lexenv binding *environment* 'function) (push (lambda () - (unless (binding-declared (lookup-in-lexenv symbol *environment* 'function)) - (error (concat "Undefined function `" name "'")))) + (let ((b (lookup-in-lexenv symbol *environment* 'function))) + (unless (binding-declared b) + (error (concat "Undefined function `" name "'"))))) *compilation-unit-checks*) binding))) @@ -932,9 +972,17 @@ (ls-compile val env))) (define-compilation setq (var val) - (concat (lookup-variable-translation var env) - " = " - (ls-compile val env))) + (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))))) + +;;; FFI Variable accessors +(define-compilation js-vref (var) + var) +(define-compilation js-vset (var val) + (concat "(" var " = " (ls-compile val env) ")")) + ;;; Literals (defun escape-string (string) @@ -952,32 +1000,51 @@ (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) + (or (cdr (assoc sexp *literal-symbols*)) + (let ((v (genlit)) + (s (concat "{name: \"" (escape-string (symbol-name sexp)) "\"}"))) + (push (cons sexp v) *literal-symbols*) + (push (concat "var " v " = " s) *toplevel-compilations*) + v))) + ((consp sexp) + (let ((c (concat "{car: " (literal (car sexp) t) ", " + "cdr: " (literal (cdr sexp) t) "}"))) + (if recursive + c + (let ((v (genlit))) + (push (concat "var " v " = " c) *toplevel-compilations*) + v)))))) +#+common-lisp (define-compilation quote (sexp) (literal sexp)) +#+ecmalisp +(define-compilation quote (sexp) + (let ((v (genlit))) + (push (ls-compile `(js-vset ,v ,sexp) env) + *toplevel-compilations*) + v)) + + (define-compilation %while (pred &rest body) - (concat "(function(){" *newline* - (indent "while(" (ls-compile pred env) " !== " (ls-compile nil) "){" *newline* - (indent (ls-compile-block body env)) - "}" - "return " (ls-compile nil) ";" *newline*) - "})()")) + (js!selfcall + "while(" (ls-compile pred env) " !== " (ls-compile nil) "){" *newline* + (indent (ls-compile-block body env)) + "}" + "return " (ls-compile nil) ";" *newline*)) (define-compilation function (x) (cond @@ -995,10 +1062,9 @@ (ls-compile ,form env))) (define-compilation progn (&rest body) - (concat "(function(){" *newline* - (indent (ls-compile-block (butlast body) env) - "return " (ls-compile (car (last body)) env) ";" *newline*) - "})()")) + (js!selfcall + (ls-compile-block (butlast body) env) + "return " (ls-compile (car (last body)) env) ";" *newline*)) (define-compilation let (bindings &rest body) (let ((bindings (mapcar #'ensure-list bindings))) @@ -1025,54 +1091,54 @@ (define-compilation block (name &rest body) (let ((tr (integer-to-string (incf *block-counter*)))) (let ((b (make-binding name 'block tr t))) - (concat "(function(){" *newline* - (indent "try {" *newline* - (indent "return " (ls-compile `(progn ,@body) - (extend-lexenv (list b) env 'block)) - ";" *newline*) - "}" *newline* - "catch (cf){" *newline* - " if (cf.type == 'block' && cf.id == " tr ")" *newline* - " return cf.value;" *newline* - " else" *newline* - " throw cf;" *newline* - "}" *newline*) - "})()")))) + (js!selfcall + "try {" *newline* + (indent "return " (ls-compile `(progn ,@body) + (extend-lexenv (list b) env 'block)) + ";" *newline*) + "}" *newline* + "catch (cf){" *newline* + " if (cf.type == 'block' && cf.id == " tr ")" *newline* + " return cf.value;" *newline* + " else" *newline* + " throw cf;" *newline* + "}" *newline*)))) (define-compilation return-from (name &optional value) (let ((b (lookup-in-lexenv name env 'block))) (if b - (concat "(function(){ throw ({" - "type: 'block', " - "id: " (binding-translation b) ", " - "value: " (ls-compile value env) ", " - "message: 'Return from unknown block " (symbol-name name) ".'" - "})})()") + (js!selfcall + "throw ({" + "type: 'block', " + "id: " (binding-translation b) ", " + "value: " (ls-compile value env) ", " + "message: 'Return from unknown block " (symbol-name name) ".'" + "})") (error (concat "Unknown block `" (symbol-name name) "'."))))) (define-compilation catch (id &rest body) - (concat "(function(){" *newline* - (indent "var id = " (ls-compile id env) ";" *newline* - "try {" *newline* - (indent "return " (ls-compile `(progn ,@body)) - ";" *newline*) - "}" *newline* - "catch (cf){" *newline* - " if (cf.type == 'catch' && cf.id == id)" *newline* - " return cf.value;" *newline* - " else" *newline* - " throw cf;" *newline* - "}" *newline*) - "})()")) + (js!selfcall + "var id = " (ls-compile id env) ";" *newline* + "try {" *newline* + (indent "return " (ls-compile `(progn ,@body)) + ";" *newline*) + "}" *newline* + "catch (cf){" *newline* + " if (cf.type == 'catch' && cf.id == id)" *newline* + " return cf.value;" *newline* + " else" *newline* + " throw cf;" *newline* + "}" *newline*)) (define-compilation throw (id &optional value) - (concat "(function(){ throw ({" - "type: 'catch', " - "id: " (ls-compile id env) ", " - "value: " (ls-compile value env) ", " - "message: 'Throw uncatched.'" - "})})()")) + (js!selfcall + "throw ({" + "type: 'catch', " + "id: " (ls-compile id env) ", " + "value: " (ls-compile value env) ", " + "message: 'Throw uncatched.'" + "})")) (defvar *tagbody-counter* 0) @@ -1138,25 +1204,24 @@ ((integerp label) (integer-to-string label))))) (if b (js!selfcall - (concat "throw ({" - "type: 'tagbody', " - "id: " (first (binding-translation b)) ", " - "label: " (second (binding-translation b)) ", " - "message: 'Attempt to GO to non-existing tag " n "'" - "})" *newline*)) + "throw ({" + "type: 'tagbody', " + "id: " (first (binding-translation b)) ", " + "label: " (second (binding-translation b)) ", " + "message: 'Attempt to GO to non-existing tag " n "'" + "})" *newline*) (error (concat "Unknown tag `" n "'."))))) (define-compilation unwind-protect (form &rest clean-up) - (concat "(function(){" *newline* - (indent "var ret = " (ls-compile nil) ";" *newline* - "try {" *newline* - (indent "ret = " (ls-compile form env) ";" *newline*) - "} finally {" *newline* - (indent (ls-compile-block clean-up env)) - "}" *newline* - "return ret;" *newline*) - "})()")) + (js!selfcall + "var ret = " (ls-compile nil) ";" *newline* + "try {" *newline* + (indent "ret = " (ls-compile form env) ";" *newline*) + "} finally {" *newline* + (indent (ls-compile-block clean-up env)) + "}" *newline* + "return ret;" *newline*)) ;;; A little backquote implementation without optimizations of any @@ -1201,27 +1266,22 @@ (let ,(mapcar (lambda (arg) `(,arg (ls-compile ,arg env))) args) ,@body))) -(defun compile-bool (x) - (concat "(" x "?" (ls-compile t) ": " (ls-compile nil) ")")) - ;;; DECLS is a list of (JSVARNAME TYPE LISPFORM) declarations. (defmacro type-check (decls &body body) - `(concat "(function(){" *newline* - (indent ,@(mapcar (lambda (decl) - `(concat "var " ,(first decl) " = " ,(third decl) ";" *newline*)) - decls) - - ,@(mapcar (lambda (decl) - `(concat "if (typeof " ,(first decl) " != '" ,(second decl) "')" *newline* - (indent "throw 'The value ' + " - ,(first decl) - " + ' is not a type " - ,(second decl) - ".';" - *newline*))) - decls) - (concat "return " (progn ,@body) ";" *newline*)) - "})()")) + `(js!selfcall + ,@(mapcar (lambda (decl) + `(concat "var " ,(first decl) " = " ,(third decl) ";" *newline*)) + decls) + ,@(mapcar (lambda (decl) + `(concat "if (typeof " ,(first decl) " != '" ,(second decl) "')" *newline* + (indent "throw 'The value ' + " + ,(first decl) + " + ' is not a type " + ,(second decl) + ".';" + *newline*))) + decls) + (concat "return " (progn ,@body) ";" *newline*))) (defun num-op-num (x op y) (type-check (("x" "number" x) ("y" "number" y)) @@ -1234,14 +1294,14 @@ (define-builtin mod (x y) (num-op-num x "%" y)) -(define-builtin < (x y) (compile-bool (num-op-num x "<" y))) -(define-builtin > (x y) (compile-bool (num-op-num x ">" y))) -(define-builtin = (x y) (compile-bool (num-op-num x "==" y))) -(define-builtin <= (x y) (compile-bool (num-op-num x "<=" y))) -(define-builtin >= (x y) (compile-bool (num-op-num x ">=" y))) +(define-builtin < (x y) (js!bool (num-op-num x "<" y))) +(define-builtin > (x y) (js!bool (num-op-num x ">" y))) +(define-builtin = (x y) (js!bool (num-op-num x "==" y))) +(define-builtin <= (x y) (js!bool (num-op-num x "<=" y))) +(define-builtin >= (x y) (js!bool (num-op-num x ">=" y))) (define-builtin numberp (x) - (compile-bool (concat "(typeof (" x ") == \"number\")"))) + (js!bool (concat "(typeof (" x ") == \"number\")"))) (define-builtin floor (x) (type-check (("x" "number" x)) @@ -1249,27 +1309,24 @@ (define-builtin cons (x y) (concat "({car: " x ", cdr: " y "})")) (define-builtin consp (x) - (compile-bool - (concat "(function(){" *newline* - (indent "var tmp = " x ";" *newline* - "return (typeof tmp == 'object' && 'car' in tmp);" *newline*) - "})()"))) + (js!bool + (js!selfcall + "var tmp = " x ";" *newline* + "return (typeof tmp == 'object' && 'car' in tmp);" *newline*))) (define-builtin car (x) - (concat "(function(){" *newline* - (indent "var tmp = " x ";" *newline* - "return tmp === " (ls-compile nil) - "? " (ls-compile nil) - ": tmp.car;" *newline*) - "})()")) + (js!selfcall + "var tmp = " x ";" *newline* + "return tmp === " (ls-compile nil) + "? " (ls-compile nil) + ": tmp.car;" *newline*)) (define-builtin cdr (x) - (concat "(function(){" *newline* - (indent "var tmp = " x ";" *newline* - "return tmp === " (ls-compile nil) "? " - (ls-compile nil) - ": tmp.cdr;" *newline*) - "})()")) + (js!selfcall + "var tmp = " x ";" *newline* + "return tmp === " (ls-compile nil) "? " + (ls-compile nil) + ": tmp.cdr;" *newline*)) (define-builtin setcar (x new) (type-check (("x" "object" x)) @@ -1280,11 +1337,10 @@ (concat "(x.cdr = " new ")"))) (define-builtin symbolp (x) - (compile-bool - (concat "(function(){" *newline* - (indent "var tmp = " x ";" *newline* - "return (typeof tmp == 'object' && 'name' in tmp);" *newline*) - "})()"))) + (js!bool + (js!selfcall + "var tmp = " x ";" *newline* + "return (typeof tmp == 'object' && 'name' in tmp);" *newline*))) (define-builtin make-symbol (name) (type-check (("name" "string" name)) @@ -1293,15 +1349,24 @@ (define-builtin symbol-name (x) (concat "(" x ").name")) -(define-builtin eq (x y) (compile-bool (concat "(" x " === " y ")"))) -(define-builtin equal (x y) (compile-bool (concat "(" x " == " y ")"))) +(define-builtin set (symbol value) + (concat "(" symbol ").value =" value)) + +(define-builtin symbol-value (x) + (concat "(" x ").value")) + +(define-builtin symbol-function (x) + (concat "(" x ").function")) + +(define-builtin eq (x y) (js!bool (concat "(" x " === " y ")"))) +(define-builtin equal (x y) (js!bool (concat "(" x " == " y ")"))) (define-builtin string (x) (type-check (("x" "number" x)) "String.fromCharCode(x)")) (define-builtin stringp (x) - (compile-bool (concat "(typeof(" x ") == \"string\")"))) + (js!bool (concat "(typeof(" x ") == \"string\")"))) (define-builtin string-upcase (x) (type-check (("x" "string" x)) @@ -1312,15 +1377,14 @@ "x.length")) (define-compilation slice (string a &optional b) - (concat "(function(){" *newline* - (indent "var str = " (ls-compile string env) ";" *newline* - "var a = " (ls-compile a env) ";" *newline* - "var b;" *newline* - (if b - (concat "b = " (ls-compile b env) ";" *newline*) - "") - "return str.slice(a,b);" *newline*) - "})()")) + (js!selfcall + "var str = " (ls-compile string env) ";" *newline* + "var a = " (ls-compile a env) ";" *newline* + "var b;" *newline* + (if b + (concat "b = " (ls-compile b env) ";" *newline*) + "") + "return str.slice(a,b);" *newline*)) (define-builtin char (string index) (type-check (("string" "string" string) @@ -1345,44 +1409,42 @@ (concat "(" (ls-compile func env) ")()") (let ((args (butlast args)) (last (car (last args)))) - (concat "(function(){" *newline* - (indent "var f = " (ls-compile func env) ";" *newline* - "var args = [" (join (mapcar (lambda (x) - (ls-compile x env)) - args) - ", ") - "];" *newline* - "var tail = (" (ls-compile last env) ");" *newline* - (indent "while (tail != " (ls-compile nil) "){" *newline* - " args.push(tail.car);" *newline* - " tail = tail.cdr;" *newline* - "}" *newline* - "return f.apply(this, args);" *newline*) - "})()"))))) + (js!selfcall + "var f = " (ls-compile func env) ";" *newline* + "var args = [" (join (mapcar (lambda (x) + (ls-compile x env)) + args) + ", ") + "];" *newline* + "var tail = (" (ls-compile last env) ");" *newline* + "while (tail != " (ls-compile nil) "){" *newline* + " args.push(tail.car);" *newline* + " tail = tail.cdr;" *newline* + "}" *newline* + "return f.apply(this, args);" *newline*)))) (define-builtin js-eval (string) (type-check (("string" "string" string)) "eval.apply(window, [string])")) (define-builtin error (string) - (concat "(function (){ throw " string "; })()")) + (js!selfcall "throw " string ";" *newline*)) (define-builtin new () "{}") -(define-builtin get (object key) - (concat "(function(){" *newline* - (indent "var tmp = " "(" object ")[" key "];" *newline* - "return tmp == undefined? " (ls-compile nil) ": tmp ;" *newline*) - "})()")) +(define-builtin oget (object key) + (js!selfcall + "var tmp = " "(" object ")[" key "];" *newline* + "return tmp == undefined? " (ls-compile nil) ": tmp ;" *newline*)) -(define-builtin set (object key value) +(define-builtin oset (object key value) (concat "((" object ")[" key "] = " value ")")) (define-builtin in (key object) - (compile-bool (concat "((" key ") in (" object "))"))) + (js!bool (concat "((" key ") in (" object "))"))) (define-builtin functionp (x) - (compile-bool (concat "(typeof " x " == 'function')"))) + (js!bool (concat "(typeof " x " == 'function')"))) (define-builtin write-string (x) (type-check (("x" "string" x)) @@ -1417,7 +1479,13 @@ (defun ls-compile (sexp &optional (env (make-lexenv))) (cond - ((symbolp sexp) (lookup-variable-translation sexp env)) + ((symbolp sexp) + (let ((b (lookup-variable sexp env))) + (ecase (binding-type b) + (lexical-variable + (binding-translation b)) + (special-variable + (ls-compile `(symbol-value ',sexp) env))))) ((integerp sexp) (integer-to-string sexp)) ((stringp sexp) (concat "\"" (escape-string sexp) "\"")) ((listp sexp) @@ -1429,13 +1497,21 @@ (compile-funcall (car sexp) (cdr sexp) env)))))) (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-trailing + (remove-if (lambda (s) (or (null s) (equal s ""))) + subs) + (concat ";" *newline*)))) + (t + (setq *toplevel-compilations* nil) + (let ((code (ls-compile sexp))) + (prog1 + (concat (join-trailing *toplevel-compilations* + (concat ";" *newline*)) + code) + (setq *toplevel-compilations* nil)))))) ;;; Once we have the compiler, we define the runtime environment and @@ -1469,22 +1545,22 @@ (setq *function-counter* ',*function-counter*) (setq *literal-counter* ',*literal-counter*) (setq *gensym-counter* ',*gensym-counter*) - (setq *block-counter* ',*block-counter*))))) + (setq *block-counter* ',*block-counter*) + ,@(mapcar (lambda (s) + `(oset *package* ,(symbol-name (car s)) + (js-vref ,(cdr s)))) + *literal-symbols*))))) (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*))) + (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))))) ;;; Finally, we provide a couple of functions to easily bootstrap @@ -1515,6 +1591,7 @@ (defun bootstrap () (setq *environment* (make-lexenv)) + (setq *literal-symbols* nil) (setq *variable-counter* 0 *gensym-counter* 0 *function-counter* 0