diff --git a/lexer.mll b/lexer.mll index 3625d921a2a03984912b62a5063afa0a3f8db1e0..cb5c12637a42a3d7da333b7b467046a8be5a1907 100644 --- a/lexer.mll +++ b/lexer.mll @@ -97,10 +97,13 @@ rule token = parse | '@' K E Y F R A M E S { KEYFRAMES_SYM } | '@' S U P P O R T S { SUPPORTS_SYM } + | (w | comment)* w A N D w (w | comment)* { SUPPORTS_AND } + | (w | comment)* w O R w (w | comment)* { SUPPORTS_OR } + | O N L Y { ONLY } | N O T { NOT } | A N D { AND } - | O R { OR } + (*| O R { OR } removed in favor of SUPPORTS_OR *) | F R O M { FROM } | T O { TO } diff --git a/parser.mly b/parser.mly index 5215576f921d37d750e5224e12aadbb4d6fef17c..4cbafc5c69154cf9634efafd4b117e2f44d52109 100644 --- a/parser.mly +++ b/parser.mly @@ -53,7 +53,8 @@ %token <float * string> UNIT_VALUE %token <string> COMBINATOR RELATION STRING IDENT HASH URI FUNCTION %token LPAREN RPAREN LBRACE RBRACE LBRACK RBRACK SEMICOL COLON COMMA DOT PLUS -%token MINUS SLASH STAR ONLY AND OR NOT FROM TO EOF +%token MINUS SLASH STAR ONLY AND (*OR*) NOT FROM TO EOF +%token SUPPORTS_AND SUPPORTS_OR (* Start symbol *) %type <Types.stylesheet> stylesheet @@ -62,8 +63,12 @@ %% (* list with arbitrary whitespace between elements and separators *) +%inline ig2(a, b): a b {} +%inline ig3(a, b, c): a b c {} %inline wslist(sep, x): S* l=separated_list(sep, terminated(x, S*)) { l } -%inline wspreceded(prefix, x): p=preceded(pair(prefix, S*), x) { p } +%inline wspreceded(prefix, x): p=preceded(ig2(prefix, S*), x) { p } + +%inline all_and: AND | SUPPORTS_AND {} cd: CDO S* | CDC S* {} @@ -112,9 +117,9 @@ media_query_list: | S* hd=media_query tl=wspreceded(COMMA, media_query)* { hd :: tl } media_query: - | prefix=only_or_not? typ=media_type S* feat=wspreceded(AND, media_expr)* + | prefix=only_or_not? typ=media_type S* feat=wspreceded(all_and, media_expr)* { (prefix, Some typ, feat) } - | hd=media_expr tl=wspreceded(AND, media_expr)* + | hd=media_expr tl=wspreceded(all_and, media_expr)* { (None, None, (hd :: tl)) } %inline only_or_not: | ONLY S* { "only" } @@ -171,18 +176,21 @@ supports_negation: | NOT S+ c=supports_condition_in_parens { Not c } supports_conjunction: - | hd=supports_condition_in_parens tl=preceded(delimited(S+, AND, S+), supports_condition_in_parens)+ + | hd=supports_condition_in_parens tl=preceded(SUPPORTS_AND, supports_condition_in_parens)+ { And (hd :: tl) } supports_disjunction: - | hd=supports_condition_in_parens tl=preceded(delimited(S+, OR, S+), supports_condition_in_parens)+ + | hd=supports_condition_in_parens tl=preceded(SUPPORTS_OR, supports_condition_in_parens)+ { Or (hd :: tl) } supports_declaration_condition: - | LPAREN S* decl=declaration RPAREN + | LPAREN S* decl=supports_declaration RPAREN { Decl decl } +supports_declaration: + | name=property S* COLON S* value=expr + { (name, value) } (*XXX: general_enclosed: | ( FUNCTION | LPAREN ) ( any | unused )* RPAREN - { } + { Enclosed expr } any: [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING @@ -248,7 +256,7 @@ pseudo: ":" ^ f ^ "(" ^ arg ^ ")" } declaration: - | name=property S* COLON S* value=expr important=boption(pair(IMPORTANT_SYM, S*)) + | name=property S* COLON S* value=expr important=boption(ig2(IMPORTANT_SYM, S*)) { (String.lowercase name, value, important) } %inline property: name=IDENT { name } diff --git a/stringify.ml b/stringify.ml index 4ee15800d92f7714804b9f30d95fc54e1a34b8c1..b165a599d2237c18ed410931106b29cca502169d 100644 --- a/stringify.ml +++ b/stringify.ml @@ -27,8 +27,7 @@ let rec filter_none = function let add_parens s = let l = String.length s in if l > 0 & s.[0] = '(' & s.[l - 1] = ')' - then String.sub s 1 (l - 2) - else s + then s else "(" ^ s ^ ")" (* * Pretty-printing @@ -76,12 +75,24 @@ let string_of_media_query query = | (Some pre, None, _) -> failwith "unexpected media query prefix \"" ^ pre ^ "\"" -let rec string_of_condition = function - | Not c -> "not " ^ add_parens (string_of_condition c) - | And c -> cat " and " (add_parens @@ string_of_condition) c - | Or c -> cat " or " (add_parens @@ string_of_condition) c - | Decl (name, value, false) -> "(" ^ name ^ ": " ^ string_of_expr value ^ ")" - | Decl (_, _, true) -> failwith "unexpected \"!important\"" +let stringify_condition w c = + let rec transform = + let p c = `Parens (transform c) in + function + | Not c -> `Not (p c) + | And c -> `And (List.map p c) + | Or c -> `Or (List.map p c) + | Decl (name, value) -> `Decl (name, value) + in + let rec str = function + | `Not c -> "not " ^ str c + | `And c -> cat " and " str c + | `Or c -> cat " or " str c + | `Decl (name, value) -> "(" ^ name ^ ":" ^ w ^ string_of_expr value ^ ")" + | `Parens (`Decl _ as d) -> str d + | `Parens c -> "(" ^ str c ^ ")" + in + str (transform c) let block = function "" -> " {}" | body -> " {\n" ^ indent body ^ "\n}" @@ -117,7 +128,7 @@ let rec string_of_statement = function in "@keyframes " ^ id ^ block (cat "\n\n" string_of_keyframe_ruleset rules) | Supports (condition, statements) -> - "@supports " ^ string_of_condition condition ^ + "@supports " ^ stringify_condition " " condition ^ block (cat "\n\n" string_of_statement statements) let string_of_stylesheet = cat "\n\n" string_of_statement @@ -158,10 +169,9 @@ let minify_media_query query = let rec minify_condition = function | Not c -> "not " ^ add_parens (minify_condition c) - | And c -> cat "and " (add_parens @@ minify_condition) c - | Or c -> cat "or " (add_parens @@ minify_condition) c - | Decl (name, value, false) -> "(" ^ name ^ ":" ^ minify_expr value ^ ")" - | Decl (_, _, true) -> failwith "unexpected \"!important\"" + | And c -> cat " and " (add_parens @@ minify_condition) c + | Or c -> cat " or " (add_parens @@ minify_condition) c + | Decl (name, value) -> "(" ^ name ^ ":" ^ minify_expr value ^ ")" let rec minify_statement = function | Ruleset (selectors, decls) -> @@ -189,7 +199,7 @@ let rec minify_statement = function in "@keyframes " ^ id ^ "{" ^ cat "" minify_keyframe_ruleset rules ^ "}" | Supports (condition, statements) -> - "@supports " ^ minify_condition condition ^ + "@supports " ^ stringify_condition "" condition ^ "{" ^ cat "" minify_statement statements ^ "}" | statement -> string_of_statement statement diff --git a/types.ml b/types.ml index 11bd01a3cf7bac6204ee936c8fba9265c72cc808..d986f927cfd240c75692920767b04618fb5e7a91 100644 --- a/types.ml +++ b/types.ml @@ -22,11 +22,13 @@ type descriptor_declaration = string * expr type keyframe_ruleset = expr * declaration list +type supports_declaration = string * expr + type condition = | Not of condition | And of condition list | Or of condition list - | Decl of declaration + | Decl of supports_declaration (*XXX: | Enclosed of expr*) type statement =