| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- open Printf
- open Types
- (** Operators *)
- let (|>) a b = b a
- (** List utilities *)
- let is_none = function None -> true | Some _ -> false
- let is_some = function None -> false | Some _ -> true
- let some_val = function None -> failwith "no value" | Some v -> v
- let rec filter_none = function
- | [] -> []
- | None :: tl -> filter_none tl
- | Some hd :: tl -> hd :: filter_none tl
- (** Reading input from file/stdin *)
- let input_all ic =
- let n = in_channel_length ic in
- let buf = String.create n in
- really_input ic buf 0 n;
- close_in ic;
- buf
- let input_buffered ic chunksize =
- let rec read_all buf bufsize pos =
- match input ic buf pos (bufsize - pos) with
- | 0 -> (close_in ic; buf)
- | nread when nread = bufsize - pos ->
- let bufsize = bufsize + chunksize in
- let pos = pos + nread in
- read_all (buf ^ String.create chunksize) bufsize pos
- | nread ->
- read_all buf bufsize (pos + nread)
- in
- read_all (String.create chunksize) chunksize 0
- (** Error printing *)
- let noloc = ("", 0, 0, 0, 0)
- let tabwidth = 4
- let count_tabs str upto =
- let rec count n = function
- | 0 -> n
- | i -> count (if String.get str (i - 1) = '\t' then n + 1 else n) (i - 1)
- in
- count 0 upto
- let rec repeat s n = if n < 1 then "" else s ^ (repeat s (n - 1))
- let retab str = Str.global_replace (Str.regexp "\t") (repeat " " tabwidth) str
- let indent n = repeat (repeat " " (tabwidth - 1)) n
- let prerr_loc (fname, ystart, yend, xstart, xend) =
- let file = open_in fname in
- (* skip lines until the first matched line *)
- for i = 1 to ystart - 1 do ignore (input_line file) done;
- (* for each line in `loc`, print the source line with an underline *)
- for l = ystart to yend do
- let line = input_line file in
- let linewidth = String.length line in
- let left = if l = ystart then xstart else 1 in
- let right = if l = yend then xend else linewidth in
- if linewidth > 0 then begin
- prerr_endline (retab line);
- prerr_string (indent (count_tabs line right));
- for i = 1 to left - 1 do prerr_char ' ' done;
- for i = left to right do prerr_char '^' done;
- prerr_endline "";
- end
- done
- let prerr_loc_msg loc msg =
- let (fname, ystart, yend, xstart, xend) = loc in
- if loc != noloc then begin
- let line_s = if yend != ystart
- then sprintf "lines %d-%d" ystart yend
- else sprintf "line %d" ystart
- in
- let char_s = if xend != xstart || yend != ystart
- then sprintf "characters %d-%d" xstart xend
- else sprintf "character %d" xstart
- in
- eprintf "File \"%s\", %s, %s:\n" fname line_s char_s;
- end;
- eprintf "%s\n" msg;
- if loc != noloc then
- try prerr_loc loc
- with Sys_error _ -> ()
- (** AST traversal *)
- #define TRAV_ALL(id, constructor) \
- trav_all_##id l = \
- let rec filter_clear = function \
- | [] -> [] \
- | Clear :: tl -> filter_clear tl \
- | constructor hd :: tl -> hd :: filter_clear tl \
- | _ -> failwith ("expected " ^ #constructor ^ " or Clear") \
- in \
- filter_clear (List.map trav_##id l)
- #define EXPECT(id, constructor) \
- expect_##id value = \
- match trav_##id value with \
- | constructor decl -> decl \
- | _ -> failwith ("expected " ^ #constructor)
- let transform_stylesheet f stylesheet =
- let rec trav_expr = function
- | Concat terms -> f (Expr (Concat (trav_all_expr terms)))
- | Function (name, arg) -> f (Expr (Function (name, expect_expr arg)))
- | Unary (op, opnd) -> f (Expr (Unary (op, expect_expr opnd)))
- | Nary (op, opnds) -> f (Expr (Nary (op, trav_all_expr opnds)))
- | value -> f (Expr value)
- and EXPECT(expr, Expr)
- and TRAV_ALL(expr, Expr) in
- let trav_declaration (name, value, important) =
- f (Declaration (name, expect_expr value, important))
- in
- let TRAV_ALL(declaration, Declaration) in
- let rec trav_selector = function
- | (No_element | All_elements | Element _) as elem ->
- f (Selector elem)
- | Id (base, id) ->
- f (Selector (Id (expect_selector base, id)))
- | Class (base, cls) ->
- f (Selector (Class (expect_selector base, cls)))
- | Attribute (base, attr, value) ->
- f (Selector (Attribute (expect_selector base, attr, value)))
- | Pseudo_class (base, cls, None) ->
- f (Selector (Pseudo_class (expect_selector base, cls, None)))
- | Pseudo_class (base, fn, Some args) ->
- let args = trav_all_selector args in
- f (Selector (Pseudo_class (expect_selector base, fn, Some args)))
- | Pseudo_element (base, elem) ->
- f (Selector (Pseudo_element (expect_selector base, elem)))
- | Combinator (left, com, right) ->
- let left = expect_selector left in
- let right = expect_selector right in
- f (Selector (Combinator (left, com, right)))
- and EXPECT(selector, Selector)
- and TRAV_ALL(selector, Selector) in
- let trav_media_expr = function
- | (_, None) as value ->
- f (Media_expr value)
- | (name, Some value) ->
- let value =
- match trav_expr value with
- | Expr value -> Some value
- | Clear -> None
- | _ -> failwith "expected Expr or Clear"
- in
- f (Media_expr (name, value))
- in
- let TRAV_ALL(media_expr, Media_expr) in
- let trav_media_query (prefix, mtype, queries) =
- f (Media_query (prefix, mtype, trav_all_media_expr queries))
- in
- let TRAV_ALL(media_query, Media_query) in
- let trav_descriptor_declaration (name, value) =
- f (Descriptor_declaration (name, expect_expr value))
- in
- let TRAV_ALL(descriptor_declaration, Descriptor_declaration) in
- let trav_keyframe_ruleset (selector, decls) =
- f (Keyframe_ruleset (expect_expr selector, trav_all_declaration decls))
- in
- let TRAV_ALL(keyframe_ruleset, Keyframe_ruleset) in
- let trav_supports_declaration (name, value) =
- f (Supports_declaration (name, expect_expr value))
- in
- let EXPECT(supports_declaration, Supports_declaration) in
- let rec trav_condition = function
- | Not c -> f (Condition (Not (expect_condition c)))
- | And l -> f (Condition (And (trav_all_condition l)))
- | Or l -> f (Condition (Or (trav_all_condition l)))
- | Decl d -> f (Condition (Decl (expect_supports_declaration d)))
- and EXPECT(condition, Condition)
- and TRAV_ALL(condition, Condition) in
- let rec trav_statement = function
- | Ruleset (selectors, decls) ->
- let selectors = trav_all_selector selectors in
- let decls = trav_all_declaration decls in
- f (Statement (Ruleset (selectors, decls)))
- | Media (queries, rulesets) ->
- let queries = trav_all_media_query queries in
- let rulesets = trav_all_statement rulesets in
- f (Statement (Media (queries, rulesets)))
- | Import (target, queries) ->
- let target = expect_expr target in
- let queries = trav_all_media_query queries in
- f (Statement (Import (target, queries)))
- | Page (pseudo, decls) ->
- let decls = trav_all_declaration decls in
- f (Statement (Page (pseudo, decls)))
- | Font_face decls ->
- let decls = trav_all_descriptor_declaration decls in
- f (Statement (Font_face decls))
- | Namespace (prefix, uri) ->
- let uri = expect_expr uri in
- f (Statement (Namespace (prefix, uri)))
- | Keyframes (prefix, id, rules) ->
- let rules = trav_all_keyframe_ruleset rules in
- f (Statement (Keyframes (prefix, id, rules)))
- | Supports (condition, statements) ->
- let condition = expect_condition condition in
- let statements = trav_all_statement statements in
- f (Statement (Supports (condition, statements)))
- | s ->
- f (Statement s)
- and TRAV_ALL(statement, Statement) in
- trav_all_statement stylesheet
- (* Expression identification *)
- let is_color = Color_names.is_color
- (* Sorting declarations *)
- let sort_stylesheet =
- transform_stylesheet begin function
- | Statement (Ruleset (selectors, decls)) ->
- let pattern = Str.regexp "^\\([^-]+\\)-" in
- let stem x =
- if Str.string_match pattern x 0 then Str.matched_group 1 x else x
- in
- let cmp (a, _, _) (b, _, _) = String.compare (stem a) (stem b) in
- Statement (Ruleset (selectors, List.stable_sort cmp decls))
- | v -> v
- end
|