viernes, 20 de marzo de 2009

HAL y las marcas (LaTeX - II)

Si quisiéramos acudir a un procedimiento automático para traducir nuestras marcas informales a marcas LaTeX, como hicimos para obtener nuestro documento HTML, deberíamos tener en cuenta la forma típica que adoptan los textos etiquetados con LaTeX, de lo que ayer se dio un brevísimo ejemplo, así como algunas de sus peculiaridades, tales como la manera de entrecomillar en español o la introducción literal de caracteres especiales.

¿Qué queremos decir con esto? La mejor manera de verlo es elaborar un fichero txt2latex.sed semejante al txt2html.sed de entonces. El fichero tendría por lo pronto este aspecto:

# Conversión de caracteres especiales
s/"\([[:alnum:]]\+\)"/<<\1>>/g
s/_/\\_/g

# Conversión de etiquetas
s/INICIO SECCIÓN//g
s/FIN SECCIÓN//g
s/INICIO TÍTULO SECCIÓN/\\section{/g
s/FIN TÍTULO SECCIÓN/}\n/g
s/INICIO PÁRRAFO//g
s/FIN PÁRRAFO/\n\n/g
s/INICIO LISTA NUMERADA/\\begin{enumerate}\n/g
s/FIN LISTA NUMERADA/\\end{enumerate}\n\n/g
s/INICIO ELEMENTO LISTA/\\item /g
s/FIN ELEMENTO LISTA/\n/g
s/INICIO NOMBRE PROGRAMA//g
s/FIN NOMBRE PROGRAMA//g
s/INICIO ORDEN//g
s/FIN ORDEN//g
s/INICIO OPCIÓN//g
s/FIN OPCIÓN//g

Buena parte de este fichero se entiende por sí mismo. Las etiquetas informales que no tienen traducción se sustituyen por nada, es decir, se eliminan. También hemos eliminado provisionalmente las etiquetas para nombre de programa, opción y orden, porque todavía no sabemos sus equivalencias en LaTeX. Allí donde queremos un salto de párrafo sustituimos la marca informal por la marca LaTeX equivalente más dos saltos de línea ---recuérdese que el salto de línea se expresa con '\n'. Estos dos saltos crean precisamente una línea en blanco. Nótese, en particular, que el carácter '\' de LaTeX debe escaparse con otro '\' (el carácter de escape de sed, y de casi todo en HAL), puesto que es un carácter especial de sed, justamente el que sirve para escapar un carácter cualquiera.

En cuanto a la primera sección del fichero, la relativa a la conversión de las comillas rectas por comillas angulares y la secuencia de escape para el carácter '_' explicada ayer, sólo nos puede extrañar la primera orden:

s/"\([[:alnum:]]\+\)"/<<\1>>/g

Entender lo que esta orden de sed significa implica conocer una nueva posibilidad de sed todavía no explicada: las referencias hacia atrás (backreferences). Miremos la expresión minuciosamente, porque casi todo nos debería resultar familiar.

s/RE/remplazo/g

es evidentemente una orden de sustitución de sed. Lo sustituido es todo lo que encaja con la expresión regular (RE) que hay a la izquierda. Analicemos primero esa expresión regular:

"\([[:alnum:]]\+\)"

Se trata de un molde para un texto entrecomillado ("texto"). Lo entrecomillado es un conjunto de caracteres alfanuméricos [[:alnum:]]. Este conjunto contiene uno o más miembros (el signo +), que en sed, frente a lo que ocurre con expresiones regulares extendidas, tiene que escaparse (\+). Además, este conjunto aparece agrupado (los paréntesis (...), que en sed deben también escaparse). Por tanto, la expresión regular designa todo fragmento de texto constituido por uno o más de un carácter alfanuméricos que esté entrecomillado por comillas rectas. En definitiva, esta expresión regular no contiene nada que no sepamos ya, salvo la necesidad de escapar algunos de sus caracteres.

Lo nuevo está en la expresión que se propone como remplazo:

<<\1>>

Las comillas angulares de inicio y cierre son justamente aquellas por las que queremos sustituir las comillas rectas del texto que encaje con la expresión regular. Lo que aparece entre estas comillas angulares es la enigmática expresión \1. Esta expresión simplemente recupera el contenido de lo registrado por sed cuando descubrió una agrupación en la expresión regular, es decir, todo lo que hay entre los paréntesis. (Como sólo se ha registrado una agrupación, la referencia hacia atrás es \1; si en la expresión regular apareciese una segunda agrupación, la referencia de esta segunda agrupación sería \2, etc.). Por tanto, el fragmento de texto constituido por los caracteres alfanuméricos va a aparecer intacto en el remplazo. Esta es la técnica habitual que se utiliza cuando una parte interna del texto que encaja con la expresión (en nuestro caso, lo entrecomillado) se quiere preservar y sólo otra parte (en nuestro caso, las comillas rectas) se quiere modificar.

Diseccionado el sentido de nuestro fichero txt2latex.sed, podemos utilizarlo para efectuar la sustitución de nuestras marcas informales ---que, recuérdese, aparecen en el fichero texto_etiquetado.txt--- por las de LaTeX y guardar el resultado en un fichero con la extensión .tex, que es la forma canónica de identificar ficheros LaTeX:

sed -f txt2latex.sed texto_etiquetado.txt >texto_etiquetado.tex

El resultado deja mucho que desear:

\section{
Navegación gráfica
}



Si hubiésemos estado visitando la página con un buen navegador gráfico que disponga de la opción de guardar las páginas web como texto, tendríamos que realizar al menos dos acciones, ambas independientes:
...

Los espacios en blanco están por doquier y se han generado saltos de línea donde no debía haberlos.

La causa del problema está en que sed, tras realizar cada sustitución, incluye el salto de línea en las líneas modificadas. Este comportamiento es conveniente en la mayoría de los casos ---nadie esperaría que sed se comiese el salto de línea, cuando de lo que se trata es de cambiar sólo una parte de una línea. Sin embargo, este educada manera de obrar es fatal para el caso que nos ocupa.

Hay varias formas de resolver el problema, que pasan por utilizar otro ayudante que no sea sed. Pero quizá la más sencilla para nuestro caso particular, y que no nos obliga a renunciar a sed, sea darle como entrada el mismo fichero pero transformado previamente en una única línea. Las marcas seguirán allí intactas y nada provocará saltos de línea innecesarios, más allá de los que nosotros hemos decidido producir en nuestras sustituciones, puesto que sed trabajará sobre una línea única.

Hacer que las múltiples líneas de un fichero se unan y constituyan una única línea se puede conseguir con distintas órdenes de HAL. Una de las más simples es la orden tr (de translate). Esta orden dispone de una opción adecuada a nuestros fines, la opción -d, que elimina (delete) de la entrada el carácter que demos como argumento a esa opción. Por ejemplo:

tr -d 'a'

eliminaría todas las aes de nuestra entrada.

Para convertir múltiples líneas en una única línea, el carácter que deberemos eliminar es justamente el salto de línea. Por tanto:

tr -d '\n'

es la orden apropiada para lograr nuestro propósito.

Hemos dicho que tr afecta a la "entrada" y no simplemente al "fichero" dado como argumento. ¿Por qué? La razón es que tr es una de esas pocas órdenes que no permite ficheros como argumentos. Dicho de otra forma, tr actúa sólo y directamente sobre el flujo de entrada del que se nutre. Esta característica la hace especialmente apta para integrarse en tuberías. Pero en nuestro caso, sigue siendo un fichero el que queremos darle como alimento.

Existe una forma de dar un fichero de alimento a una orden que trabaja únicamente sobre el flujo de entrada. Es una herramienta más de fontanería, el reverso del famoso embudo (>) que aplicamos para redirigir la salida de una orden a un fichero. Existe otro embudo, <, que realiza la función contraria: redirige un fichero hacia la entrada estándar. Si en el otro lado (el izquierdo) hay una orden que puede alimentarse directamente desde la entrada estándar (cualquiera de los filtros que hemos visto en anteriores artículos: sed, grep, etc.) o que sólo se alimenta de ella, como tr, el fichero redirigido se convertirá en la entrada de la orden.

En consecuencia, para que nuestra anterior orden de supresión de saltos de línea haga su efecto sobre el fichero texto_etiquetado.txt, deberíamos decirle a HAL lo siguiente:

tr -d '\n'< texto_etiquetado.txt

Con este paso previo se puede ya construir la orden completa, puesto que el resultado de la acción de tr se puede enviar sin problemas a sed mediante una tubería y redirigir lo que produce sed al fichero texto_etiquetado.tex:

tr -d '\n'< texto_etiquetado.txt | sed -f txt2latex.sed >texto_etiquetado.tex

Lo cual produce el documento con todas las transformaciones propias de LaTeX que hemos solicitado:

\section{Navegación gráfica}
Si hubiésemos estado visitando la página con un buen navegador gráfico que disponga de la opción de guardar las páginas web como texto, tendríamos que realizar al menos dos acciones, ambas independientes:

\begin{enumerate}
\item Ir al menú <<Archivo>> del navegador y guardar como texto la página web. (Lo que además implica seleccionar un nombre de fichero y un directorio).
\item Crear en Vim una nueva ventana para editar en ella el documento que acabamos de guardar, mediante la orden :new ruta\_fichero.
\end{enumerate}

\section{Navegación desde consola}
Si navegamos con w3m, y gracias a la interacción entre las aplicaciones de consola, bastaría ejecutar una única acción desde el propio Vim, que, además, podríamos abreviar para reproducirla en situaciones semejantes. Veamos cómo.

Con w3m el proceso de convertir una página web en texto es absolutamente elemental: añadir la opción -dump. Por ejemplo:

w3m -dump http://www.w3.org/TR/html401/struct/lists.html

nos devuelve la página dada como argumento, tal cual se presenta en el navegador, pero como puro texto.



Resumen:

  • Las expresiones regulares para sed no son idénticas a las expresiones regulares extendidas. Ello implica que muchos caracteres típicos de las EREs tengan que escaparse en sed.

  • sed registra las agrupaciones que en los patrones de expresiones regulares aparecen entre paréntesis \(...\) y los guarda para su uso posterior. Para recuperar lo contenido en esos registros se utilizan los signos de referencias hacia atrás (\1, \2, ..., \9), cada uno de los cuales remite a la primera, segunda, ..., novena agrupación de la expresión regular del caso.

  • La orden tr traduce o elimina (con la opción -d) caracteres de la entrada estándar. Por ejemplo tr -d '\n' elimina los saltos de línea del flujo de entrada.

  • Existen órdenes de HAL que se nutren únicamente de la entrada estándar, como tr y no pueden recibir un fichero como argumento.

  • El embudo < permite redirigir un fichero a la entrada estándar y, por extensión, a una orden que se alimente de la entrada estándar. Por ejemplo, tr -d '\n'< mi_fichero, hace que el fichero mi_fichero sea la entrada de la orden tr.

No hay comentarios:

Publicar un comentario