expl3 („expl-three“) ist die Programmiersprache, die modernes LaTeX von innen antreibt. Sie entstand aus dem früher „LaTeX3“ genannten Projekt und wird heute standardmäßig vom LaTeX-Kernel geladen. Sie geht weit über das hinaus, was mit \newcommand bequem möglich ist: Variablen, Datenstrukturen, Ganzzahlarithmetik und sogar Gleitkomma — eine ernsthafte Programmierschicht, auf der xparse, siunitx und fontspec aufbauen. Diese Seite zeigt den Syntaxwechsel, das besondere Namensschema und die Kernmodule.
Was expl3 ist
TeX ist im Kern ein Makroprozessor: Befehle, also Makros, werden mit \def oder \newcommand definiert. Beim Schreiben großer Pakete zeigte sich jedoch, wie uneinheitlich TeXs rohe Primitive sind; Kontrolle der Expansion und Variablenbehandlung wurden schnell Handwerk. expl3 gibt den TeX- und e-TeX-Primitiven neue Namen, benennt Funktionen und Variablen systematisch und macht die Argumenttypen jeder Funktion explizit. Über viele Jahre vom LaTeX Project aufgebaut, ist es praktisch Standardbibliothek und Programmiersprache für LaTeX zugleich.
Ein praktischer Fakt ist wichtig: expl3 wird in modernen LaTeX-Kernels standardmäßig geladen. Das offizielle interface3-Handbuch sagt ausdrücklich, dass dieses Material mit einem aktuellen LaTeX-2ε-Kernel Teil des Formats ist. \usepackage{expl3} ist daher nicht mehr nötig. Die meisten Nutzer schreiben expl3 nicht direkt, sondern begegnen ihm indirekt über xparse (\NewDocumentCommand) und Pakete wie siunitx, fontspec oder l3keys2e.
Syntax umschalten — \ExplSyntaxOn
expl3-Code steht in einem Bereich zwischen \ExplSyntaxOn und \ExplSyntaxOff. Dort ändern sich zwei Dinge an der Zeichenbehandlung, also an den Kategoriecodes. Erstens werden Leerzeichen und Zeilenumbrüche ignoriert, sodass Code eingerückt und mit Abständen lesbar geschrieben werden kann. Zweitens werden _ (Unterstrich) und : (Doppelpunkt) als Buchstaben behandelt. Normales LaTeX erlaubt sie nicht in Befehlsnamen; expl3 nutzt sie für lange, beschreibende Namen.
Dass Leerzeichen ignoriert werden, bedeutet auch: Für ein echtes Leerzeichen in der Ausgabe braucht man eine andere Schreibweise. Innerhalb eines expl3-Bereichs schreibt man ein explizites Leerzeichen als ~ (Tilde). Soll etwa „Item: apple“ gesetzt werden, schreibt man Item:~#1, denn ein rohes Leerzeichen verschwindet. Ist diese Regel verinnerlicht, liest und schreibt sich der Rest wie in einer gewöhnlichen Programmiersprache.
% expl3 はカーネルに含まれるので \usepackage{expl3} は不要
\ExplSyntaxOn
% ここでは空白・改行が無視され、 _ と : が命令名に使える
\tl_new:N \l_greeting_tl
\tl_set:Nn \l_greeting_tl { Hello,~world! }
\tl_use:N \l_greeting_tl
\ExplSyntaxOffNamensschema und Argumentsignaturen
Ein expl3-Funktionsname hat die Form \⟨module⟩_⟨description⟩:⟨arg-signature⟩. Bis zum ersten _ steht das Modul, also Datentyp oder Funktionsbereich; bis zum : folgt der beschreibende Name; danach die Argumentsignatur (arg-spec). In \seq_put_right:Nn ist seq das Modul, put_right die Beschreibung und Nn die Signatur. Jeder Buchstabe der Signatur sagt, wie das jeweilige Argument vor der Übergabe behandelt wird.
Die wichtigsten Argumentspezifizierer stehen unten, nach dem offiziellen interface3-Handbuch. N und n sind die Grundformen und reichen schon weit. Die expandierenden Formen x, e, o, f sowie die wertentnehmenden V, v kann man später nach und nach lernen.
| Spezifizierer | Bedeutung |
|---|---|
N | Keine Veränderung; ein einzelnes Token (meist eine Kontrollsequenz). |
n | Keine Veränderung; eine geklammerte Tokenliste. |
c | Wandelt das Argument über \csname in eine Kontrollsequenz um. |
V / v | Übergibt den Wert einer Variablen (V von einem Einzeltoken, v baut den Namen zuerst). |
o | Expandiert das Argument einmal vor der Verwendung. |
x / e | Vollständige Expansion (x wie \edef, nicht expandierbar; e nutzt \expanded). |
f | Expandiert bis zum ersten nicht expandierbaren Token von links nach rechts. |
p | Ein TeX-Parametertext (#1#2…), verwendet beim Definieren von Funktionen. |
Variablen werden ähnlich benannt, beginnen aber mit einem Buchstaben für den Gültigkeitsbereich. l_ bedeutet lokal, also nur innerhalb der aktuellen TeX-Gruppe veränderbar, g_ bedeutet global, c_ Konstante. Am Ende des Variablennamens steht der Typ: _tl (Tokenliste), _int (Ganzzahl), _seq (Sequenz), _prop (Property List), _clist (Kommaliste), _fp (Gleitkomma) usw. \l_my_name_tl liest man sofort als lokale Tokenlistenvariable, \g_counter_int als globale Ganzzahlvariable. Jedes Modul stellt außerdem Scratch-Variablen wie \l_tmpa_tl und \l_tmpb_int bereit.
Die Kernmodule
expl3 ist nach Datentypen in Module aufgeteilt, und alle folgen demselben Rhythmus: erzeugen, setzen, verwenden. Beginnen wir mit Funktionsdefinitionen. \cs_new:Npn definiert eine neue Funktion und meldet einen Fehler, falls der Name schon existiert (cs steht für control sequence). \cs_set:Npn definiert ebenfalls, aber lokal zur aktuellen TeX-Gruppe und ohne Fehler bei Neudefinition. Beide verwenden :Npn: N ist der zu definierende Funktionsname, p der Parametertext (#1#2…), n der Körper, also der Ersetzungstext.
Als Nächstes Tokenlisten (tl): die einfachsten Variablen, ähnlich wie Zeichenketten. Deklariert wird mit \tl_new:N, der Inhalt mit \tl_set:Nn ersetzt, und mit \tl_use:N ausgegeben. Ganzzahlen (int) folgen derselben Form: \int_new:N deklariert, \int_set:Nn weist zu, und \int_eval:n wertet einen Ganzzahlausdruck aus und liefert den Wert zurück, zum Beispiel \int_eval:n { 2 + 3 * 4 } = 14.
Sequenzen (seq) sind Listen mit Zugriff an beiden Enden, auch als Stacks nutzbar. Erzeugt werden sie mit \seq_new:N, rechts angehängt wird mit \seq_put_right:Nn, und \seq_map_inline:Nn durchläuft alle Elemente; jedes Element kommt dabei als #1 an. Property Lists (prop) sind Wörterbücher, also Abbildungen von Schlüsseln auf Werte. \prop_new:N erzeugt eine, \prop_put:Nnn speichert Schlüssel und Wert (Nnn bedeutet Variable, Schlüssel, Wert).
Kommalisten (clist) behandeln, wie der Name sagt, komma-getrennte Werte; alle Befehle beginnen mit \clist_ (\clist_new:N, \clist_set:Nn usw.). Schließlich Gleitkomma (fp): \fp_eval:n wertet Ausdrücke mit Dezimalzahlen aus und unterstützt Funktionen wie sin, sqrt und pi, etwa \fp_eval:n { sqrt(2) } oder \fp_eval:n { 2 * pi }. Wichtig ist die Trennung von \int_eval:n, das nur Ganzzahlen verarbeitet.
Ein kleines Beispiel — Funktion und Sequenz
Setzen wir das zusammen: Das Beispiel definiert mit \cs_new:Npn eine Funktion \example_add:n, die ein Element zu einer Sequenz hinzufügt. Nach einigen Aufrufen gibt \seq_map_inline:Nn alle Elemente listenartig aus. Beachten Sie, dass _ und : in Befehlsnamen problemlos geschrieben werden können, weil wir uns in einem \ExplSyntaxOn-Bereich befinden.
\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}Beim Kompilieren erscheinen „Fruit: apple“, „Fruit: banana“ und „Fruit: cherry“ auf drei Zeilen. Das #1 in \cs_new:Npn ist das Argument der definierten Funktion, während das #1 in \seq_map_inline:Nn das jeweils durchlaufene Element ist; beide kommen als n-Typ an. Beachten Sie das ~ in Fruit:~#1: Ein normales Leerzeichen würde ignoriert und „Fruit:apple“ ergeben.
Wie es praktisch verwendet wird
Beim gewöhnlichen Schreiben von Dokumenten muss man expl3 selten direkt verwenden. Sobald Sie aber ein Paket oder eine Klasse schreiben, ist expl3 heute praktisch der Standard. Häufig nimmt man eine nutzerseitige Schnittstelle mit \NewDocumentCommand (xparse) entgegen und implementiert den Körper in expl3. Halten Sie auseinander: xparse-Argumentspezifikationen wie m, O{...}, s sind Dokumentebene; expl3-Signaturen wie N, n sind Programmierebene. Erstere werden auf der xparse-Seite behandelt.
Mit expl3 lassen sich Dinge klar schreiben, die mit \newcommand allein umständlich wären. Einige Punkte sollte man behalten:
- Code immer in
\ExplSyntaxOn…\ExplSyntaxOffeinschließen. Darin werden Leerzeichen ignoriert; für ein auszugebendes Leerzeichen~verwenden. - Kein
\usepackage{expl3}nötig: es ist im Kernel. In einer.stykann es direkt nach der Paketdeklaration stehen. - Funktionen heißen
\⟨module⟩_⟨description⟩:⟨signature⟩; Variablen\⟨scope⟩_⟨name⟩_⟨type⟩, mit Scopel_/g_/c_. - Keine Befehlsnamen erfinden. Die Benennung ist strikt; das offizielle interface3-Handbuch (texdoc interface3) ist maßgeblich.