--- /dev/null
+package CSSParser;
+
+use strict;
+use Data::Dump::Streamer;
+
+#############################################################################
+#
+# This thing is an array of hashes with these keys
+#
+# simple - a CSS simple selector
+# slide - whether to slide the whole thing down to descendents
+# match - where to pass the chain on match
+#
+#############################################################################
+
+sub new {
+ my $string = shift;
+ # first split by aggregator
+ my $chains = [];
+
+ my @selectors = split /\s*,\s*/, $string;
+ for my $selector (@selectors){
+ push @$chains, parse_selector($selector);
+ }
+ return $chains;
+}
+
+# the universal selector is deliberately ignored, in Rezoom it is only
+# supported in its implied form
+sub parse_selector {
+ my $string = shift;
+ my (@tokens, @chain);
+
+ # split on combinators
+ for my $match (split(/\s*([ >+~])\s*/, $string)){
+ Dump($match);
+ # split on simple selector modifiers
+ push(@tokens, split(/([.#\[:])/, $match));
+ }
+ # now we got all selector modifiers, combinators and values separated
+ # convert them into our selector hashes
+ @tokens = grep !/\A\Z/, @tokens;
+
+ unshift @tokens, ' '; #add default combinator
+ while (@tokens){
+ my %selector;
+ my $combinator = shift @tokens;
+
+ if($combinator eq ' '){
+ $selector{slide} = 1;
+ }
+ elsif($combinator =~ /[+~>]/){
+ $selector{slide} = 0;
+ }
+ else{
+ $selector{slide} = 0;
+ unshift @tokens, $combinator;
+ }
+
+ my $simple = shift @tokens;
+
+ # glue together selector modifiers with their values
+ if($simple =~ m/[.\:\[#]/){
+ $simple .= shift @tokens;
+ }
+
+ $selector{simple} = $simple;
+
+
+
+ if($tokens[0] =~ /[ >]/){
+ $selector{match} = 'children';
+ }
+ elsif($tokens[0] eq '+'){
+ $selector{match} = 'nextsibling';
+ }
+ elsif($tokens[0] eq '~'){
+ $selector{match} = 'siblings';
+ }
+ else{
+ $selector{match} = 'self';
+ }
+
+ push @chain, \%selector;
+ }
+ return \@chain;
+}
+
+1;