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 ドキュメントを事前に解析し、異なるツールでさらに処理できるように 2 つ (またはそれ以上) の異なるファイルに分割できるツール (またはエディター) はありますか? または、TeX の機能を使用する他のアイデアはありますか?

編集: jfbu の優れた回答の延長として、コード スニペットが番号付き方程式の直後にあると仮定すると、方程式の番号もコード ファイルに書き出すことができますか? これは、2 つの出力間の相互参照に相当します。(これが不可能な場合は、jfbu の「詳細な回答」で導入された内部スニペット カウンターをどのように書き出すことができますか (先頭に # や // などのコメント記号を付けて)。

答え1

外部ツールを使わずに、Tex 内からこれを実行できます。

この更新により、コード スニペットごとに 1 つのファイルが生成され、番号は自動 (カスタマイズ可能) になります。この例では、 4 つのコード スニペット (2 つはプリアンブル内) に対応するfilename-code-01.pyfilename-code-02.pyfilename-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}

これは回答の最初のバージョンです: (\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.texgrep必要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/)、私はknitrhttp://yihui.name/knitr/(あるいは今ではむしろ古いスウィーブ、http://www.stat.uni-muenchen.de/~leisch/Sweave/)。

そうでない場合は、パッケージを試してくださいextracthttp://ctan.org/pkg/extract、それがあなたのニーズに合っているなら。

個人的にはパッケージを選ぶと思いますlistingshttp://mirrors.nic.cz/tex-archive/macros/latex/contrib/listings/listings.pdf、コードはすぐにタイプセットでき、必要に応じて環境をオン/オフにしたり(その場合、環境はコメントとして動作します)、オンザフライで抽出したり、メインの TeX ファイルの内外のコード チャンクを他のプログラムで同時にコンパイルしたりすることができます。

関連情報