expl3 / capa LaTeX3

expl3 (“expl-three”) es el lenguaje de programación que impulsa LaTeX moderno desde dentro. Nació del esfuerzo que antes se llamaba “LaTeX3” y hoy se carga por defecto en el núcleo de LaTeX. Va mucho más allá de lo que puedes escribir con \newcommand: variables, estructuras de datos, aritmética entera e incluso coma flotante, una capa de programación seria sobre la que se construyen xparse, siunitx y fontspec. Esta página recorre cómo entrar en su sintaxis, su esquema de nombres y sus módulos principales.

Qué es expl3

TeX es fundamentalmente un procesador de macros: defines comandos con \def o \newcommand. Pero al escribir un paquete grande, las primitivas crudas de TeX muestran su falta de coherencia; controlar expansiones y manejar variables se vuelve artesanía. expl3 da nuevos nombres a las primitivas de TeX y e-TeX, nombra funciones y variables sistemáticamente y hace explícitos los tipos de argumento de cada función. Desarrollado durante años por el LaTeX Project, es en la práctica una biblioteca estándar y un lenguaje de programación para LaTeX.

Un hecho importa en la práctica: expl3 se carga por defecto en los núcleos LaTeX modernos. El manual oficial interface3 lo dice claramente: con un núcleo LaTeX 2ε actualizado, este material se carga como parte del formato. Por tanto ya no necesitas \usepackage{expl3}. La mayoría de usuarios no escriben expl3 directamente; lo encuentran a través de xparse (\NewDocumentCommand) y paquetes como siunitx, fontspec y l3keys2e.

Cambiar de sintaxis — \ExplSyntaxOn

El código expl3 se escribe dentro de una región delimitada por \ExplSyntaxOn y \ExplSyntaxOff. En esa región cambian dos aspectos de cómo se leen los caracteres, sus códigos de categoría. Primero, los espacios y saltos de línea se ignoran, así que puedes indentar y separar tokens para que el código sea legible. Segundo, _ (guion bajo) y : (dos puntos) pasan a ser “letras”, de modo que pueden aparecer en nombres de comandos; LaTeX normal no lo permite, pero expl3 lo usa para nombres largos y descriptivos.

Como los espacios se ignoran, producir un espacio real en la salida requiere otra notación. Dentro de una región expl3 se escribe un espacio explícito como ~ (tilde). Para componer “Item: apple”, escribe Item:~#1, porque un espacio desnudo desaparecería. Una vez interiorizada esta regla, el resto se lee y escribe como un lenguaje de programación ordinario.

document.tex
% 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

Esquema de nombres y firmas de argumentos

Un nombre de función expl3 tiene la forma \⟨module⟩_⟨description⟩:⟨arg-signature⟩. Hasta el primer _ está el módulo (tipo de dato o área funcional); hasta el :, el nombre descriptivo; y después, la firma de argumentos (arg-spec). En \seq_put_right:Nn, por ejemplo, el módulo es seq, la descripción put_right y la firma Nn. Cada letra de la firma indica cómo se procesa ese argumento antes de pasarlo.

Los principales especificadores de argumento son los siguientes, según el manual oficial interface3. N y n son los básicos y ya permiten hacer mucho. Los que expanden (x, e, o, f) y los que extraen valores (V, v) pueden aprenderse después.

EspecificadorSignificado
NSin manipulación; un token único (normalmente una secuencia de control).
nSin manipulación; un grupo de tokens entre llaves.
cConvierte el argumento en una secuencia de control mediante \csname antes de usarlo.
V / vPasa el valor de una variable (V desde un token único, v construye primero el nombre).
oExpande el argumento una vez antes de usarlo.
x / eExpansión completa (x como \edef, no expandible; e usa \expanded).
fExpande hasta el primer token no expandible de izquierda a derecha.
pUn texto de parámetros TeX (#1#2…), usado al definir una función.

Las variables se nombran de forma parecida, pero comienzan con una letra de ámbito. l_ significa local (solo cambia dentro del grupo TeX actual), g_ global y c_ constante. Un identificador de tipo termina el nombre: _tl (lista de tokens), _int (entero), _seq (secuencia), _prop (lista de propiedades), _clist (lista separada por comas), _fp (coma flotante), etc. Así, \l_my_name_tl se lee como “variable local de lista de tokens”, y \g_counter_int como “variable entera global”. Cada módulo también proporciona variables scratch, temporales desechables, como \l_tmpa_tl y \l_tmpb_int.

Módulos principales

expl3 se divide en módulos por tipo de dato, y todos comparten el mismo ritmo: crear, asignar, usar. Empecemos por definir funciones. \cs_new:Npn define una nueva función y da error si ya existe una con ese nombre (cs significa control sequence). \cs_set:Npn también define, pero solo dentro del grupo TeX actual y sin quejarse por redefinir. Ambos usan :Npn: N es el nombre de la función definida, p el texto de parámetros (#1#2…) y n el cuerpo, el texto de reemplazo.

Luego, listas de tokens (tl): la variable más básica, usable como una cadena. Se declara con \tl_new:N, se reemplaza su contenido con \tl_set:Nn y se compone con \tl_use:N. Los enteros (int) siguen la misma forma: \int_new:N declara, \int_set:Nn asigna, y \int_eval:n evalúa una expresión entera y devuelve su valor; por ejemplo, \int_eval:n { 2 + 3 * 4 } da 14.

Las secuencias (seq) son listas con acceso por ambos extremos, también útiles como pilas. Se crean con \seq_new:N, se añade a la derecha con \seq_put_right:Nn y se recorren con \seq_map_inline:Nn, que entrega cada elemento como #1. Las listas de propiedades (prop) son diccionarios, mapas de clave a valor. Se crean con \prop_new:N y almacenan pares clave/valor con \prop_put:Nnn (Nnn significa variable, clave, valor).

Las listas con comas (clist) manejan justamente listas de valores separados por comas, y todos sus comandos empiezan por \clist_ (\clist_new:N, \clist_set:Nn, etc.). Por último, coma flotante (fp): \fp_eval:n evalúa expresiones con decimales y admite muchas funciones científicas como sin, sqrt y pi, por ejemplo \fp_eval:n { sqrt(2) } o \fp_eval:n { 2 * pi }. La clave es distinguirlo de \int_eval:n, que solo maneja enteros.

Un pequeño ejemplo: una función y una secuencia

Combinemos lo anterior. El ejemplo define con \cs_new:Npn una función \example_add:n que añade un elemento a una secuencia, la llama varias veces y luego imprime todos los elementos como una lista con \seq_map_inline:Nn. Observa que _ y : en los nombres de comandos no causan problemas porque estamos dentro de una región \ExplSyntaxOn.

document.tex
\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}

Al compilar, aparecen “Fruit: apple”, “Fruit: banana” y “Fruit: cherry” en tres líneas. El #1 en \cs_new:Npn es el argumento de la función definida, mientras que el #1 en \seq_map_inline:Nn es cada elemento recorrido; ambos llegan como tipo n. Fíjate en el ~ de Fruit:~#1: si lo sustituyes por un espacio normal, el espacio se ignora y queda “Fruit:apple”.

Cómo se usa en la práctica

En la escritura normal de documentos rara vez necesitas escribir expl3 directamente. Pero en cuanto te pones a escribir un paquete o una clase, expl3 es ya el estándar de facto. Una combinación común es recibir una orden de usuario con \NewDocumentCommand (xparse) e implementar su cuerpo en expl3. Mantén separadas dos cosas: la especificación de argumentos de xparse (m, O{...}, s, de nivel documento) no es la firma de argumentos de expl3 (N, n, de nivel programación). La primera se trata en detalle en la página de xparse.

Aprender expl3 te permite escribir con claridad cosas incómodas con solo \newcommand. Conviene recordar estos puntos:

  • Siempre envuelve el código en \ExplSyntaxOn\ExplSyntaxOff. Dentro, los espacios se ignoran; usa ~ cuando necesites uno en la salida.
  • No hace falta \usepackage{expl3}: está en el núcleo. Dentro de un .sty puedes escribirlo directamente después de la declaración del paquete.
  • Las funciones son \⟨module⟩_⟨description⟩:⟨signature⟩; las variables son \⟨scope⟩_⟨name⟩_⟨type⟩, con ámbito l_ / g_ / c_.
  • No inventes nombres de comandos. El nombrado es estricto; el manual oficial interface3 (texdoc interface3) es la fuente autorizada.