File Coverage

blib/lib/Tag/Reader/Perl.pm
Criterion Covered Total %
statement 167 177 94.3
branch 75 90 83.3
condition 78 102 76.4
subroutine 14 14 100.0
pod 4 4 100.0
total 338 387 87.3


line stmt bran cond sub pod time code
1             package Tag::Reader::Perl;
2              
3             # Pragmas.
4 14     14   265545 use strict;
  14         20  
  14         331  
5 14     14   46 use warnings;
  14         16  
  14         325  
6              
7             # Modules.
8 14     14   1199 use Class::Utils qw(set_params);
  14         48712  
  14         474  
9 14     14   191 use Error::Pure qw(err);
  14         16  
  14         399  
10 14     14   44 use Readonly;
  14         17  
  14         30059  
11              
12             # Constants.
13             Readonly::Scalar my $EMPTY_STR => q{};
14              
15             # Version.
16             our $VERSION = '0.01';
17              
18             # Constructor.
19             sub new {
20 55     55 1 23767 my ($class, @params) = @_;
21 55         100 my $self = bless {}, $class;
22              
23             # Process params.
24 55         141 set_params($self, @params);
25              
26             # Object.
27 53         230 return $self;
28             }
29              
30             # Get tag token.
31             sub gettoken {
32 170     170 1 26130 my $self = shift;
33              
34             # Stay.
35 170         184 $self->{'stay'} = 0;
36 170         168 $self->{'spec_stay'} = 0;
37 170         136 $self->{'old_stay'} = 0;
38              
39             # Data.
40 170         183 $self->{'data'} = [];
41              
42             # Tag type.
43 170         278 $self->{'tag_type'} = '!data';
44 170         123 $self->{'tag_length'} = 0;
45              
46             # Braces.
47 170         210 ($self->{'brace'}, $self->{'bracket'}) = (0, 0);
48              
49             # Quote.
50 170         163 $self->{'quote'} = $EMPTY_STR;
51              
52             # Tag line.
53 170         139 $self->{'tagline'} = $self->{'textline'};
54 170         130 $self->{'tagcharpos'} = 0;
55              
56 170 100       373 if (exists $self->{'text'}) {
    50          
57 32   66     160 while (exists $self->{'text'}
      66        
58             && $self->{'stay'} < 98
59             && defined ($self->{'char'}
60             = substr $self->{'text'}, 0, 1)) {
61              
62 705         678 $self->_gettoken;
63             }
64             } elsif (exists $self->{'filename'}) {
65 138   100     254 while ($self->{'stay'} < 98
      66        
66             && ((defined ($self->{'char'}
67             = shift @{$self->{'old_data'}}))
68             || defined ($self->{'char'}
69             = getc $self->{'filename'}))) {
70              
71 3764         4024 $self->_gettoken;
72             }
73             }
74              
75 170         123 my $data = join $EMPTY_STR, @{$self->{'data'}};
  170         407  
76 170 50       239 if ($data eq $EMPTY_STR) {
77 0         0 return ();
78             }
79             return wantarray ? ($data, $self->{'tag_type'}, $self->{'tagline'},
80 170 50       706 $self->{'tagcharpos'}) : $data;
81             }
82              
83             # Set file.
84             sub set_file {
85 52     52 1 1881 my ($self, $file, $force) = @_;
86 52 50 33     1163 if (! $file || ! -r $file) {
87 0         0 err 'Bad file.';
88             }
89 52 50 33     329 if (! $force && (defined $self->{'text'}
      33        
90             || defined $self->{'filename'})) {
91              
92 0         0 err 'Cannot set new data if exists data.';
93             }
94 52         42 my $inf;
95 52 50       1345 if (! open $inf, '<', $file) {
96 0         0 err "Cannot open file '$file'.";
97             }
98 52         104 $self->{'filename'} = $inf;
99              
100             # Reset values.
101 52         96 $self->_reset;
102              
103 52         90 return;
104             }
105              
106             # Set text.
107             sub set_text {
108 3     3 1 1041 my ($self, $text, $force) = @_;
109 3 50       8 if (! $text) {
110 0         0 err 'Bad text.';
111             }
112 3 0 0     6 if (! $force && (defined $self->{'text'}
      33        
113             || defined $self->{'filename'})) {
114              
115 0         0 err 'Cannot set new data if exists data.';
116             }
117 3         4 $self->{'text'} = $text;
118              
119             # Reset values.
120 3         5 $self->_reset;
121              
122 3         4 return;
123             }
124              
125             # Reset class values.
126             sub _reset {
127 55     55   56 my $self = shift;
128              
129             # Default values.
130 55         59 $self->{'charpos'} = 0;
131 55         59 $self->{'tagcharpos'} = 0;
132 55         53 $self->{'textline'} = 1;
133 55         50 $self->{'tagline'} = 0;
134 55         70 $self->{'old_data'} = [];
135              
136 55         69 return;
137             }
138              
139             # Main get token.
140             sub _gettoken {
141 4469     4469   2853 my $self = shift;
142              
143             # Char position.
144 4469         2931 $self->{'charpos'}++;
145              
146             # Normal tag.
147 4469 100       4539 if ($self->{'spec_stay'} == 0) {
148              
149             # Begin of normal tag.
150 1092 100 100     3223 if ($self->{'stay'} == 0 && $self->{'char'} eq '<') {
    100          
    50          
151              
152             # In tag.
153 166 100       115 if ($#{$self->{'data'}} == -1) {
  166         262  
154             $self->{'tagcharpos'}
155 111         233 = $self->{'charpos'};
156 111         91 $self->{'stay'} = 1;
157 111         82 push @{$self->{'data'}}, $self->{'char'};
  111         169  
158 111         119 $self->{'tag_length'} = 1;
159              
160             # Start of tag, after data.
161             } else {
162 55         46 $self->{'stay'} = 99;
163             }
164              
165             # Text.
166             } elsif ($self->{'stay'} == 0) {
167 190         150 push @{$self->{'data'}}, $self->{'char'};
  190         260  
168 190 100       279 if ($self->{'tagcharpos'} == 0) {
169             $self->{'tagcharpos'}
170 59         65 = $self->{'charpos'};
171             }
172              
173             # In a normal tag.
174             } elsif ($self->{'stay'} == 1) {
175              
176             # End of normal tag.
177 736 100 66     2011 if ($self->{'char'} eq '>') {
    100 100        
    100          
178 47         45 $self->{'stay'} = 98;
179 47         57 $self->_tag_type;
180 47         35 push @{$self->{'data'}}, $self->{'char'};
  47         59  
181 47         43 $self->{'tag_length'} = 0;
182              
183             # First charcter after '<' in normal tag.
184             } elsif ($self->{'tag_length'} == 1
185             && _is_first_char_of_tag($self->{'char'})) {
186              
187 111 100       175 if ($self->{'char'} eq q{!}) {
188 64         60 $self->{'spec_stay'} = 1;
189             }
190 111         90 push @{$self->{'data'}}, $self->{'char'};
  111         151  
191 111         117 $self->{'tag_length'}++;
192              
193             # Next character in normal tag (name).
194             } elsif ($self->{'tag_length'} > 1
195             && _is_in_tag_name($self->{'char'})) {
196              
197 247         145 push @{$self->{'data'}}, $self->{'char'};
  247         352  
198 247         213 $self->{'tag_length'}++;
199              
200             # Other characters.
201             } else {
202 331 50 33     849 if ($self->{'tag_length'} == 1
203             || $self->{'char'} eq '<') {
204              
205 0         0 err 'Bad tag.';
206             }
207 331         299 $self->_tag_type;
208 331         197 push @{$self->{'data'}}, $self->{'char'};
  331         486  
209             }
210             }
211              
212             # Other tags.
213             } else {
214              
215             # End of normal tag.
216 3377 100 100     9005 if ($self->{'char'} eq '>') {
    100          
    100          
    100          
    100          
217 90 100 100     499 if (($self->{'brace'} == 0
      100        
      100        
      66        
      100        
      66        
218             && $self->{'bracket'} == 0
219             && $self->{'spec_stay'} < 3)
220              
221             # Comment.
222             || ($self->{'spec_stay'} == 3
223             && join($EMPTY_STR,
224 6         22 @{$self->{'data'}}[-2 .. -1])
225             eq q{--})
226              
227             # CDATA.
228             || ($self->{'tag_type'} =~ /^!\[cdata\[/ms
229             && join($EMPTY_STR,
230 4         11 @{$self->{'data'}}[-2 .. -1])
231             eq ']]')) {
232              
233 64         46 $self->{'stay'} = 98;
234 64         50 $self->{'spec_stay'} = 0;
235 64         56 $self->{'tag_length'} = 0;
236             }
237 90 100       131 if ($self->{'spec_stay'} != 4) {
238 89         82 $self->{'bracket'}--;
239             }
240 90         48 push @{$self->{'data'}}, $self->{'char'};
  90         139  
241              
242             # Comment.
243             } elsif ($self->{'spec_stay'} == 3) {
244              
245             # '--' is bad.
246 56 50 66     79 if ($self->{'tag_length'} == 0
247 53         122 && join($EMPTY_STR, @{$self->{'data'}}
248             [-2 .. -1]) eq q{--}) {
249              
250 0         0 err 'Bad tag.';
251             }
252 56         51 $self->_tag_type;
253 56         30 push @{$self->{'data'}}, $self->{'char'};
  56         100  
254              
255             # Quote.
256             } elsif ($self->{'spec_stay'} == 4) {
257 812 100       1037 if ($self->{'char'} eq $self->{'quote'}) {
258 34         29 $self->{'spec_stay'} = $self->{'old_stay'};
259 34         33 $self->{'quote'} = $EMPTY_STR;
260             }
261 812         476 push @{$self->{'data'}}, $self->{'char'};
  812         1059  
262              
263             } elsif ($self->{'char'} eq ']') {
264 13         13 push @{$self->{'data'}}, $self->{'char'};
  13         23  
265 13         14 $self->{'brace'}--;
266              
267             # Next character in normal tag (name).
268             } elsif ($self->{'tag_length'} > 1
269             && _is_in_tag_name($self->{'char'})) {
270              
271             # Comment detect.
272 409 100 100     1328 if (($self->{'tag_length'} == 2
      100        
273             || $self->{'tag_length'} == 3)
274             && $self->{'char'} eq q{-}) {
275              
276 6         6 $self->{'spec_stay'}++;
277             }
278 409 100       535 if ($self->{'char'} eq '[') {
279 7         13 $self->{'brace'}++;
280             }
281 409         266 push @{$self->{'data'}}, $self->{'char'};
  409         566  
282 409         360 $self->{'tag_length'}++;
283              
284             # Other characters.
285             } else {
286 1997 100 66     5310 if ($self->{'quote'} eq $EMPTY_STR
287             && $self->{'char'} eq q{"}) {
288              
289 30         30 $self->{'quote'} = q{"};
290 30         23 $self->{'old_stay'} = $self->{'spec_stay'};
291 30         25 $self->{'spec_stay'} = 4;
292             }
293 1997 100 100     4880 if ($self->{'quote'} eq $EMPTY_STR
294             && $self->{'char'} eq q{'}) {
295              
296 4         6 $self->{'quote'} = q{'};
297 4         3 $self->{'old_stay'} = $self->{'spec_stay'};
298 4         4 $self->{'spec_stay'} = 4;
299             }
300 1997 100       2331 if ($self->{'char'} eq '<') {
301 22         22 $self->{'bracket'}++;
302             }
303 1997 100       2265 if ($self->{'char'} eq '[') {
304 5         6 $self->{'brace'}++;
305             }
306 1997         2176 $self->_tag_type;
307 1997         1124 push @{$self->{'data'}}, $self->{'char'};
  1997         2892  
308             }
309             }
310              
311             # Remove char from buffer.
312 4469 100       4793 if ($self->{'stay'} != 99) {
313 4414 100       5241 if (exists $self->{'text'}) {
314 689 50       666 if (length $self->{'text'} > 1) {
315 689         736 $self->{'text'} = substr $self->{'text'}, 1;
316             } else {
317 0         0 delete $self->{'text'};
318             }
319             }
320             } else {
321 55 50 33     154 if (exists $self->{'filename'}
322             && defined $self->{'char'}) {
323              
324 55         40 push @{$self->{'old_data'}}, $self->{'char'};
  55         75  
325             }
326             }
327 4469 100 100     11190 if ($self->{'stay'} == 98 || $self->{'stay'} == 99) {
328 166 100       229 if ($self->{'stay'} == 99) {
329 55         40 $self->{'charpos'}--;
330             }
331             }
332              
333             # Next line.
334 4469 100       5291 if ($self->{'char'} eq "\n") {
335 89         71 $self->{'textline'}++;
336 89         71 $self->{'charpos'} = 0;
337             }
338              
339 4469         8412 return;
340             }
341              
342             # First character in tag.
343             sub _is_first_char_of_tag {
344 111     111   107 my $char = shift;
345 111 50 100     451 if ($char eq q{!} || $char eq q{/} || $char eq q{?}
      100        
      66        
346             || $char =~ /^[\d\w]+$/ms) {
347              
348 111         272 return 1;
349             }
350 0         0 return 0;
351             }
352              
353             # Normal characters in a tag name.
354             sub _is_in_tag_name {
355 732     732   624 my $char = shift;
356 732 100 100     5290 if ($char eq q{:} || $char eq '[' || $char eq q{-} || $char eq q{%}
      100        
      100        
      100        
357             || $char =~ /^[\d\w]+$/ms) {
358              
359 656         1333 return 1;
360             }
361 76         182 return 0;
362             }
363              
364             # Process tag type.
365             sub _tag_type {
366 2431     2431   1576 my $self = shift;
367 2431 100       2829 if ($self->{'tag_length'} > 0) {
368             $self->{'tag_type'}
369 111         282 = lc join $EMPTY_STR, @{$self->{'data'}}
370 111         178 [1 .. $self->{'tag_length'} - 1];
371 111         124 $self->{'tag_length'} = 0;
372             }
373 2431         1781 return;
374             }
375              
376             1;
377              
378             __END__