initial
[rrq/newlisp/humancss.git] / humancss.lsp
1 #!/usr/local/bin/newlisp
2 #
3 # Stream filter that reads css and writes it out stylished. run with
4 #
5 # newlisp humancss.lsp < bad.css > good.css
6 #
7 # Not actually pretty-printing, but merely adding indentation and newlines.
8
9 ;(signal 2 (fn (x) (exit 0)))
10
11 (setf IN '())
12
13 ;; Load the CSS file as an array of single-character strings
14 (while (setf LINE (read-line)) (extend IN (explode LINE) '("\n")))
15 (setf IN (array (length IN) IN)) ; This should speed of indexed access
16 (setf LAST (- (length IN) 1))
17
18 ;; Coalsce comments and strings into units
19 (define (coalesce START END) ; exclusive
20   (when (< END (length IN))
21     (setf (IN START) (join (array-list (START (- END START) IN))))
22     (while (< (inc START) END) (setf (IN START) ""))))
23
24 (define (coalesce-block-comment i)
25   (let ((STAR nil) (END nil))
26     (for (j (+ 2 i) LAST 1 END)
27       (if STAR (if (= "/" (IN j)) (setf END j) (setf STAR nil))
28         (= "*" (IN j)) (setf STAR true)))
29     (when END (coalesce i (+ 1 END)))))
30
31 (define (coalesce-line-comment i)
32   (let ((END (find "\n" IN nil i)))
33     (when END (coalesce i (+ 1 END)))))
34     
35 (define (index-of-any OPTS START)
36   (if (> START LAST) nil
37     (if (find OPTS (START IN) (fn (X Y) (member Y X))) (+ START $it))))
38
39 (define (coalesce-string i) ; (IN i) is the string character
40   (let ((END nil))
41     (for (j (+ 1 i) LAST 1 END)
42       (if (= "\\" (IN j)) (coalesce j (+ 2 j))
43         (= (IN i) (IN j)) (setf END (+ 1 j))))
44     (when END (coalesce i END))))
45
46 ; Coalesce comments, meta-quotes and strings
47 (let ((SLASH nil))
48   (for (i 0 LAST)
49     (if (= "\\" (IN i)) (begin (coalesce i (+ 2 i)) (setf SLASH nil))
50       SLASH (begin (case (IN i)
51                      ("*" (coalesce-block-comment (- i 1)))
52                      ("/" (coalesce-line-comment (- i 1)))
53                      (true nil))
54                    (setf SLASH nil))
55       (= "/" (IN i)) (setf SLASH true)
56       (= "\"" (IN i)) (coalesce-string i)
57       (= "'" (IN i)) (coalesce-string i)
58       )))
59
60 (define (indent TXT n)
61   (join (clean empty? (parse TXT "\n")) (string "\n" (dup "  " n))))
62
63 ; Coalesce blocks recursively, adding a newline to it
64 (define (coalesce-block i (DEPTH 0))
65   ;(write-line 2 (string "block level " DEPTH " from " i))
66   (let ((j 0) (END nil))
67     (for (j (+ 1 i) LAST 1 END)
68       (case (IN j)
69         ("{" (coalesce-block j (+ 1 DEPTH)))
70         ("}" (setf END j))
71         (true nil)))
72     (when END
73       (setf (IN i) " {\n")
74       (coalesce i END)
75       (setf (IN i) (indent (IN i) DEPTH))
76       (extend (IN i) (if (ends-with (IN i) "\n") "}\n" "\n}\n"))
77       (setf (IN END) "")
78       )))
79
80 (for (i 0 LAST) (when (= ";" (IN i)) (setf (IN i) ";\n")))
81
82 (for (i 0 LAST) (when (= "{" (IN i)) (coalesce-block i 1)))
83
84 (write 1 (join (array-list IN)))
85
86 (exit 0)