Substantially modified the way Parenscript compilation and
[clinton/parenscript.git] / src / utils.lisp
CommitLineData
cf4cbdbd 1(in-package "PARENSCRIPT")
8e198a08 2
c4ad06ac
VS
3(defun string-join (strings separator)
4 (format nil "~{~}" (format nil "~~a~~^~a" separator) strings))
8e198a08 5
e0f0d152 6(defun string-split (string separators &key (keep-separators nil) (remove-empty-subseqs nil))
8e198a08
MB
7 (do ((len (length string))
8 (i 0 (1+ i))
9 (last 0)
10 res)
11 ((= i len)
e0f0d152 12 (let ((split (if (> i last)
b508414b
TC
13 (cons (subseq string last i) res)
14 res)))
e0f0d152
VS
15 (nreverse (if remove-empty-subseqs
16 (delete "" split :test #'string-equal)
17 split))))
8e198a08
MB
18 (when (member (char string i) separators)
19 (push (subseq string last i) res)
e0f0d152 20 (when keep-separators (push (string (char string i)) res))
c67704f3
AL
21 (setf last (1+ i)))))
22
cc4f1551
RD
23(defparameter *special-chars*
24 '((#\! . "Bang")
25 (#\? . "What")
26 (#\# . "Hash")
27 (#\@ . "At")
28 (#\% . "Percent")
29 (#\+ . "Plus")
30 (#\* . "Star")
93e99720 31 (#\/ . "Slash")
4508ae85
VS
32 (#\= . "Equals")
33 (#\: . "Colon")))
c67704f3 34
cc4f1551
RD
35;;; Parenscript-style symbol -> Javascript-style symbol
36
5ffb1eba
VS
37(defun special-symbol-delimiter? (char)
38 (or (eql char #\+) (eql char #\*)))
39
40(defun special-symbol-name? (string)
41 (nth-value 1 (cl-ppcre:scan-to-strings "[\\*|\\+](.*)[\\*|\\+]([0-9]*)" string)))
cc4f1551
RD
42
43(defun first-uppercase-p (string)
44 (and (> (length string) 1)
5ffb1eba 45 (special-symbol-delimiter? (char string 0))))
cc4f1551
RD
46
47(defun untouchable-string-p (string)
48 (and (> (length string) 1)
49 (char= #\: (char string 0))))
50
dd4442b8 51(defun symbol-name-to-js-string (symbol)
c4ad06ac
VS
52 "Given a Lisp symbol or string, produces to a valid JavaScript
53identifier by following transformation heuristics case conversion. For
54example, paren-script becomes parenScript, *some-global* becomes
55SOMEGLOBAL."
5ffb1eba
VS
56 (let ((sym-name (string symbol))
57 res
58 (do-not-touch nil)
59 (lowercase t)
60 (all-uppercase nil))
61 (when (and (not (eq symbol '[]))
62 (find-if (lambda (x) (member x '(#\. #\[ #\]))) sym-name))
63 (warn "Symbol ~A contains one of '.[]' - this compound naming convention is no longer supported by Parenscript!"
64 symbol))
65 (cond ((special-symbol-name? sym-name)
66 (setf all-uppercase t
67 sym-name (let ((parts (special-symbol-name? sym-name)))
68 (concatenate 'string (aref parts 0) (aref parts 1)))))
69 ((first-uppercase-p sym-name)
70 (setf lowercase nil
71 sym-name (subseq sym-name 1)))
72 ((untouchable-string-p sym-name)
73 (setf do-not-touch t
74 sym-name (subseq sym-name 1))))
75 (flet ((reschar (c)
76 (push (cond (do-not-touch c)
77 ((and lowercase (not all-uppercase)) (char-downcase c))
78 (t (char-upcase c)))
79 res)
80 (setf lowercase t)))
81 (dotimes (i (length sym-name))
82 (let ((c (char sym-name i)))
83 (cond ((eql c #\-)
84 (setf lowercase (not lowercase)))
85 ((assoc c *special-chars*)
86 (dolist (i (coerce (cdr (assoc c *special-chars*)) 'list))
87 (reschar i)))
88 (t (reschar c))))))
89 (coerce (nreverse res) 'string)))
46f794a4 90
45c9f9c2 91(defun ordered-set-difference (list1 list2 &key (test #'eql)) ; because the CL set-difference may not preserve order
cdf9ab0e 92 (reduce (lambda (list el) (remove el list :test test))
45c9f9c2 93 (cons list1 list2)))
cf4cbdbd 94
618fb209
VS
95(defmacro once-only ((&rest names) &body body) ;; the version from PCL
96 (let ((gensyms (loop for nil in names collect (gensym))))
97 `(let (,@(loop for g in gensyms collect `(,g (gensym))))
98 `(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n)))
99 ,(let (,@(loop for n in names for g in gensyms collect `(,n ,g)))
100 ,@body)))))