X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;ds=inline;f=src%2Fcompiler.lisp;h=ef66b1fe9f94a706132810a0b4b6ebc841b907d8;hb=f9319021c1f4d35b7ee223bab96ffbe587f049b6;hp=1d1ea5548170ec3b80b630621b63e405afae6018;hpb=dd2867ca3b53cb2353c9f1dbf85c3ebeb9667ee2;p=jscl.git diff --git a/src/compiler.lisp b/src/compiler.lisp index 1d1ea55..ef66b1f 100644 --- a/src/compiler.lisp +++ b/src/compiler.lisp @@ -207,7 +207,7 @@ `(push (list ',name (lambda ,args (block ,name ,@body))) *compilations*)) -(define-compilation if (condition true false) +(define-compilation if (condition true &optional false) (code "(" (ls-compile condition) " !== " (ls-compile nil) " ? " (ls-compile true *multiple-value-p*) " : " (ls-compile false *multiple-value-p*) @@ -266,9 +266,9 @@ (js!selfcall "var func = " (join strs) ";" *newline* (when name - (code "func.fname = \"" (escape-string name) "\";" *newline*)) + (code "func.fname = " (js-escape-string name) ";" *newline*)) (when docstring - (code "func.docstring = \"" (escape-string docstring) "\";" *newline*)) + (code "func.docstring = " (js-escape-string docstring) ";" *newline*)) "return func;" *newline*) (apply #'code strs))) @@ -368,8 +368,11 @@ (mapconcat #'parse-keyword keyword-arguments)))) ;; Check for unknown keywords (when keyword-arguments - (code "for (i=" (+ n-required-arguments n-optional-arguments) - "; i "foo's" +;;; "foo" => '"foo"' +;;; which avoids having to escape quotes where possible +(defun js-escape-string (string) + (let ((index 0) + (size (length string)) + (seen-single-quote nil) + (seen-double-quote nil)) + (flet ((%js-escape-string (string escape-single-quote-p) + (let ((output "") + (index 0)) + (while (< index size) + (let ((ch (char string index))) + (when (char= ch #\\) + (setq output (concat output "\\"))) + (when (and escape-single-quote-p (char= ch #\')) + (setq output (concat output "\\"))) + (when (char= ch #\newline) + (setq output (concat output "\\")) + (setq ch #\n)) + (setq output (concat output (string ch)))) + (incf index)) + output))) + ;; First, scan the string for single/double quotes + (while (< index size) + (let ((ch (char string index))) + (when (char= ch #\') + (setq seen-single-quote t)) + (when (char= ch #\") + (setq seen-double-quote t))) + (incf index)) + ;; Then pick the appropriate way to escape the quotes + (cond + ((not seen-single-quote) + (concat "'" (%js-escape-string string nil) "'")) + ((not seen-double-quote) + (concat "\"" (%js-escape-string string nil) "\"")) + (t (concat "'" (%js-escape-string string t) "'")))))) + +(defun lisp-escape-string (string) (let ((output "") (index 0) (size (length string))) @@ -496,7 +548,7 @@ (setq ch #\n)) (setq output (concat output (string ch)))) (incf index)) - output)) + (concat "\"" output "\""))) ;;; BOOTSTRAP MAGIC: We record the macro definitions as lists during ;;; the bootstrap. Once everything is compiled, we want to dump the @@ -550,13 +602,13 @@ (concat "[" (join (mapcar #'literal elements) ", ") "]"))) (defun dump-string (string) - (code "make_lisp_string(\"" (escape-string string) "\")")) + (code "make_lisp_string(" (js-escape-string string) ")")) (defun literal (sexp &optional recursive) (cond ((integerp sexp) (integer-to-string sexp)) ((floatp sexp) (float-to-string sexp)) - ((characterp sexp) (code "\"" (escape-string (string sexp)) "\"")) + ((characterp sexp) (js-escape-string (string sexp))) (t (or (cdr (assoc sexp *literal-table* :test #'eql)) (let ((dumped (typecase sexp @@ -679,6 +731,19 @@ ",") ")"))) +(define-compilation macrolet (definitions &rest body) + (let ((*environment* (copy-lexenv *environment*))) + (dolist (def definitions) + (destructuring-bind (name lambda-list &body body) def + (let ((binding (make-binding :name name :type 'macro :value + (let ((g!form (gensym))) + `(lambda (,g!form) + (destructuring-bind ,lambda-list ,g!form + ,@body)))))) + (push-to-lexenv binding *environment* 'function)))) + (ls-compile `(progn ,@body) *multiple-value-p*))) + + (defun special-variable-p (x) (and (claimp x 'variable 'special) t)) @@ -1309,25 +1374,31 @@ (define-builtin new () "{}") -(define-builtin oget* (object key) - (js!selfcall - "var tmp = " "(" object ")[xstring(" key ")];" *newline* - "return tmp == undefined? " (ls-compile nil) ": tmp ;" *newline*)) - -(define-builtin oset* (object key value) - (code "((" object ")[xstring(" key ")] = " value ")")) - -(define-raw-builtin oget (object key &rest keys) +(define-raw-builtin oget* (object key &rest keys) (js!selfcall "var tmp = (" (ls-compile object) ")[xstring(" (ls-compile key) ")];" *newline* (mapconcat (lambda (key) - (code "if (tmp === undefined) return " (ls-compile nil) ";" *newline*) - (code "tmp = tmp[xstring(" (ls-compile key) ")];" *newline*)) + (code "if (tmp === undefined) return " (ls-compile nil) ";" *newline* + "tmp = tmp[xstring(" (ls-compile key) ")];" *newline*)) keys) - "return tmp === undefined? " (ls-compile nil) " : js_to_lisp(tmp);" *newline*)) + "return tmp === undefined? " (ls-compile nil) " : tmp;" *newline*)) + +(define-raw-builtin oset* (value object key &rest keys) + (let ((keys (cons key keys))) + (js!selfcall + "var obj = " (ls-compile object) ";" *newline* + (mapconcat (lambda (key) + (code "obj = obj[xstring(" (ls-compile key) ")];" + "if (obj === undefined) throw 'Impossible to set Javascript property.';" *newline*)) + (butlast keys)) + "var tmp = obj[xstring(" (ls-compile (car (last keys))) ")] = " (ls-compile value) ";" *newline* + "return tmp === undefined? " (ls-compile nil) " : tmp;" *newline*))) + +(define-raw-builtin oget (object key &rest keys) + (code "js_to_lisp(" (ls-compile `(oget* ,object ,key ,@keys)) ")")) -(define-builtin oset (object key value) - (code "((" object ")[xstring(" key ")] = lisp_to_js(" value "))")) +(define-raw-builtin oset (value object key &rest keys) + (ls-compile `(oset* (lisp-to-js ,value) ,object ,key ,@keys))) (define-builtin objectp (x) (js!bool (code "(typeof (" x ") === 'object')"))) @@ -1426,8 +1497,7 @@ #+jscl (eq (symbol-package function) (find-package "COMMON-LISP")) #-jscl t) (code (ls-compile `',function) ".fvalue" arglist)) - #+jscl - ((symbolp function) + #+jscl((symbolp function) (code (ls-compile `#',function) arglist)) ((and (consp function) (eq (car function) 'lambda)) (code (ls-compile `#',function) arglist))