File Coverage

blib/lib/PDL/Doc.pm
Criterion Covered Total %
statement 231 335 68.9
branch 57 142 40.1
condition 37 79 46.8
subroutine 40 54 74.0
pod 16 19 84.2
total 381 629 60.5


line stmt bran cond sub pod time code
1             # the filter for the PDL pod format (which is a valid general perl
2             # pod format but with special interpretation of some =for directives)
3              
4             package # hide from PAUSE/MetaCPAN
5             PDL::Doc::SelectJustPod;
6 1     1   129772 use strict;
  1         2  
  1         43  
7 1     1   6 use warnings;
  1         2  
  1         69  
8 1     1   529 use parent qw(Pod::Simple::JustPod);
  1         322  
  1         7  
9              
10             sub select_head1s {
11 3     3   13 my ($self, @titles) = @_;
12 3         27 $self->{interested_head1s}{$_} = 1 for @titles;
13 3         9 $self;
14             }
15              
16             sub select_head2 {
17 3     3   7 my ($self, $name) = @_;
18 3         8 $self->{interested_head2} = $name;
19 3         5 $self;
20             }
21              
22             sub end_head1 {
23 7     7   3454 my $self = shift;
24 7         90 my ($text) = $self->{buffer} =~ /=head1 +(.*)\z/;
25 7         17 $self->{current_head1} = $text;
26 7         16 delete $self->{current_head2};
27 7         28 $self->SUPER::end_head1(@_);
28             }
29              
30             sub end_head2 {
31 6     6   1773 my $self = shift;
32 6         47 my ($text) = $self->{buffer} =~ /=head2 +(.*)\z/;
33 6         16 $self->{current_head2} = $text;
34 6         23 $self->SUPER::end_head2(@_);
35             }
36              
37             sub start_for {
38 11     11   2321 my $self = shift;
39 11         28 my ($attrs) = @_;
40 11         58 $self->{buffer} .= "@$attrs{qw(~really target)}\n\n";
41             }
42              
43             sub emit {
44 31     31   7056 my ($self) = @_;
45 12         57 print { $self->{output_fh} } $self->{buffer}, "\n\n" # superclass adds "" at start which inserts a space
46             if $self->{buffer} ne ''
47             and $self->{interested_head1s}{$self->{current_head1} // ''}
48 31 100 50     486 and ($self->{current_head2}//'') =~ /\b\Q$self->{interested_head2}\E\b/;
      100        
      100        
      100        
49 31         111 $self->{buffer} = "";
50             }
51              
52             sub end_Document { # only override is not adding "=cut"
53 3     3   159 my $self = shift;
54 3         8 $self->emit; # Make sure buffer gets flushed
55             }
56              
57             package # hide from PAUSE/MetaCPAN
58             PDL::PodParser;
59 1     1   57603 use strict;
  1         5  
  1         23  
60 1     1   3 use warnings;
  1         1  
  1         49  
61 1     1   760 use PDL::Core '';
  1         2  
  1         5  
62 1     1   545 use Pod::Simple::PullParser;
  1         5278  
  1         32  
63 1     1   5 use parent qw(Pod::Text);
  1         1  
  1         6  
64              
65             our %Title = ('Example' => 'Example',
66             'Ref' => 'Reference',
67             'Sig' => 'Signature',
68             'Opt' => 'Options',
69             'Usage' => 'Usage',
70             'Bad' => 'Bad value support',
71             );
72              
73             my @h1s = qw(OPERATORS FUNCTIONS CONSTRUCTORS METHODS);
74             sub new {
75 3     3   7 my $class = shift;
76 3         29 my $parser = $class->SUPER::new(@_);
77 3         664 $parser->accept_targets(qw(bad example ref options sig usage));
78 3         164 @{ $parser->{interested_head1s} }{@h1s} = (1) x @h1s;
  3         26  
79 3         10 $parser->{CURFUNC} = undef;
80 3         41 $parser->{SYMHASH} = {};
81 3         9 $parser->{Mode} = "";
82 3         9 $parser->{verbose} = 0;
83 3         9 $parser->{NAME} = 'UNKNOWN';
84 3         9 $parser;
85             }
86              
87             sub cmd_head1 {
88 7     7   6013 my ($this, $attrs, $text) = @_;
89 7         22 $this->{Mode} = $text;
90 7 100       42 $this->{Parmode} = $text =~ /NAME/ ? 'NAME' : 'Body';
91 7         25 '';
92             }
93              
94             sub cmd_head2 {
95 5     5   2560 my ($this, $attrs, $text) = @_;
96 5 50       24 return if $text =~ /^The\s/; # heuristic to deal with GSL::CDF descriptive =head2
97 5 100 50     33 return if !$this->{interested_head1s}{$this->{Mode} // ''};
98             # A function can have multiple names (ex: zeros and zeroes),
99             # so split at the commas
100 4         18 my @funcs = split ',', $text;
101             # Remove parentheses (so myfunc and myfunc() both work)
102 4 50       10 my @names = map {$1 if m/\s*([^\s\(]+)\s*/} @funcs;
  6         60  
103 4 50       20 barf "error parsing function list '$text'"
104             unless @funcs == @names;
105             # check for signatures
106 4         9 my $sym = $this->{SYMHASH};
107 4         12 for (@funcs) {
108 6 50       49 $sym->{$1}{Module} = $this->{NAME} if m/\s*([^\s(]+)\s*/;
109 6 50       24 $sym->{$1}{Sig} = $2 if m/\s*([^\s(]+)\s*\(\s*(.+)\s*\)\s*$/;
110             }
111             # make the first one the current function
112 4 100       20 $sym->{$names[0]}{Names} = join(',',@names) if $#names > 0;
113 4         9 my $name = shift @names;
114             # Make the other names cross-reference the first name
115 4         48 $sym->{$_}{Crossref} = $name for @names;
116 4         14 my $sig = $sym->{$name}{Sig};
117             # diagnostic output
118 4 50       15 print "\nFunction '".join(',',($name,@names))."'\n" if $this->{verbose};
119 4 0 33     12 print "\n\tSignature: $sig\n" if defined $sig && $this->{verbose};
120 4         23 $this->{CURFUNC} = $name;
121             }
122              
123             sub cmd_for {
124 10     10   6624 my ($this, $attrs, $text) = @_;
125 10 100 50     63 return if !$this->{interested_head1s}{$this->{Mode} // ''};
126 9         25 my $tgt = $attrs->{target};
127 9 100       27 $tgt = 'opt' if $tgt eq 'options';
128 9         29 $this->{Parmode} = ucfirst lc $tgt;
129 9 50       27 print "switched now to '$tgt' mode\n" if $this->{verbose};
130             print "\n\t$Title{$this->{Parmode}}\n"
131 9 50 33     54 if $this->{Parmode} !~ /Body/ && $this->{verbose};
132 9         25 '';
133             }
134              
135             sub cmd_para {
136 10     10   6129 my ($this, $attrs, $text) = @_;
137 10 100 50     83 return if $this->{Mode} ne 'NAME' and !$this->{interested_head1s}{$this->{Mode} // ''};
      100        
138 9         36 $this->checkmode($text);
139 9         31 $this->{Parmode} = 'Body'; # and reset parmode
140             }
141              
142             sub checkmode {
143 13     13   31 my ($this,$txt,$verbatim) = @_;
144 13 100 66     77 if ($this->{Mode} =~ /NAME/ && $this->{Parmode} =~ /NAME/) {
145 3 50       11 $this->{NAME} = $1 if $this->trim($txt) =~ /^\s*(\S+)\s*/;
146 3 50       11 print "\nNAME\t$this->{NAME}\n" if $this->{verbose};
147 3         7 $this->{Parmode} = 'Body';
148 3         7 return;
149             }
150 10 100       33 unless ($this->{Parmode} =~ /Body/) {
151 9         20 my $func = $this->{CURFUNC};
152 9 50       21 die "no function defined\n" unless defined $func;
153             $this->{SYMHASH}{$func}{$this->{Parmode}} .=
154 9         47 $this->trim($txt,$verbatim);
155 9 100 100     61 my $cr = ($verbatim && $this->{Parmode} ne 'Sig') ? "\n" : "";
156 9         25 my $out = "\n\t\t$cr".$this->trim($txt,$verbatim);
157 9 50       33 print "$out\n$cr" if $this->{verbose};
158             }
159 10         31 $this->{Parmode} = 'Body';
160             }
161              
162             sub cmd_verbatim {
163 4     4   3087 my ($this, $attrs, $text) = @_;
164 4 50 50     24 return if !$this->{interested_head1s}{$this->{Mode} // ''};
165 4         13 $this->checkmode($text,1);
166             }
167              
168             # this needs improvement
169             # and any formatting information should be removed?
170             # it probably depends
171             sub trim {
172 21     21   47 my ($this,$txt,$verbatim) = @_;
173 21         35 my $ntxt = "";
174             $txt =~ s/(signature|usage):\s*//i if $this->{Parmode} eq 'Sig' ||
175 21 100 100     167 $this->{Parmode} eq 'Usage';
176 21 100       60 if ($this->{Parmode} eq 'Sig') {
177 2         7 $txt =~ s/^\s*//;
178 2         17 $txt =~ s/\s*$//;
179 2         20 while( $txt =~ s/^\((.*)\)$/$1/ ) {}; # Strip BALANCED brackets
180             }
181 21         71 for (split "\n", $txt) {
182 27 100       134 s/^\s*(.*)\s*$/$1/ unless $verbatim;
183 27 50       170 $ntxt .= "$_\n" unless m/^\s*$/;
184             }
185             # $txt =~ s/^\s*(.*)\s*$/$1/;
186 21         53 chomp $ntxt;
187 21         81 return $ntxt;
188             }
189              
190             =head1 NAME
191              
192             PDL::Doc - support for PDL online documentation
193              
194             =head1 SYNOPSIS
195              
196             use PDL::Doc;
197             $onlinedc = PDL::Doc->new($docfile);
198             @match = $onlinedc->search('m/slice|clump/');
199              
200             =head1 DESCRIPTION
201              
202             An implementation of online docs for PDL.
203              
204             =head1 Using PDL documentation
205              
206             PDL::Doc's main use is in the "help" (synonym "?") and "apropos"
207             (synonym "??") commands in the perldl shell. PDL::Doc provides the
208             infrastrucure to index and access PDL's documentation through these
209             commands. There is also an API for direct access to the documentation
210             database (see below).
211              
212             The PDL doc system is built on Perl's pod (Plain Old Documentation),
213             included inline with each module. The PDL core modules are
214             automatically indexed when PDL is built and installed, and there is
215             provision for indexing external modules as well.
216              
217             To include your module's pod into the Perl::Doc index, you should
218             follow the documentation conventions below.
219              
220             =head1 PDL documentation conventions
221              
222             For a package like PDL that has I of functions it
223             is very desirable to have some form of online help to
224             make it easy for users to remind themselves of names,
225             calling conventions and typical usage of the multitude
226             of functions at their disposal. To make it straightforward
227             to extract the relevant information from the POD documentation
228             in source files that make up the PDL distribution
229             certain conventions have been adopted in formatting this
230             documentation.
231              
232             The first convention says that all documentation for
233             PDL functions appears in the POD section introduced
234             by one of the following:
235              
236             =head1 FUNCTIONS
237             =head1 OPERATORS
238             =head1 METHODS
239             =head1 CONSTRUCTORS
240              
241             If you're documenting an object-oriented interface to a class
242             that your module defines, you should use METHODS and CONSTRUCTORS
243             as appropriate. If you are simply adding functions to PDL,
244             use FUNCTIONS and OPERATORS as appropriate.
245              
246             Individual functions or methods in these section are introduced by
247              
248             =head2 funcname
249              
250             where signature is the argumentlist for a PP defined function as
251             explained in L. Generally, PDL documentation is in valid POD
252             format (see L) but uses the C<=for> directive in a
253             special (and non-conformant) way. The C<=for> directive is used to
254             flag to the PDL Pod
255             parser that information is following that will be used to generate
256             online help. In the POD standard, C<=for> is used to provide
257             information in I paragraph. PDL uses it to signal information
258             is in the I paragraph.
259              
260             The PDL Pod parser recognises the following C<=for> directives:
261              
262             =over 5
263              
264             =item Ref
265              
266             indicates that the one line reference for this function follows,
267             e.g.,
268              
269             =for ref
270              
271             Returns an ndarray of lags to parent.
272              
273             =item Sig
274              
275             the signature for the current function follows, e.g.,
276              
277             =for sig
278              
279             Signature: (a(n), [o]b(), [t]tmp(n))
280              
281             =item Usage
282              
283             an indication of the possible calling conventions for the current
284             function, e.g.,
285              
286             =for usage
287              
288             wpic($pdl,$filename[,{ options... }])
289              
290             =item Options
291              
292             lists options for the current function, e.g.,
293              
294             =for options
295              
296             CONVERTER => 'ppmtogif', # explicitly specify pbm converter
297             FLAGS => '-interlaced -transparent 0', # flags for converter
298             IFORM => 'PGM', # explicitly specify intermediate format
299             XTRAFLAGS => '-imagename iris', # additional flags to defaultflags
300             FORMAT => 'PCX', # explicitly specify output image format
301             COLOR => 'bw', # specify color conversion
302             LUT => $lut, # use color table information
303              
304              
305             =item Example
306              
307             gives examples of typical usage for the current function:
308              
309             =for example
310              
311             wpic $pdl, $file;
312             $im->wpic('web.gif',{LUT => $lut});
313             for (@images) {
314             $_->wpic($name[0],{CONVERTER => 'ppmtogif'})
315             }
316              
317             =item Bad
318              
319             provides information on how the function handles bad values. The
320             documentation under this directive should indicate if this function
321             accepts ndarrays with bad values and under what circumstances this function
322             might return ndarrays with bad values.
323              
324             =back
325              
326             The PDL podparser is implemented as a simple state machine. Any of
327             the above C<=for> statements switches the podparser into a state
328             where the following paragraph is accepted as information for the
329             respective field (C, C, C, C or C).
330             Only the text up to
331             the end of the current paragraph is accepted, for example:
332              
333             =for example
334              
335             ($x,$y) = $z->func(1,3); # this is part of the accepted info
336             $x = func($z,0,1); # this as well
337              
338             $x = func($c,$d); # but this isn't
339              
340             To make the resulting pod documentation also easily digestible for the
341             existing pod filters (pod2man, pod2text, pod2html, etc) the actual
342             textblock of information must be separated from the C<=for> directive
343             by at least one blank line. Otherwise, the textblock will be lost in
344             the translation process when the "normal" podformatters are used. The
345             general idea behind this format is that it should be easy to extract
346             the information for online documentation, automatic generation of a
347             reference card, etc but at the same time the documentation should be
348             translated by the standard podformatters without loss of contents
349             (and without requiring any changes in the existing POD format).
350              
351             The preceding explanations should be further explained by the
352             following example (extracted from PDL/IO/Misc/misc.pd):
353              
354             =head2 rcols()
355              
356             =for ref
357              
358             Read ASCII whitespaced cols from file into ndarrays efficiently.
359              
360             If no columns are specified all are assumed
361             Will optionally only process lines matching a pattern.
362             Can take file name or *HANDLE.
363              
364             =for usage
365              
366             Usage: ($x,$y,...) = rcols(*HANDLE|"filename", ["/pattern/",$col1, $col2,] ...)
367              
368             e.g.,
369              
370             =for example
371              
372             ($x,$y) = rcols 'file1'
373             ($x,$y,$z) = rcols 'file2', "/foo/",3,4
374             $x = PDL->rcols 'file1';
375              
376             Note: currently quotes are required on the pattern.
377              
378              
379             which is translated by, e.g, the standard C converter into:
380              
381             rcols()
382              
383             Read ASCII whitespaced cols from file into ndarrays efficiently.
384              
385             If no columns are specified all are assumed Will optionally only
386             process lines matching a pattern. Can take file name or *HANDLE.
387              
388             Usage: ($x,$y,...) = rcols(*HANDLE|"filename", ["/pattern/",$col1, $col2,] ...)
389              
390             e.g.,
391              
392             ($x,$y) = rcols 'file1'
393             ($x,$y,$z) = rcols 'file2', "/foo/",3,4
394             $x = PDL->rcols 'file1';
395              
396             Note: currently quotes are required on the pattern.
397              
398             It should be clear from the preceding example that readable output
399             can be obtained from this format using the standard converters and
400             the reader will hopefully get a feeling how they can easily intersperse
401             the special C<=for> directives with the normal POD documentation.
402              
403             =head2 Which directives should be contained in the documentation
404              
405             The module documentation should
406             start with the
407              
408             =head1 NAME
409              
410             PDL::Modulename -- do something with ndarrays
411              
412             section (as anyway required by C) since the PDL podparser
413             extracts the name of the module this function belongs to from
414             that section.
415              
416             Each function that is I only for internal use by the module
417             should be documented, introduced with the C<=head2> directive
418             in the C<=head1 FUNCTIONS> section. The only field that every function
419             documented along these lines should have is the I field preceding
420             a one line description of its intended functionality (suitable for
421             inclusion in a concise reference card). PP defined functions (see L)
422             should have a I field stating their signature. To facilitate
423             maintenance of this documentation for such functions the 'Doc' field
424             has been introduced into the definition of C (see again L)
425             which will take care that name and signature of the so defined function
426             are documented in this way (for examples of this usage see, for example,
427             L.
428             Similarly, the 'BadDoc' field provides a means of
429             specifying information on how the routine handles the presence of
430             bad values: this will be automatically created if
431             C is not supplied, or set to C.
432              
433             Furthermore, the documentation for each function should contain
434             at least one of the I or I fields. Depending on the
435             calling conventions for the function under consideration presence
436             of both fields may be warranted.
437              
438             If a function has options that should be given as a hash reference in
439             the form
440              
441             {Option => Value, ...}
442              
443             then the possible options (and aproppriate values) should be explained
444             in the textblock following the C<=for Opt> directive (see example above
445             and, e.g., PDL::IO::Pic).
446              
447             It is well possible that some of these conventions appear to be clumsy
448             at times and the author is keen to hear of any suggestions for better
449             alternatives.
450              
451             =cut
452              
453             package PDL::Doc;
454 1     1   20454 use strict;
  1         2  
  1         31  
455 1     1   4 use warnings;
  1         1  
  1         43  
456 1     1   3 use PDL::Core '';
  1         1  
  1         10  
457 1     1   6 use File::Basename;
  1         1  
  1         77  
458 1     1   391 use File::Spec::Functions qw(file_name_is_absolute abs2rel rel2abs catdir catfile);
  1         727  
  1         83  
459 1     1   6 use Cwd (); # to help Debian packaging
  1         1  
  1         22  
460 1     1   4 use Config;
  1         2  
  1         29  
461 1     1   3 use Encode;
  1         2  
  1         2911  
462              
463             our $pager = $ENV{PERLDOC_PAGER} // $ENV{PAGER} // $Config{pager};
464              
465             =head1 METHODS
466              
467             =head2 new
468              
469             $onlinedc = PDL::Doc->new('file.pdl',[more files]);
470              
471             =cut
472              
473             sub new {
474 0     0 1 0 my ($type,@files) = @_;
475 0         0 my $this = bless {},$type;
476 0         0 $this->{File} = [@files];
477 0         0 $this->{Scanned} = [];
478 0         0 $this->{Outfile} = $files[0];
479 0         0 $this;
480             }
481              
482             =head2 new_from_hash
483              
484             $onlinedc = PDL::Doc->new_from_hash(\%hash);
485              
486             The hash must conform to the 3-level hash format.
487              
488             =cut
489              
490             sub new_from_hash {
491 1     1 1 2235 my ($type, $hash) = @_;
492 1         5 my $this = bless {},$type;
493 1         6 $this->{File} = [];
494 1         3 $this->{Scanned} = [];
495 1         4 $this->{SYMS} = $hash;
496 1         4 $this;
497             }
498              
499             =head2 addfiles
500              
501             add another file to the online database associated with this object.
502              
503             =cut
504              
505             sub addfiles {
506 0     0 1 0 my ($this,@files) = @_;
507 0         0 push @{$this->{File}}, @files;
  0         0  
508             }
509              
510             =head2 outfile
511              
512             set the name of the output file for this online db
513              
514             =cut
515              
516             sub outfile {
517 0     0 1 0 my ($this,$file) = @_;
518 0 0       0 $this->{Outfile} = $file if defined $file;
519 0         0 return $this->{Outfile};
520             }
521              
522             =head2 ensuredb
523              
524             Make sure that the database is slurped in
525              
526             =cut
527              
528             sub ensuredb {
529 1     1 1 2 my ($this) = @_;
530 1         4 while (my $fi = pop @{$this->{File}}) {
  1         6  
531 0 0       0 open my $fh, $fi or barf "can't open database $fi, scan docs first";
532 0         0 my $got_hash = decodedb($fh, $fi);
533 0   0     0 merge_hash($this->{SYMS} ||= {}, $got_hash);
534 0         0 push @{$this->{Scanned}}, $fi;
  0         0  
535             }
536 1         2 return $this->{SYMS};
537             }
538              
539             =head2 savedb
540              
541             save the database (i.e., the hash of PDL symbols) to the file associated
542             with this object.
543              
544             =cut
545              
546             sub savedb {
547 0     0 1 0 my ($this) = @_;
548 0         0 my $hash = $this->ensuredb;
549 0 0       0 open my $fh, '>', $this->{Outfile} or barf "can't write to symdb $this->{Outfile}: $!";
550 0         0 encodedb($hash, $fh, dirname($this->{Outfile}));
551             }
552              
553             =head2 gethash
554              
555             Return the PDL symhash (e.g. for custom search operations). To see what
556             it has stored in it in JSON format:
557              
558             perl -MPDL::Doc -MJSON::PP -e \
559             'print encode_json +PDL::Doc->new(PDL::Doc::_find_inc([qw(PDL pdldoc.db)]))->gethash' |
560             json_pp -json_opt pretty,canonical
561              
562             The symhash is a multiply nested hash ref with the following structure:
563              
564             $symhash = {
565             function_name => {
566             module::name => {
567             Module => 'module::name',
568             Sig => 'signature string',
569             Bad => 'bad documentation string',
570             ...
571             },
572             },
573             function_name => {
574             module::name => {
575             Module => 'module::name',
576             Sig => 'signature string',
577             Bad => 'bad documentation string',
578             ...
579             },
580             },
581             };
582              
583             The three-layer structure is designed to allow the symhash (and the
584             underlying database) to handle functions that have the same name but
585             reside in different module namespaces.
586              
587             The possible keys for each function/module entry include:
588              
589             Module - module name
590             Sig - signature
591             Crossref - the function name for the documentation, if it has multiple
592             names (ex: the documentation for zeros is under zeroes)
593             Names - a comma-separated string of all the function's names
594             Example - example text (optional)
595             Ref - one-line reference string
596             Opt - options
597             Usage - short usage explanation
598             Bad - explanation of behavior when it encounters bad values
599              
600             =cut
601              
602 0     0 1 0 sub gethash { $_[0]->ensuredb }
603              
604             =head2 search
605              
606             Search a PDL symhash
607              
608             =for usage
609              
610             $onldc->search($regex, $fields [, $sort])
611              
612             Searching is by default case insensitive. Other flags can be
613             given by specifying the regexp in the form C
614             where C can be replaced with any other non-alphanumeric
615             character. $fields is an array reference for all hash fields
616             (or simply a string if you only want to search one field)
617             that should be matched against the regex. Valid fields are
618              
619             Name, # name of the function
620             Module, # module the function belongs to
621             Ref, # the one-line reference description
622             Example, # the example for this function
623             Opt, # options
624             File, # the path to the source file these docs have been extracted from
625              
626             If you wish to have your results sorted by function name, pass a true
627             value for C<$sort>.
628              
629             The results will be returned as an array of triplets in the form
630              
631             @results = (
632             [funcname, module, {SYMHASH_ENTRY}],
633             [funcname, module, {SYMHASH_ENTRY}],
634             ...
635             );
636              
637             See the example at the end of the documentation to see how you might
638             use this.
639              
640             =cut
641              
642              
643             sub search {
644 1     1 1 9 my ($this,$pattern,$fields,$sort) = @_;
645 1 50       5 $sort = 0 unless defined $sort;
646 1         5 my $hash = $this->ensuredb;
647 1         3 my @match = ();
648              
649             # Make a single scalar $fields work
650 1 50       5 $fields = [$fields] if ref($fields) eq '';
651              
652 1         12 $pattern = $this->checkregex($pattern);
653              
654 1         7 while (my ($name,$mods_hash) = each %$hash) {
655 2         8 while (my ($module,$val) = each %$mods_hash) {
656 2         5 FIELD: for (@$fields) {
657 2 100 33     47 if ($_ eq 'Name' and $name =~ /$pattern/i
      66        
      66        
658             or defined $val->{$_} and $val->{$_} =~ /$pattern/i
659             ) {
660             $val = $hash->{$val->{Crossref}}{$module} #we're going to assume that any Crossref'd documentation is also in this module
661 1 50 33     7 if defined $val->{Crossref} && defined $hash->{$val->{Crossref}}{$module};
662 1         4 push @match, [$name,$module,$val];
663 1         8 last FIELD;
664             }
665             }
666             }
667             }
668 1 50 33     8 @match = sort {$a->[0] cmp $b->[0]} @match if (@match && $sort);
  0         0  
669 1         5 return @match;
670             }
671              
672              
673             # parse a regexp in the form
674             # m/^[a-z]+/ismx
675             # where the pairs of '/' can be replaced by any other pair of matching
676             # characters
677             # if the expression doesn't start with 'm' followed by a nonalphanumeric
678             # character, return as-is
679             sub checkregex {
680 1     1 0 4 my ($this,$regex) = @_;
681 1 50       9 return "(?i)$regex" unless $regex =~ /^m[^a-z,A-Z,0-9]/;
682 0         0 my $sep = substr($regex,1,1);
683 0         0 substr($regex,0,2) = '';
684 0         0 $sep = '(?
685              
686 0         0 my ($pattern,$mod) = split($sep,$regex,2);
687 0 0 0     0 barf "unknown regex modifiers '$mod'" if $mod && $mod !~ /[imsx]+/;
688 0 0       0 $pattern = "(?$mod)$pattern" if $mod;
689 0         0 return $pattern;
690             }
691              
692             =head2 scan
693              
694             Scan a source file using the PDL podparser to extract information
695             for online documentation
696              
697             =cut
698              
699             sub scan {
700 0     0 1 0 my ($this,$file,$verbose) = @_;
701 0 0       0 barf "can't find file '$file'" unless -f $file;
702 0         0 $file = Cwd::abs_path($file); # help Debian packaging
703 0 0       0 $verbose = 0 unless defined $verbose;
704 0 0       0 my $text = do { open my $infile, '<', $file or die "$file: $!"; local $/; <$infile> };
  0         0  
  0         0  
  0         0  
705             # Handle RPM etc. case where we are building away from the final location
706 0         0 my $file2 = $file;
707 0 0       0 $file2 =~ s/^$ENV{BUILDROOTPREFIX}// if $ENV{BUILDROOTPREFIX};
708 0         0 my $mod_hash = scantext($text, $file2, $verbose);
709 0   0     0 merge_hash($this->{SYMS} ||= {}, $mod_hash);
710 0         0 scalar values %$mod_hash; # how many functions found
711             }
712              
713             =head2 scantree
714              
715             Scan whole directory trees for online documentation in
716             C<.pm> (module definition) and C<*.pod> (general
717             documentation) files (using the File::Find module).
718              
719             =cut
720              
721             sub scantree {
722 0     0 1 0 my ($this,$dir,$verbose) = @_;
723 0 0       0 $verbose = 0 unless defined $verbose;
724 0         0 require File::Find;
725 0         0 print "Scanning $dir ... \n\n";
726 0         0 my $ntot = 0;
727             my $sub = sub {
728 0 0   0   0 return if -d $File::Find::name;
729             return if
730 0 0 0     0 $File::Find::dir !~ /script$/ and
731             $File::Find::name !~ /\.(?:pm|pod)$/;
732 0 0 0     0 return if $File::Find::name =~ /(?:Index\.pod|PP\.pm)$/ or
733             $File::Find::dir =~ m#/PP#;
734 0         0 printf "%-20s", $_.'...';
735 0         0 $ntot += my $n = $this->scan($File::Find::name,$verbose);
736 0         0 print "\t$n functions\n";
737 0         0 };
738             File::Find::find({
739             no_chdir => 1,
740             wanted => $sub,
741 0     0   0 preprocess => sub { sort @_ }
742 0         0 }, $dir);
743 0         0 print "\nfound $ntot functions\n";
744 0         0 $ntot;
745             }
746              
747              
748             =head2 funcdocs
749              
750             extract the complete documentation about a function from its
751             source file using the PDL::PodParser filter.
752              
753             =cut
754              
755             sub funcdocs {
756 0     0 1 0 my ($this,$func,$module,$fout) = @_;
757 0         0 my $hash = $this->ensuredb;
758 0 0       0 barf "unknown function '$func'" unless defined($hash->{$func});
759 0 0       0 barf "funcdocs now requires 3 arguments" if defined fileno $module;
760 0         0 my $file = $hash->{$func}{$module}{File};
761 0         0 my $dbf = $hash->{$func}{$module}{Dbfile};
762 0 0       0 $file = Cwd::abs_path($file) if file_name_is_absolute($file);
763 0         0 $dbf = Cwd::abs_path($dbf); # help Debian packaging
764 0 0 0     0 $file = rel2abs($file, dirname($dbf))
765             if !file_name_is_absolute($file) && $dbf;
766 0         0 funcdocs_fromfile($func,$file,$fout);
767             }
768              
769             =head1 FUNCTIONS
770              
771             =head2 decodedb
772              
773             $hash = decodedb($fh, $filename);
774              
775             Decode the 3-level hash out of a saved PDL::Doc database.
776              
777             =cut
778              
779             sub decodedb {
780 2     2 1 1156 my ($fh, $filename) = @_;
781 2         6 binmode $fh;
782 2         4 my %hash;
783 2         13 while (read $fh, my $plen, 2) {
784 4         13 my ($len) = unpack "v", $plen;
785 4         13 read $fh, my($txt), $len;
786 4         23 $txt = Encode::decode('UTF-8', $txt);
787 4         231 my ($sym, $module, @a) = split chr(0), $txt;
788 4 50       17 push @a, "" if @a % 2; # Add null string at end if necessary -- solves bug with missing REF section.
789 4         49 $hash{$sym}{$module} = { @a, Dbfile => $filename }; # keep the origin pdldoc.db path
790             }
791 2         17 \%hash;
792             }
793              
794             =head2 merge_hash
795              
796             merge_hash(\%pdldoc_into, \%pdldoc_from); # for 3-level hash only
797              
798             Merge a 3-level PDL::Doc hash into another one.
799              
800             =cut
801              
802             sub merge_hash {
803 1     1 1 1942 my ($into, $from) = @_;
804 1         5 for my $func (keys %$from) {
805 1         3 my $val = $from->{$func};
806             # copy the 3-layer hash/database structure: $into->{funcname}{'PDL::SomeModule'} = {Ref=>...}
807 1         4 for my $func_mod (keys %$val) {
808 1         6 $into->{$func}{$func_mod} = $val->{$func_mod};
809             }
810             }
811             }
812              
813             =head2 encodedb
814              
815             encodedb($hash, $fh, $outdir);
816              
817             =cut
818              
819             sub encodedb {
820 2     2 1 3046 my ($hash, $fh, $outdir) = @_;
821 2         7 binmode $fh;
822 2         13 for my $name (sort keys %$hash) {
823 4         10 my $mods_hash = $hash->{$name};
824 4         15 for my $module (sort keys %$mods_hash) {
825 4         8 my $val = $mods_hash->{$module};
826 4         8 my $fi = $val->{File};
827 4 50 33     17 $val->{File} = abs2rel($fi, $outdir)
828             #store paths to *.pm files relative to pdldoc.db
829             if file_name_is_absolute($fi) && -f $fi;
830 4         45 delete $val->{Dbfile}; # no need to store Dbfile
831 4         65 my $txt = Encode::encode('UTF-8', join chr(0),$name,$module,map +($_=>$val->{$_}), sort keys %$val);
832 4         211 print $fh pack("v",length($txt)).$txt;
833             }
834             }
835             }
836              
837             =head2 scantext
838              
839             $hash = scantext($module_text, $filename, $verbose);
840              
841             Scan a single string (intended to be the contents of a file),
842             returning a hash of the functions found therein.
843              
844             =cut
845              
846             sub scantext {
847 3     3 1 161423 my ($text, $filename, $verbose) = @_;
848 3         36 my $parser = PDL::PodParser->new;
849 3         9 $parser->{verbose} = $verbose;
850 3         58 open my $outfile, '>', \(my $outfile_text);
851 3         25 $parser->output_fh($outfile);
852 3         26 eval { $parser->parse_string_document($text) };
  3         16  
853 3 50 33     543 warn "cannot parse: $@" if $@ and $@ ne "no function defined\n";
854 3         7 my %hash;
855 3         6 $_->{File} = $filename for values %{ $parser->{SYMHASH} };
  3         26  
856 3         7 for my $key (sort keys %{ $parser->{SYMHASH} }) {
  3         18  
857 6         11 my $val = $parser->{SYMHASH}{$key};
858             #set up the 3-layer hash/database structure: $hash{funcname}{'PDL::SomeModule'} = $val
859 6 50       17 if (defined($val->{Module})) {
860 6         25 $hash{$key}{$val->{Module}} = $val;
861             } else {
862 0         0 warn "no Module for $key in $filename\n";
863             }
864             }
865             # pass2 - scan for module name and function
866 3         47 $parser = Pod::Simple::PullParser->new;
867 3         333 $parser->set_source(\$text);
868 3         74 my $title = eval { $parser->get_title };
  3         14  
869 3 50       5268 warn("cannot parse '$filename'"), return \%hash if $@;
870 3         39 my ($name,$does) = split /\s*-+\s*/, $title, 2;
871 3 50 33     30 $does = 'Hmmm ????' if $does and $does =~ /^\s*$/;
872 3 100       28 my $type =
    50          
873             $filename =~ /script/ ? 'Script:' :
874             $filename =~ /\.pod$/ ? 'Manual:' :
875             'Module:';
876 3 50 33     41 $hash{$name}{$name} = {Ref=>"$type $does",File=>$filename} if $name and $name !~ /^\s*$/;
877 3         95 \%hash;
878             }
879              
880             sub funcdocs_fromfile {
881 0     0 0 0 my ($func,$file) = @_;
882 0 0       0 barf "can't find file '$file'" unless -f $file;
883 0     0   0 local $SIG{PIPE}= sub {}; # Prevent crashing if user exits the pager
884 0 0       0 open my $in, '<', $file or barf "can't open file $file";
885 0         0 my $out = $_[2];
886 0 0       0 open $out, "| pod2text | $PDL::Doc::pager" if !defined $out;
887 0 0       0 barf "can't open output handle" unless $out;
888 0         0 getfuncdocs($func,$in,$out);
889 0         0 print $out "Docs from $file\n\n";
890             }
891              
892             sub getfuncdocs {
893 3     3 0 5794 my ($func,$in,$out) = @_;
894 3         37 my $parser = PDL::Doc::SelectJustPod->new;
895 3         368 $parser->select_head1s(qw(OPERATORS FUNCTIONS CONSTRUCTORS METHODS));
896 3         12 $parser->select_head2($func);
897 3         20 $parser->parse_from_file($in,$out);
898             }
899              
900             =head2 add_module
901              
902             =for usage
903              
904             use PDL::Doc;
905             PDL::Doc::add_module("PDL::Stats"); # add PDL::Stats, PDL::Stats::GLM, ...
906              
907             =for ref
908              
909             The C function allows you to add POD from a particular Perl
910             module (and as of PDL 2.083, in fact all modules starting with that as
911             a prefix) that you've installed somewhere in C<@INC>. It searches for the
912             active PDL document database and the module's .pod and .pm files, and
913             scans and indexes the module(s) into the database.
914              
915             C is meant to be added to your module's Makefile as part of the
916             installation script. This is done automatically by
917             L, but if the top level of your
918             distribution is Perl modules (like L), then add a
919             C manually in the F:
920              
921             use PDL::Core::Dev;
922             sub MY::postamble {
923             my $oneliner = PDL::Core::Dev::_oneliner(qq{exit if \$ENV{DESTDIR}; use PDL::Doc; eval { PDL::Doc::add_module(shift); }});
924             qq|\ninstall :: pure_install\n\t$oneliner \$(NAME)\n|;
925             }
926              
927             =cut
928              
929             sub _find_inc {
930 0     0     my ($what, $want_dir) = @_;
931 0           my @ret;
932 0           for my $dir (@INC) {
933 0 0         my $ent = $want_dir ? catdir($dir, @$what) : catfile($dir, @$what);
934 0 0         push @ret, $ent if $want_dir ? -d $ent : -f $ent;
    0          
935             }
936 0           @ret;
937             }
938              
939             sub add_module {
940 0     0 1   my ($module) = @_;
941 0           my ($file) = _find_inc([qw(PDL pdldoc.db)], 0);
942 0 0         die "Unable to find docs database - therefore not updating it.\n" if !defined $file;
943 0 0         die "No write permission for $file - not updating docs database.\n"
944             if !-w $file;
945 0           print "Found docs database $file\n";
946 0           my $pdldoc = PDL::Doc->new($file);
947 0           my @pkg = my @mfile = split /::/, $module;
948 0           my $mlast = pop @mfile;
949 0           my @found = map _find_inc([@mfile, $mlast.$_]), qw(.pm .pod);
950 0 0         die "Unable to find a .pm or .pod file in \@INC for module $module\n" if !@found;
951 0           $pdldoc->ensuredb;
952 0           my $n = 0;
953 0           $n += $pdldoc->scan($_) for @found;
954 0           print "Added @found, $n functions.\n";
955 0           $n += $pdldoc->scantree($_) for _find_inc(\@pkg, 1);
956 0           eval { $pdldoc->savedb; };
  0            
957 0 0         warn $@ if $@;
958 0           print "PDL docs database updated - total $n functions.\n";
959             }
960              
961             =head1 PDL::DOC EXAMPLE
962              
963             Here's an example of how you might use the PDL Doc database in your
964             own code.
965              
966             use PDL::Doc;
967             # Find the pdl documentation
968             my ($file) = _find_inc([qw(PDL pdldoc.db)], 0);
969             die "Unable to find docs database!\n" unless defined $file;
970             print "Found docs database $file\n";
971             my $pdldoc = PDL::Doc->new($file);
972             # Print the reference line for zeroes:
973             print map{$_->{Ref}} values %{$pdldoc->gethash->{zeroes}};
974             # Or, if you remember that zeroes is in PDL::Core:
975             print $pdldoc->gethash->{zeroes}{'PDL::Core'}{Ref};
976              
977             # Get info for all the functions whose examples use zeroes
978             my @entries = $pdldoc->search('zeroes','Example',1,1);
979              
980             # All the functions that use zeroes in their example:
981             print "Functions that use 'zeroes' in their examples include:\n";
982             foreach my $entry (@entries) {
983             # Unpack the entry
984             my ($func_name, $module, $sym_hash) = @$entry;
985             print "$func_name\n";
986             }
987             print "\n";
988              
989             #Or, more concisely:
990             print map "$_->[0]\n", @entries;
991              
992             # Let's look at the function 'mpdl'
993             @entries = $pdldoc->search('mpdl', 'Name');
994             # I know there's only one:
995             my $entry = $entries[0];
996             my ($func_name, undef, $sym_hash) = @$entry;
997             print "mpdl info:\n";
998             foreach my $key (sort keys %$sym_hash) {
999             # Unpack the entry
1000             print "---$key---\n$sym_hash->{$key}\n";
1001             }
1002              
1003             =head2 Finding Modules
1004              
1005             How can you tell if you've got a module for one of your entries?
1006             The Ref entry will begin with 'Module:' if it's a module. In code:
1007              
1008             # Prints:
1009             # Module: fundamental PDL functionality and vectorization/broadcasting
1010             print $pdldoc->gethash->{'PDL::Core'}{'PDL::Core'}{Ref}, "\n"
1011              
1012             =head1 BUGS
1013              
1014             Quite a few shortcomings which will hopefully be fixed following
1015             discussions on the pdl-devel mailing list.
1016              
1017             =head1 AUTHOR
1018              
1019             Copyright 1997 Christian Soeller Ec.soeller@auckland.ac.nzE
1020             and Karl Glazebrook Ekgb@aaoepp.aao.gov.auE
1021              
1022             Further contributions copyright 2010 David Mertens
1023             Edcmertens.perl@gmail.comE
1024              
1025             Documentation database restructuring 2019 Derek Lamb
1026              
1027             All rights reserved. There is no warranty. You are allowed
1028             to redistribute this software / documentation under certain
1029             conditions. For details, see the file COPYING in the PDL
1030             distribution. If this file is separated from the PDL distribution,
1031             the copyright notice should be included in the file.
1032              
1033             =cut
1034              
1035             1;