File Coverage

blib/lib/CSS/Selector/Parser.pm
Criterion Covered Total %
statement 51 54 94.4
branch 27 30 90.0
condition n/a
subroutine 4 4 100.0
pod 1 1 100.0
total 83 89 93.2


line stmt bran cond sub pod time code
1             package CSS::Selector::Parser;
2             {
3             $CSS::Selector::Parser::VERSION = '0.003';
4             }
5             # ABSTRACT: parse CSS selectors to Perl data structures
6              
7 1     1   24205 use strict;
  1         2  
  1         33  
8 1     1   4 use warnings;
  1         1  
  1         37  
9              
10 1         8 use Sub::Exporter -setup => {
11             exports => [ qw(parse_selector) ],
12 1     1   852 };
  1         13684  
13              
14             my $re_name = qr/[-\w]+/;
15             # taken from HTML::Selector::XPath
16             my $re_attr_value = qr/^\[\s*([^~\|=\s]+)\s*([~\|]?=)\s*"([^"]+)"\s*\]/;
17             my $re_attr_exist = qr/^\[([^\]]*)\]/;
18             my $re_pseudo = qr/^:([()a-z0-9_-]+)/;
19             my $re_combinator = qr/^(\s*[>+\s])/;
20             my $re_comma = qr/^\s*,/;
21              
22             sub parse_selector {
23 8     8 1 30 local $_ = shift;
24 8         21 my %options = @_;
25              
26 8         14 my @rules;
27 8         97 s/\s+$//;
28 10         11 RULE: {
29             #warn "RULE: $_\n";
30 8         11 my $combinator;
31             my @selector;
32 11         34 SIMPLE: {
33 10         12 s/^\s+//;
34             #warn "SIMPLE: $_\n";
35 11         17 my ($element, $id, $class, %attr, %pseudo);
36 11 100       1172 $element = $1 if s/^($re_name|\*)//;
37 31 100       182 SUB: {
38 11         613 $id = $1, redo SUB if s/^\#($re_name)//;
39 26 100       133 if (s/^\.($re_name)//) {
40 10         36 $class = join '.', grep(defined, $class, $1);
41 10         18 redo SUB;
42             }
43 16 50       69 if (s/$re_attr_value//) {
44 0         0 $attr{$1}{$2} = $3;
45 0         0 redo SUB;
46             }
47 16 100       63 if (s/$re_attr_exist//) {
48 1 50       6 $attr{$1} = undef unless exists $attr{$1};
49 1         3 redo SUB;
50             }
51             # XXX grab :not first
52 15 100       151 if (s/$re_pseudo//) {
53 4         9 my $p = $1;
54 4 100       17 if ($p =~ s/\((.+)\)$//) {
55 1         4 $pseudo{$p} = $1;
56             } else {
57 3         8 $pseudo{$p} = undef;
58             }
59 4         10 redo SUB;
60             }
61             }
62              
63 11 100       629 if ($options{class_as_array}) {
64 2 100       14 $class = defined $class ? [split /\.+/, $class] : [];
65             }
66              
67 11         67 my $simple = {
68             element => $element,
69             id => $id,
70             class => $class,
71             attr => \%attr,
72             pseudo => \%pseudo,
73             combinator => $combinator,
74             };
75 11         44 for (keys %$simple) {
76 66 100       480 delete $simple->{$_} unless defined $simple->{$_};
77             }
78 11         31 for (qw(attr pseudo)) {
79 22 100       21 delete $simple->{$_} unless %{$simple->{$_}};
  22         78  
80             }
81             #warn Dumper($simple);
82 11         20 push @selector, $simple;
83              
84 11         12 $combinator = undef;
85              
86 11 100       53 if (s/$re_combinator//) {
87 1         3 $combinator = $1;
88 1         3 redo SIMPLE;
89             }
90              
91 10         13 push @rules, \@selector;
92 10 100       46 redo RULE if s/$re_comma//;
93 8 50       33 last RULE unless $_;
94 0         0 die "fell off the end of parsing: $_\n";
95             }
96             }
97 8         83 return @rules;
98             }
99              
100             1;
101              
102              
103              
104             =pod
105              
106             =head1 NAME
107              
108             CSS::Selector::Parser - parse CSS selectors to Perl data structures
109              
110             =head1 VERSION
111              
112             version 0.003
113              
114             =head1 SYNOPSIS
115              
116             use CSS::Selector::Parser 'parse_selector';
117              
118             my @rules = parse_selector('#foo .bar, baz:quux');
119             # [ { id => 'foo' }, { class => 'bar', combinator => ' ' } ]
120             # [ { element => 'baz', pseudo => { quux => undef } ]
121              
122             =head1 DESCRIPTION
123              
124             This module parses CSS selectors and gives back a series of Perl data
125             structures corresponding to the selectors.
126              
127             =head1 FUNCTIONS
128              
129             CSS::Selector::Parser uses L. See its documentation for various
130             ways to customize exporting.
131              
132             =head2 parse_selector
133              
134             my @rules = parse_selector($selector);
135             my @rules = parse_selector($selector, %options);
136              
137             CSS selectors are mapped to Perl data structures. Each set of selectors is
138             returned as an arrayref of hashrefs (see L for an example).
139              
140             Supported options:
141              
142             =over
143              
144             =item class_as_array
145              
146             If set, C will always be an arrayref, even if no class was present in
147             the selector (in which case it will be empty).
148              
149             See the description of C below.
150              
151             =back
152              
153             The hashrefs have:
154              
155             =over
156              
157             =item element
158              
159             C in C.
160              
161             =item id
162              
163             C in C. Note: NOT C<[id="..."]>.
164              
165             =item class
166              
167             C in C if C option is not set.
168              
169             [C, C] in C if C option is set.
170              
171             Note: NOT C<[class="..."]>.
172              
173             =item attr
174              
175             A hashref of attribute selectors, each of which has a hashref of operators and
176             values:
177              
178             parse_selector('[foo="bar"]')
179             # [ { attr => { foo => { '=' => 'bar' } } } ]
180              
181             Attribute selectors can also test for presence:
182              
183             parse_selector('[foo]')
184             # [ { attr => { foo => undef } } ]
185              
186             =item pseudo
187              
188             A hashref of pseudo-classes and their contents, if present:
189              
190             parse_selector(':active:nth(2)')
191             # [ { pseudo => { active => undef, nth => 2 } } ]
192              
193             =item combinator
194              
195             All hashrefs after the first will have this. One of C<<[ >+]>>. See
196             L for an example.
197              
198             =back
199              
200             =head1 SEE ALSO
201              
202             L, from which I stole code
203              
204             =head1 AUTHOR
205              
206             Hans Dieter Pearcey
207              
208             =head1 COPYRIGHT AND LICENSE
209              
210             This software is copyright (c) 2011 by Hans Dieter Pearcey .
211              
212             This is free software; you can redistribute it and/or modify it under
213             the same terms as the Perl 5 programming language system itself.
214              
215             =cut
216              
217              
218             __END__