diff --git a/Makefile b/Makefile
index 404517de9bad6f0e3e512c4283ccb26bbea00de0..d8cd24373e7418038cec2af596c4cd148cf5a272 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,7 @@
RESULT := mincss
PRE_TGTS := types
-MODULES := color_names util stringify parser lexer parse color shorthand main
+MODULES := color_names util stringify parser lexer parse color \
+ shorthand main
ALL_NAMES := $(PRE_TGTS) $(MODULES)
OCAMLCFLAGS := -g
diff --git a/parser.mly b/parser.mly
index 3dac561b11e0a2085ef5f17ef355ca86b3410dc1..4b05e3f4e93a17f5a2369b38d62424b93e1b0f6f 100644
--- a/parser.mly
+++ b/parser.mly
@@ -37,6 +37,18 @@
| Unary ("-", Number (n, u)) -> Number (-.n, u)
| Unary ("+", (Number _ as n)) -> n
| value -> value
+
+ let rec append_addons base = function
+ | [] ->
+ base
+ | `Id id :: tl ->
+ append_addons (Id (base, id)) tl
+ | `Class cls :: tl ->
+ append_addons (Class (base, cls)) tl
+ | `Attribute (attr, value) :: tl ->
+ append_addons (Attribute (base, attr, value)) tl
+ | `Pseudo (f, args) :: tl ->
+ append_addons (Pseudo (base, f, args)) tl
%}
(* Tokens *)
@@ -207,39 +219,44 @@ ruleset:
selector:
| simple=simple_selector S*
- { Simple simple }
+ { simple }
| left=simple_selector S+ right=selector
- { Combinator (Simple left, " ", right) }
+ { Combinator (left, " ", right) }
| left=simple_selector S* com=combinator right=selector
- { Combinator (Simple left, com, right) }
+ { Combinator (left, com, right) }
%inline combinator:
| PLUS S* { "+" }
| c=COMBINATOR S* { c }
simple_selector:
| elem=element_name addons=element_addon*
- { elem ^ String.concat "" addons }
+ { append_addons elem addons }
| addons=element_addon+
- { String.concat "" addons }
-%inline element_addon: a=HASH | a=cls | a=attrib | a=pseudo { a }
+ { append_addons No_element addons }
+%inline element_addon:
+ | id=HASH { `Id id }
+ | addon=cls
+ | addon=attrib
+ | addon=pseudo { addon }
element_name:
- | tag=IDENT { String.lowercase tag }
- | STAR { "*" }
+ | tag=IDENT { Element (String.lowercase tag) }
+ | STAR { All_elements }
cls:
| DOT name=IDENT
- { "." ^ name }
+ { `Class name }
attrib:
- | LBRACK S* left=IDENT S* right=pair(RELATION, rel_value)? RBRACK
- { let right = match right with None -> "" | Some (op, term) -> op ^ term in
- "[" ^ String.lowercase left ^ right ^ "]" }
+ | LBRACK S* left=IDENT S* RBRACK
+ { `Attribute (String.lowercase left, None) }
+ | LBRACK S* left=IDENT S* op=RELATION right=rel_value RBRACK
+ { `Attribute (String.lowercase left, Some (op, right)) }
%inline rel_value:
- | S* id=IDENT S* { id }
- | S* s=STRING S* { "\"" ^ s ^ "\"" }
+ | S* id=IDENT S* { Ident id }
+ | S* s=STRING S* { Strlit s }
pseudo:
| COLON id=IDENT
- { ":" ^ (String.lowercase id) }
+ { `Pseudo (String.lowercase id, None) }
| COLON f=FUNCTION args=wslist(COMMA, simple_selector) RPAREN
- { ":" ^ String.lowercase f ^ "(" ^ String.concat "," args ^ ")" }
+ { `Pseudo (String.lowercase f, Some args) }
declaration:
| name=property S* COLON S* value=expr important=boption(ig2(IMPORTANT_SYM, S*))
diff --git a/stringify.ml b/stringify.ml
index ad89086a00bbd13c269852945e7192c7e3e3c199..5e115d2c374c921fac7489040598437828f79ee2 100644
--- a/stringify.ml
+++ b/stringify.ml
@@ -39,12 +39,30 @@ let string_of_declaration (name, value, important) =
let imp = if important then " !important" else "" in
name ^ ": " ^ string_of_expr value ^ imp ^ ";"
-let rec string_of_selector = function
- | Simple simple -> simple
+let rec stringify_selector w selector =
+ let str = stringify_selector w in
+ match selector with
+ | No_element -> ""
+ | All_elements -> "*"
+ | Element elem -> elem
+ | Id (base, id) ->
+ str base ^ "#" ^ id
+ | Class (base, cls) ->
+ str base ^ "." ^ cls
+ | Attribute (base, attr, None) ->
+ str base ^ "[" ^ attr ^ "]"
+ | Attribute (base, attr, Some (op, value)) ->
+ str base ^ "[" ^ attr ^ w ^ op ^ w ^ string_of_expr value ^ "]"
+ | Pseudo (base, sel, None) ->
+ str base ^ ":" ^ sel
+ | Pseudo (base, fn, Some args) ->
+ str base ^ ":" ^ fn ^ "(" ^ cat ("," ^ w) str args ^ ")"
| Combinator (left, " ", right) ->
- string_of_selector left ^ " " ^ string_of_selector right
+ str left ^ " " ^ str right
| Combinator (left, com, right) ->
- string_of_selector left ^ " " ^ com ^ " " ^ string_of_selector right
+ str left ^ w ^ com ^ w ^ str right
+
+let string_of_selector = stringify_selector " "
let string_of_media_expr = function
| (feature, None) -> "(" ^ feature ^ ")"
@@ -140,10 +158,7 @@ let minify_declaration (name, value, important) =
let imp = if important then "!important" else "" in
name ^ ":" ^ minify_expr value ^ imp
-let rec minify_selector = function
- | Simple simple -> simple
- | Combinator (left, com, right) ->
- minify_selector left ^ com ^ minify_selector right
+let rec minify_selector = stringify_selector ""
let minify_media_feature = function
| (feature, None) -> "(" ^ feature ^ ")"
diff --git a/types.ml b/types.ml
index fd2c87a0330ee9e3132cf3650543e213f3b04bac..657e5b70c310219f1f1649a87e89eb85ae5a4554 100644
--- a/types.ml
+++ b/types.ml
@@ -11,9 +11,21 @@ type expr =
type declaration = string * expr * bool
+type selector =
+ | No_element
+ | All_elements
+ | Element of string
+ | Id of selector * string
+ | Class of selector * string
+ | Pseudo of selector * string * selector list option
+ | Attribute of selector * string * (string * expr) option
+ | Combinator of selector * string * selector
+
+ (*
type selector =
| Simple of string
| Combinator of selector * string * selector
+ *)
type media_expr = string * expr option
type media_query = string option * string option * media_expr list
diff --git a/util.ml b/util.ml
index c4cd3e8a83e44018f2f2eb120340a6d2e06ab6b6..8d88d82804c998ead11b9417ed7fc0b0c61f2ced 100644
--- a/util.ml
+++ b/util.ml
@@ -132,12 +132,26 @@ let transform_stylesheet f stylesheet =
in
let TRAV_ALL(declaration, Declaration) in
- let trav_selector = function
- | Simple _ as s -> f (Selector s)
+ 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 (base, sel, None) ->
+ f (Selector (Pseudo (expect_selector base, sel, None)))
+ | Pseudo (base, fn, Some args) ->
+ let args = trav_all_selector args in
+ f (Selector (Pseudo (expect_selector base, fn, Some args)))
| Combinator (left, com, right) ->
+ let left = expect_selector left in
+ let right = expect_selector right in
f (Selector (Combinator (left, com, right)))
- in
- let TRAV_ALL(selector, Selector) in
+ and EXPECT(selector, Selector)
+ and TRAV_ALL(selector, Selector) in
let trav_media_expr = function
| (_, None) as value ->