stringify.ml 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. open Types
  2. open Util
  3. let tab = " "
  4. let indent = Str.global_replace (Str.regexp "^\\(.\\)") (tab ^ "\\1")
  5. let prefix_space = function "" -> "" | s -> " " ^ s
  6. let rec cat sep fn = function
  7. | [] -> ""
  8. | [hd] -> fn hd
  9. | hd :: tl -> fn hd ^ sep ^ cat sep fn tl
  10. let string_of_num n =
  11. if float_of_int (int_of_float n) = n
  12. then string_of_int (int_of_float n)
  13. else string_of_float n
  14. (*
  15. * Pretty-printing
  16. *)
  17. let rec string_of_expr = function
  18. | Ident id -> id
  19. | Strlit str -> "\"" ^ str ^ "\""
  20. | Uri uri when String.contains uri ')' -> "url(\"" ^ uri ^ "\")"
  21. | Uri uri -> "url(" ^ uri ^ ")"
  22. | Concat values -> cat " " string_of_expr values
  23. | Number (n, None) -> string_of_num n
  24. | Number (n, Some u) -> string_of_num n ^ u
  25. | Function (name, arg) -> name ^ "(" ^ string_of_expr arg ^ ")"
  26. | Hexcolor hex -> "#" ^ hex
  27. | Unary (op, opnd) -> op ^ string_of_expr opnd
  28. | Nary (",", opnds) -> cat ", " string_of_expr opnds
  29. | Nary (op, opnds) -> cat op string_of_expr opnds
  30. let string_of_declaration (name, value, important) =
  31. let imp = if important then " !important" else "" in
  32. name ^ ": " ^ string_of_expr value ^ imp ^ ";"
  33. let rec string_of_selector = function
  34. | Simple simple -> simple
  35. | Combinator (left, " ", right) ->
  36. string_of_selector left ^ " " ^ string_of_selector right
  37. | Combinator (left, com, right) ->
  38. string_of_selector left ^ " " ^ com ^ " " ^ string_of_selector right
  39. let string_of_media_feature = function
  40. | (feature, None) -> "(" ^ feature ^ ")"
  41. | (feature, Some value) -> "(" ^ feature ^ ": " ^ string_of_expr value ^ ")"
  42. let string_of_media_query =
  43. let features_str = cat " and " string_of_media_feature in
  44. function
  45. | (None, None, []) -> ""
  46. | (None, Some mtype, []) -> mtype
  47. | (Some pre, Some mtype, []) -> pre ^ " " ^ mtype
  48. | (None, None, features) -> features_str features
  49. | (None, Some mtype, features) -> mtype ^ " and " ^ features_str features
  50. | (Some pre, Some mtype, features) ->
  51. pre ^ " " ^ mtype ^ " and " ^ features_str features
  52. | (Some pre, None, _) ->
  53. failwith "unexpected media query prefix \"" ^ pre ^ "\""
  54. let stringify_condition w c =
  55. let rec transform =
  56. let p c = `Parens (transform c) in
  57. function
  58. | Not c -> `Not (p c)
  59. | And c -> `And (List.map p c)
  60. | Or c -> `Or (List.map p c)
  61. | Decl (name, value) -> `Decl (name, value)
  62. in
  63. let rec str = function
  64. | `Not c -> "not " ^ str c
  65. | `And c -> cat " and " str c
  66. | `Or c -> cat " or " str c
  67. | `Decl (name, value) -> "(" ^ name ^ ":" ^ w ^ string_of_expr value ^ ")"
  68. | `Parens (`Decl _ as d) -> str d
  69. | `Parens c -> "(" ^ str c ^ ")"
  70. in
  71. str (transform c)
  72. let block = function "" -> " {}" | body -> " {\n" ^ indent body ^ "\n}"
  73. let rec string_of_statement = function
  74. | Ruleset (selectors, decls) ->
  75. cat ", " string_of_selector selectors ^
  76. block (cat "\n" string_of_declaration decls)
  77. | Media (queries, rulesets) ->
  78. "@media" ^ prefix_space (cat ", " string_of_media_query queries) ^
  79. block (cat "\n\n" string_of_statement rulesets)
  80. | Import (target, []) ->
  81. "@import " ^ string_of_expr target ^ ";"
  82. | Import (target, queries) ->
  83. "@import " ^ string_of_expr target ^ " " ^ cat ", " string_of_media_query queries ^ ";"
  84. | Charset charset ->
  85. "@charset \"" ^ charset ^ "\";"
  86. | Page (None, decls) ->
  87. "@page" ^ block (cat "\n" string_of_declaration decls)
  88. | Page (Some pseudo, decls) ->
  89. "@page :" ^ pseudo ^ block (cat "\n" string_of_declaration decls)
  90. | Font_face decls ->
  91. let string_of_descriptor_declaration (name, value) =
  92. name ^ ": " ^ string_of_expr value ^ ";"
  93. in
  94. "@font-face" ^ block (cat "\n" string_of_descriptor_declaration decls)
  95. | Namespace (None, uri) ->
  96. "@namespace " ^ string_of_expr uri ^ ";"
  97. | Namespace (Some prefix, uri) ->
  98. "@namespace " ^ prefix ^ " " ^ string_of_expr uri ^ ";"
  99. | Keyframes (prefix, id, rules) ->
  100. let string_of_keyframe_ruleset (expr, decls) =
  101. string_of_expr expr ^ block (cat "\n" string_of_declaration decls)
  102. in
  103. "@" ^ prefix ^ "keyframes " ^ id ^
  104. block (cat "\n\n" string_of_keyframe_ruleset rules)
  105. | Supports (condition, statements) ->
  106. "@supports " ^ stringify_condition " " condition ^
  107. block (cat "\n\n" string_of_statement statements)
  108. let string_of_stylesheet = cat "\n\n" string_of_statement
  109. (*
  110. * Minified stringification
  111. *)
  112. let rec minify_expr = function
  113. | Concat values -> cat " " minify_expr values
  114. | Function (name, arg) -> name ^ "(" ^ minify_expr arg ^ ")"
  115. | Unary (op, opnd) -> op ^ minify_expr opnd
  116. | Nary (",", opnds) -> cat "," minify_expr opnds
  117. | Nary (op, opnds) -> cat op minify_expr opnds
  118. | expr -> string_of_expr expr
  119. let minify_declaration (name, value, important) =
  120. let imp = if important then "!important" else "" in
  121. name ^ ":" ^ minify_expr value ^ imp
  122. let rec minify_selector = function
  123. | Simple simple -> simple
  124. | Combinator (left, com, right) ->
  125. minify_selector left ^ com ^ minify_selector right
  126. let minify_media_feature = function
  127. | (feature, None) -> "(" ^ feature ^ ")"
  128. | (feature, Some value) -> "(" ^ feature ^ ":" ^ minify_expr value ^ ")"
  129. let minify_media_query query =
  130. let features_str = cat "and " minify_media_feature in
  131. match query with
  132. | (None, None, features) -> features_str features
  133. | (None, Some mtype, features) -> mtype ^ " and " ^ features_str features
  134. | (Some pre, Some mtype, features) ->
  135. pre ^ " " ^ mtype ^ " and " ^ features_str features
  136. | _ -> string_of_media_query query
  137. let rec minify_statement = function
  138. | Ruleset (selectors, decls) ->
  139. cat "," minify_selector selectors ^
  140. "{" ^ cat ";" minify_declaration decls ^ "}"
  141. | Media (queries, rulesets) ->
  142. "@media" ^ prefix_space (cat "," minify_media_query queries) ^
  143. "{" ^ cat "" minify_statement rulesets ^ "}"
  144. | Import (target, []) ->
  145. "@import " ^ string_of_expr target ^ ";"
  146. | Import (target, queries) ->
  147. "@import " ^ string_of_expr target ^ " " ^
  148. cat "," string_of_media_query queries ^ ";"
  149. | Page (None, decls) ->
  150. "@page{" ^ cat ";" minify_declaration decls ^ "}"
  151. | Page (Some pseudo, decls) ->
  152. "@page :" ^ pseudo ^ "{" ^ cat ";" minify_declaration decls ^ "}"
  153. | Font_face decls ->
  154. let minify_descriptor_declaration (name, value) =
  155. name ^ ":" ^ string_of_expr value
  156. in
  157. "@font-face{" ^ cat ";" minify_descriptor_declaration decls ^ "}"
  158. | Keyframes (prefix, id, rules) ->
  159. let minify_keyframe_ruleset (expr, decls) =
  160. minify_expr expr ^ "{" ^ cat ";" minify_declaration decls ^ "}"
  161. in
  162. "@" ^ prefix ^ "keyframes " ^ id ^
  163. "{" ^ cat "" minify_keyframe_ruleset rules ^ "}"
  164. | Supports (condition, statements) ->
  165. "@supports " ^ stringify_condition "" condition ^
  166. "{" ^ cat "" minify_statement statements ^ "}"
  167. | statement -> string_of_statement statement
  168. let minify_stylesheet = cat "" minify_statement