Weave: Write documentation pages

Previous: Tangle: write code output ] [ Top: xmlp alpha ] [ Next: Write index page ]

The weave step is the other main function of traditional literate programming: it takes the items as defined and creates the documentation file. Since the documentation in this case is a set of pages, the philosophy is a little different. For each main item, we write a separate documentation page (subitems are left on the same page as their respective parents, with headings.)

The file <project>_format.html is used to generate each of these pages; a more elaborate formatting control mechanism would be nice later, but this will do the trick for now. Code pieces are written enclosed in <code> tags, and <insert> tags are replaced with the label, not the name, of the respective items.

The overall code for weave looks a lot like the scan code, because it's doing very similar things. It scans lines of input, using global variables to mark its current state.

Setup is straightforward: we rewind the INPUT filehandle, load the template into a list (because we'll be scanning it once for each item page) and set some globals to blank values.
 
seek INPUT, 0, 0;

open TEM, $format_html;
@template = <TEM>;
close TEM;

$name = '';
$item = 0;
$piece = 0;

while (<INPUT>) {
So let's get down to business scanning. The thing weave is interested in is items, so let's look for <item> tags first. For each item we encounter, we'll set up replacement tags as follows:

Eventually we'll want change dates as well, but we're not tracking it yet, so there's not much point. All in good time.
 
   if (/(<item .*>)/i)
   {
      $tag = $1;
      $tag =~ s/^<item\s+//i;
      $attr = "";
      %thisitem = (name => '', label => '', pattern => '', language => '');
      foreach $part (split /"/, $tag) {
         if ($attr eq '') {
            $attr = $part;
            $attr =~ s/^\s*//;
            $attr =~ s/\s*=\s*$//;
         } else {
            $thisitem{$attr} = $part;
            $attr = '';
         }
      }

      $name = $thisitem{name};

      if ($parent{$name} eq '') {
         $tags{name} = $name;
         $tags{url} = $url{$name};
         $tags{label} = $label{$name};

         if ($item == 0) {
            $tags{prev} = "index.html";
            $tags{prevlabel} = "index";
         } else {
            $i = $items[$item-1];
            if ($parent{$i} ne '') { $i = $parent{$i}; }
            $tags{prev} = $url{$i};
            $tags{prevlabel} = $label{$i};
         }

         $tags{body} = '';
      } else {
         $n = $name;
         $n =~ s/^.*?\.//;
         $tags{body} .= "<br><br>\n";
         $tags{body} .= "<a name=\"$n\">\n";
         $tags{body} .= "<i>$label{$name}</i><br>\n";
      }

      if ($item == $#items) {
         $tags{next} = "index.html";
         $tags{nextlabel} = "index";
      } else {
         $tags{next} = $url{$items[$item+1]};
         $tags{nextlabel} = $label{$items[$item+1]};
      }

      next;
   }
   next if $name eq '';
And of course we terminate the item similarly to the scanner. The difference is that we also write out the current item's page using the various tags we've accumulated during scanning.
 
   if (/(<\/item\s*>)/i) {
      if (($parent{$name} eq '' && $children{$name} == 0) || $lastchild{$parent{$name}} eq $name)
      {
         if ($parent{$name} eq '') {
            open OUT, ">$url{$name}";
         } else {
            open OUT, ">$url{$parent{$name}}";
         }
         foreach $line (@template) {
            $_ = $line;
            while (/\[##(.*?)##\]/) {
               $tag=$1;
               s/\[##$tag##\]/$tags{$tag}/e;
            }
            print OUT;
         }
         close OUT;
      }

      $name = '';
      $item++;

      next;
   }
While in an <item>, we scan for pieces, just like scan. Text inside pieces is formatted differently (with spacing and linebreaks intact.)
 
   if (/(<piece.*>)/i)
   {
      $tag = $1;
      $tag =~ s/^<piece\s*//i;
      $attr = "";
      %thispiece = (add-to => '', language => '');
      foreach $part (split /"/, $tag) {
         if ($attr eq '') {
            $attr = $part;
            $attr =~ s/^\s*//;
            $attr =~ s/\s*=\s*$//;
         } else {
            $thispiece{$attr} = $part;
            $attr = '';
         }
      }

      $tags{body} .= "<table width=100%>\n";
      $tags{body} .= "<tr><td width=30 bgcolor=eeeeee>&nbsp;</td><td width=100%>\n";
      if ($thispiece{'add-to'} ne '') {
         $tags{body} .= "<i>Add the following to \"$label{$thispiece{'add-to'}}\"</i><br>\n";
      }
      $tags{body} .= "<pre>";
      $piece = 1;
      next;
   }
   if (/<\/piece\s*>/i) {
      $tags{body} .= "</pre>";
      $tags{body} .= "</td></tr></table>\n";
      $piece = 0;
      next;
   }
Almost done. The only remaining thing is to format <insert> tags.
 
   if (/(<insert\s.*>)/i)
   {
      $tag = $1;
      $before = $`;
      $after = $';

      $tag =~ s/^<insert\s+//i;
      $attr = "";
      %thisinsert = (name => '');
      foreach $part (split /"/, $tag) {
         if ($attr eq '') {
            $attr = $part;
            $attr =~ s/^\s*//;
            $attr =~ s/\s*=\s*$//;
         } else {
            $thisinsert{$attr} = $part;
            $attr = '';
         }
      }
      if ($thisinsert{name} =~ /^\./) {
         $thisinsert{name} = $name . $thisinsert{name};
      }

      $tags{body} .= $before . "<i>See <a href=\"$url{$thisinsert{name}}\">";
      $tags{body} .= "$label{$thisinsert{name}}</a></i>$after";
      next;
   }
And finally, any line which doesn't contain an <insert> and which is actually within an <item> simply gets tacked onto the current item's body after we make sure all the characters are going to display correctly. Note especially the attention to detail with the [## delimiter -- if we didn't replace that with a version containing an HTML entity instead (&#35;) then we'd have a problem with recursion once we get into the template application code. Which, of course, I realized only after having a problem with recursion once I got into the template application code.
 
   s/&/&amp;/g if $piece;
   s/\[\[/&lt;/g if $piece;
   s/\[\#\#/[&#35;#/g;

   $tags{body} .= $_ if $name ne '';
}
Previous: Tangle: write code output ] [ Top: xmlp alpha ] [ Next: Write index page ]


This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2000, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license.