expl3(エクスプ・スリー)は、現代の LaTeX を内側で支えるプログラミング言語です。かつて「LaTeX3」と呼ばれた取り組みから生まれ、いまでは LaTeX カーネルに標準で読み込まれています。\newcommand で書ける範囲を超えた、変数・データ構造・整数演算・浮動小数点までそなえた本格的な層で、xparse・siunitx・fontspec などはこの上に作られています。このページでは構文の切り替え方、独特の命名規則、そして主要なモジュールを順に見ていきます。
expl3 とは何か
TeX は本来マクロプロセッサで、\def や \newcommand で命令(マクロ)を定義していきます。しかし大きなパッケージを書こうとすると、TeX の生のプリミティブは一貫性に欠け、引数の展開や変数の扱いが職人芸になりがちでした。expl3 は、TeX と e-TeX のプリミティブに新しい名前を与え、関数と変数を体系的に名づけ、引数の型を明示する一貫したインターフェースを用意します。LaTeX Project が長年かけて整備した、いわば「LaTeX のための標準ライブラリ兼プログラミング言語」です。
実用上の重要な事実が一つあります。新しい LaTeX カーネルでは expl3 が標準で読み込まれている こと。公式の interface3 マニュアルにも「最新の LaTeX 2ε カーネルでは、この素材はフォーマットの一部として読み込まれる」と明記されています。したがって \usepackage{expl3} を書く必要はもうありません。多くの利用者は expl3 を直接書かず、xparse(\NewDocumentCommand)や siunitx・fontspec・l3keys2e といったパッケージ越しに間接的に使っています。
構文を切り替える — \ExplSyntaxOn
expl3 のコードは \ExplSyntaxOn と \ExplSyntaxOff で囲んだ区間に書きます。この区間では、文字の扱い(カテゴリコード)が二つ変わります。第一に、空白と改行が無視される こと。おかげでコードをインデントし、トークンの間に空きを入れて、読みやすく書けます。第二に、**_(アンダースコア)と :(コロン)が「文字」として扱われる** こと。ふつうの LaTeX ではこれらは命令名に使えませんが、expl3 ではこれらを含んだ長い命令名を作れます。
空白が無視されるということは、出力に 本物の空白を入れたいときは別の書き方が要る ということでもあります。expl3 区間では、半角スペースを ~(チルダ)で表します。たとえば本文に「Item: りんご」と出したいなら、Item:~#1 のように書きます(素の空白は消えてしまうため)。この一点さえ押さえれば、あとは普通のプログラミング言語のように読み書きできます。
% expl3 はカーネルに含まれるので \usepackage{expl3} は不要
\ExplSyntaxOn
% ここでは空白・改行が無視され、 _ と : が命令名に使える
\tl_new:N \l_greeting_tl
\tl_set:Nn \l_greeting_tl { Hello,~world! }
\tl_use:N \l_greeting_tl
\ExplSyntaxOff命名規則と引数シグネチャ
expl3 の関数名は、\⟨module⟩_⟨description⟩:⟨arg-signature⟩ という形をしています。最初の _ までが モジュール名(そのデータ型や機能の分類)、: の手前までが 説明的な名前、そして : の後ろが 引数シグネチャ(arg-spec)です。たとえば \seq_put_right:Nn なら、モジュールは seq(シーケンス)、説明は put_right(右端に追加)、シグネチャは Nn。シグネチャの各文字は、その位置の引数を 渡す前にどう処理するか を表します。
主な引数指定子は次のとおりです(公式 interface3 マニュアルの記述に基づきます)。N と n がもっとも基本で、これだけでもかなりのことができます。展開を伴う x・e・o・f や、変数の値を取り出す V・v は、慣れてから少しずつ覚えれば十分です。
| 指定子 | 意味 |
|---|---|
N | 加工しない、単一トークン(多くは制御綴 1 個)。 |
n | 加工しない、波括弧でくくったトークン列。 |
c | 中身を **\csname で命令名に変換** してから渡す。 |
V / v | 変数の値を取り出して渡す(V は単一トークン、v は命令名から構築)。 |
o | 一度だけ展開してから渡す。 |
x / e | 完全展開(x は \edef 相当・展開不可、e は \expanded 相当)。 |
f | 最初の展開不可トークンまで 展開(先頭から左→右に)。 |
p | TeX の パラメータテキスト(#1#2…)。関数定義で使う。 |
変数も同様に名づけますが、先頭に スコープを表す 1 文字 が付きます。l_ は 局所(現在の TeX グループ内だけで変える)、g_ は 大域(グローバルに変える)、c_ は 定数(値を変えない)です。変数名の末尾には型を表す識別子が付き、_tl(トークンリスト)、_int(整数)、_seq(シーケンス)、_prop(プロパティリスト)、_clist(コンマリスト)、_fp(浮動小数点)などになります。たとえば \l_my_name_tl は「局所のトークンリスト変数」、\g_counter_int は「大域の整数変数」と一目で分かります。なお各モジュールは \l_tmpa_tl・\l_tmpb_int のような スクラッチ変数(使い捨ての一時変数)も用意しています。
主要なモジュール
expl3 はデータ型ごとにモジュールへ分かれており、どれも「作る・設定する・使う」という同じリズムで操作できます。まず 関数の定義 から。\cs_new:Npn は新しい関数を定義し、すでに同名の関数があればエラーにします(cs は control sequence の意)。\cs_set:Npn は同じく定義しますが、現在の TeX グループ内に限定され、再定義してもエラーになりません。どちらも :Npn で、N=定義する関数名、p=パラメータテキスト(#1#2…)、n=本体(置換テキスト)を表します。
次に **トークンリスト(tl)**。これは文字列のように使える、最も基本的な変数です。\tl_new:N で宣言し、\tl_set:Nn で中身を入れ替え(以前の内容は消えます)、\tl_use:N で本文に出力します。**整数(int)** も同じ形です。\int_new:N で宣言、\int_set:Nn で代入し、\int_eval:n は中の整数式を計算してその値を返します(たとえば \int_eval:n { 2 + 3 * 4 } は 14)。
**シーケンス(seq)** は、両端から出し入れできるリスト(スタックにも使える)です。\seq_new:N で作り、\seq_put_right:Nn で右端に要素を追加し、\seq_map_inline:Nn で全要素を順に処理します。後者では、各要素が #1 として渡されます。**プロパティリスト(prop)** は、いわゆる辞書(キーと値の対応表)です。\prop_new:N で作り、\prop_put:Nnn でキーと値の組を格納します(Nnn は「変数・キー・値」の三引数)。
**コンマリスト(clist)** は、その名のとおりコンマ区切りの値の並びを扱うモジュールで、命令はすべて \clist_ で始まります(\clist_new:N、\clist_set:Nn など)。最後に **浮動小数点(fp)**。\fp_eval:n は小数を含む式を評価し、sin・sqrt・pi など多くの科学関数も使えます(たとえば \fp_eval:n { sqrt(2) } や \fp_eval:n { 2 * pi })。整数しか扱えない \int_eval:n との使い分けが要点です。
小さな例 — 関数とシーケンス
以上を組み合わせてみます。次の例は、\cs_new:Npn で「シーケンスに項目を一つ加える」関数 \example_add:n を定義し、それを何度か呼んでから、\seq_map_inline:Nn で全項目を箇条書き風に出力します。命令名に _ や : が含まれていても、\ExplSyntaxOn 区間なので問題なく書ける点に注目してください。
\documentclass{article}
\begin{document}
\ExplSyntaxOn
% シーケンスを 1 個用意する
\seq_new:N \l_example_fruits_seq
% 「右端に 1 項目追加する」関数を定義する
\cs_new:Npn \example_add:n #1
{ \seq_put_right:Nn \l_example_fruits_seq {#1} }
% 何度か呼ぶ
\example_add:n { apple }
\example_add:n { banana }
\example_add:n { cherry }
% 全項目を出力する(各項目が #1 に入る。 ~ は空白)
\seq_map_inline:Nn \l_example_fruits_seq
{ Fruit:~#1 \par }
\ExplSyntaxOff
\end{document}これをコンパイルすると、「Fruit: apple」「Fruit: banana」「Fruit: cherry」が 3 行に分かれて出ます。\cs_new:Npn の #1 は定義する関数の引数、\seq_map_inline:Nn の #1 は走査中の各要素で、どちらも n 型(波括弧の中身)として扱われています。Fruit:~#1 の ~ を素の空白にすると、空白は無視されて「Fruit:apple」とくっついてしまう点に注意してください。
実際の使われ方
expl3 を直接書く必要は、ふつうの文書執筆ではほとんどありません。けれども パッケージやクラスを作る 段になると、expl3 は今や事実上の標準です。利用者向けの命令を \NewDocumentCommand(xparse)で受け取り、その中身を expl3 で実装する、という組み合わせがよく使われます。xparse の引数指定(m・O{...}・s など「文書レベル」の引数)と、expl3 の引数シグネチャ(N・n など「プログラミングレベル」の処理指定)は別のものなので、混同しないようにしましょう。前者は xparse のページで詳しく扱います。
expl3 を学ぶと、\newcommand だけでは難しかった処理が見通しよく書けるようになります。覚えておきたい要点をまとめます。
- コードは必ず
\ExplSyntaxOn…\ExplSyntaxOffで囲む。区間内では空白は無視され、空白を出したいときは~を使う。 - カーネルに含まれるので
\usepackage{expl3}は不要。.styの中ではパッケージ宣言の後にそのまま書ける。 - 関数は
\⟨module⟩_⟨description⟩:⟨signature⟩、変数は\⟨scope⟩_⟨name⟩_⟨type⟩。スコープはl_/g_/c_。 - 確信のない命令名は使わない。命名は厳密で、公式の interface3 マニュアル(texdoc interface3)が一次資料。