Skip to content

Commit

Permalink
autodoc: Add documentation; fix white space
Browse files Browse the repository at this point in the history
  • Loading branch information
khwilliamson committed Jul 26, 2024
1 parent 1208707 commit 154b302
Showing 1 changed file with 75 additions and 28 deletions.
103 changes: 75 additions & 28 deletions autodoc.pl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/perl -w

use Text::Tabs;
#

# Unconditionally regenerate:
#
# pod/perlintern.pod
Expand Down Expand Up @@ -121,22 +121,39 @@
require './regen/regen_lib.pl';
require './regen/embed_lib.pl';

my %described_elsewhere;
# This is the main data structure. Each key is the name of a potential API
# element available to be documentable. Each value is a hash containing
# information about that element, such as its prototype. It is initialized
# from embed.fnc, and added to as we go along.
my %elements;

# This hash is used to organize the data in %elements for output. The top
# level keys are either 'api' or 'intern' for the two pod files generated by
# this program.
#
# See database of global and static function prototypes in embed.fnc
# This is used to generate prototype headers under various configurations,
# export symbols lists for different platforms, and macros to provide an
# implicit interpreter context argument.
# Under those are hashes for each section in its corresponding pod. There is
# a section for API elements involved with SV handling; another for AV
# handling, etc.
#

# Under each section are hashes for each group in the section. Each group
# consists of one or more API elements that share the same pod. Each group
# hash contains fields for the common parts of the group, and then an array of
# all the API elements that comprise it. The array determines the ordering of
# the output for the API elements.
#
# The elements are pointers to the leaf nodes in %elements. These contain all
# the information needed to know what to output for each.
my %docs;
my %elements;

# This hash is populated with the names of other pod files that we determine
# contain relevant information about the API elements. It is used just for
# the SEE ALSO section
my %described_elsewhere;

my $link_text = "Described in";

my $description_indent = 4;
my $usage_indent = 3; # + initial blank yields 4 total
my $usage_indent = 3; # + initial verbatim block blank yields 4 total

my $AV_scn = 'AV Handling';
my $callback_scn = 'Callback Functions';
Expand Down Expand Up @@ -446,6 +463,11 @@

sub where_from_string($;$) {
my ($file, $line_num) = @_;

# Returns a string of hopefully idiomatic text about the location given by
# the input parameters. The line number is not always available, and this
# centralizes into one function the logic to deal with that

return "in $file" unless $line_num;
return "at $file, line $line_num";
}
Expand Down Expand Up @@ -485,10 +507,10 @@ sub check_and_add_proto_defn {
# usage signature for $element, and marks the place in the source where
# the documentation is found. Handling that happens naturally here.

# This is currently used only by config.h. See comments at the definition
# of this line type. If there is an existing prototype definition, defer
# to that (by setting the parameters to empty); otherwise use the one
# passed in.
# This definition type is currently used only by config.h. See comments
# at the definition of this line type. If there is an existing prototype
# definition, defer to that (by setting the parameters to empty);
# otherwise use the one passed in.
if ($definition_type == CONDITIONAL_APIDOC) {
if (exists $elements{$element}) {
my @dummy;
Expand Down Expand Up @@ -732,6 +754,11 @@ ($$$$)

sub handle_apidoc_line ($$$$) {
my ($file, $line_num, $type, $arg) = @_;

# This just does a couple of checks that would otherwise have to be
# duplicated in the calling code, and calls check_and_add_proto_defn() to
# do the real work.

my $proto_as_written = $arg;
my $proto = $proto_as_written;
$proto = "||$proto" if $proto !~ /\|/;
Expand Down Expand Up @@ -770,19 +797,19 @@ ($$$$)
return $updated;
}

sub destination_pod ($) {
sub destination_pod ($) { # Into which pod should the element go whose flags
# are $1
my $flags = shift;
return "unknown" if $flags eq "";
return "api" if $flags =~ /A/;
return "intern";
}

sub autodoc ($$) { # parse a file and extract documentation info
my($fh,$file) = @_;
my($fh, $file) = @_;

my $section = $initial_file_section{$file}
if defined $initial_file_section{$file};

my $file_is_C = $file =~ / \. [ch] $ /x;

# Count lines easier and handle apidoc continuation lines
Expand Down Expand Up @@ -876,6 +903,8 @@ ($$)

# Here the line starts a new section ...
$section = $arg;

# Convert $foo to its value
if ($section =~ / ^ \$ /x) {
$section .= '_scn' unless $section =~ / _scn $ /x;
$section = eval "$section";
Expand All @@ -884,6 +913,8 @@ ($$)
}
die "Unknown section name '$section' in $file near line $line_num\n"
unless defined $valid_sections{$section};

# Drop down to accumulate the heading text for this section.
}
elsif ($outer_line_type == PLAIN_APIDOC) {
my $leader_ref =
Expand Down Expand Up @@ -935,8 +966,8 @@ ($$)
#
# Accumulation stops at a terminating line, which is one of:
# 1) =cut
# 2) =head (in a C file only =head1)
# 3) an end comment line in a C file: m:^\s*\*/:
# 2) =headN (N must be 1 in a C file)
# 3) an end comment line in a C file: m: ^ \s* [*] / :x
# 4) =for apidoc... (except apidoc_item lines)
my $head_ender_num = ($file_is_C) ? 1 : "";
while (1) {
Expand All @@ -949,8 +980,9 @@ ($$)

if ($file_is_C && $inner_arg =~ m: ^ \s* \* / $ :x) {

# End of comment line in C files is a fall-back terminator,
# but warn only if there actually is some accumulated text
# End of comment line in C files is a fall-back
# terminator, but warn only if there actually is some
# accumulated text
warn "=cut missing? "
. where_from_string($file, $line_num)
. "\n$inner_arg" if $text =~ /\S/;
Expand All @@ -960,7 +992,7 @@ ($$)
$text .= $inner_arg;
}

# Here, are done accumulating the text for this item. Trim it
# Here, are done accumulating the text for this element. Trim it
$text =~ s/ ^ \s* //x;
$text =~ s/ \s* $ //x;
$text .= "\n" if $text ne "";
Expand All @@ -979,7 +1011,7 @@ ($$)
my $item0 = ${$items[0]};
my $element_name = $item0->{name};

# Here, we have accumulated into $text, the pod for $element_name
# Here, into $text, we have accumulated the pod for $element_name

die "No =for apidoc_section nor =head1 "
. where_from_string($file, $line_num)
Expand Down Expand Up @@ -1316,7 +1348,7 @@ sub parse_config_h {
chomp $was;
if ($was ne "" && $was !~ m/$link_text/) {
die "Multiple descriptions for $name\n"
. "The '$section' section contained\n'$was'";
. "The '$section' section contained\n'$was'";
}
$docs{'api'}{$section}{$name}->{pod} = $configs{$name}{pod};
$configs{$name}{section} = $section;
Expand Down Expand Up @@ -2062,7 +2094,7 @@ sub dictionary_order {
return $a cmp $b;
}

sub output($) {
sub output($) { # Output a complete pod file
my $destpod = shift;
my $podname = $destpod->{podname};
my $dochash = $destpod->{docs};
Expand Down Expand Up @@ -2153,14 +2185,18 @@ ($)
read_only_bottom_close_and_rename($fh);
}

# Beginning of actual processing. First process embed.fnc
foreach (@{(setup_embed())[0]}) {
my $embed= $_->{embed}
or next;
my $file = $_->{source};
my ($flags, $ret_type, $func, $args) =
@{$embed}{qw(flags return_type name args)};
check_and_add_proto_defn($func, $file,
undef, # Unknown line number in embed.fnc
# embed.fnc data doesn't currently furnish the
# line number
undef,

$flags, $ret_type, $args,

# This is like an 'apidoc_defn' line, in that it
Expand All @@ -2171,7 +2207,7 @@ ($)
}

# glob() picks up docs from extra .c or .h files that may be in unclean
# development trees.
# development trees, so use MANIFEST instead
my @headers;
my @non_headers;
open my $fh, '<', 'MANIFEST'
Expand All @@ -2192,14 +2228,19 @@ ($)
}
close $fh or die "Error whilst reading MANIFEST: $!";

# Now have the files to examine saved. Do the headers first to minimize the
# number of forward references that we would have to deal with later
for my $file (@headers, @non_headers) {
open my $fh, '<', $file or die "Cannot open $file for docs: $!\n";
autodoc($fh, $file);
close $fh or die "Error closing $file: $!\n";
}

# Code in this file depends on doing config.h last.
parse_config_h();

# Here, we have parsed everything

# Any apidoc group whose leader element's documentation wasn't known by the
# time it was parsed has been placed in 'unknown' member of %docs. We should
# now be able to figure out which real pod file to place it in, and its usage.
Expand All @@ -2224,8 +2265,9 @@ ($)

# $destpod now gives the correct pod for this group. Prepare to move it
# to there
die "$destpod unexpectedly has an entry in $section_name for $group_name"
if defined $docs{$destpod}{$section_name}{$group_name};
die "$destpod unexpectedly has an entry in $section_name for "
. $group_name if defined $docs{$destpod}{$section_name}{$group_name};

$docs{$destpod}{$section_name}{$group_name} =
delete $unknown->{$section_name}{$group_name};
}
Expand Down Expand Up @@ -2283,9 +2325,14 @@ ($)
}
}

# Here %docs is populated; and we're ready to output

my %api = ( podname => 'perlapi', docs => $docs{'api'} );
my %intern = ( podname => 'perlintern', docs => $docs{'intern'} );

# But first, look for inconsistencies and populate the lists of elements whose
# documentation is missing

for my $which (\%api, \%intern) {
my (@deprecated, @experimental, @missings);
for my $name (sort dictionary_order keys %elements) {
Expand Down

0 comments on commit 154b302

Please sign in to comment.