switch replace to collect then emit, document that it isn't strictly required to...
[catagits/HTML-Zoom.git] / lib / HTML / Zoom / SelectorParser.pm
CommitLineData
456a815d 1package HTML::Zoom::SelectorParser;
2
3use strict;
4use warnings FATAL => 'all';
5use Carp qw(confess);
6
7my $sel_char = '-\w_';
8my $sel_re = qr/([$sel_char]+)/;
9
10sub new { bless({}, shift) }
11
12sub _raw_parse_simple_selector {
13 for ($_[1]) { # same pos() as outside
14
15 # '*' - match anything
16
17 /\G\*/gc and
18 return sub { 1 };
19
20 # 'element' - match on tag name
21
22 /\G$sel_re/gc and
23 return do {
24 my $name = $1;
25 sub { $_[0]->{name} && $_[0]->{name} eq $name }
26 };
27
28 # '#id' - match on id attribute
29
30 /\G#$sel_re/gc and
31 return do {
32 my $id = $1;
33 sub { $_[0]->{attrs}{id} && $_[0]->{attrs}{id} eq $id }
34 };
35
36 # '.class1.class2' - match on intersection of classes
37
38 /\G((?:\.$sel_re)+)/gc and
39 return do {
40 my $cls = $1; $cls =~ s/^\.//;
41 my @cl = split(/\./, $cls);
42 sub {
43 $_[0]->{attrs}{class}
44 && !grep $_[0]->{attrs}{class} !~ /\b$_\b/, @cl
45 }
46 };
47
48 confess "Couldn't parse $_ as starting with simple selector";
49 }
50}
51
52sub parse_selector {
53 my $self = $_[0];
54 my $sel = $_[1]; # my pos() only please
55 die "No selector provided" unless $sel;
56 local *_;
57 for ($sel) {
58 my @sub;
59 PARSE: { do {
60 push(@sub, $self->_raw_parse_simple_selector($_));
61 last PARSE if (pos == length);
62 /\G\s*,\s*/gc or confess "Selectors not comma separated";
63 } until (pos == length) };
64 return $sub[0] if (@sub == 1);
65 return sub {
66 foreach my $inner (@sub) {
67 if (my $r = $inner->(@_)) { return $r }
68 }
69 };
70 }
71}
72
73
741;