Простой предварительный парсер для разделения TeX и комментариев?

Простой предварительный парсер для разделения TeX и комментариев?

Я хочу написать математическую статью с формулами и фрагментами программ, бок о бок. Однако я не хочу, чтобы программы отображались или документировались TeX, просто иметь возможность извлечь их в файл, если это необходимо.

Обратите внимание, что мое намерение не имеет ничего общего с буквальным программированием, но, возможно, я могу (неправильно) использовать некоторые из этих инструментов для своих целей? Например, предполагая специальный токен %# для предварительного анализатора, такой файл может выглядеть так:

\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}

Мой вопрос: Есть ли инструменты (или редакторы), которые могут подготовить документ TeX и разделить его на два (или более) разных файла, предназначенных для дальнейшей обработки разными инструментами? Или есть какие-то другие идеи, использующие возможности TeX?

EDIT: В дополнение к превосходному ответу jfbu: Предполагая, что фрагмент кода находится сразу после пронумерованного уравнения, можем ли мы также записать номер уравнения в файл кода? Это будет равносильно перекрестной ссылке между двумя выходами. (Если это невозможно, как можно записать внутренний счетчик фрагментов, введенный в «развернутый ответ» jfbu (с предшествующим знаком комментария, например # или // ?).

решение1

Это можно сделать изнутри текса, без внешних инструментов.

Это обновление генерирует один файл для каждого фрагмента кода с автоматической (настраиваемой) нумерацией. Этот пример создает filename-code-01.py, filename-code-02.py, filename-code-03.py, filename-code-04.pyсоответствующие четырем фрагментам кода (два из них находятся в преамбуле).

Дополнительное обновление в ответ на редактирование OP: первая строка каждого выходного фрагмента кода теперь является строкой комментария с номером фрагмента кода. Вопрос об использовании номера уравнения более деликатный, поскольку извлечение фрагментов кода выполняется как часть кода преамбулы, до того, как что-либо будет фактически набрано в документе.

$ 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

Содержание preparseC-code-01.py:

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

Тег строки, используемый для идентификации фрагментов кода в .texисходном файле, — %#<space><space>.

Код:

\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}

Это первая версия ответа: (я удалил a, \makeatletterкоторый был бесполезен, и закомментировал \show\LineTagстроку отладки)

Следующий код при компиляции также будет извлечен в filename-codeотмеченные строки.

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

Код:

\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}

решение2

Есть более обширные инструменты, но если вы сохраните свой пример, то fff.texэто grepвсе sed, что вам нужно.

grep -v "^%#" fff.tex

производит

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

и

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

производит

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

решение3

Это также можно сделать с помощью простого скрипта Perl, который, на мой взгляд, более удобочитаем, чем grepи sedс опциями.

#!/usr/bin/perl

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

Документ сохранен в формате document.tex, а код Perl сохранен в формате extractComments.pl.

Производит:

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

решение4

Мои мысли: Если ваш фрагмент кода — R (http://www.r-project.org/), я бы рассмотрел knitr,http://yihui.name/knitr/(или теперь скорее старый Суэйв,http://www.stat.uni-muenchen.de/~leisch/Sweave/).

Если нет, я бы попробовал пакет extract,http://ctan.org/pkg/extract, если это соответствует вашим потребностям.

Лично я бы, наверное, выбрал пакет listings,http://mirrors.nic.cz/tex-archive/macros/latex/contrib/listings/listings.pdf, коды можно набирать сразу, и при необходимости можно включать/выключать их окружения (тогда они будут вести себя как комментарии), извлекать их на лету, и можно иметь фрагменты кода внутри и вне основного файла TeX, которые можно будет компилировать другими программами одновременно.

Связанный контент