File Coverage

blib/lib/HTML/Blitz/Matcher.pm
Criterion Covered Total %
statement 102 103 99.0
branch 29 44 65.9
condition 2 3 66.6
subroutine 10 10 100.0
pod 0 5 0.0
total 143 165 86.6


line stmt bran cond sub pod time code
1             # This code can be redistributed and modified under the terms of the GNU
2             # General Public License as published by the Free Software Foundation, either
3             # version 3 of the License, or (at your option) any later version.
4             # See the "COPYING" file for details.
5             package HTML::Blitz::Matcher 0.1001;
6 11     11   84 use HTML::Blitz::pragma;
  11         23  
  11         87  
7 11         1014 use HTML::Blitz::SelectorType qw(
8             LT_DESCENDANT
9             LT_CHILD
10             LT_SIBLING
11             LT_ADJACENT_SIBLING
12 11     11   12707 );
  11         41  
13 11     11   105 use Scalar::Util ();
  11         26  
  11         632  
14              
15             use constant {
16 11         2285 INTBITS => length(sprintf '%b', ~0),
17 11     11   64 };
  11         26  
18              
19 276 50   276 0 823 method new($class: $rules) {
  276 50       695  
  276         552  
  276         567  
  276         427  
20 276         11440 bless {
21             slices => [
22             map [ $_, { cur => 0, stack => [{ extra_bits => 0 }] } ], @$rules
23             ],
24             doc_state => [
25             {
26             nth_child => 0,
27             nth_child_of_type => {},
28             on_leave => [],
29             },
30             ],
31             }, $class
32             }
33              
34 1546     1546   9094 fun _guniq(@values) {
  1546         9752  
35 1546         5453 my ($seen_undef, %seen_ref, %seen_str);
36             grep
37             !(
38             ref($_) ? $seen_ref{Scalar::Util::refaddr $_} :
39 1546 0       9896 defined($_) ? $seen_str{$_} :
    50          
40             $seen_undef
41             )++,
42             @values
43             }
44              
45 1546 50   1546 0 3274 method enter($tag, $attributes) {
  1546 50       3027  
  1546         2392  
  1546         20500  
  1546         2165  
46 1546         2689 my $doc_state = $self->{doc_state};
47 1546         6509 my $dsp = $doc_state->[-1];
48 1546         2896 my $nth_child = ++$dsp->{nth_child};
49 1546         7556 my $nth_child_of_type = ++$dsp->{nth_child_of_type}{$tag};
50 1546         9493 push @$doc_state, {
51             nth_child => 0,
52             nth_child_of_type => {},
53             on_leave => [],
54             };
55              
56 1546         2653 my @ret;
57              
58 1546         2076 for my $slice (@{$self->{slices}}) {
  1546         8134  
59 1771         3628 my ($glass, $goop) = @$slice;
60 1771         2887 my $cur = $goop->{cur};
61 1771         4337 my $stack = $goop->{stack};
62 1771         11114 my $sp = $stack->[-1];
63 1771         2907 my $extra_volatile = $sp->{extra_volatile};
64 1771         3223 $sp->{extra_volatile} = [];
65              
66 1771         12050 push @$stack, my $sp_next = {
67             extra_bits => 0,
68             };
69 1771         7186 my $cur_next;
70              
71 1771         2560 for my $i ($cur, @{$sp->{extra}}, @$extra_volatile) {
  1771         4980  
72 1806         3021 my $sss = $glass->[$i];
73 1806 100       7119 $sss->matches($tag, $attributes, $nth_child, $nth_child_of_type)
74             or next;
75              
76 521         1569 my $link = $sss->link_type;
77 521         890 my $k = $i + 1;
78 521         937 my $bit_shift = $k - $cur - 1;
79 521 50       1141 $bit_shift < INTBITS
80             or die "Internal error: Too many combinators in a single selector (" . ($bit_shift + 1) . " exceeds limit of " . INTBITS . ")";
81 521         905 my $bit = 1 << $bit_shift;
82              
83 521 100       1169 if (!defined $link) {
    100          
    100          
    100          
    50          
84 439         1234 push @ret, $glass->[$k];
85             } elsif ($link eq LT_DESCENDANT) {
86 33         57 $cur_next = $k;
87             } elsif ($link eq LT_CHILD) {
88 44 50       72 if (!($sp_next->{extra_bits} & $bit)) {
89 44         49 $sp_next->{extra_bits} |= $bit;
90 44         4072 push @{$sp_next->{extra}}, $k;
  44         90  
91             }
92             } elsif ($link eq LT_SIBLING) {
93 1 50       4 if (!($sp->{extra_bits} & $bit)) {
94 1         2 $sp->{extra_bits} |= $bit;
95 1         3 push @{$sp->{extra}}, $k;
  1         2  
96             }
97             } elsif ($link eq LT_ADJACENT_SIBLING) {
98 4         5 push @{$sp->{extra_volatile}}, $k;
  4         8  
99             } else {
100 0         0 die "Internal error: unexpected selector combinator '$link'";
101             }
102             }
103              
104 1771 100       5290 if (defined $cur_next) {
105 33         70 $stack->[-1] = {
106             cur => $cur,
107             extra_bits => 0,
108             };
109 33         99 $goop->{cur} = $cur_next;
110             }
111             }
112              
113             _guniq @ret
114 1546         5342 }
115              
116 1508 50   1508 0 7387 method leave(@args) {
  1508         2389  
  1508         3098  
  1508         2069  
117 1508         3555 my $dsp = pop @{$self->{doc_state}};
  1508         3458  
118 1508 100       4423 if (defined(my $marker = $dsp->{marker})) {
119 4         8 splice @{$self->{slices}}, $marker;
  4         25  
120             }
121              
122 1508         2235 for my $slice (@{$self->{slices}}) {
  1508         5910  
123 1736         2946 my $goop = $slice->[1];
124 1736         5723 my $stack = $goop->{stack};
125 1736         2712 my $sp_prev = pop @$stack;
126 1736 100       8324 if (defined(my $cur = $sp_prev->{cur})) {
127 33         72 $goop->{cur} = $cur;
128             }
129             }
130              
131 1508         3026 for my $cb (reverse @{$dsp->{on_leave}}) {
  1508         8054  
132 9         29 $cb->(@args);
133             }
134             }
135              
136 9 50   9 0 44 method on_leave($callback) {
  9 50       22  
  9         17  
  9         25  
  9         12  
137 9         20 push @{$self->{doc_state}[-1]{on_leave}}, $callback;
  9         46  
138             }
139              
140 7 50   7 0 23 method add_temp_rule(@temp_rules) {
  7         9  
  7         18  
  7         12  
141 7         24 my $slices = $self->{slices};
142 7   66     37 $self->{doc_state}[-1]{marker} //= @$slices;
143 7         55 push @$slices, map [ $_, { cur => 0, stack => [{ extra_bits => 0 }] } ], @temp_rules;
144             }
145              
146             1