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

Add support for :nth-child formula syntax

parent 8d36a788
...@@ -114,12 +114,25 @@ rule token = parse ...@@ -114,12 +114,25 @@ rule token = parse
| (s | comment)* s comment* O R comment* s (s | comment)* | (s | comment)* s comment* O R comment* s (s | comment)*
{ advance_pos lexbuf; WS_OR } { advance_pos lexbuf; WS_OR }
| (['-' '+'] as a_sign)? (['0'-'9']* as a) N
(w (['-' '+'] as b_sign) w (['0'-'9']+ as b))?
{
let a = if a = "" then 1 else int_of_string a in
let b = match b with None -> 0 | Some n -> int_of_string n in
let apply_sign n = function Some '-' -> -n | _ -> n in
let a = apply_sign a a_sign in
let b = apply_sign b b_sign in
FORMULA (a, b)
}
| O N L Y { ONLY } | O N L Y { ONLY }
| N O T { NOT } | N O T { NOT }
| A N D { AND } | A N D { AND }
(*| O R { OR } removed in favor of WS_OR *) (*| O R { OR } removed in favor of WS_OR *)
| F R O M { FROM } | F R O M { FROM }
| T O { TO } | T O { TO }
| O D D { ODD }
| E V E N { EVEN }
| ident as id { IDENT id } | ident as id { IDENT id }
......
...@@ -27,7 +27,7 @@ let parse_args () = ...@@ -27,7 +27,7 @@ let parse_args () =
Optimization flags (if none are specified, all are enabled):\n \ Optimization flags (if none are specified, all are enabled):\n \
-w, --whitespace Eliminate unnecessary whitespaces (has the greatest \ -w, --whitespace Eliminate unnecessary whitespaces (has the greatest \
effect, omit for pretty-printing)\n \ effect, omit for pretty-printing)\n \
-c, --simple Shorten colors and font weights\n \ -c, --simple Shorten colors, font weights and nth-child\n \
-s, --shorthands Generate shorthand properties\n \ -s, --shorthands Generate shorthand properties\n \
-d, --duplicates Prune duplicate properties (WARNING: may affect \ -d, --duplicates Prune duplicate properties (WARNING: may affect \
cross-browser hacks)\n \ cross-browser hacks)\n \
......
...@@ -62,7 +62,8 @@ ...@@ -62,7 +62,8 @@
%token <string> URI FUNCTION %token <string> URI FUNCTION
%token LPAREN RPAREN LBRACE RBRACE LBRACK RBRACK SEMICOL COLON DOUBLE_COLON %token LPAREN RPAREN LBRACE RBRACE LBRACK RBRACK SEMICOL COLON DOUBLE_COLON
%token COMMA DOT PLUS MINUS SLASH STAR ONLY AND (*OR*) NOT FROM TO EOF %token COMMA DOT PLUS MINUS SLASH STAR ONLY AND (*OR*) NOT FROM TO EOF
%token WS_AND WS_OR %token WS_AND WS_OR ODD EVEN
%token <int * int> FORMULA
(* Start symbol *) (* Start symbol *)
%type <Types.stylesheet> stylesheet %type <Types.stylesheet> stylesheet
...@@ -241,10 +242,8 @@ simple_selector: ...@@ -241,10 +242,8 @@ simple_selector:
| addons=element_addon+ | addons=element_addon+
{ append_addons No_element addons } { append_addons No_element addons }
%inline element_addon: %inline element_addon:
| id=HASH { `Id id } | id=HASH { `Id id }
| addon=cls | addon=cls | addon=attrib | addon=pseudo_class { addon }
| addon=attrib
| addon=pseudo { addon }
element_name: element_name:
| tag=IDENT { Element (String.lowercase tag) } | tag=IDENT { Element (String.lowercase tag) }
| STAR { All_elements } | STAR { All_elements }
...@@ -259,13 +258,32 @@ attrib: ...@@ -259,13 +258,32 @@ attrib:
%inline rel_value: %inline rel_value:
| S* id=IDENT S* { Ident id } | S* id=IDENT S* { Ident id }
| S* s=STRING S* { Strlit s } | S* s=STRING S* { Strlit s }
pseudo: pseudo_class:
| COLON id=IDENT | COLON id=IDENT
{ `Pseudo_class (String.lowercase id, None) } { `Pseudo_class (String.lowercase id, None) }
| COLON f=FUNCTION args=wslist(COMMA, simple_selector) RPAREN | COLON f=FUNCTION args=wslist(COMMA, function_arg) RPAREN
{ `Pseudo_class (String.lowercase f, Some args) } { `Pseudo_class (String.lowercase f, Some args) }
| DOUBLE_COLON id=IDENT | DOUBLE_COLON id=IDENT
{ `Pseudo_element (String.lowercase id) } { `Pseudo_element (String.lowercase id) }
function_arg:
| s=selector
{ Nested_selector s }
| EVEN
{ Nth Even }
| ODD
{ Nth Odd }
| f=FORMULA
{ let a, b = f in Nth (Formula (a, b)) }
| sign=sign? n=NUMBER
{
if is_int n then begin
let b = int_of_float (match sign with Some MINUS -> -.n | _ -> n) in
Nth (Formula (0, b))
end else
raise (Syntax_error ("unexpected float '" ^ string_of_float n ^
"', expected int"))
}
%inline sign: PLUS { PLUS } | MINUS { MINUS }
declaration: declaration:
| name=property S* COLON S* value=expr important=boption(ig2(IMPORTANT_SYM, S*)) | name=property S* COLON S* value=expr important=boption(ig2(IMPORTANT_SYM, S*))
......
open Str
open Types open Types
let hex6 = Str.regexp "\\([0-9a-f]\\)\\1\\([0-9a-f]\\)\\2\\([0-9a-f]\\)\\3" let hex6 = regexp "\\([0-9a-f]\\)\\1\\([0-9a-f]\\)\\2\\([0-9a-f]\\)\\3"
let is_num = function let is_num = function
| Number (n, (None | Some "%")) -> true | Number (n, (None | Some "%")) -> true
...@@ -14,8 +15,8 @@ let clip = function ...@@ -14,8 +15,8 @@ let clip = function
let rec shorten_expr = function let rec shorten_expr = function
(* #aabbcc -> #abc *) (* #aabbcc -> #abc *)
| Hexcolor h when Str.string_match hex6 h 0 -> | Hexcolor h when string_match hex6 h 0 ->
let gr n = Str.matched_group n h in let gr n = matched_group n h in
shorten_expr (Hexcolor (gr 1 ^ gr 2 ^ gr 3)) shorten_expr (Hexcolor (gr 1 ^ gr 2 ^ gr 3))
(* rgb(r,g,b) -> #rrggbb *) (* rgb(r,g,b) -> #rrggbb *)
...@@ -50,12 +51,32 @@ let shorten_font_weight = function ...@@ -50,12 +51,32 @@ let shorten_font_weight = function
| Ident "bold" -> Number (700.0, None) | Ident "bold" -> Number (700.0, None)
| v -> v | v -> v
let shorten_nth = function
(* even -> 2n *)
| Even -> Formula (2, 0)
(* 2n+1 | 2n-1 -> odd *)
| Formula (2, (1 | -1)) -> Odd
(* -n+1 -> 1 *)
| Formula (-1, 1) -> Formula (0, 1)
| v -> v
let compress = let compress =
Util.transform_stylesheet begin function Util.transform_stylesheet begin function
| Expr value -> Expr (shorten_expr value) | Expr value ->
Expr (shorten_expr value)
| Declaration ("font-weight", value, imp) -> | Declaration ("font-weight", value, imp) ->
Declaration ("font-weight", shorten_font_weight value, imp) Declaration ("font-weight", shorten_font_weight value, imp)
| Declaration (("border" | "outline") as name, Ident "none", imp) -> | Declaration (("border" | "outline") as name, Ident "none", imp) ->
Declaration (name, Number (0., None), imp) Declaration (name, Number (0., None), imp)
| Pseudo_class_arg (Nth nth) ->
Pseudo_class_arg (Nth (shorten_nth nth))
(* Remove rulesets with :nth-child(0) selector *)
| Selector (Pseudo_class (_, cls, Some [Nth (Formula (0, 0))]))
when string_match (regexp "nth-") cls 0 ->
Clear
(* Remove rulesets with no selectors or declarations *)
| Statement (Ruleset ([], _))
| Statement (Ruleset (_, [])) ->
Clear
| v -> v | v -> v
end end
...@@ -17,7 +17,7 @@ let rec cat sep fn = function ...@@ -17,7 +17,7 @@ let rec cat sep fn = function
*) *)
let string_of_num n = let string_of_num n =
if float_of_int (int_of_float n) = n if is_int n
then string_of_int (int_of_float n) then string_of_int (int_of_float n)
else string_of_float n else string_of_float n
...@@ -57,7 +57,7 @@ let rec stringify_selector w selector = ...@@ -57,7 +57,7 @@ let rec stringify_selector w selector =
| Pseudo_class (base, cls, None) -> | Pseudo_class (base, cls, None) ->
str base ^ ":" ^ cls str base ^ ":" ^ cls
| Pseudo_class (base, fn, Some args) -> | Pseudo_class (base, fn, Some args) ->
str base ^ ":" ^ fn ^ "(" ^ cat ("," ^ w) str args ^ ")" str base ^ ":" ^ fn ^ "(" ^ cat ("," ^ w) (stringify_arg w) args ^ ")"
| Pseudo_element (base, elem) -> | Pseudo_element (base, elem) ->
str base ^ "::" ^ elem str base ^ "::" ^ elem
| Combinator (left, " ", right) -> | Combinator (left, " ", right) ->
...@@ -65,6 +65,27 @@ let rec stringify_selector w selector = ...@@ -65,6 +65,27 @@ let rec stringify_selector w selector =
| Combinator (left, com, right) -> | Combinator (left, com, right) ->
str left ^ w ^ com ^ w ^ str right str left ^ w ^ com ^ w ^ str right
and stringify_arg w = function
| Nested_selector s -> stringify_selector w s
| Nth nth -> stringify_nth w nth
and stringify_nth w = function
| Even -> "even"
| Odd -> "odd"
| Formula (0, b) -> string_of_int b
| Formula (a, b) ->
begin
match a with
| 1 -> "n"
| -1 -> "-n"
| a -> string_of_int a ^ "n"
end ^ begin
match b with
| 0 -> ""
| b when b < 0 -> w ^ "-" ^ w ^ string_of_int (-b)
| b -> w ^ "+" ^ w ^ string_of_int b
end
let string_of_selector = stringify_selector " " let string_of_selector = stringify_selector " "
let string_of_media_expr = function let string_of_media_expr = function
......
a:link {
margin: 0;
}
div:nth-child(even) { /* div:nth-child(2n) */
margin: 0;
}
div:nth-child(2n + 1) { /* div:nth-child(odd) */
margin: 1;
}
div:nth-child(-1n +2) { /* div:nth-child(-n+2) */
margin: 2;
}
div:nth-child(0n+ 1) { /* div:nth-child(1) */
margin: 3;
}
div:nth-child(0) { /* removed */
margin: 4;
}
div:nth-child(2n) { /* div:nth-child(2n) */
margin: 5;
}
div:nth-child(-n+0) { /* div:nth-child(-n) */
margin: 6;
}
div:nth-child(+3n) { /* div:nth-child(3n) */
margin: 7;
}
div:nth-child(-n+1) { /* div:nth-child(1) */
margin: 8;
}
...@@ -18,16 +18,17 @@ type selector = ...@@ -18,16 +18,17 @@ type selector =
| Element of string | Element of string
| Id of selector * string | Id of selector * string
| Class of selector * string | Class of selector * string
| Pseudo_class of selector * string * selector list option | Pseudo_class of selector * string * pseudo_class_arg list option
| Pseudo_element of selector * string | Pseudo_element of selector * string
| Attribute of selector * string * (string * expr) option | Attribute of selector * string * (string * expr) option
| Combinator of selector * string * selector | Combinator of selector * string * selector
and pseudo_class_arg =
(* | Nested_selector of selector
type selector = | Nth of nth
| Simple of string and nth =
| Combinator of selector * string * selector | Even | Odd
*) (* a and b in an+b *)
| Formula of int * int
type media_expr = string * expr option type media_expr = string * expr option
type media_query = string option * string option * media_expr list type media_query = string option * string option * media_expr list
...@@ -73,6 +74,7 @@ type box = ...@@ -73,6 +74,7 @@ type box =
| Expr of expr | Expr of expr
| Declaration of declaration | Declaration of declaration
| Selector of selector | Selector of selector
| Pseudo_class_arg of pseudo_class_arg
| Media_expr of media_expr | Media_expr of media_expr
| Media_query of media_query | Media_query of media_query
| Descriptor_declaration of descriptor_declaration | Descriptor_declaration of descriptor_declaration
......
...@@ -142,7 +142,7 @@ let transform_stylesheet f stylesheet = ...@@ -142,7 +142,7 @@ let transform_stylesheet f stylesheet =
| Pseudo_class (base, cls, None) -> | Pseudo_class (base, cls, None) ->
f (Selector (Pseudo_class (expect_selector base, cls, None))) f (Selector (Pseudo_class (expect_selector base, cls, None)))
| Pseudo_class (base, fn, Some args) -> | Pseudo_class (base, fn, Some args) ->
let args = trav_all_selector args in let args = trav_all_pseudo_class_arg args in
f (Selector (Pseudo_class (expect_selector base, fn, Some args))) f (Selector (Pseudo_class (expect_selector base, fn, Some args)))
| Pseudo_element (base, elem) -> | Pseudo_element (base, elem) ->
f (Selector (Pseudo_element (expect_selector base, elem))) f (Selector (Pseudo_element (expect_selector base, elem)))
...@@ -151,7 +151,14 @@ let transform_stylesheet f stylesheet = ...@@ -151,7 +151,14 @@ let transform_stylesheet f stylesheet =
let right = expect_selector right in let right = expect_selector right in
f (Selector (Combinator (left, com, right))) f (Selector (Combinator (left, com, right)))
and EXPECT(selector, Selector) and EXPECT(selector, Selector)
and TRAV_ALL(selector, Selector) in and TRAV_ALL(selector, Selector)
and trav_pseudo_class_arg = function
| Nested_selector s ->
f (Pseudo_class_arg (Nested_selector (expect_selector s)))
| Nth _ as elem ->
f (Pseudo_class_arg elem)
and TRAV_ALL(pseudo_class_arg, Pseudo_class_arg) in
let trav_media_expr = function let trav_media_expr = function
| (_, None) as value -> | (_, None) as value ->
...@@ -230,11 +237,11 @@ let transform_stylesheet f stylesheet = ...@@ -230,11 +237,11 @@ let transform_stylesheet f stylesheet =
trav_all_statement stylesheet trav_all_statement stylesheet
(* Expression identification *) (** Expression identification *)
let is_color = Color_names.is_color let is_color = Color_names.is_color
(* Sorting declarations *) (** Sorting declarations *)
let sort_stylesheet = let sort_stylesheet =
transform_stylesheet begin function transform_stylesheet begin function
...@@ -247,3 +254,6 @@ let sort_stylesheet = ...@@ -247,3 +254,6 @@ let sort_stylesheet =
Statement (Ruleset (selectors, List.stable_sort cmp decls)) Statement (Ruleset (selectors, List.stable_sort cmp decls))
| v -> v | v -> v
end end
(** Misc *)
let is_int n = float_of_int (int_of_float n) = n
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