From 90db3f19e6481aa43899dc9dce3fb37d24479aac Mon Sep 17 00:00:00 2001
From: Taddeus Kroes <taddeuskroes@gmail.com>
Date: Fri, 18 Jul 2014 14:34:26 +0200
Subject: [PATCH] Fixed parsing conflicts and stringification issues with
@supports
---
lexer.mll | 5 ++++-
parser.mly | 26 +++++++++++++++++---------
stringify.ml | 38 ++++++++++++++++++++++++--------------
types.ml | 4 +++-
4 files changed, 48 insertions(+), 25 deletions(-)
diff --git a/lexer.mll b/lexer.mll
index 3625d92..cb5c126 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 5215576..4cbafc5 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 4ee1580..b165a59 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 11bd01a..d986f92 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 =
--
GitLab