util.ml 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. open Printf
  2. open Types
  3. (** Operators *)
  4. let (|>) a b = b a
  5. (** List utilities *)
  6. let is_none = function None -> true | Some _ -> false
  7. let is_some = function None -> false | Some _ -> true
  8. let some_val = function None -> failwith "no value" | Some v -> v
  9. let rec filter_none = function
  10. | [] -> []
  11. | None :: tl -> filter_none tl
  12. | Some hd :: tl -> hd :: filter_none tl
  13. (** Reading input from file/stdin *)
  14. let input_all ic =
  15. let n = in_channel_length ic in
  16. let buf = Bytes.create n in
  17. really_input ic buf 0 n;
  18. close_in ic;
  19. buf
  20. let input_buffered ic chunksize =
  21. let rec read_all buf bufsize pos =
  22. match input ic buf pos (bufsize - pos) with
  23. | 0 -> (close_in ic; buf)
  24. | nread when nread = bufsize - pos ->
  25. let bufsize = bufsize + chunksize in
  26. let pos = pos + nread in
  27. read_all (buf ^ Bytes.create chunksize) bufsize pos
  28. | nread ->
  29. read_all buf bufsize (pos + nread)
  30. in
  31. read_all (Bytes.create chunksize) chunksize 0
  32. (** Error printing *)
  33. let noloc = ("", 0, 0, 0, 0)
  34. let tabwidth = 4
  35. let count_tabs str upto =
  36. let rec count n = function
  37. | 0 -> n
  38. | i -> count (if String.get str (i - 1) = '\t' then n + 1 else n) (i - 1)
  39. in
  40. count 0 upto
  41. let rec repeat s n = if n < 1 then "" else s ^ (repeat s (n - 1))
  42. let retab str = Str.global_replace (Str.regexp "\t") (repeat " " tabwidth) str
  43. let indent n = repeat (repeat " " (tabwidth - 1)) n
  44. let prerr_loc (fname, ystart, yend, xstart, xend) =
  45. let file = open_in fname in
  46. (* skip lines until the first matched line *)
  47. for i = 1 to ystart - 1 do ignore (input_line file) done;
  48. (* for each line in `loc`, print the source line with an underline *)
  49. for l = ystart to yend do
  50. let line = input_line file in
  51. let linewidth = String.length line in
  52. let left = if l = ystart then xstart else 1 in
  53. let right = if l = yend then xend else linewidth in
  54. if linewidth > 0 then begin
  55. prerr_endline (retab line);
  56. prerr_string (indent (count_tabs line right));
  57. for i = 1 to left - 1 do prerr_char ' ' done;
  58. for i = left to right do prerr_char '^' done;
  59. prerr_endline "";
  60. end
  61. done
  62. let prerr_loc_msg loc msg =
  63. let (fname, ystart, yend, xstart, xend) = loc in
  64. if loc != noloc then begin
  65. let line_s = if yend != ystart
  66. then sprintf "lines %d-%d" ystart yend
  67. else sprintf "line %d" ystart
  68. in
  69. let char_s = if xend != xstart || yend != ystart
  70. then sprintf "characters %d-%d" xstart xend
  71. else sprintf "character %d" xstart
  72. in
  73. eprintf "File \"%s\", %s, %s:\n" fname line_s char_s;
  74. end;
  75. eprintf "%s\n" msg;
  76. if loc != noloc then
  77. try prerr_loc loc
  78. with Sys_error _ -> ()
  79. (** AST traversal *)
  80. #define TRAV_ALL(id, constructor) \
  81. trav_all_##id l = \
  82. let rec filter_clear = function \
  83. | [] -> [] \
  84. | Clear :: tl -> filter_clear tl \
  85. | constructor hd :: tl -> hd :: filter_clear tl \
  86. | _ -> failwith ("expected " ^ #constructor ^ " or Clear") \
  87. in \
  88. filter_clear (List.map trav_##id l)
  89. #define EXPECT(id, constructor) \
  90. expect_##id value = \
  91. match trav_##id value with \
  92. | constructor decl -> decl \
  93. | _ -> failwith ("expected " ^ #constructor)
  94. let transform_stylesheet f stylesheet =
  95. let f x =
  96. match f x with
  97. | Expr (Concat [expr] | Nary (_, [expr])) -> Expr expr
  98. | value -> value
  99. in
  100. let rec trav_expr = function
  101. | Concat terms -> f (Expr (Concat (trav_all_expr terms)))
  102. | Function (name, arg) -> f (Expr (Function (name, expect_expr arg)))
  103. | Unary (op, opnd) -> f (Expr (Unary (op, expect_expr opnd)))
  104. | Nary (op, opnds) -> f (Expr (Nary (op, trav_all_expr opnds)))
  105. | value -> f (Expr value)
  106. and EXPECT(expr, Expr)
  107. and TRAV_ALL(expr, Expr) in
  108. let trav_declaration (name, value, important) =
  109. f (Declaration (name, expect_expr value, important))
  110. in
  111. let TRAV_ALL(declaration, Declaration) in
  112. let rec trav_selector = function
  113. | (No_element | All_elements | Element _) as elem ->
  114. f (Selector elem)
  115. | Id (base, id) ->
  116. f (Selector (Id (expect_selector base, id)))
  117. | Class (base, cls) ->
  118. f (Selector (Class (expect_selector base, cls)))
  119. | Attribute (base, attr, value) ->
  120. f (Selector (Attribute (expect_selector base, attr, value)))
  121. | Pseudo_class (base, cls, None) ->
  122. f (Selector (Pseudo_class (expect_selector base, cls, None)))
  123. | Pseudo_class (base, fn, Some args) ->
  124. let args = trav_all_pseudo_class_arg args in
  125. f (Selector (Pseudo_class (expect_selector base, fn, Some args)))
  126. | Pseudo_element (base, elem) ->
  127. f (Selector (Pseudo_element (expect_selector base, elem)))
  128. | Combinator (left, com, right) ->
  129. let left = expect_selector left in
  130. let right = expect_selector right in
  131. f (Selector (Combinator (left, com, right)))
  132. and EXPECT(selector, Selector)
  133. and TRAV_ALL(selector, Selector)
  134. and trav_pseudo_class_arg = function
  135. | Nested_selector s ->
  136. f (Pseudo_class_arg (Nested_selector (expect_selector s)))
  137. | Nth _ as elem ->
  138. f (Pseudo_class_arg elem)
  139. and TRAV_ALL(pseudo_class_arg, Pseudo_class_arg) in
  140. let trav_media_expr = function
  141. | (_, None) as value ->
  142. f (Media_expr value)
  143. | (name, Some value) ->
  144. let value =
  145. match trav_expr value with
  146. | Expr value -> Some value
  147. | Clear -> None
  148. | _ -> failwith "expected Expr or Clear"
  149. in
  150. f (Media_expr (name, value))
  151. in
  152. let TRAV_ALL(media_expr, Media_expr) in
  153. let trav_media_query (prefix, mtype, queries) =
  154. f (Media_query (prefix, mtype, trav_all_media_expr queries))
  155. in
  156. let TRAV_ALL(media_query, Media_query) in
  157. let trav_descriptor_declaration (name, value) =
  158. f (Descriptor_declaration (name, expect_expr value))
  159. in
  160. let TRAV_ALL(descriptor_declaration, Descriptor_declaration) in
  161. let trav_keyframe_ruleset (selector, decls) =
  162. f (Keyframe_ruleset (expect_expr selector, trav_all_declaration decls))
  163. in
  164. let TRAV_ALL(keyframe_ruleset, Keyframe_ruleset) in
  165. let trav_supports_declaration (name, value) =
  166. f (Supports_declaration (name, expect_expr value))
  167. in
  168. let EXPECT(supports_declaration, Supports_declaration) in
  169. let rec trav_condition = function
  170. | Not c -> f (Condition (Not (expect_condition c)))
  171. | And l -> f (Condition (And (trav_all_condition l)))
  172. | Or l -> f (Condition (Or (trav_all_condition l)))
  173. | Decl d -> f (Condition (Decl (expect_supports_declaration d)))
  174. and EXPECT(condition, Condition)
  175. and TRAV_ALL(condition, Condition) in
  176. let rec trav_statement = function
  177. | Ruleset (selectors, decls) ->
  178. let selectors = trav_all_selector selectors in
  179. let decls = trav_all_declaration decls in
  180. f (Statement (Ruleset (selectors, decls)))
  181. | Media (queries, rulesets) ->
  182. let queries = trav_all_media_query queries in
  183. let rulesets = trav_all_statement rulesets in
  184. f (Statement (Media (queries, rulesets)))
  185. | Import (target, queries) ->
  186. let target = expect_expr target in
  187. let queries = trav_all_media_query queries in
  188. f (Statement (Import (target, queries)))
  189. | Page (pseudo, decls) ->
  190. let decls = trav_all_declaration decls in
  191. f (Statement (Page (pseudo, decls)))
  192. | Font_face decls ->
  193. let decls = trav_all_descriptor_declaration decls in
  194. f (Statement (Font_face decls))
  195. | Namespace (prefix, uri) ->
  196. let uri = expect_expr uri in
  197. f (Statement (Namespace (prefix, uri)))
  198. | Keyframes (prefix, id, rules) ->
  199. let rules = trav_all_keyframe_ruleset rules in
  200. f (Statement (Keyframes (prefix, id, rules)))
  201. | Supports (condition, statements) ->
  202. let condition = expect_condition condition in
  203. let statements = trav_all_statement statements in
  204. f (Statement (Supports (condition, statements)))
  205. | s ->
  206. f (Statement s)
  207. and TRAV_ALL(statement, Statement) in
  208. trav_all_statement stylesheet
  209. (** Expression identification *)
  210. let is_color = Color_names.is_color
  211. (** Sorting declarations *)
  212. let sort_stylesheet =
  213. let pattern = Str.regexp "^\\([^-]+\\)-\\(.*\\)$" in
  214. transform_stylesheet begin function
  215. | Statement (Ruleset (selectors, decls)) ->
  216. let split x =
  217. if Str.string_match pattern x 0
  218. then Some (Str.matched_group 1 x, Str.matched_group 2 x)
  219. else None
  220. in
  221. let rec cmp a b =
  222. match split a, split b with
  223. | Some (base_a, sub_a), Some (base_b, sub_b) when base_a = base_b ->
  224. cmp sub_a sub_b
  225. | Some (base_a, _), Some (base_b, _) ->
  226. String.compare base_a base_b
  227. | Some (base_a, _), None when base_a = b -> 1
  228. | Some (base_a, _), None -> String.compare base_a b
  229. | None, Some (base_b, _) when a = base_b -> -1
  230. | None, Some (base_b, _) -> String.compare a base_b
  231. | None, None -> String.compare a b
  232. in
  233. let cmp_decls (a, _, _) (b, _, _) = cmp a b in
  234. Statement (Ruleset (selectors, List.stable_sort cmp_decls decls))
  235. | v -> v
  236. end
  237. (** Misc *)
  238. let is_int n = float_of_int (int_of_float n) = n