comparison src/css.sml @ 1170:52c6ac6a59f1

Basic analysis of tag and CSS class usage
author Adam Chlipala <adamc@hcoop.net>
date Sat, 27 Feb 2010 14:57:57 -0500
parents
children c316ca3c9ec6
comparison
equal deleted inserted replaced
1169:420e38516dc2 1170:52c6ac6a59f1
1 (* Copyright (c) 2010, Adam Chlipala
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * - Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * - Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * - The names of contributors may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 *)
27
28 structure Css :> CSS = struct
29
30 structure IM = IntBinaryMap
31
32 structure SM = BinaryMapFn(struct
33 type ord_key = string
34 val compare = String.compare
35 end)
36
37 datatype inheritable = Block | List | Table | Caption | Td
38 datatype others = OBlock | OTable | OTd | Tr | NonReplacedInline | ReplacedInline | Width | Height
39
40 fun inheritableToString x =
41 case x of
42 Block => "B"
43 | List => "L"
44 | Table => "T"
45 | Caption => "C"
46 | Td => "D"
47
48 fun othersToString x =
49 case x of
50 OBlock => "b"
51 | OTable => "t"
52 | OTd => "d"
53 | Tr => "-"
54 | NonReplacedInline => "N"
55 | ReplacedInline => "R"
56 | Width => "W"
57 | Height => "H"
58
59 type summary = inheritable list * others list
60
61 fun merge' (ls1, ls2) = foldl (fn (x, ls) => if List.exists (fn y => y = x) ls then ls else x :: ls) ls2 ls1
62 fun merge ((in1, ot1), (in2, ot2)) = (merge' (in1, in2), merge' (ot1, ot2))
63 fun mergePC {parent = (in1, ot1), child = in2} = (merge' (in1, in2), ot1)
64
65 val nada = ([], [])
66 val block = ([Block], [OBlock, Width, Height])
67 val inline = ([], [NonReplacedInline])
68 val list = ([Block, List], [OBlock, Width, Height])
69 val replaced = ([], [ ReplacedInline, Width, Height])
70 val table = ([Block, Table], [OBlock, OTable, Width, Height])
71 val tr = ([Block], [OBlock, Tr, Width])
72 val td = ([Block, Td], [OBlock, OTd, Height])
73
74 val tags = [("span", inline),
75 ("div", block),
76 ("p", block),
77 ("b", inline),
78 ("i", inline),
79 ("tt", inline),
80 ("h1", block),
81 ("h2", block),
82 ("h3", block),
83 ("h4", block),
84 ("h5", block),
85 ("h6", block),
86 ("li", list),
87 ("ol", list),
88 ("ul", list),
89 ("hr", block),
90 ("a", inline),
91 ("img", replaced),
92 ("form", block),
93 ("hidden", replaced),
94 ("textbox", replaced),
95 ("password", replaced),
96 ("textarea", replaced),
97 ("checkbox", replaced),
98 ("upload", replaced),
99 ("radio", replaced),
100 ("select", replaced),
101 ("submit", replaced),
102 ("label", inline),
103 ("ctextbox", replaced),
104 ("button", replaced),
105 ("ccheckbox", replaced),
106 ("cselect", replaced),
107 ("ctextarea", replaced),
108 ("tabl", table),
109 ("tr", tr),
110 ("th", td),
111 ("td", td)]
112
113 val tags = foldl (fn ((tag, css), tags) =>
114 SM.insert (tags, tag, css)) SM.empty tags
115
116 open Core
117
118 fun summarize file =
119 let
120 fun decl ((d, _), st as (globals, classes)) =
121 let
122 fun getTag (e, _) =
123 case e of
124 EFfi ("Basis", tag) => SOME tag
125 | ECApp (e, _) => getTag e
126 | EApp (e, _) => getTag e
127 | _ => NONE
128
129 fun exp ((e, _), classes) =
130 case e of
131 EPrim _ => ([], classes)
132 | ERel _ => ([], classes)
133 | ENamed n =>
134 (case IM.find (globals, n) of
135 NONE => []
136 | SOME (_, sm) => sm,
137 classes)
138 | ECon (_, _, _, NONE) => ([], classes)
139 | ECon (_, _, _, SOME e) => exp (e, classes)
140 | EFfi _ => ([], classes)
141 | EFfiApp (_, _, es) => expList (es, classes)
142
143 | EApp (
144 (EApp (
145 (EApp (
146 (EApp (
147 (ECApp (
148 (ECApp (
149 (ECApp (
150 (ECApp (
151 (ECApp (
152 (ECApp (
153 (ECApp (
154 (ECApp (
155 (EFfi ("Basis", "tag"),
156 _), _), _), _), _), _), _), _), _), _), _), _), _), _), _), _), _),
157 (ECon (_, _, _, SOME (ENamed class, _)), _)), _),
158 attrs), _),
159 tag), _),
160 xml) =>
161 let
162 val (sm, classes) = exp (xml, classes)
163 val (sm', classes) = exp (attrs, classes)
164 val sm = merge' (sm, sm')
165 in
166 case getTag tag of
167 NONE => (sm, classes)
168 | SOME tag =>
169 case SM.find (tags, tag) of
170 NONE => (sm, classes)
171 | SOME sm' =>
172 let
173 val sm'' = mergePC {parent = sm', child = sm}
174 val old = Option.getOpt (IM.find (classes, class), nada)
175 val classes = IM.insert (classes, class, merge (old, sm''))
176 in
177 (merge' (#1 sm', sm), classes)
178 end
179 end
180
181 | EApp (
182 (EApp (
183 (EApp (
184 (EApp (
185 (ECApp (
186 (ECApp (
187 (ECApp (
188 (ECApp (
189 (ECApp (
190 (ECApp (
191 (ECApp (
192 (ECApp (
193 (EFfi ("Basis", "tag"),
194 _), _), _), _), _), _), _), _), _), _), _), _), _), _), _), _), _),
195 _), _),
196 attrs), _),
197 tag), _),
198 xml) =>
199 let
200 val (sm, classes) = exp (xml, classes)
201 val (sm', classes) = exp (attrs, classes)
202 val sm = merge' (sm, sm')
203 in
204 case getTag tag of
205 NONE => (sm, classes)
206 | SOME tag =>
207 case SM.find (tags, tag) of
208 NONE => (sm, classes)
209 | SOME sm' => (merge' (#1 sm', sm), classes)
210 end
211
212 | EApp (e1, e2) =>
213 let
214 val (sm1, classes) = exp (e1, classes)
215 val (sm2, classes) = exp (e2, classes)
216 in
217 (merge' (sm1, sm2), classes)
218 end
219 | EAbs (_, _, _, e) => exp (e, classes)
220 | ECApp (e, _) => exp (e, classes)
221 | ECAbs (_, _, e) => exp (e, classes)
222 | EKAbs (_, e) => exp (e, classes)
223 | EKApp (e, _) => exp (e, classes)
224 | ERecord xets => expList (map #2 xets, classes)
225 | EField (e, _, _) => exp (e, classes)
226 | EConcat (e1, _, e2, _) =>
227 let
228 val (sm1, classes) = exp (e1, classes)
229 val (sm2, classes) = exp (e2, classes)
230 in
231 (merge' (sm1, sm2), classes)
232 end
233 | ECut (e, _, _) => exp (e, classes)
234 | ECutMulti (e, _, _) => exp (e, classes)
235 | ECase (e, pes, _) =>
236 let
237 val (sm, classes) = exp (e, classes)
238 val (sms, classes) = expList (map #2 pes, classes)
239 in
240 (merge' (sm, sms), classes)
241 end
242 | EWrite e => exp (e, classes)
243 | EClosure (_, es) => expList (es, classes)
244 | ELet (_, _, e1, e2) =>
245 let
246 val (sm1, classes) = exp (e1, classes)
247 val (sm2, classes) = exp (e2, classes)
248 in
249 (merge' (sm1, sm2), classes)
250 end
251 | EServerCall (_, es, _) => expList (es, classes)
252
253 and expList (es, classes) = foldl (fn (e, (sm, classes)) =>
254 let
255 val (sm', classes) = exp (e, classes)
256 in
257 (merge' (sm, sm'), classes)
258 end) ([], classes) es
259 in
260 case d of
261 DCon _ => st
262 | DDatatype _ => st
263 | DVal (_, n, _, e, _) =>
264 let
265 val (sm, classes) = exp (e, classes)
266 in
267 (IM.insert (globals, n, (NONE, sm)), classes)
268 end
269 | DValRec vis =>
270 let
271 val (sm, classes) = foldl (fn ((_, _, _, e, _),
272 (sm, classes)) =>
273 let
274 val (sm', classes) = exp (e, classes)
275 in
276 (merge' (sm', sm), classes)
277 end) ([], classes) vis
278 in
279 (foldl (fn ((_, n, _, _, _), globals) => IM.insert (globals, n, (NONE, sm))) globals vis,
280 classes)
281 end
282 | DExport _ => st
283 | DTable _ => st
284 | DSequence _ => st
285 | DView _ => st
286 | DDatabase _ => st
287 | DCookie _ => st
288 | DStyle (_, n, s) => (IM.insert (globals, n, (SOME s, [])), classes)
289 | DTask _ => st
290 end
291
292 val (globals, classes) = foldl decl (IM.empty, IM.empty) file
293 in
294 {Overall = IM.foldl (fn ((_, sm), sm') => merge' (sm, sm')) [] globals,
295 Classes = ListMergeSort.sort (fn ((s1, _), (s2, _)) => String.compare (s1, s2) = GREATER)
296 (List.mapPartial (fn (i, sm) =>
297 case IM.find (globals, i) of
298 SOME (SOME s, _) => SOME (s, sm)
299 | _ => NONE) (IM.listItemsi classes))}
300 end
301
302 type report = {Overall : inheritable list,
303 Classes : (string * summary) list}
304
305 end