簡單的預解析器來分離 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 功能的想法嗎?

編輯:延伸到 jfbu 的優秀答案:假設程式碼片段緊接在編號方程式之後,我們是否也可以將方程式的編號寫到程式碼檔案中?這相當於兩個輸出之間的交叉引用。 (如果這是不可能的,那麼如何寫出 jfbu 的「詳細答案」中引入的內部片段計數器(帶有 # 或 // 之類的前置註釋符號?)。

答案1

您可以從 tex 內部完成此操作,無需外部工具。

此更新為每個程式碼片段產生一個文件,並帶有自動(可自訂)編號。此範例產生filename-code-01.pyfilename-code-02.py、 、filename-code-03.pyfilename-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.tex那麼grepsed就是您所需要的

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/)。

如果不是,我會嘗試這個extract包,http://ctan.org/pkg/extract,如果它符合您的需求。

就我個人而言,我可能會選擇listings套餐,http://mirrors.nic.cz/tex-archive/macros/latex/contrib/listings/listings.pdf,程式碼可以立即排版,如果需要,我們可以打開/關閉它的環境(然後它們將表現為註釋),即時提取它們,我們可以在主 TeX 內部和外部擁有程式碼區塊檔案可以同時被其他程式編譯。

相關內容