Ejecutar Common-Lisp en LaTeX: el paquete lisp-on-tex
He estado probando estos primeros días soleados de octubre el paquete experimental lisp-on-tex
, escrito por Hakuta
Shizuya. Como su nombre anuncia (y los característicos guiones también), se trata de una biblioteca destinada a evaluar
código Lisp a través del proceso de compilación de LaTeX. Algo que, a primera vista, me puso los dientes largos, tan
devoto y apasionado como soy por este bello y potente lenguaje de programación, en gran parte en su dialecto Emacs-Lisp.
Con Lisp siempre es bueno recordar estas palabras de Stallman:
The most powerful programming language is Lisp. If you don’t know Lisp (or its variant, Scheme), you don’t know what it means for a programming language to be powerful and elegant. Once you learn Lisp, you will see what is lacking in most other languages.
Unlike most languages today, which are focused on defining specialized data types, Lisp provides a few data types which are general. Instead of defining specific types, you build structures from these types. Thus, rather than offering a way to define a list-of-this type and a list-of-that type, Lisp has one type of lists which can hold any sort of data.
Where other languages allow you to define a function to search a list-of-this, and sometimes a way to define a generic list-search function that you can instantiate for list-of-this, Lisp makes it easy to write a function that will search any list — and provides a range of such functions.
In addition, functions and expressions in Lisp are represented as data in a way that makes it easy to operate on them.
When you start a Lisp system, it enters a read-eval-print loop. Most other languages have nothing comparable to `read’, nothing comparable to `eval’, and nothing comparable to `print’. What gaping deficiencies!
— Richard Stallman, How I do my computing, en su página personal.
Con todas esas premisas, cualquier caballo de Troya lispiano, aunque sea un potrillo, no puede sino ser bienvenido entre el engranaje de nuestro cajista binario TeX y su director editorial binario LaTeX. Porque (y es lo que aquí nos ocupa) las posibilidades tipográficas que se desprenderían de ello son más que interesantes. Echemos un vistazo a las primeras impresiones.
Primeros juegos
Antes de empezar, conviene insistir que este paquete se encuentra aún en desarrollo (más bien lento, al parecer, pues su última actualización es de 2015), y esto trae como consecuencia cosas como que la documentación es inexistente. Hay que conformarse con un escueto y espartano Readme, aunque suficiente para sacar algunas conclusiones de entrada. A saber:
- El código Lisp ha de encerrarse en el comando
\lispinterp{...}
, - Los símbolos de Lisp han de llevar siempre una barra invertida, como los comandos de TeX. Por ejemplo, un
concat
se convierte aquí en\concat
, - Las cadenas de texto se ponen con comillas simples, no dobles.
Añadido a eso, la plena funcionalidad del paquete depende de la infraestructura de LaTeX3, la futura versión de LaTeX,
también en desarrollo. Por suerte, podemos usar gran parte del kernel de LaTeX3 sobre LaTeX2ε si cargamos en nuestro
preámbulo el paquete expl3
. Y es aquí donde vino el primer tropezón leve. Un módulo de lisp-on-tex
que nos hacía
mucha gracia probar, lisp-mod-l3regex
, para trabajar las expresiones regulares sobre Lisp, requería del paquete de
LaTeX3 l3regex.sty
. Pero no encontraba el paquete y devolvía error. La causa estaba en que l3regex.sty
se encontraba
plegado dentro del propio expl3
. Se solucionó fácilmente añadiendo la línea \expandafter\def\csname
[email protected]\endcsname{}
, y con eso ya teníamos nuestro preámbulo listo para el recreo:
\documentclass{article} \usepackage{fontspec} \setmainfont{Linux Libertine O} \usepackage{expl3} \expandafter\def\csname [email protected]\endcsname{} \usepackage{lisp-on-tex} \usepackage{lisp-mod-l3regex}
Las primeras pruebas que hice, como comenté antes, estaban relacionadas con expresiones regulares. El paquete viene ya con algunas funciones definidas. Por ejemplo, \regReplaceAll
es lo más parecido a lo que en Elisp
sería un replace-regex-in-string
, con los mismos tres argumentos (el regex a reemplazar, el regex que reemplaza y la
cadena donde reemplazar). Con esta función, ya he probado a definir algunos comandos sencillos para LaTeX. Como éste, que convertiría todo carácter «a» en «xxx»:
\def\prueba#1{% \lispinterp{ (\texprint (\regReplaceAll 'a' 'xxx' '#1')) }}
Podemos probarlo si escribimos algo así como:
\prueba{la casa de arriba}
Con esa misma estructura, este otro comando nos pondría en negrita todos los números romanos. Ojo, para que no salga el
literal \textbf{...}
sino su resultado, obsérvese la sintaxis. Por otra parte, \1
para el grupo, es lo esperable:
\def\pruebabis#1{% \lispinterp{ (\texprint (\regReplaceAll '(\b[IVXLCDM]+\b)' '\c{textbf}\cB\{\1\cE\}' '#1')) }}
Lo probamos con:
\pruebabis{El siglo XXI y el volumen VI}
Y este tercer comando nos pondría en negrita cualquier texto en griego, ya sea poli- o monotónico:
\def\pruebatris#1{% \lispinterp{ (\texprint (\regReplaceAll '([\x{1F00}-\x{1FFE}\x{0370}-\x{03FF}]+)' '\c{textbf}\cB\{\1\cE\}' '#1')) }}
Y podemos probarlo con:
\pruebatris{Lorem ipsum dolor sit amet, consectetuer adipiscing
elit. Donec hendrerit tempor tellus. Δαρείου καὶ Παρυσάτιδος
γίγνονται παῖδες δύο. Donec pretium posuere tellus. Proin quam
nisl, tincidunt et, mattis eget, convallis nec, purus.}
Un comando menos trivial. ¡Y hasta un entorno!
Hace no mucho, escribí para Emacs una sencilla función para evitar (como casi siempre) un trabajo latoso. El caso es que
andaba trabajando en la composición de un extenso libro, cuyo autor tuvo a bien diseminar a través del texto infinidad
de pasajes en griego: palabras unas veces; otras, frases de una o dos líneas, más o menos. Naturalmente, lo ideal sería
incluir todos esos segmentos en un comando \foreignlanguage{greek}{...}
de Babel, para asegurar el correcto guionado
del griego antiguo, pero se puede morir cualquiera si tiene que ir haciéndolo a mano y uno por uno. Primero, se me
ocurrió intentar escribir algún script en Perl, hasta que caí en la cuenta de que con Elisp sería más sencillo, así que,
tras algunas pruebas ensayo/error di con una regex que funciona y que no devuelve ningún falso positivo. La tuve que
segmentar en una expresión concat
porque es bastante larga y entorpece la depuración del código. Pero traducida viene a
decir: «localiza cadenas de texto dentro de unos parámetros determinados, y que incluyen los caracteres de los rangos
Unicode ’griego básico’ (\u0370-\u03FF
) y ’griego extendido’ (\u1F00-\u1FFE
), signos de puntuación y espacios». Y la
función quedó, finalmente, así:
(defun babel-griego-en-region () (interactive) (save-restriction (narrow-to-region (region-beginning) (region-end)) (save-excursion (goto-char (point-min)) (replace-regexp (concat "\\(^\\|\\w+[[:blank:]]+\\|[,.;?¿:«»]+[[:blank:]]+\\|{\\)" "\\([,.;?¿:«»··\u1F00-\u1FFE\u0370-\u03FF ]+\\)" "\\([[:blank:]]+\\|\n\\|\\'\\|}\\)") "\\1\\\\foreignlanguage{greek}{\\2}\\3" nil))) (deactivate-mark))
Y ahora que andaba trasteando con este lisp-on-tex
, lo siguiente que me vino a la cabeza fue intentar adaptar esa
regex para que la sustitución se ejecutase durante la compilación. Que conste que ya se me había ocurrido hacerlo dentro
de Lua (con LuaLaTeX, se entiende), pero las expresiones regulares complejas no son el fuerte de Lua, reconozcámoslo.
Con lisp-on-tex
, por contra, no fue muy difícil adaptar mi regex, mutatis mutandis. Y a lo siguiente que llegué es a
una macro con la misma estructura de las de los ejemplos anteriores:
\def\babelgriego#1{% \lispinterp{ (\texprint (\regReplaceAll (\concat '(^|\w+\s+|[,.;?¿:«»]+\s+)' '([,.;?¿:«»··\n\s\x{1F00}-\x{1FFE}\x{0370}-\x{03FF}]+)' '(\s+|\n)') '\1\c{foreignlanguage}\cB\{greek\cE\}\cB\{\2\cE\}\3' '#1')) }}
Pero, claro, cuánto mejor sería que esto se convirtiese en un entorno. El problema es que el comando de este intérprete
de Lisp, \lispinterp
, es una macro «corta» y no puede actuar a través de los párrafos: TeX nos devolvería el típico
error de «runaway argument». Podemos resolverlo echando mano de un \everypar
, pero hay muchos paquetes que lo
redefinen. Por suerte, viene en nuestra ayuda el paquete everyhook
para hacernos el apaño, ya que (en palabras de su
autor) «takes control of the six TEX token parameters \everypar
, \everymath
, \everydisplay
, \everyhbox
, \everyvbox
and \everycr
.»
Cargamos, entonces, el paquete (y de paso showhyphens
, para comprobar que los puntos de corte de sílaba están
activados para los pasajes en griego, y son los correctos). A continuación definimos la macro que aplicará por párrafo
nuestra función de Elisp. Y, finalmente, el entorno:
\usepackage{showhyphens} \usepackage{everyhook} \def\bgriegopar#1\par{\babelgriego{#1}\par} \newenvironment{fragsgriego}{% \PushPreHook{par}{\bgriegopar\null}} {\PopPreHook{par}}
Sólo nos queda ya probar nuestro entorno (aquí, el resultado de la compilación):
\begin{fragsgriego} Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. Consectetuer adipiscing elit Παρυσάτιδος γίγνονται. \end{fragsgriego}
Figura 1: Nuestro ejemplo compilado con los cortes de sílaba correctos para griego antiguo y español
∞
Publicado: 07/10/19
Última actualización: 21/01/22
Esta obra está bajo una licencia de Creative Commons Reconocimiento-NoComercial 4.0 Internacional.