Escape Javascript strings
authorDavid Vázquez <davazp@gmail.com>
Wed, 19 Jun 2013 04:08:23 +0000 (06:08 +0200)
committerDavid Vázquez <davazp@gmail.com>
Wed, 19 Jun 2013 04:08:23 +0000 (06:08 +0200)
experimental/codegen.lisp

index ea52dfd..0fa2c5c 100644 (file)
       x
       (list x)))
 
+(defun concat (&rest strs)
+    (apply #'concatenate 'string strs))
+
+(defmacro while (condition &body body)
+  `(do ()
+       ((not ,condition))
+     ,@body))
+
 (defvar *js-output* t)
 
+;;; Two seperate functions are needed for escaping strings:
+;;;  One for producing JavaScript string literals (which are singly or
+;;;   doubly quoted)
+;;;  And one for producing Lisp strings (which are only doubly quoted)
+;;;
+;;; The same function would suffice for both, but for javascript string
+;;; literals it is neater to use either depending on the context, e.g:
+;;;  foo's => "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 js-format (fmt &rest args)
   (apply #'format *js-output* fmt args))
 
@@ -44,7 +99,7 @@
     ((numberp form)
      (js-format "~a" form))
     ((stringp form)
-     (js-format "'~a'" form))
+     (js-format "~a" (js-escape-string form)))
     ((symbolp form)
      (case form
        (true  (js-format "true"))