WEBシステムは 文芸的プログラミングを支援するためにKnuthが開発したシステムである。 WEBシステムはもともとKnuthのTeXシステムを記述するために開発 されたものなのでPascalとTeXが対象になっているが、 Cが使えるようにしたCWEBシステム Cとtroffとの組み合わせが使えるcwebシステム、 任意のプログラミング言語に対応させることのできる Spiderといったシステムも開発されている。

WEB文書は複数のプログラムモジュールの集合という形になっており、
各モジュールはその解説部分とプログラム部分とで構成される。
WEB文書をtangleというプログラムに通すと
プログラム部だけが抽出されてひとつのPascalプログラムになり、
weaveというプログラムに通すと解説部とプログラム部の
両方が融合したTeXの文書になる。
ここではプログラム部は美しくフォーマットされ、
モジュール毎に章立てが行なわれ、
目次や索引も完備した完全なドキュメントが生成される。
このように、ひとつのWEBファイルからプログラムと
そのドキュメントの両方を生成することができるのが特長である。
weaveによってTeX文書に変換してから
TeXでフォーマットを行ない、dvi2psなどで
印刷形式にする必要ががある。この場合、
weave/TeX/dvi2ps/ghostscript
を順に適用する必要ががあり非常にわずらわしいので、
作業を頻繁に行ないにくいという問題がある。
WEB文書のかわりに普通のHTMLファイルを使うようにすれば
このような問題は解決すると考えられる。
プログラムをHTMLファイルの中に記述するという形式で
文芸的プログラミングを行なうことにすれば、
tangleに相当するプログラムは必要であるが、
weave関連プログラムはすべて省略して
直接HTMLファイルをブラウザで眺めることができる。
HTMLファイルに対して
tangleに相当する処理を行なう
wtangleプログラムを開発した。
wtangleプログラムは、
HTMLファイルとして記述された、簡易文芸的プログラミング文書から
プログラム部分だけを抽出するPerlプログラムである。
本HTML文書に対してwtangleを適用すると
wtangleプログラムが得られるので、
コンパイラのようにブートストラップ式に開発していくことが
できるはずである。
以下にwtangleプログラムの全ソースとその解説を記述する。
prologue()でプログラムの初期化を行ない、
main()でHTMLファイルからの
プログラム抽出処理を行ない、
epilogue()でファイル書き出しを行なう。
#!/usr/bin/env perl # # wtangle - Web Tangle # &prologue; &main; &epilogue;
prologue()では変数の初期化などを行なう。
%SIGに関数名を指定しておくと、
signalが発生したときに$SIG{シグナル名}の関数が呼ばれる。
ここではSIGINTなどを受け取った時に
finish()が呼ばれるようにしている。
sub prologue {
require 'cacheout.pl'; # 複数ファイル出力ライブラリ
if($#ARGV < 0){
print STDERR "wtangle --- Web Tangle\n";
print STDERR "Usage: wtangle documentfiles\n";
exit 0;
}
$SIG{'INT'} = $SIG{'TERM'} =
$SIG{'QUIT'} = $SIG{'HUP'} = 'finish';
}
<pre>,
</pre>
で囲まれた部分をファイルに書き出す。
ファイル名は
<pre>の中で
"file=ファイル名{,ファイル名...}"と記述する。
HTMLファイルが更新されたり場合でも
プログラム自体は変更されていない場合があるので、
直接プログラムを書き出さずに
tmpname()で計算される
テンポラリファイルに一旦書き出した後で
diff()でもとのファイルと
比較を行ない、異なっている場合のみファイルを更新するようにする。
sub main {
while($file = shift(@ARGV)){
unless(open(f,$file)){
print STDERR "Can't open <$file>\n";
&finish;
}
while(){
if(m#^${lt}/pre>$#){
@tmpfiles = ();
}
elsif(m#^${lt}pre\s*file=(.*)>$#){
@files = split(/,/,$1);
@tmpfiles = ();
foreach $file (@files){
$allfiles{$file} = 1;
push(@tmpfiles,&tmpname($file));
}
}
else { # cachout()を使って現在行を各ファイルに書き出す
foreach $tmpfile (@tmpfiles){
&cacheout($tmpfile);
$taghead = pack(c,60);
s/&.{0}lt;/$taghead/g;
print $tmpfile $_;
}
}
}
close(f);
}
}
sub epilogue {
foreach $file (keys %allfiles){
$tmpfile = &tmpname($file);
close($tmpfile);
if(! -e $file || &diff($tmpfile,$file)){
push(@newfiles,$file);
&move($tmpfile,$file);
chmod 0555,$file; # 書込み禁止/実行可能にする
}
}
sort @newfiles;
$n = $#newfiles + 1;
print STDERR
$n == 0 ? "No file is updated.\n" :
$n == 1 ? "File '$newfiles[0]' is updated.\n" :
"Files '".join("', '",@newfiles[0..$n-2]).
"' and '".$newfiles[$n-1]."' are updated.\n";
&finish;
}
table>タグを用いて表形式のリストにしている。
HTMLを用いているのでこのように表示形式を柔軟に選ぶことができる。
sub tmpname {
local($_) = @_;
s#/#_#g;
"/tmp/stangle$_$$";
}
|
テンポラリに使用されるファイル名を計算する。 |
sub finish {
foreach $file (keys %allfiles){
$tmpfile = &tmpname($file);
# unlink($tmpfile) if -e $tmpfile;
}
exit;
}
|
テンポラリに使用されたファイルを全て削除してから プログラムを終了する。 |
sub move {
local($from,$to) = @_;
return if $from eq $to;
system "/bin/mv -f $from $to";
if($?){ # mv実行エラー
print STDERR "Can't move $from to $to\n";
&finish;
}
}
|
ファイル移動を行なう。
簡単のためmvコマンドを使用している。
|
sub diff {
local($file1,$file2) = @_;
system "/usr/bin/diff $file1 $file2 > /dev/null";
$? >> 8; # コマンドステータス
}
|
ファイル比較を行なう。
簡単のためdiffコマンドを使用している。
|