1898184e3Ssthen#!perl -w 2898184e3Ssthenuse strict; 3898184e3Ssthenuse Pod::Simple::SimpleTree; 4898184e3Ssthen 5898184e3Ssthenmy ($tap, $test, %Missing); 6898184e3Ssthen 76fb12b70Safresh1BEGIN { 8898184e3Ssthen @ARGV = grep { not($_ eq '--tap' and $tap = 1) } @ARGV; 96fb12b70Safresh1 if ($tap) { 106fb12b70Safresh1 require Test::More; 116fb12b70Safresh1 Test::More->import; 126fb12b70Safresh1 } 136fb12b70Safresh1} 14898184e3Ssthen 15898184e3Ssthenmy (%Kinds, %Flavor, @Types); 16898184e3Ssthenmy %Omit; 17898184e3Ssthen 18898184e3Ssthenmy $p = Pod::Simple::SimpleTree->new; 19898184e3Ssthen$p->accept_targets('Pod::Functions'); 20898184e3Ssthenmy $tree = $p->parse_file(shift)->root; 21898184e3Ssthen 22898184e3Ssthenforeach my $TL_node (@$tree[2 .. $#$tree]) { 23898184e3Ssthen next unless $TL_node->[0] eq 'over-text'; 24898184e3Ssthen my $i = 2; 25898184e3Ssthen while ($i <= $#$TL_node) { 26898184e3Ssthen if ($TL_node->[$i][0] ne 'item-text') { 27898184e3Ssthen ++$i; 28898184e3Ssthen next; 29898184e3Ssthen } 30898184e3Ssthen 31898184e3Ssthen my $item_text = $TL_node->[$i][2]; 32898184e3Ssthen die "Confused by $item_text at line $TL_node->[$i][1]{start_line}" 33898184e3Ssthen if ref $item_text; 34898184e3Ssthen $item_text =~ s/\s+\z//s; 35898184e3Ssthen 36898184e3Ssthen if ($TL_node->[$i+1][0] ne 'for' 37898184e3Ssthen || $TL_node->[$i+1][1]{target} ne 'Pod::Functions') { 38898184e3Ssthen ++$i; 39898184e3Ssthen ++$Missing{$item_text} unless $Omit{$item_text}; 40898184e3Ssthen next; 41898184e3Ssthen } 42898184e3Ssthen my $data = $TL_node->[$i+1][2]; 43898184e3Ssthen die "Confused by $data at line $TL_node->[$i+1][1]{start_line}" 44898184e3Ssthen unless ref $data eq 'ARRAY'; 45898184e3Ssthen my $text = $data->[2]; 46898184e3Ssthen die "Confused by $text at line $TL_node->[$i+1][1]{start_line}" 47898184e3Ssthen if ref $text; 48898184e3Ssthen 49898184e3Ssthen $i += 2; 50898184e3Ssthen 51898184e3Ssthen if ($text =~ s/^=//) { 52898184e3Ssthen # We are in "Perl Functions by Category" 53898184e3Ssthen die "Expected a paragraph after =item at $TL_node->[$i-2][1]{start_line}" 54898184e3Ssthen unless $TL_node->[$i][0] eq 'Para'; 55898184e3Ssthen my $para = $TL_node->[$i]; 56898184e3Ssthen # $text is the "type" of the built-in 57898184e3Ssthen # Anything starting ! is not for inclusion in Pod::Functions 58898184e3Ssthen 59898184e3Ssthen foreach my $func (@$para[2 .. $#$para]) { 60898184e3Ssthen next unless ref $func eq 'ARRAY'; 61b8851fccSafresh1 my $c_node = 62b8851fccSafresh1 $func->[0] eq 'C' && !ref $func->[2] ? $func : 63b8851fccSafresh1 $func->[0] eq 'L' && ref $func->[2] 64b8851fccSafresh1 && $func->[2][0] eq 'C' && !ref $func->[2][2] ? $func->[2] : 65b8851fccSafresh1 die "Expected only C<> blocks in paragraph after item at $TL_node->[$i-2][1]{start_line}"; 66b8851fccSafresh1 # Everything is plain text (ie $c_node->[2] is everything) 67898184e3Ssthen # except for C<-I<X>>. So untangle up to one level of nested <> 68898184e3Ssthen my $funcname = join '', map { 69898184e3Ssthen ref $_ ? $_->[2] : $_ 70b8851fccSafresh1 } @$c_node[2..$#$c_node]; 71898184e3Ssthen $funcname =~ s!(q.?)//!$1/STRING/!; 72898184e3Ssthen push @{$Kinds{$text}}, $funcname; 73898184e3Ssthen } 74898184e3Ssthen if ($text =~ /^!/) { 75898184e3Ssthen ++$Omit{$_} foreach @{$Kinds{$text}}; 76898184e3Ssthen } else { 77898184e3Ssthen push @Types, [$text, $item_text]; 78898184e3Ssthen } 79898184e3Ssthen } else { 80898184e3Ssthen $item_text =~ s/ .*//; 81898184e3Ssthen # For now, just remove any metadata about when it was added: 82898184e3Ssthen $text =~ s/^\+\S+ //; 83898184e3Ssthen $Flavor{$item_text} = $text; 84898184e3Ssthen ++$Omit{$item_text} if $text =~ /^!/; 85898184e3Ssthen } 86898184e3Ssthen } 87898184e3Ssthen} 88898184e3Ssthen 89898184e3Ssthen# Take the lists of functions for each type group, and invert them to get the 90898184e3Ssthen# type group (or groups) for each function: 91898184e3Ssthenmy %Type; 92898184e3Ssthenwhile (my ($type, $funcs) = each %Kinds) { 93898184e3Ssthen push @{$Type{$_}}, $type foreach @$funcs; 94898184e3Ssthen} 95898184e3Ssthen 96898184e3Ssthen# We sort __SUB__ after sub, but before substr, but __PACKAGE__ after package, 97b8851fccSafresh1# and __END__ after END. (We create a temporary array of two elements, where 98b8851fccSafresh1# the second has the underscores squeezed out, and sort on that element 99b8851fccSafresh1# first.) 100898184e3Ssthensub sort_funcs { 101898184e3Ssthen map { $_->[0] } 102b8851fccSafresh1 sort { uc $a->[1] cmp uc $b->[1] 103b8851fccSafresh1 || $b->[1] cmp $a->[1] 104b8851fccSafresh1 || $a->[0] =~ /^_/ # here $a and $b are identical when 105b8851fccSafresh1 # underscores squeezed out; so if $a 106b8851fccSafresh1 # begins with an underscore, it should 107b8851fccSafresh1 # sort after $b 108b8851fccSafresh1 || $a->[0] cmp $b->[0] 109b8851fccSafresh1 } map { my $f = tr/_//dr; [ $_, $f ] } 110898184e3Ssthen @_; 111898184e3Ssthen} 112898184e3Ssthen 113898184e3Ssthenif ($tap) { 114898184e3Ssthen foreach my $func (sort_funcs(keys %Flavor)) { 1156fb12b70Safresh1 ok ( $Type{$func}, "$func is mentioned in at least one category group"); 116898184e3Ssthen } 117898184e3Ssthen foreach (sort keys %Missing) { 118898184e3Ssthen # Ignore anything that looks like an alternative for a function we've 119898184e3Ssthen # already seen; 120898184e3Ssthen s!(?: [A-Z].*| \(\)|\( LIST \)| /PATTERN/.*)!!; 121898184e3Ssthen next if $Flavor{$_}; 122898184e3Ssthen if (/^[_a-z]/) { 1236fb12b70Safresh1 fail( "function '$_' has no summary for Pod::Functions" ); 124898184e3Ssthen } else { 1256fb12b70Safresh1 fail( "for Pod::Functions" ); 126898184e3Ssthen } 127898184e3Ssthen } 128898184e3Ssthen foreach my $kind (sort keys %Kinds) { 129898184e3Ssthen my $funcs = $Kinds{$kind}; 130898184e3Ssthen ++$test; 131898184e3Ssthen my $want = join ' ', sort_funcs(@$funcs); 1326fb12b70Safresh1 is ("@$funcs", $want, "category $kind is correctly sorted" ); 133898184e3Ssthen } 1346fb12b70Safresh1 done_testing(); 135898184e3Ssthen exit; 136898184e3Ssthen} 137898184e3Ssthen 138898184e3Ssthen# blead will run this with miniperl, hence we can't use autodie 139898184e3Ssthenmy $real = 'Functions.pm'; 140898184e3Ssthenmy $temp = "Functions.$$"; 141898184e3Ssthen 142898184e3SsthenEND { 143898184e3Ssthen return if !defined $temp || !-e $temp; 144898184e3Ssthen unlink $temp or warn "Can't unlink '$temp': $!"; 145898184e3Ssthen} 146898184e3Ssthen 147898184e3Ssthenforeach ($real, $temp) { 148898184e3Ssthen next if !-e $_; 149898184e3Ssthen unlink $_ or die "Can't unlink '$_': $!"; 150898184e3Ssthen} 151898184e3Ssthen 152898184e3Ssthenopen my $fh, '>', $temp or die "Can't open '$temp' for writing: $!"; 153898184e3Ssthenprint $fh <<'EOT'; 154898184e3Ssthenpackage Pod::Functions; 155898184e3Ssthenuse strict; 156898184e3Ssthen 157898184e3Ssthen=head1 NAME 158898184e3Ssthen 159898184e3SsthenPod::Functions - Group Perl's functions a la perlfunc.pod 160898184e3Ssthen 161898184e3Ssthen=head1 SYNOPSIS 162898184e3Ssthen 163898184e3Ssthen use Pod::Functions; 164898184e3Ssthen 165898184e3Ssthen my @misc_ops = @{ $Kinds{ 'Misc' } }; 166898184e3Ssthen my $misc_dsc = $Type_Description{ 'Misc' }; 167898184e3Ssthen 168898184e3Ssthenor 169898184e3Ssthen 170898184e3Ssthen perl /path/to/lib/Pod/Functions.pm 171898184e3Ssthen 172898184e3SsthenThis will print a grouped list of Perl's functions, like the 173898184e3SsthenL<perlfunc/"Perl Functions by Category"> section. 174898184e3Ssthen 175898184e3Ssthen=head1 DESCRIPTION 176898184e3Ssthen 177898184e3SsthenIt exports the following variables: 178898184e3Ssthen 179898184e3Ssthen=over 4 180898184e3Ssthen 181898184e3Ssthen=item %Kinds 182898184e3Ssthen 183898184e3SsthenThis holds a hash-of-lists. Each list contains the functions in the category 184898184e3Ssthenthe key denotes. 185898184e3Ssthen 186898184e3Ssthen=item %Type 187898184e3Ssthen 188898184e3SsthenIn this hash each key represents a function and the value is the category. 189898184e3SsthenThe category can be a comma separated list. 190898184e3Ssthen 191898184e3Ssthen=item %Flavor 192898184e3Ssthen 193898184e3SsthenIn this hash each key represents a function and the value is a short 194898184e3Ssthendescription of that function. 195898184e3Ssthen 196898184e3Ssthen=item %Type_Description 197898184e3Ssthen 198898184e3SsthenIn this hash each key represents a category of functions and the value is 199898184e3Ssthena short description of that category. 200898184e3Ssthen 201898184e3Ssthen=item @Type_Order 202898184e3Ssthen 203898184e3SsthenThis list of categories is used to produce the same order as the 204898184e3SsthenL<perlfunc/"Perl Functions by Category"> section. 205898184e3Ssthen 206898184e3Ssthen=back 207898184e3Ssthen 208898184e3Ssthen=cut 209898184e3Ssthen 210*eac174f2Safresh1our $VERSION = '1.14'; 211898184e3Ssthen 212*eac174f2Safresh1use Exporter 'import'; 213898184e3Ssthen 214898184e3Ssthenour @EXPORT = qw(%Kinds %Type %Flavor %Type_Description @Type_Order); 215898184e3Ssthen 216898184e3Ssthenour(%Kinds, %Type, %Flavor, %Type_Description, @Type_Order); 217898184e3Ssthen 218898184e3Ssthenforeach ( 219898184e3SsthenEOT 220898184e3Ssthen 221898184e3Ssthenforeach (@Types) { 222898184e3Ssthen my ($type, $desc) = @$_; 223898184e3Ssthen $type = "'$type'" if $type =~ /[^A-Za-z]/; 224898184e3Ssthen $desc =~ s!([\\'])!\\$1!g; 225898184e3Ssthen printf $fh " [%-9s => '%s'],\n", $type, $desc; 226898184e3Ssthen} 227898184e3Ssthen 228898184e3Ssthenprint $fh <<'EOT'; 229898184e3Ssthen ) { 230898184e3Ssthen push @Type_Order, $_->[0]; 231898184e3Ssthen $Type_Description{$_->[0]} = $_->[1]; 232898184e3Ssthen}; 233898184e3Ssthen 234898184e3Ssthenwhile (<DATA>) { 235898184e3Ssthen chomp; 236898184e3Ssthen s/^#.*//; 237898184e3Ssthen next unless $_; 238898184e3Ssthen my($name, @data) = split "\t", $_; 239898184e3Ssthen $Flavor{$name} = pop @data; 240898184e3Ssthen $Type{$name} = join ',', @data; 241898184e3Ssthen for my $t (@data) { 242898184e3Ssthen push @{$Kinds{$t}}, $name; 243898184e3Ssthen } 244898184e3Ssthen} 245898184e3Ssthen 246898184e3Ssthenclose DATA; 247898184e3Ssthen 248898184e3Ssthenmy( $typedesc, $list ); 249898184e3Ssthenunless (caller) { 250898184e3Ssthen foreach my $type ( @Type_Order ) { 251898184e3Ssthen $list = join(", ", sort @{$Kinds{$type}}); 252898184e3Ssthen $typedesc = $Type_Description{$type} . ":"; 253898184e3Ssthen write; 254898184e3Ssthen } 255898184e3Ssthen} 256898184e3Ssthen 257898184e3Ssthenformat = 258898184e3Ssthen 259898184e3Ssthen^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 260898184e3Ssthen $typedesc 261898184e3Ssthen~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 262898184e3Ssthen $typedesc 263898184e3Ssthen ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 264898184e3Ssthen $list 265898184e3Ssthen. 266898184e3Ssthen 267898184e3Ssthen1; 268898184e3Ssthen 269898184e3Ssthen__DATA__ 270898184e3SsthenEOT 271898184e3Ssthen 272898184e3Ssthenforeach my $func (sort_funcs(keys %Flavor)) { 273898184e3Ssthen my $desc = $Flavor{$func}; 274898184e3Ssthen die "No types listed for $func" unless $Type{$func}; 275898184e3Ssthen next if $Omit{$func}; 2766fb12b70Safresh1 print $fh join("\t", $func, (sort @{$Type{$func}}), $desc), "\n"; 277898184e3Ssthen} 278898184e3Ssthen 279898184e3Ssthenclose $fh or die "Can't close '$temp': $!"; 280898184e3Ssthenrename $temp, $real or die "Can't rename '$temp' to '$real': $!"; 281