Whenever you write code, you also write documentation, right? Wait, don’t answer that! Let me just pretend that you said “yes”.
As with Perl 5, Perl 6 has a built-in documentation format called Pod. In Perl 5, POD was short for “Plain Ol’ Documentation”, but in Perl 6 we just call it Pod and there’s no acronym. One of the great things about Pod in Perl 6 is that the compiler parses Pod for you. The resulting structure is essentially a tree (really, an Array of trees).
Let’s take a look at a few examples …
use v6;
=begin pod
=head1 NAME
example1.p6 - It's a script to show you how Pod works in Perl 6!
=end pod
sub MAIN {
say $=pod.WHAT;
say $=pod[0].WHAT;
}
When I run this script I get:
$ perl6 ./bin/example1.p6
(Array)
(Named)
The $=pod
variable is a reference to the parsed Pod from the current file.
From the code above we can see that the $=pod
variable contains an Array
. Each element of that array is a Pod node of some sort, and those nodes in turn will usually contain more Pod nodes, and so on. Essentially, Pod is parsed into a tree that you can recursively descend and examine.
The $=pod
variable is an array is because you can have several distinct chunks of Pod in a single file …
use v6;
=begin pod
=head1 NAME
example2.p6 - It's a script to show you how Pod works in Perl 6!
=end pod
sub MAIN {
say $=pod.elems;
say $=pod[0].WHAT;
say $=pod[1].WHAT;
}
=begin pod
=head1 DESCRIPTION
This is where I'd put some more stuff about the script if it did anything.
=end pod
When we run this script we get:
$ perl6 ./bin/example2.p6
2
(Named)
(Named)
So there are now two elements in $=pod
, one for each chunk of Pod delimited by our =begin pod
/=end pod
pairs.
Pod Syntax in Perl 6
Up until this point I’ve shown you some Pod without actually explaining what it is. Let’s take a look at some valid Pod constructs in Perl 6 so you can get a better sense of how it looks, both when you write it and when you’re writing code to process it.
Perl 6 Pod is purely semantic. In other words, you describe what something is, rather than how to present it. It’s left up to code that turns Pod into something else (HTML, Markdown, man pages) to decide how to present any given construct.
Perl 6 Pod has quite a bit more to it than we can cover here, so see Synopsis 26 for all the details. We’ll cover some of the highlights and then look more closely at how to process Pod programmatically.
Blocks
Pod supports several different ways to declare a block. You can use =begin
and =end
markers …
=begin head1
Heading Goes Here
=end head1
Or you can use a =for
marker …
=for head1
Heading Goes Here
These block styles allow you to include configuration information:
=begin head3 :include-in-toc
This will show up in TOC
=end head3
=for head4 :!include-in-toc
Not in the TOC
With a =for
block, only the lines immediately following the =for
is part of that block. Once there is an empty line, anything that follows is separate.
You can use an “abbreviated” block …
=head1 Heading Goes Here
… but abbreviated blocks don’t allow for configuration information. Like =for
blocks, abbreviated blocks end at the first empty line.
There are many types of blocks, including table
, code
, I/O blocks (input
and output
), lists, and more.
Paragraphs
Text that isn’t explicitly part of a block is considered to be a paragraph block …
use v6;
=begin pod
=begin head1
NAME
=end head1
This is a paragraph block.
=end pod
sub MAIN {
say $=pod[0].contents[1].WHAT;
}
The output will be (Para)
, because the second element of contents
is a plain paragraph.
Lists
Lists are much simpler in Perl 6 than they were in Perl 5 …
=begin pod
=item1 The first item
=item1 The second item
=item2 This is a sub-item of the second item
=item1 The third item
Of course, you can also use the =for
and =begin
/=end
form with list items too.
Formatting Codes
There are a variety of formatting codes available …
See L<http://design.perl6.org/S26.html> for details.
Some times you need to mark some text as C<code>,
B<being the basis of the sentence>,
or I<important>.
And More
There are a lot of new things with Perl 6 Pod, and if you want to learn more I encourage you to read Synopsis 26 for all of the gory details.
Processing Pod with Perl 6
As I said before, the Perl 6 compiler parses Pod in your program and makes it available to you via various variables. We already saw the use of $=pod
, which contains all of the Pod in the current file. That pod is an array of Pod::Block
objects. In future versions of Rakudo, you will also be able to get at specific types of Pod blocks by name with variables such as $=head1
or $=TITLE
, but this is not yet implemented.
Let’s write a simple program to show us an outline of the Pod in a given file. For example, given some Pod like this …
=head1 NAME
...
=head1 DESCRIPTION
...
=head1 METHODS
...
=METHOD $obj.foo
...
=METHOD $obj.bar
...
… we want to print this output …
NAME DESCRIPTION METHODS $obj.foo (METHOD) $obj.bar (METHOD)
Here’s the code to do just that …
use v6;
sub MAIN {
for $=pod.kv -> $i, $block {
say "Block $i";
recurse-blocks($block);
print "\n";
}
}
sub recurse-blocks (Pod::Block $block) {
given $block {
when Pod::Block::Named {
if $block.name eq 'pod' {
for $block.contents.values -> $b {
recurse-blocks($b);
}
}
else {
my $output = $block.contents[0].contents[0]
~ " ({$block.name})";
depth-say( 2, $output );
}
}
when Pod::Heading {
depth-say( $block.level,
$block.contents[0].contents[0] );
}
}
}
sub depth-say (Int $depth, Str $thing) {
print ' ' x $depth;
say $thing;
}
This code takes some shortcuts wherever it uses $block.contents[0].contents[0]
by making the very big assumption that a block contains one element which is a single Pod::Block::Para
. It then assumes that the Para block contains one element which is plain text.
In practice, blocks may contain arbitrarily nested blocks because of things like formatting codes, but this gives you an idea of how you might walk through the Pod tree.
Of course, because Perl 6 is still Perl, there’s a module for that! I’ve sketched out a preliminary module called Pod::TreeWalker
that will do the tree walking for you, generating events as it finds different nodes. You provide a listener object that is called for each event.
GitHub Repo
All of the code shown in these examples is on GitHub. I’ve also written an additional script that uses Pod::TreeWalker
to implement the outlining I demonstrate above.