Commit bcf78917 authored by Taddeüs Kroes's avatar Taddeüs Kroes

Merge branch 'minimal'

parents 5408e87f 5003457c
RESULT := mincss RESULT := mincss
PRE_TGTS := types PRE_TGTS := types
MODULES := color_names util stringify parser lexer parse selector color \ MODULES := color_names util stringify parser lexer parse selector simple \
shorthand main shorthand duplicates main
ALL_NAMES := $(PRE_TGTS) $(MODULES) ALL_NAMES := $(PRE_TGTS) $(MODULES)
OCAMLCFLAGS := -g OCAMLCFLAGS := -g
...@@ -36,10 +36,10 @@ lexer.cmi: lexer.ml ...@@ -36,10 +36,10 @@ lexer.cmi: lexer.ml
parser.cmx: parser.cmi lexer.cmx parser.cmx: parser.cmi lexer.cmx
parser.mli: parser.ml parser.mli: parser.ml
parse.cmx: lexer.cmi parser.cmx parse.cmx: lexer.cmi parser.cmx
main.cmx: parse.cmx util.cmx color.cmx shorthand.cmx main.cmx: parse.cmx util.cmx simple.cmx shorthand.cmx duplicates.cmx
util.cmx: OCAMLCFLAGS += -pp cpp util.cmx: OCAMLCFLAGS += -pp cpp
util.cmx color.cmx: color_names.cmx util.cmx simple.cmx: color_names.cmx
stringify.cmx parser.cmx color.cmx shorthand.cmx: util.cmi stringify.cmx parser.cmx simple.cmx shorthand.cmx: util.cmi
$(addsuffix .cmx,$(MODULES)): $(addsuffix .cmi,$(PRE_TGTS)) $(addsuffix .cmx,$(MODULES)): $(addsuffix .cmi,$(PRE_TGTS))
clean: clean:
......
...@@ -3,14 +3,12 @@ open Types ...@@ -3,14 +3,12 @@ open Types
let compress = function let compress = function
| Ident "aliceblue" -> Hexcolor "f0f8ff" | Ident "aliceblue" -> Hexcolor "f0f8ff"
| Ident "antiquewhite" -> Hexcolor "faebd7" | Ident "antiquewhite" -> Hexcolor "faebd7"
| Ident "aqua" -> Hexcolor "0ff"
| Ident "aquamarine" -> Hexcolor "7fffd4" | Ident "aquamarine" -> Hexcolor "7fffd4"
| Hexcolor "f0ffff" -> Ident "azure" | Hexcolor "f0ffff" -> Ident "azure"
| Hexcolor "f5f5dc" -> Ident "beige" | Hexcolor "f5f5dc" -> Ident "beige"
| Hexcolor "ffe4c4" -> Ident "bisque" | Hexcolor "ffe4c4" -> Ident "bisque"
| Ident "black" -> Hexcolor "000" | Ident "black" -> Hexcolor "000"
| Ident "blanchedalmond" -> Hexcolor "ffebcd" | Ident "blanchedalmond" -> Hexcolor "ffebcd"
| Ident "blue" -> Hexcolor "00f"
| Ident "blueviolet" -> Hexcolor "8a2be2" | Ident "blueviolet" -> Hexcolor "8a2be2"
| Hexcolor "a52a2a" -> Ident "brown" | Hexcolor "a52a2a" -> Ident "brown"
| Ident "burlywood" -> Hexcolor "deb887" | Ident "burlywood" -> Hexcolor "deb887"
...@@ -20,7 +18,6 @@ let compress = function ...@@ -20,7 +18,6 @@ let compress = function
| Hexcolor "ff7f50" -> Ident "coral" | Hexcolor "ff7f50" -> Ident "coral"
| Ident "cornflowerblue" -> Hexcolor "6495ed" | Ident "cornflowerblue" -> Hexcolor "6495ed"
| Ident "cornsilk" -> Hexcolor "fff8dc" | Ident "cornsilk" -> Hexcolor "fff8dc"
| Ident "cyan" -> Hexcolor "0ff"
| Ident "darkblue" -> Hexcolor "00008b" | Ident "darkblue" -> Hexcolor "00008b"
| Ident "darkcyan" -> Hexcolor "008b8b" | Ident "darkcyan" -> Hexcolor "008b8b"
| Ident "darkgoldenrod" -> Hexcolor "b8860b" | Ident "darkgoldenrod" -> Hexcolor "b8860b"
...@@ -110,7 +107,6 @@ let compress = function ...@@ -110,7 +107,6 @@ let compress = function
| Hexcolor "dda0dd" -> Ident "plum" | Hexcolor "dda0dd" -> Ident "plum"
| Ident "powderblue" -> Hexcolor "b0e0e6" | Ident "powderblue" -> Hexcolor "b0e0e6"
| Hexcolor "800080" -> Ident "purple" | Hexcolor "800080" -> Ident "purple"
| Ident "red" -> Hexcolor "f00"
| Ident "rosybrown" -> Hexcolor "bc8f8f" | Ident "rosybrown" -> Hexcolor "bc8f8f"
| Ident "royalblue" -> Hexcolor "4169e1" | Ident "royalblue" -> Hexcolor "4169e1"
| Ident "saddlebrown" -> Hexcolor "8b4513" | Ident "saddlebrown" -> Hexcolor "8b4513"
......
open Types
module SM = Map.Make(String)
let prune_duplicates decls =
let keep = Array.make (List.length decls) true in
let rec tag db index = function
| (name, _, imp) :: tl when SM.mem name db ->
(* previous value exists, one needs to be removed *)
let prev_index, prev_imp = SM.find name db in
if not imp && prev_imp then begin
keep.(index) <- false;
tag db (index + 1) tl
end else begin
keep.(prev_index) <- false;
tag (SM.add name (index, imp) db) (index + 1) tl
end
| (name, _, imp) :: tl ->
tag (SM.add name (index, imp) db) (index + 1) tl
| [] -> ()
in
tag SM.empty 0 decls;
let rec prune i = function
| [] -> []
| hd :: tl when keep.(i) -> hd :: prune (i + 1) tl
| _ :: tl -> prune (i + 1) tl
in
prune 0 decls
let transform = function
| Statement (Ruleset (selectors, decls)) ->
Statement (Ruleset (selectors, prune_duplicates decls))
| v -> v
let compress = Util.transform_stylesheet transform
...@@ -2,63 +2,114 @@ open Lexing ...@@ -2,63 +2,114 @@ open Lexing
open Types open Types
type args = { type args = {
mutable infiles : string list; infiles : string list;
mutable outfile : string option; outfile : string option;
mutable verbose : bool; verbose : bool;
mutable prune : bool; whitespace : bool;
mutable unfold : bool; simple : bool;
mutable upto : int option; shorthands : bool;
duplicates : bool;
echo : bool;
sort : bool;
} }
(* Parse command-line arguments *)
let parse_args () = let parse_args () =
let args = { let usage =
infiles = []; "Usage: " ^ Sys.argv.(0) ^
outfile = None; " [<options>] [<file> ...]\n\
verbose = false; \n\
prune = true; Generic options:\n \
unfold = true; -h, --help Show this help message\n \
upto = None; -v, --verbose Verbose mode: show compression rate\n \
} in -o <file> Output file (defaults to stdout)\n \
<file> ... Input files (default is to read from stdin)\n\
\n\
Optimization flags (if none are specified, all are enabled):\n \
-w, --whitespace Eliminate unnecessary whitespaces (has the greatest \
effect, omit for pretty-printing)\n \
-c, --simple Shorten colors and font weights\n \
-s, --shorthands Generate shorthand properties\n \
-d, --duplicates Prune duplicate properties (WARNING: may affect \
cross-browser hacks)\n \
-p, --pretty Shorthand for -c -s -d\n \
-e, --echo Just parse and pretty-print, no optimizations\n\
\n\
Formatting options:
--sort Sort declarations in each selector group\n\
"
in
let args_spec = [ let default_args = {
("<file> ...", Arg.Rest (fun _ -> ()), infiles = [];
" Input files (default is to read from stdin)"); outfile = None;
verbose = false;
("-o", Arg.String (fun s -> args.outfile <- Some s), whitespace = false;
"<file> Output file (defaults to stdout)"); simple = false;
shorthands = false;
("-v", Arg.Unit (fun _ -> args.verbose <- true), duplicates = false;
" Verbose mode: show compression rate"); echo = false;
sort = false;
("-no-prune", Arg.Unit (fun _ -> args.prune <- false), } in
" Don't prune duplicate properties (skip step 5 below)");
("-no-unfold", Arg.Unit (fun _ -> args.unfold <- false),
" Only minify whitespace, colors and shorthands \
(skip steps 2-7 below)");
("-upto", Arg.Int (fun i -> args.upto <- Some i),
"<step> Stop after the specified step (for debugging): \
\n \
1: parse \n \
2: unfold shorthands \n \
3: unfold selectors \n \
4: unfold blocks \n \
5: prune duplicates \n \
6: combine selectors \n \
7: concatenate blocks \n \
8: optimize blocks \n \
9: minify");
] in
let usage = let rec handle args = function
"Usage: " ^ Sys.argv.(0) ^ " [-o <file>] [-v] [-no-prune] [-upto <step>] \ | ("-v" | "--verbose") :: tl ->
[<file> ...] " handle {args with verbose = true} tl
| ("-w" | "--whitespace") :: tl ->
handle {args with whitespace = true} tl
| ("-c" | "--simple") :: tl ->
handle {args with simple = true} tl
| ("-s" | "--shorthands") :: tl ->
handle {args with shorthands = true} tl
| ("-d" | "-duplicates") :: tl ->
handle {args with duplicates = true} tl
| ("-p" | "--pretty") :: tl ->
handle {args with simple = true; shorthands = true; duplicates = true} tl
| ("-e" | "--echo") :: tl ->
handle {args with echo = true} tl
| "--sort" :: tl ->
handle {args with sort = true} tl
| ("-h" | "--help") :: tl ->
prerr_string usage;
raise Exit_success
| ["-o"] ->
raise (Failure ("missing output file name"))
| "-o" :: next :: tl when next.[0] = '-' ->
raise (Failure ("missing output file name"))
| "-o" :: filename :: tl ->
handle {args with outfile = Some filename} tl
| arg :: tl when arg.[0] = '-' ->
prerr_string usage;
raise (Failure ("unknown option " ^ arg))
| filename :: tl ->
handle {args with infiles = args.infiles @ [filename]} tl
| [] -> args
in in
Arg.parse args_spec (fun f -> args.infiles <- args.infiles @ [f]) usage; match handle default_args (List.tl (Array.to_list Sys.argv)) with
args | { echo = true; _ } as args ->
{ args with
whitespace = false;
simple = false;
shorthands = false;
duplicates = false }
| { whitespace = false;
simple = false;
shorthands = false;
duplicates = false;
_ } as args ->
{ args with
whitespace = true;
simple = true;
shorthands = true;
duplicates = true }
| args -> args
let parse_files = function let parse_files = function
| [] -> | [] ->
...@@ -68,6 +119,8 @@ let parse_files = function ...@@ -68,6 +119,8 @@ let parse_files = function
let rec loop = function let rec loop = function
| [] -> [] | [] -> []
| filename :: tl -> | filename :: tl ->
if not (Sys.file_exists filename) then
raise (Failure ("file " ^ filename ^ " does not exist"));
let input = Util.input_all (open_in filename) in let input = Util.input_all (open_in filename) in
let stylesheet = Parse.parse_input filename input in let stylesheet = Parse.parse_input filename input in
(input, stylesheet) :: loop tl (input, stylesheet) :: loop tl
...@@ -76,21 +129,6 @@ let parse_files = function ...@@ -76,21 +129,6 @@ let parse_files = function
(String.concat "" inputs, List.concat stylesheets) (String.concat "" inputs, List.concat stylesheets)
let handle_args args = let handle_args args =
let steps =
(*let switch flag fn = if flag then fn else fun s -> s in*)
[
(*
switch args.unfold Unfold.unfold_shorthands;
switch args.unfold Unfold.unfold_selectors;
switch args.unfold Unfold.unfold_blocks;
switch (args.unfold && args.prune) Unfold.prune_duplicates;
switch args.unfold Combine.combine_selectors;
switch args.unfold Concat.concat_blocks;
Optimize.optimize_blocks;
*)
]
in
let write_output = let write_output =
match args.outfile with match args.outfile with
| None -> print_endline | None -> print_endline
...@@ -98,31 +136,31 @@ let handle_args args = ...@@ -98,31 +136,31 @@ let handle_args args =
fun css -> let f = open_out name in output_string f css; close_out f fun css -> let f = open_out name in output_string f css; close_out f
in in
let upto = match args.upto with Some i -> i | None -> 0 in let switch flag fn = if flag then fn else fun x -> x in
let input, stylesheet = parse_files args.infiles in let input, css = parse_files args.infiles in
let css = css
let rec do_steps i stylesheet = function (* unfold before pruning duplicates so that shorthand components are
| _ when i = upto -> * correctly pruned *)
write_output (Stringify.string_of_stylesheet stylesheet) |> switch args.shorthands Shorthand.unfold_stylesheet
|> switch args.simple Simple.compress
| [] -> |> switch args.duplicates Duplicates.compress
let output = Stringify.minify_stylesheet stylesheet in |> switch args.shorthands Shorthand.compress
write_output output; |> switch args.sort Util.sort_stylesheet
if args.verbose then begin
let il = String.length input in
let ol = String.length output in
Printf.fprintf stderr "compression: %d -> %d bytes (%d%% of original)\n"
il ol (int_of_float (float_of_int ol /. float_of_int il *. 100.))
end
| step :: tl ->
do_steps (i + 1) (step stylesheet) tl
in in
let output =
if args.whitespace
then Stringify.minify_stylesheet css
else Stringify.string_of_stylesheet css
in
write_output output;
do_steps 1 stylesheet steps if args.verbose then begin
let il = String.length input in
let ol = String.length output in
Printf.fprintf stderr "compression: %d -> %d bytes (%d%% of original)\n"
il ol (int_of_float (float_of_int ol /. float_of_int il *. 100.))
end
(* Main function, returns exit status *) (* Main function, returns exit status *)
let main () = let main () =
...@@ -137,6 +175,8 @@ let main () = ...@@ -137,6 +175,8 @@ let main () =
prerr_endline ("Error: " ^ msg ^ ": " ^ Stringify.string_of_box box); prerr_endline ("Error: " ^ msg ^ ": " ^ Stringify.string_of_box box);
| Failure msg -> | Failure msg ->
prerr_endline ("Error: " ^ msg); prerr_endline ("Error: " ^ msg);
| Exit_success ->
exit 0
end; end;
exit 1 exit 1
......
...@@ -10,7 +10,8 @@ let pattern = Str.regexp ("^\\(background\\|border\\|font\\|list-style" ^ ...@@ -10,7 +10,8 @@ let pattern = Str.regexp ("^\\(background\\|border\\|font\\|list-style" ^
"\\|outline\\|padding\\|margin\\)-\\(.*\\)$") "\\|outline\\|padding\\|margin\\)-\\(.*\\)$")
let order = function let order = function
| "background" -> ["color"; "image"; "repeat"; "attachment"; "position"] | "background" -> ["color"; "image"; "repeat"; "attachment"; "position-x";
"position-y"]
| "border" -> ["width"; "style"; "color"] | "border" -> ["width"; "style"; "color"]
| "font" -> ["style"; "variant"; "weight"; "size"; "family"] | "font" -> ["style"; "variant"; "weight"; "size"; "family"]
| "list-style" -> ["type"; "position"; "image"] | "list-style" -> ["type"; "position"; "image"]
...@@ -27,13 +28,17 @@ let rec decls_mem name = function ...@@ -27,13 +28,17 @@ let rec decls_mem name = function
(* find the value of the last declaration of some property (since the earlier (* find the value of the last declaration of some property (since the earlier
* values are overridden), unless an earlier !important value was found *) * values are overridden), unless an earlier !important value was found *)
let decls_find name decls = let decls_find name decls =
let rec wrap known = function let rec wrap known must_be_imp = function
| [] -> known | [] ->
| (nm, value, true) :: _ when nm = name -> Some value known
| (nm, value, false) :: tl when nm = name -> wrap (Some value) tl | (nm, value, false) :: tl when nm = name && not must_be_imp ->
| _ :: tl -> wrap known tl wrap (Some value) false tl
| (nm, value, true) :: tl when nm = name ->
wrap (Some value) true tl
| _ :: tl ->
wrap known must_be_imp tl
in in
match wrap None decls with match wrap None false decls with
| None -> raise Not_found | None -> raise Not_found
| Some value -> value | Some value -> value
...@@ -232,7 +237,8 @@ let rec unfold = function ...@@ -232,7 +237,8 @@ let rec unfold = function
let make_shorthands decls = let make_shorthands decls =
(* unfold currently existing shorthands into separate properties for merging (* unfold currently existing shorthands into separate properties for merging
* with override properties that are defined later on *) * with override properties that are defined later on *)
let decls = unfold decls in (*let decls = unfold decls in
XXX: done by main function for correct pruning of duplicate declarations*)
(* find shorthand names for which properties are present *) (* find shorthand names for which properties are present *)
let rec find_props = function let rec find_props = function
...@@ -259,7 +265,8 @@ let make_shorthands decls = ...@@ -259,7 +265,8 @@ let make_shorthands decls =
let keep_prop = function let keep_prop = function
| ("line-height", _, _) -> | ("line-height", _, _) ->
not (decls_mem "font" shorthands) not (decls_mem "font" shorthands)
| (name, _, _) -> | (name, _, imp) ->
imp ||
not (Str.string_match pattern name 0) || not (Str.string_match pattern name 0) ||
let base = Str.matched_group 1 name in let base = Str.matched_group 1 name in
let sub = Str.matched_group 2 name in let sub = Str.matched_group 2 name in
......
...@@ -12,7 +12,7 @@ let clip = function ...@@ -12,7 +12,7 @@ let clip = function
| Number (n, Some "%") when n > 100. -> Number (100., Some "%") | Number (n, Some "%") when n > 100. -> Number (100., Some "%")
| value -> value | value -> value
let rec short = function let rec shorten_expr = function
(* #aabbcc -> #abc *) (* #aabbcc -> #abc *)
| Hexcolor h when Str.string_match hex6 h 0 -> | Hexcolor h when Str.string_match hex6 h 0 ->
let gr n = Str.matched_group n h in let gr n = Str.matched_group n h in
...@@ -27,7 +27,7 @@ let rec short = function ...@@ -27,7 +27,7 @@ let rec short = function
| Number (n, Some "%") -> int_of_float (n *. 2.55 +. 0.5) | Number (n, Some "%") -> int_of_float (n *. 2.55 +. 0.5)
| _ -> assert false | _ -> assert false
in in
short (Hexcolor (Printf.sprintf "%02x%02x%02x" (i r) (i g) (i b))) shorten_expr (Hexcolor (Printf.sprintf "%02x%02x%02x" (i r) (i g) (i b)))
(* clip rgb values, e.g. rgb(-1,256,0) -> rgb(0,255,0) *) (* clip rgb values, e.g. rgb(-1,256,0) -> rgb(0,255,0) *)
| Function ("rgb", Nary (",", [r; g; b])) -> | Function ("rgb", Nary (",", [r; g; b])) ->
...@@ -35,15 +35,22 @@ let rec short = function ...@@ -35,15 +35,22 @@ let rec short = function
(* rgba(r,g,b,1.0) -> rgb(r,g,b) *) (* rgba(r,g,b,1.0) -> rgb(r,g,b) *)
| Function ("rgba", Nary (",", [r; g; b; Number (1., None)])) -> | Function ("rgba", Nary (",", [r; g; b; Number (1., None)])) ->
short (Function ("rgb", Nary (",", [r; g; b]))) shorten_expr (Function ("rgb", Nary (",", [r; g; b])))
(* TODO: hsl[a](...) *) (* TODO: hsl[a](...) *)
(* transform color names to shorter hex codes and vice-versa *) (* transform color names to shorter hex codes and vice-versa *)
| v -> Color_names.compress v | v -> Color_names.compress v
let shorten_font_weight = function
| Ident "normal" -> Number (400.0, None)
| Ident "bold" -> Number (700.0, None)
| v -> v
let transform = function let transform = function
| Expr value -> Expr (short value) | Expr value -> Expr (shorten_expr value)
| Declaration ("font-weight", value, imp) ->
Declaration ("font-weight", shorten_font_weight value, imp)
| v -> v | v -> v
let compress = Util.transform_stylesheet transform let compress = Util.transform_stylesheet transform
...@@ -86,3 +86,5 @@ exception Syntax_error of string ...@@ -86,3 +86,5 @@ exception Syntax_error of string
exception Loc_error of loc * string exception Loc_error of loc * string
exception Box_error of box * string exception Box_error of box * string
exception Exit_success
...@@ -231,3 +231,19 @@ let transform_stylesheet f stylesheet = ...@@ -231,3 +231,19 @@ let transform_stylesheet f stylesheet =
(* Expression identification *) (* Expression identification *)
let is_color = Color_names.is_color let is_color = Color_names.is_color
(* Sorting declarations *)
let sort_stylesheet =
let transform_sort_decls = 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
in
transform_stylesheet transform_sort_decls
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment