xparse パッケージでスゴイ LaTeX マクロを作ろう！

:::note info
これは「[TeX ＆ LaTeX Advent Caleandar 2014](http://www.adventar.org/calendars/553)」の1日目の記事です。
（2日目は [k16shikano さん](http://note.golden-lucky.net/2014/12/latex-git.html?spref=tw) です。）
:::

昨年のアドベントカレンダーでは、[「完全攻略！ LaTeXのマクロ定義」](http://qiita.com/zr_tex8r/items/5067307890d36c0e4882)と題して、「LaTeXのマクロ（ユーザ定義命令）」を作る命令（`\newcommand`など）の使い方を紹介しました。そこで述べられているように、LaTeX標準での命令定義機能には割と大きな制約があります。

  * 複数の引数をとる命令の場合、最初の1つしかオプション引数（`[...]`で表される省略可能な引数）にできない。
  * ユーザ定義命令の引数が「段落をまたぐ」ことの可否を選べるが、全部の引数について一律に許可する（`\newcommand`）か、あるいは一律に禁止する（`\newcommand*`）かの選択しかなく、個別の引数ごとに選ぶことができない。
  * LaTeXの慣習では、ある命令の“変種”を命令の制御綴の後に`*`を置いたもので表す。（例えば`\hspace`命令に対する`\hspace*`など。）しかし、標準の命令定義機能はこれに対応できない。
  * LaTeXの慣習では、必須の引数は`{...}`、オプション引数は`[...]`で表すのが原則である。しかし例外的に「変な括弧」が用いられること（例えば`\framebox(1,1){...}`やBeamerの`\only<2>{...}`など）がある。標準の命令定義機能はこれにも対応できない。

これらの制限を全て取り払って「極めて自由な書式をもつユーザ命令を定義可能にする」機能を提供するのが、“LaTeX3チーム”が開発した**xparseパッケージ**です。本記事では、このxparseパッケージの基本的な利用方法を解説します。

:::note info
なお、“LaTeX3チーム”とは文字通り“LaTeX3”（次世代の LaTeX）を開発するチームのことであって、xparseパッケージもLaTeX3と多少の繋がりがあるのですが、ここではLaTeX3については触れません。少なくとも今ある xparse パッケージは普通の「LaTeX2e のパッケージ」です。
:::

## （追記）イマドキのLaTeXは最初からxparseが使える

本記事は「xparseパッケージ」についての解説ですが、2020-10-01付の更新でxparseの機能（の大半）が**LaTeXカーネルに取り込まれました**。従って、新しいLaTeXでは本記事で説明される範囲の機能については「xparseパッケージ」の**読込は不要**です。

ただし「古いLaTeXでxparseの機能を使いたい」という場合には、「xparseパッケージ」の読込が必要になります。

```tex
\usepackage{xparse}
```

:::note warn
本記事で「LaTeX**標準**の命令」という場合、それはxparseの機能がカーネルに取り込まれるより前の“古い標準”のことを指すものとします。
:::

## 前提知識

* LaTeXマクロを定義するための（旧来の）LaTeX標準の命令（`\newcommand`等）が使いこなせること。

※:sushi: TeX言語（LaTeXでなくて）のプログラマ向けの補足的な情報を、:sushi:を付けて示すことにします。

## 基本編

:::note info
前述の通り、現在のLaTeXでは「xparseパッケージ」の読込は不要です。
:::

:::note info
以下で示す例では、`\color`命令のためにcolorパッケージが読み込まれていることを前提にします。
:::

### 新たに命令を定義する

すなわち、標準の`\newcommand`に相当するものです。xparseでは、`\NewDocumentCommand`という命令を使います。

```latex
\NewDocumentCommand\命令名{引数仕様}{定義本体}
```

これを従来の`\newcommand`と比較してみましょう。

```latex
\newcommand[*]{\命令名}[引数の個数]{定義本体}
```

`\NewDocumentCommand`では非常に複雑な「引数の書式」、例えば「引数は5個あって、そのうち2・4番目はオプションで、最初の3つは段落をまたがない」のようなものが許容されます。そのため、単なる「引数の個数」だけでは済まなくなり、代わりに**引数仕様**（argument specification）という文字列を定義時に指定する必要があります。この引数仕様は少し複雑で取っつきにくいものですが、xparseを使う上で不可欠なものなので、取りあえずは必要なものから確実に理解していくことにしましょう。

まずは、一番簡単な、引数のない場合です。赤い大きな文字で「アレ」と出力する命令`\myAre`を定義してみましょう。この場合、引数仕様には空の文字列を指定します。なお、引数仕様は必須引数なので決して省略できないことに注意して下さい。

```latex
% \myAre: 赤い大きな文字で"アレ"と出力する.
\NewDocumentCommand\myAre{}{{\color{red}\Large アレ}}
% (参考: \newcommand では以下の通り.)
%\newcommand{\myAre}{{\color{red}\Large アレ}}
% 使ってみる
{\TeX}は{\myAre}です。
```

![xparse-1.png](https://qiita-image-store.s3.amazonaws.com/0/14550/7eaac818-0bed-93cb-6774-cf5d78ca4a86.png)

次に、必須の引数のみをもつ命令を定義しましょう。例として、2つの必須引数をもつ命令`\myAlert`を定義してみます。`\newcommand`の場合は引数の個数を表す`[2]`を付けるのでした。これに対して、`\NewDocumentCommand`の場合は、引数仕様として、文字`m`（＜mandatory）を2つ並べた文字列`m m`を指定します（空白は任意）。

```latex
% \myAlert{色名}{テキスト} : 指定の色を用いて大きな文字で出力する.
\NewDocumentCommand\myAlert{m m}{{\color{#1}\Large #2}}
% (参考: \newcommand では以下の通り.)
%\newcommand*{\myAlert}[2]{{\color{#1}\Large #2}}
% 使ってみる
{\TeX}は\myAlert{red}{アレ}です。
```

![xparse-1.png](https://qiita-image-store.s3.amazonaws.com/0/14550/7eaac818-0bed-93cb-6774-cf5d78ca4a86.png)

### 既存の命令の定義を変更する、など

`\NewDocumentCommand`命令は`\newcommand`に相当するものなので、当該の名前の命令が既に定義済の場合はエラーが発生します[^flashy]。

[^flashy]: 昔の実装では、expl3の機能を使ったエラー通知では過多な装飾を伴った“ド派手”なエラーメッセージが端末に出力されていましたが、現在の実装では旧来のLaTeX2eのエラーメッセージと同じ形式に改められています。（ド派手なメッセージに未対応な統合環境でエラーの識別に失敗するのを避けるためらしい。）

```
! LaTeX cmd Error: Command '\myAlert' already defined.

For immediate help type H <return>.
 ...

<*> ...Command\myAlert{m m}{{\color{#1}\Large #2}}
```

既に定義済の命令の定義を変更するには、`\RenewDocumentCommand`命令を利用します。標準の`\renewcommand`に相当するものです。

```latex
\RenewDocumentCommand\命令名{引数仕様}{定義本体}
```

標準の `\providecommand`に相当するもの、つまり「当該の命令が未定義の場合のみ定義する」を実行するには`\ProvideDocumentCommand`を利用します。

```latex
\ProvideDocumentCommand\命令名{引数仕様}{定義本体}
```

ここまでは標準と同じですが、実はxparseにはもう1つのパターンがあります。`\DeclareDocumentCommand`命令は「当該の命令が定義済か否かに関わらず無条件に指定の定義を実行する」という動作をします。

```latex
\DeclareDocumentCommand\命令名{引数仕様}{定義本体}
```

[「LaTeX のマクロ定義」](http://qiita.com/zr_tex8r/items/5067307890d36c0e4882)の記事を読んだ人ならば、標準LaTeXがこのパターンをサポートしていない理由は既に解っているでしょう。記事の中で、「TeXのプリミティブ`\def`は無条件に定義を書き換えてしまうので危険」という話をしましたが、この `\DeclareDocumentCommand`は結局同じ危険性を持ち合わせているわけです。この命令の使用については慎重であるべきだと私は思います。

### オプション引数をもつ命令

`\newcommand`等の標準の命令では、「オプション引数にできるのは先頭の引数のみ」という大きな制約がありました。xparseでは、必須引数とオプション引数を任意に混在させた書式の命令が定義できます。

オプション引数をもつ命令を定義するには、引数仕様として`m`ではなく大文字の`O`（＜optional）を指定します。つまり、「2つの引数をもち、そのうち1つ目がオプションである」という場合は`O`と`m`を順に並べればよいわけです。ただし、実際には`O`は「引数を省略した時の既定の値」を“引数”にとって`O{値}`のように書く必要があります。すなわち、「2つの引数をもち、そのうち1つ目がオプションで既定値が`red`である」ような命令`\myAlertA`を定義する場合は以下の文を実行します。

```latex
% \myAlertA[色名]{テキスト} : 指定の色(既定値はred)を用いて大きな文字で出力する.
\NewDocumentCommand\myAlertA{O{red} m}{{\color{#1}\Large #2}}
% (参考: \newcommand の場合.)
%\newcommand*{\myAlertA}[2][red]{{\color{#1}\Large #2}}
```

引数仕様の中の1文字が1つの引数に対応するので、これまでの説明から、「2つ目の引数だけをオプションにする」「両方の引数をオプションにする」方法も容易に解るでしょう。それぞれ次のようになります。（これらは`\newcommand`では実現できません。）

```latex
% \myAlertB{色名}[テキスト] : 指定の色を用いて大きな文字でテキスト(既定値は"アレ")を出力する.
\NewDocumentCommand\myAlertB{m O{アレ}}{{\color{#1}\Large #2}}
% \myAlertC[色名][テキスト] : 指定の色(既定値はred)を用いて大きな文字でテキスト(既定値は"アレ")を出力する.
\NewDocumentCommand\myAlertC{O{red} O{アレ}}{{\color{#1}\Large #2}}
```

もう少し複雑な例として、「必須引数の前後にオプション引数がある」命令を定義してみます。

```latex
% \myAreA[テキスト]{色名}[フォント命令] :
%   "(テキスト)はアレ" と出力する. "アレ"の部分は
%   指定の色とフォントが適用される.
\NewDocumentCommand\myAreA{O{\TeX} m O{\Large}}
  {#1は{\color{#2}#3アレ}}
```

この引数の形式の場合、`[テキスト]`と`[フォント命令]`の両方について明示・省略が選べるようになります。

  * `\myAreA{red}.` → `\TeX は{\color{red}\Large アレ}.`
  * `\myAreA[TikZ]{red}.` → `TikZは{\color{red}\Large アレ}.`
  * `\myAreA{red}[\huge].` → `\TeX は{\color{red}\huge アレ}.`
  * `\myAreA[TikZ]{blue}[\gtfamily].` → `TikZは{\color{blue}\gtfamily アレ}.`


### 引数が段落をまたぐのを許可する

LaTeX標準の機能で命令を定義する場合、引数が段落をまたぐことを許すか否かを選択することができました。つまり、`\newcommand`などの「`*`なし」の命令を用いた場合は引数は段落をまたぐことができて（これを**長い引数**（long argument）と呼びます）、`\newcommand*`などの「`*`つき」の命令を用いた場合は引数は段落をまたぐことが禁止されました（これを**短い引数**（short argument）と呼びます）。

xparseの機能を用いて定義された命令の場合、既定では引数は「短い引数」になります。これを「長い引数」に変えたい場合は、引数仕様の中の対応する文字の前に`+`を付けます。つまり、引数の各々に対して、それが「長い」か「短い」かを指定することができるわけです。

```latex
% \myFramedParBox{枠の色}[内部の色]{幅}{テキスト} :
% テキストを指定の幅で組んで, それを指定の枠と内部の色(既定値white)
% をもつ枠の中に入れる.
\NewDocumentCommand\myFramedParBox{m O{white} m +m}
  {\fcolorbox{#1}{#2}{\parbox{#3}{#4}}}
```

上の例では、色名と寸法値が入るはずの引数は「短い」ものとしつつ、最後のテキスト（`\parbox`の引数となるもの）の引数については、指定を`+m`として「長い」引数にしています。従って、次のように、改段落（空行で表される）を含むテキストを引数にとることが可能です。

```latex
\myFramedParBox{red}[yellow]{10zw}{%
{\TeX}はアレです。

しかしTikZはもっともっとアレです。
}
```

![xparse-3.png](https://qiita-image-store.s3.amazonaws.com/0/14550/690f9b0e-1887-d812-dab8-0cb9d273a166.png)

### 補足

  * 今までに紹介した命令（`\ﾅﾝﾄｶDocumentCommand`）で定義した命令は必ず（LaTeXの意味で）「頑強（robust）」（さらに言うと、「保護付（protected）」と同様の状態）になります。「LaTeXのマクロ」の範囲で頑強・保護付になって不利になるという場面はほぼあり得ないと思うので、「頑強」の概念をまだ理解していない人は特にこれを気にする必要はありません。  
    ※:sushi: 要するにe-TeXの`\protected`により保護されたマクロになります。

## 発展編

### ① 既定値無しのオプション引数

オプション引数の指定`O`は必ず「既定値の指定」をもち、省略した時の動作は「既定値を指定したときと同じ」でした。これに対して、「引数が指定されたか否かで条件分岐したい」という場合が考えられます。
例えば、先の例の`\myAlertA[色名]{テキスト}`について、「色名が省略された場合は特定の“既定の色”にするのでなく、そもそも色変更を行わない」という動作に変えたいとします。この命令を`\myAlertD`とすると、この命令の動作は次のようになるべきで、どうしても条件分岐が必要となってきます。

  * `\myAlertD[red]{アレ}` → `{\color{red}\Large アレ}`
  * `\myAlertD{アレ}` → `{\Large アレ}`

これを実現するには、「既定値なしのオプション引数」を表す引数仕様文字である小文字の`o`＜optional）を利用します。

```latex
\NewDocumentCommand\myAlertD{o m}{%
% #1=色名(省略可), #2=テキスト
}
```

これで、`\myAlertD{アレ}`のように実際に色名を省略して実行した場合、引数 `#1` には“値無し”を意味する特殊な「NoValueマーカー」が紐づけられます。従って、あとは値が「NoValueマーカー」であるかを判定して条件分岐する機構があればよさそうです。xparseではそのような命令を用意しています。

```latex
\IfNoValueTF{引数}{値無し}{値有り}
\IfNoValueT{引数}{値無し}
\IfNoValueF{引数}{値有り}
\IfValueTF{引数}{値有り}{値無し}
\IfValueT{引数}{値有り}
\IfValueF{引数}{値無し}
```

これらは何れも、「`引数`が『NoValueマーカー』に一致する（値無し）か否（値有り）かに応じて `値無し` と `値有り` のコードの一方を実行する（存在しない場合は何もしない）」という動作を行います。（:sushi: これらの命令は完全展開可能です。）

今の場合、`#1`が“値有り”である時に `\color{#1}` を実行すればよいので、

```latex
% \myAlertE[色名]{テキスト} : 大きい文字で出力する.
%   色名が指定された場合, その色を用いる.
\NewDocumentCommand\myAlertD{o m}{%
% #1=色名(省略可), #2=テキスト
  {%
    \IfValueT{#1}{\color{#1}}%
    \Large #2%
  }%
}
```

実際に試してみましょう。

```latex
{\color{blue}{\TeX}は\myAlertD[red]{アレ}で\myAlertD{アレ}です。}
```

![xparse-4.png](https://qiita-image-store.s3.amazonaws.com/0/14550/37d3f653-99ba-9682-d501-90bbbc99d75a.png)

### ② “*-形”の条件分岐

例えば、以下のような「*-形」をもつ命令`\myAlertE`を定義したいとします。

  * `\myAlertE{アレ}` → `{\Large アレ}`（大きくするだけ）
  * `\myAlertE*{アレ}` → `{\color{red}\Large アレ}`（*-形の時はさらに赤くする）

xparseの場合、「`*`があるか」というのを一種の引数とみなし、それを引数仕様文字`s`（＜star）で表します。`\myAlertE`の場合、先頭に「`*`の引数」があるので以下のような引数仕様になります。

```latex
\NewDocumentCommand\myAlertE{s m}{%
% #1=*の有無, #2=テキスト
}
```

この場合、`#1`には「`*`があるか」の真偽値を示す特殊なフラグが紐づけられます。この真偽値フラグを以下の命令に渡して条件分岐ができます。

```latex
\IfBooleanTF{引数}{真}{偽}
\IfBooleanT{引数}{真}
\IfBooleanF{引数}{偽}
```

これらは何れも、「`引数`の真偽値フラグの値に応じて`真`と`偽`のコードの一方を実行する（存在しない場合は何もしない）」という動作を行います。（:sushi: 完全展開可能です。）

最終的に`\myAlertE`の実装は以下のようになります。

```latex
% \myAlertE[*]{テキスト} : 大きい文字で出力する.
%   *-形の場合, 赤色を用いる.
\NewDocumentCommand\myAlertE{s m}{%
% #1=*の有無, #2=テキスト
  {%
    \IfBooleanT{#1}{\color{red}}%
    \Large #2%
  }%
}
```

実際に試してみます。

```latex
{\TeX}は\myAlertE{アレ}で\myAlertE*{アレ}です。
```

![xparse-5.png](https://qiita-image-store.s3.amazonaws.com/0/14550/4d941e77-5503-b364-13a1-159153a10f24.png)

ちなみに、引数仕様`s`は先頭引数以外でも使用できます。なので次のような命令定義も可能です。

```latex
% \mySampleA[*]{テキスト1}[*]{テキスト2} :
%   "(テキスト1)は(テキスト2)" と出力. テキスト2は大きな字になる.
%   * がある場合, その後ろの引数のテキストが赤色になる.
\NewDocumentCommand\mySampleA{s m s m}{%
  {\IfBooleanT{#1}{\color{red}}#2}は%
  {\IfBooleanT{#3}{\color{red}}\Large #4}%
}
% 使用例
\mySampleA*{\TeX}{アレ}、だが\mySampleA{TikZ}*{もっとアレ}。
```

![xparse-6.png](https://qiita-image-store.s3.amazonaws.com/0/14550/2b58482f-ca34-d060-3c78-f4cb0dd9d3d3.png)

#### “*”以外の記号を使う

LaTeXにおいて、`*`以外の記号による条件分布を行う命令が使われることがあります。例えばxkeyvalパッケージには`\setkeys`という命令がありますが、これには`*`-形`\setkeys*`の他に“`+`-形”`\setkeys+`も持ちます。

xparseではこのような“変なスター”で分岐する命令を作ることもできます。引数仕様に`S`の代わりに`t記号`（＜token）を指定するだけです。定義本体での引数の扱いは`s`と全く同じです。

```latex
% \myNum[*][+]{カウンタ名} : カウンタの値を算用数字で
%   出力する. * 付の場合は赤くなり, さらに + 付の場合は
%  フォントがアホになる.
\NewDocumentCommand\myNum{s t+ m}{%
  {%
    \IfBooleanT{#1}{\color{red}}%
    \IfBooleanT{#2}{\LARGE\usefont{OT1}{cmfr}{m}{it}}%
    \arabic{#3}%
  }%
}
% 使用例
\newcounter{foo}\setcounter{foo}{42}
\myNum{foo} \myNum*{foo} \myNum+{foo} \myNum*+{foo}
```

![xparse-7.png](https://qiita-image-store.s3.amazonaws.com/0/14550/ae0db2c9-cc5d-b294-b050-61fa81b3955b.png)

### ③ “変な括弧”を使う

次のように解釈される命令`\myPutText`を定義したいとします。

:::note info
これはpicture環境内で用いて、「指定の座標に指定の位置をアンカーとしてテキストを出力する」ためのものです。
:::

  * `\myPutText(座標)[位置]{テキスト}`  
    → `\put(座標){\makebox(0,0)[位置]{テキスト}}`
      * `座標`は必須、`位置`はオプションで既定値は`c`。

通常のLaTeXの命令と異なり、この命令は`( )`で囲まれる必須引数を持っています。このような“変な括弧”で囲まれた必須引数を扱う引数仕様文字が`r`（＜required）で、これは後に「括弧として使う文字のペア」を付けて使います。今の場合、引数仕様の中で`r()`を指定することになります。

:::note info
この場合、もし`\myPutText`の直後に`(`が無かったとすると、「`(`が無い」という趣旨のエラーメッセージが出ます。
:::

```latex
% \myPutText(座標)[位置]{テキスト} : 指定の座標にテキストを出力.
\NewDocumentCommand\myPutText{r() O{c} m}{%
  \put(#1){\makebox(0,0)[#2]{#3}}%
}
% 使用例
\begin{picture}(20,20)
  \myPutText(10,10)[br]{\color{red}\TeX}
  \myPutText(10,10)[tl]{\color{blue}\TeX}
  \myPutText(10,10){\color{magenta}\TeX}
\end{picture}
```

![xparse-8.png](https://qiita-image-store.s3.amazonaws.com/0/14550/8f60ea0f-a913-d6d0-89e1-9f906de172b9.png)

また同様に、“変な括弧”をもつオプション指定を扱うこともできます。

  * `r()`の代わりに`d()`を指定すると、変な括弧であることを除いて`o`と同じ動作になります。すなわち、`(`がない場合は当該引数は「NoValueマーカー」になります。（d＜delimited）
  * `r()`の代わりに`D(){既定値}`を指定すると、変な括弧であることを除いて`O{既定値}`と同じ動作になります。すなわち、`(`が無い場合は当該引数は`既定値`になります。

## 引数仕様文字の一覧

| 文字 | 意味 |
|:---:|:---|
| `m`     | 通常の必須引数 |
| `o`     | オプション引数、既定値無し |
| `O{値}` | オプション引数、既定値有り |
| `s`     | `*` の有無を判定 |
| `t?`    | 変なスター版の`s`（`*`の代わりに`?`） |
| `r()`   | 変な括弧版の必須引数（`{}`の代わりに`()`） |
| `d()`   | 変な括弧版の`o`（`[]`の代わりに`()`） |
| `D(){値}` | 変な括弧版の`O`（`[]`の代わりに`()`） |
| `+`     | （他の文字の前に付く） 長い引数 |

なお、引数仕様文字には、ここで紹介しなかったもっと変態なもの（verbatim引数、区切り付引数、……）も存在するので、変態に興味がある人はxparseのマニュアルを参照してください。（ただし、xparseのマニュアルの説明には少しばかりexpl3の概念が入り込んでいることに注意。）

:::note info
参考：verbatimな引数（`v`）を使う例の紹介

* 「[xparse で verbatim な引数を扱う話](http://d.hatena.ne.jp/zrbabbler/20141222/1419193916)」
:::

## まとめ

少々複雑な引数指定をもつユーザ定義命令が必要な場合も心配は要りません！ TeX言語:sushi:も要りません！ xparseの機能を活用しましょう！