bstファイルの編集を試みたら、output.check の中で動いている output.nunnull が結構やっかいな動きをしていたので、その覚書です。
コードはTeXに標準搭載されている acm.bst で解説していますが、他のbstファイルでも基礎的な考え方は同じです。
article関数の大まかな構造
まず、文献の情報が大まかにどのように出力されているかを確かめるために、article関数の定義を見てみます。
FUNCTION {article}
{ output.bibitem
format.authors "author" output.check
new.block
format.title "title" output.check
new.block
crossref missing$
{ format.journal.vol.num.date output
format.pages output
}
{ format.article.crossref output.nonnull
format.pp.pages output
}
if$
new.block
note output
fin.entry
}
output.bibitem はTeXの文献を書く際に用いる「 \bibitem{ }」などの文字列をスタックにプッシュするコマンドです。
format.authors、format.title などはbibファイルから著者やタイトルなどの情報を読み込み、適当なスタイルに変換して、その文字列をプッシュするコマンド。
これらのコマンドは著者やタイトルなどの情報を用意する(スタックにプッシュする)だけです。実際に用意された文字列を、bblファイルに書き込んでいるのが「output.check」というコマンドです。
さらに、output.check は書き込むだけでなく、著者名・タイトル・雑誌名などの間に、状況に応じてカンマやピリオドも付けて出力するようにプログラムされています。
output.check と output.nonnull の仕組み
では、「output.check」の仕組みを説明します。まず acm.bst の場合、コードは次のようになっています。
FUNCTION {output}
{ duplicate$ empty$
'pop$
'output.nonnull
if$
}
FUNCTION {output.check}
{ 't :=
duplicate$ empty$
{ pop$ "empty " t * " in " * cite$ * warning$ }
'output.nonnull
if$
}
output は単にこれの警告のないバージョンです。
核は次の output.nunnull です。
FUNCTION {output.nonnull}
{ 's :=
output.state mid.sentence =
{ ", " * write$ }
{ output.state after.block =
{ add.period$ write$
newline$
"\newblock " write$
}
{ output.state before.all =
'write$
{ add.period$ " " * write$ }
if$
}
if$
mid.sentence 'output.state :=
}
if$
s
}
output.nunnull は、ariticle関数の中の各段階で「output.state」に入力された数値から、センテンスの途中(mid.sentence)なのか、センテンスの終わり(after.sentence)なのかなどを判断し、カンマやピリオドを状況に応じて入力するようにプログラムされたコマンドです。
acm.bst では、これらの状況を判断するために、次の4つの数値がコードの序盤で用意されています。
FUNCTION {init.state.consts}
{ #0 'before.all :=
#1 'mid.sentence :=
#2 'after.sentence :=
#3 'after.block :=
}
output.stateが、この 0~3 のどの数値なのかによって、カンマを出力するのか、ピリオドを出力するのかを決めます、
ちなみに、output.state は一番初めにoutput.bibitem の中で before.all の値に設定されます。
output.nunnull の実際の仕組みは次の通りです。
※1. ここで注意しなければならないのは、初めにformat.authors や format.titleでプッシュされたものを、ポップして変数 s に代入している点です。ポップしたことによって、スタック内にはformat.authorsで入力した情報は一旦なくなっています。
なので article関数の中で書かれている「output.check」はその1行上の情報に、カンマやピリオドを付けて write していることになります。
FUNCTION {article}
{ output.bibitem
format.authors "author" output.check %ここのoutput.checkはbibitemを書いている
new.block
format.title "title" output.check. %ここのoutput.checkはauthorの情報を書いている
new.block
new.blockの役割
new.block は acm.bst 特有のコードですが、これは output.state を after.block の値に設定しろというコマンドです。
FUNCTION {new.block}
{ output.state before.all =
'skip$
{ after.block 'output.state := }
if$
}
これをarticle 関数の定義に入れることで、著者名(format.authors)やタイトル(format.title)の後では、output.check の条件分岐が、output.state=after.block であるときをたどるように設定しているわけです。つまり、著者名やタイトルの後で、ピリオドやnewline, newblock などを付けて、writeするようにしています。
この辺りのピリオドやカンマの設定を変えたければ、new.block をコメントアウトしたり、output.nunnull のコードを変更すればよいのです。
ちなみに author部分の下の new.block をコメントアウトすると著者名の後ろにカンマが入り、title部分の下の new.blockをコメントアウトするとタイトルの後ろにカンマが入ります。
これは、output.checkの仕組みの後半で、変数 output.state に mid.sentence の値が代入されたものが、そのまま維持されるからです。
FUNCTION {article}
{ output.bibitem
format.authors "author" output.check
new.block %ここをコメントアウトすると、著者名の後ろにカンマが入る。
format.title "title" output.check.
new.block %ここをコメントアウトすると、タイトルの後ろにカンマが入る。
※2. 上のコードの4行目のnew.block が output.check の後ろにあるのに、authorの後ろにカンマが入るのは、※1で説明したように「format.authors や format.titleでプッシュされたものを、ポップして変数 s に代入している」操作があるためです。
実際には3行目のoutput.checkは2行目の output.bibitem をwrite しているだけです。
他のbstファイルについて
他のbstファイルでは、output.nunnull の条件分岐を複雑化させて、カンマなど入り方を調整してあったりします。例えば ieeetr.bst では、「FUNCTION {init.state.consts}」で6つの値を用意し、クォーテーションマークの後ろでは、カンマもピリオドも出力せず、スペースのみを出力するようにプログラムされていたりします。詳しくは以下の記事の後半をご覧ください。
参考サイト
Bibtexのスタイルファイル:bstファイルの文法
↑bibtexコマンドの文法が非常に分かりやすくまとまっています
Bibtexのスタイルの改変
↑junsrt.bstというbstファイルのコードの解説が分かりやすい
コメント