お久しぶりです.N.Mです.
今年度も卒論,修論,博論のシーズンが近づいてきましたね.
昨年の記事でbstファイルを書き換える方法に触れました.その時に軽くすっ飛ばしてしまった,日本語での情報がなかったbstファイルの文法についてまとめとこうと思います.bstファイルを書き換える事態になって,bstファイルのプログラムの意味が分からないときにリファレンスとして読んでいただければと思います.
(内容はここを自分なりに要約したものになります.)
(2020/01/06追記:Twitterにて,参考にした文献の日本語訳があることを教えていただきました(日本語訳).こちらの調査不足で申し訳ございませんでした.)
そもそもbstファイルとは
論文情報のデータベース(bibファイル)から,参考文献のフォーマットになるように整形するためのプログラムがbstファイルです.ここをいじることで参考文献のフォーマットや並び順などを変更できます.
(ちなみにbstファイルで成形された参考文献のデータはbblファイルという中間ファイルとして出力され,TeXではこれをそのまま埋め込んで参考文献を表示します.つまり,bstファイルのプログラムで出力される結果はTeXでそのまま使える形になります.)
bstファイルで扱われるオブジェクト,データ型
bstファイルのプログラムで使われるオブジェクトには,定数,グローバル変数,エントリ変数,フィールド変数,関数,スタックがあります.変数は3種類ありますが,以下のように違いがあります.
- グローバル変数... 文献とは無関係に用意される変数.宣言すると文献の数に関わらず1つだけ用意される.
- エントリ変数... 文献ごとに用意される変数で,bstファイル内で後から値を入れたり,使用したりする変数.
- フィールド変数... 文献ごとに用意される変数で,bibファイルから抽出される文献情報が格納される変数.読み取り専用
bstファイルの定数,変数では文字列型と整数型のみ用意されています.(ブーリアンは0か1が入る整数で表現します.)定数を使用する場合は以下のように表現します.
- 文字列の定数...両端にダブルクォーテーション(").("は必ず同じ行になるようにする) 例."example"
- 整数の定数...数字の前にシャープ(#) 例.#1
変数や関数の命名には以下の制限があります.
- 数字から始めることはできない
- TeXの特殊文字(#$%&~_^{})は使用不可(アルファベットと数字以外は.(ドット)だけにとどめたほうが安全)
- 大文字小文字の違いは同じものとみなされる
bstファイルプログラムの基本はスタック
bstファイルのプログラムでは,演算子や関数を使用する際にスタックを使用します.演算子や関数の引数に使われる値はまずスタックにプッシュ(入力)されます.演算子や関数を使用すると,引数をスタックからポップし(取り出し),結果を最後にプッシュします.
変数については,変数の値をスタックに入れる場合と変数名をスタックに入れる場合があります.(代入の引数として変数名を使う場合があります.)変数の値を入れる場合はそのまま変数名を,変数名を入れる場合は変数名の前にシングルクォーテーション(')をつけます.
例:文字列型変数labelがあり,中にあらかじめ"Label "が入っているとします.
label "A" * 'label :=
*はスタックから2つポップして,連結した文字列をスタックにプッシュします.:=は変数名と値をスタックの上からポップし,値を変数に代入します(詳細は後述).図のように処理が行われ,結果としてlabel変数に"label A"が代入されます.
この例では演算子を使用していますが,関数を使用するときも演算子の部分を関数名に変更すれば大丈夫です.
(スタックから引数をポップして関数内で使用されます.)
bstファイルのコマンド
bstファイルには計10個のコマンドが用意されており,
うち5つ (ENTRY
, INTEGERS
, STRINGS
, MACRO
, FUNCTION
)は変数や関数を定義するためのもの,
うち1つ (READ
)はbibファイルからエントリのリストへのデータ読み込みのためのもの,
残りの4つ (EXECUTE
, ITERATE
, REVERSE
, SORT
)はエントリのリストや出力に対する処理のためのものです.
コマンドは関数とは異なり,引数としてスタックのデータを使用しません.コマンドの後に引数を{}で囲んで使用します.
コマンドを呼び出す順番には制約があります.
-
ENTRY
とREAD
は必ず1回ずつ呼び出す.(多くてもダメ,少なくてもダメ) -
ENTRY
とすべてのMACRO
,エントリタイプと同じ名前の関数,default.type
関数を定義するFUNCTION
は,READ
より前に呼び出す.※エントリタイプ... articleやbookなど,bibファイルで文献ごとの最初に宣言する型 -
READ
はEXECUTE
,ITERATE
,REVERSE
,SORT
コマンドより前に呼び出す. - 変数や関数は使用される関数よりも前にコマンドで宣言される必要がある.
ENTRY
エントリ変数やフィールド変数の宣言に使用するコマンドです.引数は3つで,以下のように入力します.
- 第1引数... 使用するフィールド変数名.ここで入力した変数名のデータがbibファイルから抽出され,フィールド変数に格納されます.
- 第2引数... 使用する整数型のエントリ変数名.なければ空でも大丈夫です.
- 第3引数... 使用する文字列型のエントリ変数名.なければ空でも大丈夫です.
ここで宣言した変数の他に,Bibtex側でcrossref
というフィールド変数(クロス参照時に使用)と,sort.key$
という文字列型エントリ変数(SORT
で使用)が自動で用意されます.
例:
ENTRY
{
author
month
pages
title
volume
year
}
{ number }
{ label }
このように表記し,後述のREADコマンドを呼ぶことで,bibファイルからauthor, month, pages, title, volume, yearの部分が抽出され,そのままフィールド変数としてbstファイルで使用できます.また,文献ごとに整数型のnumber変数と,文字列型のlabel変数が用意されます.
INTEGERS
整数型の変数を宣言するためのコマンドです.引数は1つで,変数名の羅列を入力します.また,文字列型変数が格納できる最大文字数を格納するentry.max$
(エントリ変数用)とglobal.max$
(グローバル変数用)が自動的に宣言されます.
例:
INTEGERS {
flagA
flagB
}
このようにすることで,整数型のグローバル変数flagAとflagBが宣言されます.
STRINGS
INTEGERS
の文字列版で,文字列型変数を宣言するコマンドです.引数もINTEGERS
と同じで変数名の羅列を入力します.
FUNCTION
新しい関数を定義するためのコマンドです.引数は2つです.
- 第1引数... 関数名
- 第2引数... 関数として定義する処理の羅列
前述のように,使用される場所よりも前に定義する必要があります.また,再帰呼び出しはできません.
例:
FUNCTION {initialize}
{
#0 'flagA :=
#0 'flagB :=
}
このように,flagAとflagBを0に初期化するinitialize関数を定義できます.
MACRO
文字列に置き換えるマクロを定義します.引数は2つです
- 第1引数... マクロ名
- 第2引数... 置き換えられる文字列(ダブルクォーテーション(")で囲む)
ここでマクロを定義すると,bstファイル内で関数や変数と同じように使えるだけでなく,
bibファイル内のマクロ名と同じ文字列も置き換えられて格納されます.
最低限,以下の例のような月を3文字で省略する表現に対するマクロは用意する必要があるようです.
例:
MACRO {jan} {"January"}
READ
bibファイルから引用された文献についてデータを抽出し,ENTRY
で宣言したフィールド変数に代入します.引数はありません.値のない部分があった場合はmissingとしてマークされます.
EXECUTE
関数を実行するコマンドで,引数には実行する関数名を1つ入力します.
例:
EXECUTE {initialize}
initialize関数が実行されます.
ITERATE
引用されている文献ごとに1つの関数を実行します.EXECUTE
と同様に引数には実行する関数名を1つ入力します.
デフォルトでは引用された順番に関数を実行しますが,後述のSORT
を使用することで順番を変更できます.
REVERSE
ITERATE
と同様に引用されている文献ごとに1つの関数を実行します.ITERATE
とは逆の順序で実行します.
SORT
文字列型のエントリ変数sort.key$
がアルファベットの辞書式順になるように,文献を並び替えます.引数はとりません.
bstファイルのビルドイン関数
変数ではsort.key$
のようにあらかじめ用意された変数(ビルドイン変数)がありました.
bstファイルで使われる関数でも同様に,ユーザがFUNCTION
で定義する関数以外にも,あらかじめ用意されているビルドイン関数がいくつか存在します.ビルドイン変数と同様に,関数名の後に$がついているものが該当します(演算子を除く).
ユーザが定義する関数と同様にスタックにプッシュされたデータをポップして引数に使用しますが,もし型があっていない場合はBibtexで警告が表示され,結果としてスタックに0か空文字列をプッシュします.
以下,ビルドイン関数を紹介していきます.
>
引数としてスタックから2つポップします.整数型を想定しています.
2番目にポップされた整数が,最初にポップされた整数よりも大きければ1を,そうでなければ0をスタックにプッシュします.
例.
#3 #2 > → スタックに1をプッシュ
#5 #8 > → スタックに0をプッシュ
<
>の逆です.引数としてスタックから2つポップします.整数型を想定しています.
2番目にポップされた整数が,最初にポップされた整数よりも小さければ1を,そうでなければ0をスタックにプッシュします.
=
引数としてスタックから2つポップします.整数型か文字列型を想定しています.
最初にポップされたものが,2番目にポップされたものと一致していれば1を,そうでなければ0をスタックにプッシュします.
※他のプログラム言語とは違い>=や<=に当たるものはありません.また,論理和,論理積,否定もありません.そのため,これらの関数は自分で定義する必要があります.
+
引数としてスタックから2つポップします.整数型を想定しています.
ポップされた2つの整数の和をスタックにプッシュします.
-
引数としてスタックから2つポップします.整数型を想定しています.
2番目にポップされた整数から,1番目にポップされた整数を引いた差をスタックにプッシュします.
例.
#3 #2 - → スタックに1をプッシュ
#8 #3 - → スタックに5をプッシュ
*
引数としてスタックから2つポップします.文字列型を想定しています.
2番目にポップされた文字列の後ろに,最初にポップされた文字列を連結します.連結された文字列をスタックにプッシュします.
例.
"Hexagram" "NM" * → "HexagramNM"をスタックにプッシュ
:=
引数としてスタックから2つポップします.最初に変数名がポップされ,次にその変数の型の値がポップされることを想定しています.
最初にポップされた変数に,次にポップされた値を代入します.スタックへのプッシュはありません.
例.
#0 'flagA := → flagAという変数に0を代入
add.period$
引数としてスタックから1つポップします.文字列型を想定しています.
ポップされた文字列の'}'でない最後の文字が,.?!のいずれかでなければ,ポップされた文字列の末尾に.を追加します.最後,結果をスタックにプッシュします.
call.type$
現在処理している文献のエントリタイプ名と同じ名前の関数を実行します.未知のタイプだった場合はdefault.type
関数を実行します.そのため,これを呼ぶ前にbibファイルでありうるエントリタイプと同じ名前の関数すべてと,default.type
関数を定義しておく必要があります.
例.以下の文献がbibファイルにある場合,
@article{japaneseTest2,
author = {山田 五郎 and 山田 六郎},
year = {2019},
title = {文献2}
}
この文献を処理しているときに,call.type$を呼び出した場合はarticle関数が実行されます.
change.case$
引数としてスタックから2つポップします.文字列型を想定しています.
最初にポップされた文字列に応じて,2番目にポップされた文字列の大文字小文字を切り替え,その結果をスタックにプッシュします.
- 最初にポップされた文字列が"t"または"T"... 文字列内の最初の文字と:(コロン)やスペースの後に続く最初の文字以外を,すべて小文字に変換します.
- 最初にポップされた文字列が"l"または"L"... 文字列内のすべての文字を小文字に変換します.
- 最初にポップされた文字列が"u"または"U"... 文字列内のすべての文字を大文字に変換します.
- 上記以外... 警告が表示され,2番目にポップされた文字列をそのままスタックにプッシュします.
chr.to.int$
引数としてスタックから1つポップします.1文字のみ含む文字列を想定しています.
ポップされた文字を対応するASCIIコードの数値に変換し,その整数をスタックにプッシュします.
cite$
引数はとりません.
現在処理している文献のラベル(文字列)をスタックにプッシュします.
例.以下の文献がbibファイルにある場合,
@article{japaneseTest2,
author = {山田 五郎 and 山田 六郎},
year = {2019},
title = {文献2}
}
この文献を処理しているときに,cite$を呼び出した場合は"japaneseTest2"という文字列がスタックにプッシュされます.
duplicate$
引数としてスタックから1つポップします.
ポップされたものを2つスタックにプッシュします.(結果として,スタックの上にあるものが複製されます.)
empty$
引数としてスタックから1つポップします.
ポップされたものがmissingとマークされたフィールド変数,もしくはスペース以外の文字を含まない文字列だった場合は1を,そうでない場合は0をスタックにプッシュします.
format.name$
引数としてスタックから3つポップします.最初に名前のフォーマット文字列(ここで触れられている,"{ff}"などの文字列),2番目に整数,3番目にandでつなげられた人物名の文字列(人物リスト)がポップされることを想定しています.
この関数を実行すると,2番目にポップされた整数番目にあたる,3番目にポップされた人物リストのなかの人物名を最初にポップされたフォーマット文字列に従ってします.整形された人物名の文字列をスタックにプッシュします.
例.authorがフィールド変数として存在する場合,
author #2 "{ff~}{vv~}{ll}{, jj}" format.name$
authorの2番目に入力されている人物の名前を"{ff~}{vv~}{ll}{, jj}"のフォーマットに従って整形し,スタックにプッシュします.
if$
引数としてスタックから3つポップします.最初と2番目に{}で囲まれた処理の羅列が,3番目に整数がポップされることを想定しています.
3番目の整数が0より大きければ2番目にポップされた処理の羅列を,そうでなければ最初にポップされた処理の羅列を実行します.
例.
flagA
{処理A}
{処理B}
if$
この場合,flagAが0より大きければ処理Aを実行し,そうでなければ処理Bを実行します.
int.to.chr$
引数としてスタックから1つポップします.整数を想定しています.
ポップされた整数をASCIIコードとして解釈し,1文字に変換します.変換された文字をスタックにプッシュします.
int.to.str$
引数としてスタックから1つポップします.整数を想定しています.
整数を文字列に変換し,変換された文字列をスタックにプッシュします.
例.
#10 int.to.str$ → "10"という文字列がスタックにプッシュされます.
missing$
引数としてスタックから1つポップします.
ポップされたものがmissingをマークされているフィールド変数なら1を,そうでなければ0をスタックにプッシュします.
newline$
引数はとりません.
出力バッファが空の時に空白の行をbblファイルに書き込みます.
num.names$
引数としてスタックから1つポップします.文字列(人物名のリスト)を想定しています.
ポップされた文字列に含まれる人物の数(文字列内のスペースで囲まれたandの数+1)をスタックにプッシュします.
pop$
引数としてスタックから1つポップします.それ以外の処理は行いません.
スタックから不要なものを除きたいときに使う関数です.
preamble$
引数はとりません.
bibファイル内に含まれるPREAMBLEで定義した文字列をすべて連結し,スタックにプッシュします.
purify$
引数としてスタックから1つポップします.文字列を想定しています.
文字列の中から,スペースやハイフン以外の数字やアルファベットでない文字(ハイフンはスペースに変換されます),特殊文字を表示させるために使われているアルファベット文字列(\alphaのalphaなど)を取り除き,スタックにプッシュします.
quote$
引数はとりません.
スタックにダブルクオーテーション(")をプッシュします
skip$
引数はとりません.何も処理をしません.
if$の分岐で,処理を行わないときにこの関数を入れます.
stack$
引数はとりません.
スタックの中身をすべてポップし,表示します.デバッグ用の関数です.
substring$
引数としてスタックから3つポップします.最初に2つ整数がポップされ,最後に文字列がポップされることを想定しています.
最後にポップされた文字列について,2番目にポップされた整数番目の文字から最初にポップされた整数個分の文字だけ取り出した部分文字列をスタックにプッシュします.
例.
"HexagramNM" #3 #5 substring$ → 3番目の文字であるxから5文字取り出した部分文字列"xagra"がスタックにプッシュされます.
swap$
引数としてスタックから2つポップします.
ポップされた2つのものがスタック内で入れ替わるように,再度プッシュされます.
text.length$
引数としてスタックから1つポップします.文字列を想定しています.
ポップされた文字列の文字数をスタックにプッシュします.この文字数について特殊文字の部分は1文字としてカウントし,{}は文字数にカウントしません.
text.prefix$
引数としてスタックから2つポップします.最初に整数がポップされ,2番目に文字列がポップされることを想定しています.
2番目にポップされた文字列について,最初の文字から最初にポップされた整数個分文字を取り出し,その部分文字列をスタックにプッシュします.
(substring$との違いとして,特殊文字を1文字としてカウントする,{}は文字にカウントしないといったことが挙げられます.)
top$
引数としてスタックから1つポップします.
ポップされたものをターミナルやログファイルに表示します.デバッグ用の関数です.
type$
引数はとりません.
現在処理をしている文献のエントリタイプ (articleなど)の文字列をスタックにプッシュします.未定義のエントリタイプの場合は空の文字列をプッシュします.
warning$
引数としてスタックから1つポップします.
警告メッセージを表示した後に,ポップされたものを表示します.警告メッセージ数のカウントも1増やします.
while$
引数としてスタックから2つポップします.{}で囲まれた処理の羅列が2つポップされることを想定しています.2番目にポップされた処理を実行した後でスタックに残る値が0より大きくなる限り,最初にポップされた処理が繰り返し実行されます.
例.
{flagA #1 =}
{ 処理A }
while$
flagAが1である限り,処理Aが繰り返し実行されます.
width$
引数としてスタックから1つポップします.文字列を想定しています.
その文字列を出力した際の幅を整数としてスタックにプッシュします.
write$
引数としてスタックから1つポップします.文字列を想定しています.
ポップされた文字列を出力バッファに書き込み,bblファイルに出力します.
まとめ
bstファイルの文法は
・関数などの処理はスタックを用いて実行される.
・10のコマンド
・ビルドイン関数
を押さえておけば,ある程度理解できるかと思います.これがbstファイル変更の手助けになればと思います.