X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fcompiler.lisp;h=ef66b1fe9f94a706132810a0b4b6ebc841b907d8;hb=f9319021c1f4d35b7ee223bab96ffbe587f049b6;hp=02a3d43113a1b81a3e39778c7f9f76303425c5a4;hpb=2cb3364352641d3f4e6bd0a68907b915d69c0f6e;p=jscl.git diff --git a/src/compiler.lisp b/src/compiler.lisp index 02a3d43..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)) @@ -1313,8 +1378,8 @@ (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) " : tmp;" *newline*)) @@ -1323,10 +1388,11 @@ (js!selfcall "var obj = " (ls-compile object) ";" *newline* (mapconcat (lambda (key) - "obj = obj[xstring(" (ls-compile key) ")];" - "if (obj === undefined) throw 'Impossible to set Javascript property.';" *newline*) + (code "obj = obj[xstring(" (ls-compile key) ")];" + "if (obj === undefined) throw 'Impossible to set Javascript property.';" *newline*)) (butlast keys)) - "obj[xstring(" (ls-compile (car (last keys))) ")] = " (ls-compile value) ";" *newline*))) + "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)) ")"))