¿Analizador previo simple para separar TeX y comentarios?

¿Analizador previo simple para separar TeX y comentarios?

Quiero escribir un artículo matemático con fórmulas y fragmentos de programas, uno al lado del otro. Sin embargo, no quiero que TeX muestre o documente los programas, solo poder extraerlos en un archivo si lo desea.

Tenga en cuenta que mi intención no tiene nada que ver con la programación literal, pero ¿quizás pueda (mal) usar algunas de estas herramientas para mi propósito? Por ejemplo, suponiendo un token especial %# para el analizador previo, dicho archivo podría verse así:

\documentclass[]{article}
\begin{document}
The factorial is an important function:
\begin{equation}
    n! = \prod_{k=1}^n k
\end{equation}
%#  n := 1;
%#  for k from 1 to n 
%#      n := n * k;
\end{document}

Mi pregunta: ¿Existen herramientas (o editores) que puedan preparar un documento TeX y dividirlo en dos (o más) archivos diferentes destinados a su posterior procesamiento con diferentes herramientas? ¿O alguna otra idea que utilice las funciones de TeX?

EDITAR: En extensión de la excelente respuesta de jfbu: Suponiendo que el fragmento de código está inmediatamente después de una ecuación numerada, ¿podríamos escribir también el número de la ecuación en el archivo de código? Esto equivaldría a una referencia cruzada entre los dos productos. (Si esto no es posible, ¿cómo se puede escribir el contador de fragmentos interno introducido en la 'respuesta elaborada' de jfbu (con un signo de comentario antepuesto como # o //?).

Respuesta1

Puedes hacer esto desde dentro de tex, sin herramientas externas.

Esta actualización genera un archivo para cada fragmento de código con numeración automática (personalizable). Este ejemplo produce filename-code-01.py, filename-code-02.py, filename-code-03.py, filename-code-04.pycorrespondientes a los cuatro fragmentos de código (dos están en el preámbulo).

Actualización adicional en respuesta a una edición del OP: la primera línea de cada fragmento de código de salida ahora es una línea de comentario con el número del fragmento de código. La cuestión sobre el uso del número de ecuación es más delicada, ya que la extracción de los fragmentos de código se realiza como parte del código del preámbulo, antes de que se haya escrito algo en el documento.

$ ls preparseC*
preparseC-code-01.py    preparseC-code-04.py    preparseC.log
preparseC-code-02.py    preparseC.aux       preparseC.tex
preparseC-code-03.py    preparseC.dvi

Los contenidos de preparseC-code-01.py:

# Code snippet 1
n := 1;
for k from 1 to n
    n := n * k;

La etiqueta de línea utilizada para identificar fragmentos de código en el .texarchivo fuente es %#<space><space>.

Código:

\documentclass{article}

\newcounter{snippetno}

% customize \thesnippetno as desired, for example this produces
% filename-code-01.py
% filename-code-02.py
% ...
% filename-code-10.py
% etc...
% This command should be expandable
\renewcommand{\thesnippetno}
  {\jobname-code-\ifnum\value{snippetno}<10 %<- leave a space
        0\fi
   \arabic{snippetno}.py}


%%%%%%%% PREPARSING
\newread\parsein
\openin\parsein \jobname.tex

\newwrite\parseout
% this version will create one file for each code snippet
\newif\ifOutputtingLines

% adapt the following to the line tag you want to use
% the \detokenize is not needed here, but in case your tag
% uses letters, put them in it (do not use \catcode for letters
% a they may be in use in \def \endgroup etc..)
%
% THIS VERSION USES %#<space><space> AS LINE TAG
% (two spaces must be present and will be removed in the outputs)
\begingroup
%% ADDED DEFINITION OF \COMMENTTAG FOR USE IN FIRST LINE OF CODE SNIPPET FILES
\catcode`\% 12
\catcode`\# 12
\def\x{\endgroup\def\COMMENTTAG{#}\edef\LineTag{\detokenize{%#}\space\space}}
\x
%\show\LineTag % debugging

\begingroup
\edef\x{\endgroup
        \unexpanded{\def\CheckLineAux #1}\LineTag\relax \unexpanded{{#1}}
        \unexpanded{\def\CheckLine #1}\LineTag \unexpanded{#2}\relax
        \unexpanded{{\if\relax #1\relax 
                        \ifOutputtingLines\else
           \stepcounter{snippetno}%
           \immediate\openout\parseout \thesnippetno\relax
%% ------------------------ ADDED TO INSERT CODE SNIPPET NUMBER IN THE FILE
           \immediate\write\parseout 
                  {\COMMENTTAG\space Code snippet \arabic{snippetno}}%
%% ------------------------
           \OutputtingLinestrue
                        \fi
           \immediate\write\parseout {\CheckLineAux #2\relax}%
                     \else
                        \ifOutputtingLines
                          \immediate\closeout\parseout 
                          \OutputtingLinesfalse
                        \fi
                     \fi}}%
}
\x

\begingroup\endlinechar-1
\loop
  \ifeof\parsein
    % if \end{document} is not missing no need to \closeout\parseout
    % necessarily already done, and OutputtingLines toggle necessarily false
    \closein\parsein
  \else
    \readline\parsein to \tmpline
    \if\relax\tmpline\relax % found empty line
        \ifOutputtingLines\immediate\closeout\parseout
              \OutputtingLinesfalse
        \fi
    \else
        \expandafter\expandafter\expandafter \CheckLine
                \expandafter \tmpline\LineTag \relax
    \fi 
\repeat
\endgroup

%%%%%%%% END OF PREPARSING

% Some code snippets may already be put here in the preamble
% 
%#  n := 1;
%#  for k from 1 to n 
%#      n := n * k;

%#  y := 1;
%#  for k from 1 to n 
%#      y := y * (x + k -1);

% Notice that in this variant the line tag is %#<space><space>
% and is removed on output

\begin{document}
The factorial is an important function:
\[
    n! = \prod_{k=1}^n k
\]
%#  n := 1;
%#  for k from 1 to n 
%#      n := n * k;
The (so-called) Pochhammer coefficient also:
\[
    (x)_n = \prod_{k=1}^n (x+k-1)
\]
%#  y := 1;
%#  for k from 1 to n 
%#      y := y * (x + k -1);
\end{document}

Esta es la primera versión de la respuesta: (eliminé una \makeatletterque era inútil y comenté la \show\LineTaglínea de depuración)

Lo siguiente, en la compilación, también se extraerá a filename-codelas líneas etiquetadas.

  n := 1;
  for k from 1 to n
      n := n * k;

Código:

\documentclass{article}

%%%%%%%% PREPARSING
\newread\parsein
\openin\parsein \jobname.tex
\newwrite\parseout
\immediate\openout\parseout \jobname-code

% adapt the following to the line tag you want to use
% the \detokenize is not needed here, but in case your tag
% uses letters, put them in it (do not use \catcode for letters
% a they may be in use in \def \endgroup etc..)
\begingroup
\catcode`\% 12
\catcode`\# 12
\def\x{\endgroup\edef\LineTag{\detokenize{%#}}}
\x
%\show\LineTag % debugging
\begingroup
\edef\x{\endgroup
        \unexpanded{\def\CheckLineAux #1}\LineTag\relax \unexpanded{{#1}}
        \unexpanded{\def\CheckLine #1}\LineTag \unexpanded{#2}\relax
        \unexpanded{{\if\relax #1\relax 
                      \immediate\write\parseout {\CheckLineAux #2\relax}%
                     \fi}}%
}
\x

\begingroup\endlinechar-1
\loop
  \ifeof\parsein
    \immediate\closeout\parseout
    \closein\parsein
  \else
    \readline\parsein to \tmpline
    \if\relax\tmpline\relax\else
        \expandafter\expandafter\expandafter \CheckLine
                \expandafter \tmpline\LineTag \relax
    \fi 
\repeat
\endgroup

%%%%%%%% END OF PREPARSING

\begin{document}
The factorial is an important function:
\[
    n! = \prod_{k=1}^n k
\]
%#  n := 1;
%#  for k from 1 to n 
%#      n := n * k;
\end{document}

Respuesta2

Hay herramientas más extensas, pero si guarda su ejemplo en fff.texese momento grep, sedes todo lo que necesita

grep -v "^%#" fff.tex

produce

\documentclass[]{article}
\begin{document}
The factorial is an important function:
\[
    n! = \prod_{k=1}^n k
\]
\end{document}

y

grep "^%#" fff.tex | sed "s/^%#//"

produce

  n := 1;
  for k from 1 to n 
      n := n * k;

Respuesta3

También se puede hacer con un script perl simple, que para mí es más legible que grepy sedcon opciones.

#!/usr/bin/perl

while(<STDIN>)
{
  if( (substr $_, 0, 2) eq "%#" )
    {
      print substr $_, 2;
    }
}

Documento guardado en document.texy código Perl guardado en extractComments.pl.

Produce:

hpek@melda:~/programming/perl$ cat document.tex |./extractComments.pl 
  n := 1;
  for k from 1 to n 
      n := n * k;
hpek@melda:~/programming/perl$ 

Respuesta4

Mis pensamientos: si su fragmento de código es R (http://www.r-project.org/), Yo consideraría knitr,http://yihui.name/knitr/(o ahora más bien el viejo Sweave,http://www.stat.uni-muenchen.de/~leisch/Sweave/).

Si no es así, probaría el extractpaquete,http://ctan.org/pkg/extract, si se ajusta a tus necesidades.

Personalmente, probablemente elegiría el listingspaquete,http://mirrors.nic.cz/tex-archive/macros/latex/contrib/listings/listings.pdf, los códigos se pueden componer de inmediato y, si fuera necesario, podríamos activar/desactivar sus entornos (entonces se comportarían como comentarios), extraerlos sobre la marcha y podríamos tener fragmentos de código dentro y fuera del TeX principal. Archivo compilable por otros programas al mismo tiempo.

información relacionada