Literate Programming For Perl


Suppose if you will, that for the moment, you can wave a magic wand and solve the problem of program documentation– what might that solution look like (and function like) and how might it be implemented? Here are the beginnings (pre-alpha at best) of my solution, worked out in HTML, CSS, and DHTML . All this for a source file written in Perl? Yes, but remember we're talking magic wand here!

The first thing that comes to mind is that we need to be able to see the actual code. Given style sheets, that is comparitively easy:

Source Listing for ftstamp.pl

#!/usr/bin/perl -w
# ftstamp.pl -- file modification time as time stamp.
use strict;
use warnings;
use Getopt::Std;
use Wild;
use diagnostics;

my %OPTIONS;
getopts('ud',\%OPTIONS);
WildARGV();

foreach (@ARGV) {
    if ($OPTIONS{d}) {
        print "ftstamp -d $_\n";
    }
    else {
        stamp($_) unless (-d);
    }
}

sub stamp {
    my $s = shift;
    my $modified = time - ((-M $_) * 86400);
    my $t = localtime($modified);
    my $touched;
    my @filetext;

    open(FILE,$s);
    @filetext =;
    close(FILE);
    foreach (@filetext) {
        if ($_ =~ /Page last touched (.+?)(?:<|\n)/) {
            if ($1 ne $t) {
                s/\Q$1/$t/;
                $touched = 1;
            }
        }
    }
    if ($touched) {
        open(FILE,'>'.$_);
        print FILE @filetext;
        close(FILE);
        utime $modified,$modified,$_ if $OPTIONS{u};
        print "$_ touched: $t\n";
    }
}
    

Not particularly difficult, just come up with a new form of the venerable <PRE></PRE> tag and away we go. On the other hand, hardly a solution and barely a start. Given the medium, we should at least expect color typing and some bare bones typography of some sort. Here is try the second:

Source Listing for ftstamp.pl(2)

#!/usr/bin/perl -w
# ftstamp.pl -- file modification time as time stamp.
use strict;
use warnings;
use Getopt::Std;
use Wild;
use diagnostics;

my %OPTIONS;
getopts('ud',\%OPTIONS);
WildARGV();

foreach (@ARGV) {
    if ($OPTIONS{d}) {
        print "ftstamp -d $_\n";
    }
    else {
        stamp($_);
    }
}

sub stamp {
    my $s = shift;
    my $modified = time - ((-M $_) * 86400);
    my $t = localtime($modified);
    my $touched;
    my @filetext;

    open(FILE,$s);
    @filetext = <FILE>;
    close(FILE);
    foreach (@filetext) {
        if ($_ =~ /Page last touched (.+?)(?:<|\n)/) {
            if ($1 ne $t) {
                print "\$1=$1\n";
                print "quotemeta(\$1)=",quotemeta($1),"\n";
                print "\$t=$t\n";
                s/quotemeta($1)/$t/;
                $touched = 1;
                print "\$_=$_\n";
            }
        }
    }
    if ($touched) {
        open(FILE,'>'.$_);
        print FILE @filetext;
        close(FILE);
        utime $modified,$modified,$_ if $OPTIONS{u};
        print "$_ touched: $t\n";
    }
}
    

Once again, not particularly difficult. The hard part here done with PerlTidy and the -html option. Still, this is not what I've got in mind when I do the Literate Programming vision thing. Perhaps if we approach the problem in a fashion similar to how we might have written the code in the first place, i.e. top-down (or more accurately, pseudo-code to perl-code!) In that case, the beginning might look more like:

Pesudo-Code Source Listing for ftstamp.pl(3)

Hmmm… let's see. First the usual boiler-plate, bang-line, 'uses' and obvious global variables. I'll call that:

<<boiler-plate>>

Then we have the ever present parse the command line and set the switches code, initial code we run once:

<<initial and start-up code>>

Now that we have our ducks in a row, the final ingredient could just as easily be labeled; do-it! So we will:

<<do-it>>

This is the proto-typical example of pseudo-code, without much imagination it's clear that this could be applied to virtually any piece of code, in fact it is so general that it doesn't do much good. Let's try that once more from the top:

Pesudo-Code Source Listing for ftstamp.pl(4)

ftstamp A Perl script to timestamp '.html' files based on file modification date. A fairly simple minded file filter, ftstamp, given a list of files to examine will search for a 'marker' string (in this case, "Page last touched "). If found, the string is used as an anchor for the file modification time and date, i.e. it will replace "Page last touched whatever" with "Page last touched Fri Mar 30 13:20:14. It may then optionally set the file's actual time and date to the same value as the replacement string. ftstamp will do this for all files specified on the command line.

In general this breaks down to the following broad steps:

<<boiler-plate>>
First the usual and necessary header material, bang-line, 'uses' and similar required text.

#!/usr/bin/perl -w
# ftstamp.pl -- file modification time as time stamp.
use strict;
use warnings;
use Getopt::Std;
use Wild;
use diagnostics;
    
<<initial and start-up code>>
Then we have the ever present parse the command line and set the switches code, initial code we run once and only once.

my %OPTIONS;
getopts('ud',\%OPTIONS);
WildARGV();
    
<<do-it>>
Now that we have our ducks in a row, the penultimate ingredient could just as easily be labeled; do-it! So we did.

foreach (@ARGV) {
    if ($OPTIONS{d}) {
        print "ftstamp -d $_\n";
    }
    else {
        stamp($_);
    }
}
    
<<the heart of the matter>>
For the moment I'll only list the sole function of the script as is. At some future point, I'll create nested comments and code for it as well.

sub stamp {
    my $s = shift;
    my $modified = time - ((-M $_) * 86400);
    my $t = localtime($modified);
    my $touched;
    my @filetext;

    open(FILE,$s);
    @filetext = <FILE>;
    close(FILE);
    foreach (@filetext) {
        if ($_ =~ /Page last touched (.+?)(?:<|\n)/) {
            if ($1 ne $t) {
                print "\$1=$1\n";
                print "quotemeta(\$1)=",quotemeta($1),"\n";
                print "\$t=$t\n";
                s/quotemeta($1)/$t/;
                $touched = 1;
                print "\$_=$_\n";
            }
        }
    }
    if ($touched) {
        open(FILE,'>'.$_);
        print FILE @filetext;
        close(FILE);
        utime $modified,$modified,$_ if $OPTIONS{u};
        print "$_ touched: $t\n";
    }
}
    


 Page last touched:
Send e-mail to: hsmyers@gmail.com 


Validated by W3C