File Coverage

blib/lib/Pod/POM.pm
Criterion Covered Total %
statement 174 221 78.7
branch 69 110 62.7
condition 11 24 45.8
subroutine 15 18 83.3
pod 4 11 36.3
total 273 384 71.0


line stmt bran cond sub pod time code
1             #============================================================= -*-Perl-*-
2             #
3             # Pod::POM
4             #
5             # DESCRIPTION
6             # Parses POD from a file or text string and builds a tree structure,
7             # hereafter known as the POD Object Model (POM).
8             #
9             # AUTHOR
10             # Andy Wardley
11             #
12             # Andrew Ford (co-maintainer as of 03/2009)
13             #
14             # COPYRIGHT
15             # Copyright (C) 2000-2009 Andy Wardley. All Rights Reserved.
16             # Copyright (C) 2009 Andrew Ford. All Rights Reserved.
17             #
18             # This module is free software; you can redistribute it and/or
19             # modify it under the same terms as Perl itself.
20             #
21             # REVISION
22             # $Id: POM.pm 91 2013-12-31 07:36:02Z ford $
23             #
24             #========================================================================
25              
26             package Pod::POM;
27             $Pod::POM::VERSION = '2.01';
28             require 5.006;
29              
30 18     18   71109 use strict;
  18         33  
  18         468  
31 18     18   86 use warnings;
  18         29  
  18         542  
32              
33 18     18   7396 use Pod::POM::Constants qw( :all );
  18         38  
  18         2781  
34 18     18   7977 use Pod::POM::Nodes;
  18         42  
  18         803  
35 18     18   8448 use Pod::POM::View::Pod;
  18         43  
  18         534  
36              
37 18     18   86 use parent qw( Exporter );
  18         28  
  18         69  
38              
39             our $DEBUG = 0 unless defined $DEBUG;
40             our $ROOT = 'Pod::POM::Node::Pod'; # root node class
41             our $TEXTSEQ = 'Pod::POM::Node::Sequence'; # text sequence class
42             our $DEFAULT_VIEW = 'Pod::POM::View::Pod'; # default view class
43              
44              
45             #------------------------------------------------------------------------
46             # allow 'meta' to be specified as a load option to activate =meta tags
47             #------------------------------------------------------------------------
48              
49             our @EXPORT_OK = qw( meta );
50             our @EXPORT_FAIL = qw( meta );
51             our $ALLOW_META = 0;
52              
53             sub export_fail {
54 1     1 0 125 my $class = shift;
55 1         3 my $meta = shift;
56 1 50       4 return ($meta, @_) unless $meta eq 'meta';
57 1         1 $ALLOW_META++;
58 1         48 return @_;
59             }
60              
61              
62              
63             #------------------------------------------------------------------------
64             # new(\%options)
65             #------------------------------------------------------------------------
66              
67             sub new {
68 21     21 1 57924 my $class = shift;
69 21 50       242 my $config = ref $_[0] eq 'HASH' ? shift : { @_ };
70              
71             bless {
72             CODE => $config->{ code } || 0,
73             WARN => $config->{ warn } || 0,
74 21   100     450 META => $config->{ meta } || $ALLOW_META,
      100        
      33        
75             WARNINGS => [ ],
76             FILENAME => '',
77             ERROR => '',
78             }, $class;
79             }
80              
81              
82             #------------------------------------------------------------------------
83             # parse($text_or_file)
84             #
85             # General purpose parse method which attempts to Do The Right Thing in
86             # calling parse_file() or parse_text() according to the argument
87             # passed. A hash reference can be specified that contains a 'text'
88             # or 'file' key and corresponding value. Otherwise, the argument can
89             # be a reference to an input handle which is passed off to parse_file().
90             # If the argument is a text string that contains '=' at the start of
91             # any line then it is treated as Pod text and passed to parse_text(),
92             # otherwise it is assumed to be a filename and passed to parse_file().
93             #------------------------------------------------------------------------
94              
95             sub parse {
96 0     0 1 0 my ($self, $input) = @_;
97 0         0 my $result;
98              
99 0 0 0     0 if (ref $input eq 'HASH') {
    0          
100 0 0       0 if ($input = $input->{ text }) {
    0          
101 0         0 $result = $self->parse_text($input, $input->{ name });
102             }
103             elsif ($input = $input->{ file }) {
104 0         0 $result = $self->parse_file($input);
105             }
106             else {
107 0         0 $result = $self->error("no 'text' or 'file' specified");
108             }
109             }
110             elsif (ref $input || $input !~ /^=/m) { # doesn't look like POD text
111 0         0 $result = $self->parse_file($input);
112             }
113             else { # looks like POD text
114 0         0 $result = $self->parse_text($input);
115             }
116              
117 0         0 return $result;
118             }
119              
120              
121             #------------------------------------------------------------------------
122             # parse_file($filename_or_handle)
123             #
124             # Reads the content of a Pod file specified by name or file handle, and
125             # passes it to parse_text() for parsing.
126             #------------------------------------------------------------------------
127              
128             sub parse_file {
129 5     5 1 35 my ($self, $file) = @_;
130 5         10 my ($text, $name);
131              
132 5 100       21 if (ref $file) { # assume open filehandle
133 4         294 local $/ = undef;
134 4         12 $name = '';
135 4         87 $text = <$file>;
136             }
137             else { # a file which must be opened
138 1         2 local *FP;
139 1         4 local $/ = undef;
140 1 50       3 $name = ( $file eq '-' ? '' : $file );
141 1 50       29 open(FP, $file) || return $self->error("$file: $!");
142 1         26 $text = ;
143 1         9 close(FP);
144             }
145              
146 5         33 $self->parse_text($text, $name);
147             }
148              
149              
150             #------------------------------------------------------------------------
151             # parse_text($text, $name)
152             #
153             # Main parser method. Scans the input text for Pod sections and splits
154             # them into paragraphs. Builds a tree of Pod::POM::Node::* objects
155             # to represent the Pod document in object model form.
156             #------------------------------------------------------------------------
157              
158             sub parse_text {
159 58     58 1 185260 my ($self, $text, $name) = @_;
160 58         93 my ($para, $paralen, $gap, $type, $line, $inpod, $code, $result, $verbatim);
161 58         294 my $warn = $self->{ WARNINGS } = [ ];
162              
163 58         397 my @stack = ( );
164 58         455 my $item = $ROOT->new($self);
165 58 50       163 return $self->error($ROOT->error())
166             unless defined $item;
167 58         95 push(@stack, $item);
168              
169 58 100       149 $name = '' unless defined $name;
170 58         288 $self->{ FILENAME } = $name;
171              
172 58         116 $code = $self->{ CODE };
173 58         122 $line = \$self->{ LINE };
174 58         150 $$line = 1;
175 58         79 $inpod = 0;
176              
177 58         269 my @encchunks = split /^(=encoding.*)/m, $text;
178 58         99 $text = shift @encchunks;
179 58         192 while (@encchunks) {
180 2         6 my($encline,$chunk) = splice @encchunks, 0, 2;
181 2         13 require Encode;
182 2         15 my($encoding) = $encline =~ /^=encoding\s+(\S+)/;
183 2 100 66     16 if ($encoding ne 'utf8' || !Encode::is_utf8($chunk)) {
184 1         5 Encode::from_to($chunk, $encoding, "utf8");
185             }
186 2         888886 Encode::_utf8_on($chunk);
187             # $text .= "xxx$encline";
188 2         16 $text .= $chunk;
189             }
190              
191             # patch from JJ
192             # while ($text =~ /(?:(.*?)(\n{2,}))|(.+$)/sg) {
193 58         622 while ($text =~ /(?:(.*?)((?:\s*\n){2,}))|(.+$)/sg) {
194 846 100       3634 ($para, $gap) = defined $1 ? ($1, $2) : ($3, '');
195              
196 846 100       3832 if ($para =~ s/^==?(\w+)\s*//) {
    100          
    100          
197 431         757 $type = $1;
198             # switch on for =pod or any other =cmd, switch off for =cut
199 431 100       1072 if ($type eq 'pod') { $inpod = 1; next }
  2 100       3  
  2         4  
200 13         23 elsif ($type eq 'cut') { $inpod = 0; next }
  13         22  
201 416         555 else { $inpod = 1 };
202              
203 416 100       918 if ($type eq 'meta') {
204             $self->{ META }
205 2 50       26 ? $stack[0]->metadata(split(/\s+/, $para, 2))
206             : $self->warning("metadata not allowed", $name, $$line);
207 2         5 next;
208             }
209             }
210             elsif (! $inpod) {
211 9 100       25 next unless $code;
212 4         7 $type = 'code';
213 4         7 $para .= $gap;
214 4         5 $gap = '';
215             }
216             elsif ($para =~ /^\s+/) {
217 39         77 $verbatim .= $para;
218 39         53 $verbatim .= $gap;
219 39         60 next;
220             }
221             else {
222 367         508 $type = 'text';
223 367         576 chomp($para); # catches last line in file
224             }
225              
226 785 100       1624 if ($verbatim) {
227 23         63 while(@stack) {
228 38         199 $verbatim =~ s/\s+$//s;
229 38         131 $result = $stack[-1]->add($self, 'verbatim', $verbatim);
230            
231 38 50       193 if (! defined $result) {
    100          
    50          
    50          
    50          
232 0         0 $self->warning($stack[-1]->error(), $name, $$line);
233 0         0 undef $verbatim;
234 0         0 last;
235             }
236             elsif (ref $result) {
237 23         36 push(@stack, $result);
238 23         34 undef $verbatim;
239 23         41 last;
240             }
241             elsif ($result == REDUCE) {
242 0         0 pop @stack;
243 0         0 undef $verbatim;
244 0         0 last;
245             }
246             elsif ($result == REJECT) {
247 0         0 $self->warning($stack[-1]->error(), $name, $$line);
248 0         0 pop @stack;
249             }
250             elsif (@stack == 1) {
251 0         0 $self->warning("unexpected $type", $name, $$line);
252 0         0 undef $verbatim;
253 0         0 last;
254             }
255             else {
256 15         41 pop @stack;
257             }
258             }
259             }
260              
261 785         1659 while(@stack) {
262 1356         4291 $result = $stack[-1]->add($self, $type, $para);
263            
264 1356 100       4445 if (! defined $result) {
    100          
    100          
    100          
    100          
265 3         8 $self->warning($stack[-1]->error(), $name, $$line);
266 3         14 last;
267             }
268             elsif (ref $result) {
269 725         947 push(@stack, $result);
270 725         1030 last;
271             }
272             elsif ($result == REDUCE) {
273 54         67 pop @stack;
274 54         99 last;
275             }
276             elsif ($result == REJECT) {
277 3         10 $self->warning($stack[-1]->error(), $name, $$line);
278 3         19 pop @stack;
279             }
280             elsif (@stack == 1) {
281 3         10 $self->warning("unexpected $type", $name, $$line);
282 3         14 last;
283             }
284             else {
285 568         1383 pop @stack;
286             }
287             }
288             }
289             continue {
290 846         1298 $$line += ($para =~ tr/\n//);
291 846         8418 $$line += ($gap =~ tr/\n//);
292             }
293              
294 58 100       162 if ($verbatim) {
295 1         9 while(@stack) {
296 1         7 $verbatim =~ s/\s+$//s;
297 1         12 $result = $stack[-1]->add($self, 'verbatim', $verbatim);
298            
299 1 50       10 if (! defined $result) {
    50          
    0          
    0          
    0          
300 0         0 $self->warning($stack[-1]->error(), $name, $$line);
301 0         0 undef $verbatim;
302 0         0 last;
303             }
304             elsif (ref $result) {
305 1         2 push(@stack, $result);
306 1         2 undef $verbatim;
307 1         2 last;
308             }
309             elsif ($result == REDUCE) {
310 0         0 pop @stack;
311 0         0 undef $verbatim;
312 0         0 last;
313             }
314             elsif ($result == REJECT) {
315 0         0 $self->warning($stack[-1]->error(), $name, $$line);
316 0         0 pop @stack;
317             }
318             elsif (@stack == 1) {
319 0         0 $self->warning("unexpected $type", $name, $$line);
320 0         0 undef $verbatim;
321 0         0 last;
322             }
323             else {
324 0         0 pop @stack;
325             }
326             }
327             }
328              
329 58         252 return $stack[0];
330             }
331              
332              
333             #------------------------------------------------------------------------
334             # parse_sequence($text)
335             #
336             # Parse a text paragraph to identify internal sequences (e.g. B)
337             # which may be nested within each other. Returns a simple scalar (no
338             # embedded sequences) or a reference to a Pod::POM::Text object.
339             #------------------------------------------------------------------------
340              
341             sub parse_sequence {
342 640     640 0 1000 my ($self, $text) = @_;
343 640         706 my ($cmd, $lparen, $rparen, $plain);
344 640         1373 my ($name, $line, $warn) = @$self{ qw( FILENAME LINE WARNINGS ) };
345 640         703 my @stack;
346              
347 640         1832 push(@stack, [ '', '', 'EOF', $name, $line, [ ] ] );
348            
349 640         6697 while ($text =~ /
350             (?: ([A-Z]) (< (?:<+\s)?) ) # open
351             | ( (?:\s>+)? > ) # or close
352             | (?: (.+?) # or text...
353             (?= # ...up to
354             (?: [A-Z]< ) # open
355             | (?: (?: \s>+)? > ) # or close
356             | $ # or EOF
357             )
358             )
359             /gxs) {
360 1386 100       4823 if (defined $1) {
    100          
    50          
361 192         422 ($cmd, $lparen) = ($1, $2);
362 192         408 $lparen =~ s/\s$//;
363 192         315 ($rparen = $lparen) =~ tr//;
364 192         1705 push(@stack, [ $cmd, $lparen, $rparen, $name, $line, [ ] ]);
365             }
366             elsif (defined $3) {
367 204         349 $rparen = $3;
368 204         389 $rparen =~ s/^\s+//;
369 204 100       481 if ($rparen eq $stack[-1]->[RPAREN]) {
370 189   50     588 $cmd = $TEXTSEQ->new(pop(@stack))
371             || return $self->error($TEXTSEQ->error());
372 189         271 push(@{ $stack[-1]->[CONTENT] }, $cmd);
  189         1418  
373             }
374             else {
375 15 100       94 $self->warning((scalar @stack > 1
376             ? "expected '$stack[-1]->[RPAREN]' not '$rparen'"
377             : "spurious '$rparen'"), $name, $line);
378 15         45 push(@{ $stack[-1]->[CONTENT] }, $rparen);
  15         146  
379             }
380             }
381             elsif (defined $4) {
382 990         1630 $plain = $4;
383 990         1100 push(@{ $stack[-1]->[CONTENT] }, $plain);
  990         2264  
384 990         3481 $line += ($plain =~ tr/\n//);
385             }
386             else {
387 0         0 $self->warning("unexpected end of input", $name, $line);
388 0         0 last;
389             }
390             }
391              
392 640         1461 while (@stack > 1) {
393 3         6 $cmd = pop @stack;
394 3         64 $self->warning("unterminated '$cmd->[CMD]$cmd->[LPAREN]' starting",
395             $name, $cmd->[LINE]);
396 3   33     23 $cmd = $TEXTSEQ->new($cmd)
397             || $self->error($TEXTSEQ->error());
398 3         5 push(@{ $stack[-1]->[CONTENT] }, $cmd);
  3         9  
399             }
400              
401 640   33     2126 return $TEXTSEQ->new(pop(@stack))
402             || $self->error($TEXTSEQ->error());
403             }
404              
405              
406             #------------------------------------------------------------------------
407             # default_view($viewer)
408             #
409             # Accessor method to return or update the $DEFVIEW package variable,
410             # loading the module for any package name specified.
411             #------------------------------------------------------------------------
412              
413             sub default_view {
414 1     1 0 6 my ($self, $viewer) = @_;
415 1 50       3 return $DEFAULT_VIEW unless $viewer;
416 1 50       8 unless (ref $viewer) {
417 1         2 my $file = $viewer;
418 1         4 $file =~ s[::][/]g;
419 1         2 $file .= '.pm';
420 1         2 eval { require $file };
  1         515  
421 1 50       11 return $self->error($@) if $@;
422             }
423              
424 1         4 return ($DEFAULT_VIEW = $viewer);
425             }
426              
427              
428             #------------------------------------------------------------------------
429             # warning($msg, $file, $line)
430             #
431             # Appends a string of the form " at $file line $line" to $msg if
432             # $file is specified and then stores $msg in the internals
433             # WARNINGS list. If the WARN option is set then the warning is
434             # raised, either via warn(), or by dispatching to a subroutine
435             # when WARN is defined as such.
436             #------------------------------------------------------------------------
437              
438             sub warning {
439 27     27 0 51 my ($self, $msg, $file, $line) = @_;
440 27         43 my $warn = $self->{ WARN };
441 27 50 33     146 $line = 'unknown' unless defined $line && length $line;
442 27 50       99 $msg .= " at $file line $line" if $file;
443              
444 27         36 push(@{ $self->{ WARNINGS } }, $msg);
  27         62  
445              
446 27 100       108 if (ref $warn eq 'CODE') {
    100          
447 6         15 &$warn($msg);
448             }
449             elsif ($warn) {
450 6         49 warn($msg, "\n");
451             }
452             }
453              
454              
455             #------------------------------------------------------------------------
456             # warnings()
457             #
458             # Returns a reference to the (possibly empty) list of warnings raised by
459             # the most recent call to any of the parse_XXX() methods
460             #------------------------------------------------------------------------
461              
462             sub warnings {
463 4     4 0 16 my $self = shift;
464 4 50       10 return wantarray ? @{ $self->{ WARNINGS } } : $self->{ WARNINGS };
  4         19  
465             }
466              
467              
468             #------------------------------------------------------------------------
469             # error($msg)
470             #
471             # Sets the internal ERROR member and returns undef when called with an
472             # argument(s), returns the current value when called without.
473             #------------------------------------------------------------------------
474              
475             sub error {
476 0     0 0   my $self = shift;
477 0           my $errvar;
478              
479             {
480 18     18   47553 no strict qw( refs );
  18         42  
  18         3003  
  0            
481 0 0         if (ref $self) {
482 0           $errvar = \$self->{ ERROR };
483             }
484             else {
485 0           $errvar = \${"$self\::ERROR"};
  0            
486             }
487             }
488 0 0         if (@_) {
489 0 0         $$errvar = ref($_[0]) ? shift : join('', @_);
490 0           return undef;
491             }
492             else {
493 0           return $$errvar;
494             }
495             }
496              
497              
498              
499             sub DEBUG {
500 0 0   0 0   print STDERR "DEBUG: ", @_ if $DEBUG;
501             }
502              
503             1;
504              
505             __END__