BibLaTeXの文献ファイルをLaTeX，markdown，docxに変換

BibLaTeXで日本語と英語の混在を扱えるようにbbxファイルを作成した際にBibLaTeXがどんな風にして文献リストを作成しているのかがわかったので，その経験をもとに，BibLaTeXが吐き出すbblファイルをプレーンなLaTeXあるいはbibitem，markdown，docxに変換するツール「bblconverter」を作ってみました。

https://github.com/sbtseiji/bblconverter

bblファイル自体は非常に単純な構造で，bibファイルの各項目をリスト形式に並べただけなので，これをbibitemやmarkdownにするのはさほど難しいことではありません。ですが，心理学研究とは文献の記載ルールが異なるジャーナルに投稿する場合や，将来的に文献リストの書式が変更になった場合などのことを考えると，bblファイルを直接LaTeXやmarkdownに変換する方式だと，その都度スクリプトを書き直さなくてはならなくなるので，それは避けたいところです。そこで，[こちらの記事](https://qiita.com/cuhey3/items/956e16126496393bd9b3)を参考に，bblconverterでは文献リストのフォーマットをYAMLのDSLで行う形式にしてみました。

なお，bblconverterではbblファイルをYAMLのリストに変換した上で処理をしますので，bblファイルだけでなく，YAMLで書かれた文献ファイルから，LaTeX，markdown，docxの文献リストを作成できます。

## YAML形式の文献ファイル

bblファイルから変換されたYAML形式の文献ファイルは，次のような形になっています。

```
- entry: Clement2002
  entrytype: book
  skip: false
  author:
  - family: Clement
    familyi: C.
    given: E.
    giveni: E.
  publisher:
  - Wiley
  sortinit: C
  sortinithash: 4d103a86280481745c9c897c925753c0
  extradatescope: labelyear
  labeldatesource:
  labelnamesource: author
  labeltitlesource: title
  subtitle: The Cornerstone of Learning
  title: Cognitive Flexibility
  year: 2002
  dateera: ce
```

各エントリは，「`entry:`」というキーと引用キー（下の例の場合は「Clement2002」）のペアで始まり，その後に文献情報の各要素が並びます。「entrytype:」は文献のタイプで，これはbibファイルの「@article」や「@book」に対応しています。その後の「skip:」は，この文献を処理するかどうかのフラグです。翻訳書やシリーズもののうちの1冊など，「related」フィールドを用いる文献の場合には，「related」で関連づけられた文献情報が存在しますが，それらはこの「skip:」フィールドが「true」になっていて，文献リスト作成時には無視されるようになっています。
なお，「author:」など，著者名や編集者名に関連するフィールドは，リスト形式で各著者の情報が記載されています。このリスト部分の「family:」は姓，「familyi:」は姓のイニシャル，「given:」は名，「given:」は名のイニシャルを意味します。

「publisher:」もBibLaTeXではリストとして扱われているのでリスト形式になっていますが，出版社を複数記載することはまずないと思いますので，bibconverterの中では最初の項目だけを見るようにしています。なので，「publisher: Wiley」のように書いてあっても多分動くと思います。むしろ，複数指定してある場合にはエラーになるかもしれません。

このYAMLはbblファイルの中身をほぼそのままYAMLに書き起こしただけになっているので，文献リストの作成に必要のない項目もたくさん含まれています。この例では，「sortinit:」や「extradatescope:」，「labelnamesource:」，「labeltitlesource」，「dateera: ce」の項目はなくても構いません。その他どのような項目があるのかは，実際のbblファイルをYAMLに変換してみてみるとよいでしょう。bblconverterでは，bblファイルから変換したYAML形式の文献ファイルを作成できます。

## 文献リスト作成用のYAML

bblconverterでは，文献リストの作成をYAMLで書かれたDSL（Domain Specific Language）を用いて行います。そのため，各ジャーナルの文献スタイルに合わせたフォーマッタYAMLを用意すれば，幅広いジャーナルに対応可能です。

bblconverterのフォーマッタYAMLは，`constants:`（定数），`names:`（namesリストフィールド），`driver:`（各文献タイプのフォーマッタ）の3パートで構成されています。最初の`constants:`（定数）は，たとえば文献リストに表示する著者名の最大人数など，その文献書式で使用される定数を指定するパートです。ここには，次のようにして辞書形式で定数名と値を指定します。ここで定義された定数は，`driver:`パート内の条件節で参照できます。定数を用いる必要がなければ，このパートはなくても構いません。

```
constants:
  maxnames: 20
```

2つ目の`names:`は，著者名や編集者名など，BibLaTeXでnamelistとして扱われているフィールドを指定する部分です。このパートは，次のようにリスト形式で記載します。

```
names:
  - author
  - editor
  - editora
  - translator
  - translatora
  - origauthor
```

3つ目の`driver:`は，フォーマッタYAMLのメインの部分です。ここには，`article:`や`book:`など，各文献タイプごとの書式を記述します。各文献タイプの書式は，基本は「`フィールド名: フィールド書式`」という構成になっていて，これを文献リストにおける記載順にリスト形式で記述していきます。たとえば，学術論文の場合に「著者名（出版年）論文タイトル　掲載誌名，巻号，ページ番号」と記載するのであれば，次のような形でYAMLを書けばよいのです。このとき，各フィールドの名前は，BibLaTeXが使用するフィールド名（YAML形式のbibファイルで使用されているフィールド名）でなくてはなりません。


```
  article:
    - author: 著者名の書式
    - year: 出版年の書式
    - title: 論文タイトルの書式
    - journaltitle: 掲載誌名の書式
    - volume: 巻号の書式
    - pages: ページ番号の書式
```

各フィールドの書式は，次のコマンドをリスト形式で繋げて書いていきます。

* value::フィールド名　フィールドの値を取得して表示
* text::"文字列"　文字列を表示
* delim::区切り文字　区切り文字を表示
* italic::true　イタリック体を開始
* italic::false　イタリック体を停止
* bold::true　太字を開始
* bold::false　太字を停止
* punct::句読点　句読点を表示

最初の`value::`は，フィールド名で指定したフィールドの値を取得して表示するコマンドです。たとえば，その文献の掲載誌名を表示したい場合には，次のように記載します。

```
  - journaltitle: value::journaltitle
```

2つ目の`text::`は，指定した文字列をそのまま表示するコマンドです。たとえば，出版年を「(2001).」のように().で囲んで表示したい場合には，次のように記載します。

```
  - year: 
    - text::"("
    - value::year
    - text::")."
```

3つ目の`delim::`は，区切り文字を表示するコマンドです。YAMLではコロン（：）など一部の文字が特殊文字として認識されるため，それを回避するために`delim::COLON`などとして使用します。
区切り文字として定義されているのは，`COLON`（:），`SPACE`（空白），`COMMA`（,），`PERIOD`（.），`DOT`（.），`DOTS`（…），`EMDASH`（—），`NDASH`（–），`LINEBREAK`（改行）です。

`italic::true`と`italic::false`，`bold::true`と`bold::false`は，スタイルの指定に使用するコマンドです。たとえば，書籍タイトルを斜体表示にしたい場合には，次のように記載します。

```
  - booktitle: 
    - italic::true
    - value::booktitle
    - italic::false
```

最後の`punct::`は，タイトル末尾のピリオドなどを適切に処理するためのコマンドです。bibファイルに登録されている英文タイトルで，最後にピリオドがついているものとついていないものが混在していた場合，そのままでは文献リストでも表記がバラバラになってしまいます。たとえばtitleフィールドの書式に次のように記載してあったとしましょう。

```
  - title: 
    - value::title
    - delim::DOT
```

この場合，bibファイルのtitleフィールドに「This is my first paper」とピリオドなしで書かれているものは，「This is my first paper.」というように適切な表示になるのですが，「This is my second paper.」のようにtitleフィールドの内容にすでにピリオドが付けられているものの場合には，「This is my second paper..」のように，ピリオドが重複してしまいます。ここで，「`delim::DOT`」や「`text::"."`」とする代わりに次のように`punct::`を用いると，どちらの場合にも「This is my first paper.」，「This is my second paper.」となって，文末のピリオドが適切に表示されるようになります。

```
  - title: 
    - value::title
    - punct::"."
```

## 条件節

bblconverterでは，`driver:`のYAMLの中で条件節を使用できます。条件節は，`cond::`で示され，次のように3つの要素からなるリストとして記載されます。偽の場合の処理については，省略しても構いません。

```
  - - cond::条件節
    - 真の場合の処理
    - 偽の場合の処理
```

`cond::`で始まる条件節としては，次のものが用意されています。

* cond::ifequal[値1,値2]　値1と値2が同じかどうかを確かめます。
* cond::ifgreater[値1,値2]　値1が値2より大きいかどうかを確かめます。
* cond::ifgreatereq[値1,値2]　値1が値2以上であるかどうかを確かめます。
* cond::ifless[値1,値2]　値1が値2より小さいかどうかを確かめます。
* cond::iflesseq[値1,値2]　値1が値2以下であるかどうかを確かめます。
* cond::ifdef[field::フィールド名,true]　フィールド名の値が定義されているかどうかを確かめます。
* cond::ifdef[field::フィールド名,false]　フィールド名の値が未定義かどうかを確かめます。
  
これらの条件節は，`&&`，`||`，`^^`という論理演算子を用いて，2つを組み合わせることもできます。2つ目の条件節には`cond::`は不要です。

* &&  論理積　(例) cond::ifgreater[listcount,2]&&ifless[listcount,5]（listcountが2より大，かつ，5より小）
* ||  論理和　(例) cond::ifgreater[listcount,2]||ifless[listcount,5]（listcountが2より大，または，5より小）
* ^^  排他的論理和　(例) cond::ifgreater[listtotal,2]^^ifdef[field::title,false]（listtotalが2より大，titleフィールドが未定義のいずれか一方のみが真）

## 変数

これらの条件節では，`constants:`で指定された定数の他，`listcount`と`listtotal`という変数を使用できます。`listcount`は現在処理中の著者が先頭から数えて何番目かを示す値で，`listtotal`は，その文献項目に含まれる著者名の総数を示す値です。これらの変数を使用すると，次のようにして著者名が20名を超える場合の省略処理を行うことも可能になります。

```
jname: &jname 
    - cond::ifequal[listcount,maxnames]&&ifless[listcount,listtotal]
    - delim::DOTS
    - - cond::ifgreater[listcount,maxnames]&&ifless[listcount,listtotal]
      - text::""
      - - cond::ifequal[listcount,1]
        - - value::family
          - text::" "
          - value::given
        - - cond::ifless[listcount,maxnames]
          - - text::"・"
            - value::family
            - text::" "
            - value::given
          - - value::family
            - text::" "
            - value::given
```