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 コマンドを使用している。
|