保存环境内容以便在其他地方使用

保存环境内容以便在其他地方使用

我想保存环境的内容以便稍后在多个地方使用宏使用。

这是真实的用例。

  1. 我有一个绘制“概率”树的环境。
  2. 我想保存绘制树的代码(它使用了的语法forest)。这需要使用ID来标识内容。
  3. 然后稍后我想通过给出 ID 将原始内容与宏一起使用:这个宏将分析用于绘制树的代码,例如自动进行几个演算(这部分将使用 Lua 完成,我知道如何做到这一点)。

输入的内容就是Lua代码所要分析的内容。

环境中使用的代码如下所示。

\begin{probatree}
    [
        [A, pweight = a
            [B, apweight* = b]
            [C, bpweight* = c]
        ]
        [D, pweight* = d]
    ]
\end{probatree}

这是一个起始 MWE。

\documentclass{article}

\usepackage{luacode}
\usepackage{forest}   

\begin{luacode}
function calctree(a)
    tex.print("-+-+")
    tex.print("")
    tex.print("$" .. a .. "$")
    tex.print("")
    tex.print("-+-+")
end    
\end{luacode}

% This fully expands the argument
\newcommand\calctree[1]{\directlua{calctree("#1")}}%

\newenvironment{multilines}{}{}

\begin{document}    

\calctree{OOO}

\calctree{a (b + 3) - c^2}

% Save the content of the environment to use it later: this needs to give
% a single ID to the environment, this ID will be used to store internally
% the content.
%
% ????
\begin{multilines}
    A
    
    B
    
    C
\end{multilines}

Bla, bla, ...

% Use the content with \calctree.
%
% ????

Blo, blo, ...

% Use the content another time with \calctree.
%
% ????

\end{document}

答案1


由于答案的字符数限制为 30,000 个,我需要将答案分成两部分。

这是我的回答的第二部分。

我的回答第一部分包含 TeX 工作原理以及第 2 部分中提供的代码/工作示例的一般解释。

我的回答第二部分保存工作示例。


\makeatletter
%%///////////// Code for interface \UD@GatherCodeSnippetLoop and ///////////////
%%///////////// environment DefineCodeSnippet and all suplementary things //////
%%===============================================================================
%% End \romannumeral-driven expansion safely:
%%===============================================================================
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%===============================================================================
%% Check whether argument is empty:
%%===============================================================================
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\expandafter\UD@stopromannumeral\@secondoftwo}{%
  \expandafter\UD@stopromannumeral\@firstoftwo}%
}%
%%===============================================================================
%% Obtain control sequence token from name of control sequence token:
%%===============================================================================
%% \CsNameToCsToken<stuff not in braces>{NameOfCs}
%% ->  <stuff not in braces>\NameOfCs
%% (<stuff not in braces> may be empty.)
\@ifdefinable\CsNameToCsToken{%
  \long\def\CsNameToCsToken#1#{\romannumeral\InnerCsNameToCsToken{#1}}%
}%
\newcommand\InnerCsNameToCsToken[2]{%
  \expandafter\UD@exchange\expandafter{\csname#2\endcsname}{\UD@stopromannumeral#1}%
}%
\newcommand\UD@exchange[2]{#2#1}%
%%===============================================================================
%% Check if string of non-special character-tokens is substring
%% of another string of non-special character-tokens:
%%===============================================================================
%% \UD@checkstringsubsetof{<possible substring>}{<string>}%
%%   {<tokens if <possible substring> and <string> are equal>}%
%%   {<tokens if <possible substring> is strict subset of <string>>}%
%%   {<<tokens if <possible substring> is not subset of <string>>}%
%% The length of <possible substring> must not exceed the length of <string>!!!
\newcommand\UD@checkstringsubsetof[5]{%
  \UD@checkstringsubsetofloop#1\relax#2\relax{{#3}{#4}}{#5}%
}%
\@ifdefinable\UD@checkstringsubsetofloop{%
  \def\UD@checkstringsubsetofloop#1#2\relax#3#4\relax{%
    \if\string#1\string#3\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
    {%
      \ifx\relax#2\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
      {%
        \ifx\relax#4\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
        {\expandafter\@firstoftwo\@firstoftwo}{\expandafter\@secondoftwo\@firstoftwo}%
      }%
      {\UD@checkstringsubsetofloop#2\relax#4\relax}%
    }{\expandafter\@secondoftwo}%
  }%
}%
%%===============================================================================
%% Gather non-special character-tokens until encountering
%% \end{<name of environment>} :
%%===============================================================================
%% \UD@GatherCodeSnippetLoop{<non-special characters gathered so far>}%
%%                          {<subset of name of environment gathered so far>}%
%%                          {<tokens to apply to stuff gathered>}%
%%                          <non-special characters>\end{<name of environment>}
%%
%% Character-wise gathers <non-special characters>, replaces any non-space by its
%% \catcode-12-pendant. (Space is handled by giving it catcode 12 before applying 
%% the routine.) 
%% Then does 
%%  <tokens to apply to stuff gathered>{<non-special characters gathered so far>}
\newcommand\UD@GatherCodeSnippetLoop{%
  \expandafter\UD@@GatherCodeSnippetLoop\expandafter{\@currenvir}%
}%
\newcommand\UD@@GatherCodeSnippetLoop[1]{%
  \end{#1}%
  \begingroup
  \let\do\@makeother % <- this and the next line switch to
  \dospecials        %    verbatim-category-code-régime.
  \do\^^I%
  \do\^^M%
  \begingroup
  \escapechar=-1\relax
  \edef\@tempa{\string\\end\string{#1\string}}%
  \expandafter\endgroup
  \expandafter\UD@@@GatherCodeSnippetLoop
  \expandafter{\@tempa}%
}%
\newcommand\UD@@@GatherCodeSnippetLoop[5]{%
  \UD@checkstringsubsetof{#3#5}{#1}{%
    \expandafter\expandafter\expandafter\UD@exchange
    \expandafter\expandafter\expandafter{%
    \expandafter\expandafter\expandafter{%
      \UD@RemoveLeadingNTrailingCarriageReturn{#2}%
    }}{#4}%
  }{%
    \expandafter\UD@exchange\expandafter{\expandafter{%
      \romannumeral
      \expandafter\UD@exchange\expandafter{%
        \romannumeral\UD@StringifyIfNotSpace{#5}%
      }{\UD@stopromannumeral#3}%
    }}{\UD@@@GatherCodeSnippetLoop{#1}{#2}}{#4}%
  }{%
    \UD@checkstringsubsetof{#5}{#1}{%
      \expandafter\expandafter\expandafter\UD@exchange
      \expandafter\expandafter\expandafter{%
      \expandafter\expandafter\expandafter{%
        \UD@RemoveLeadingNTrailingCarriageReturn{#2#3}%
      }}{#4}%
    }{%
      \expandafter\UD@exchange\expandafter{\expandafter{%
         \romannumeral\UD@StringifyIfNotSpace{#5}%
      }}{\UD@@@GatherCodeSnippetLoop{#1}{#2#3}}{#4}%
    }{%
      \expandafter\UD@exchange\expandafter{\expandafter{%
        \romannumeral
        \expandafter\UD@exchange\expandafter{%
          \romannumeral\UD@StringifyIfNotSpace{#5}%
        }{\UD@stopromannumeral#2#3}%
      }}{\UD@@@GatherCodeSnippetLoop{#1}}{}{#4}%
    }%
  }%
}%
\newcommand\UD@StringifyIfNotSpace[1]{%
  \if\string#1 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  {\UD@stopromannumeral}{\expandafter\UD@stopromannumeral\string}#1%
}%
%%===============================================================================
%% Catcode-régime for defining components used by \UD@GatherCodeSnippetLoop
%% and for defining user-level-macros/environments using \UD@GatherCodeSnippetLoop
%% via \begingroup<catcode-settings>\@firstofone{\endgroup<Definitions>}-trick:
%%===============================================================================
\begingroup
\@makeother\^^M%
\catcode`\Z=14\relax%
\@makeother\%Z
\@firstofone{Z
  \endgroupZ
  ZZ=============================================================================
  ZZ Begin of code for removing one leading and one trailing explicit
  ZZ <carriage-return>-character-token of catcode 12(other) from _verbatimized_ 
  ZZ argument
  ZZ=============================================================================
  ZZ Check whether_verbatimized_ argument has a leading explicit 
  ZZ <carriage-return>-character-token of catcode 12(other):
  ZZ-----------------------------------------------------------------------------
  ZZ \UD@CheckWhetherLeadingCarriageReturn{<Argument which is to be checked>}%
  ZZ                                      {<Tokens to be delivered in case
  ZZ                                       <argument which is to be checked>'s
  ZZ                                       1st token is an explicit <carriage-
  ZZ                                       return>-character-token of 
  ZZ                                       catcode 12(other)>}%
  ZZ                                      {<Tokens to be delivered in case
  ZZ                                       <argument which is to be checked>'s
  ZZ                                       1st token is not an explicit
  ZZ                                       <carriage-return>-character-token of 
  ZZ                                       catcode 12(other)>}%
  \newcommand\UD@CheckWhetherLeadingCarriageReturn[1]{Z
    \UD@@CheckWhetherLeadingCarriageReturn\UD@SelDom#1\UD@SelDom^^M\UD@@SelDomZ
  }Z
  \@ifdefinable\UD@@CheckWhetherLeadingCarriageReturn{Z
    \long\def\UD@@CheckWhetherLeadingCarriageReturn#1\UD@SelDom^^M#2\UD@@SelDom{Z
      \UD@CheckWhetherNull{#2}{\@secondoftwo}{\@firstoftwo}Z
    }Z
  }Z
  ZZ-----------------------------------------------------------------------------
  ZZ Check whether_verbatimized_ argument having a leading explicit <carriage-
  ZZ return>-character-token of catcode 12(other) consists only of such tokens:
  ZZ-----------------------------------------------------------------------------
  ZZ \UD@CheckWhetherOnlyCarriageReturn{<Argument which is to be checked and 
  ZZ                                    which is known to have a leading explicit
  ZZ                                    <carriage-return>-character-token of
  ZZ                                    catcode 12(other)>}%
  ZZ                                   {<Tokens to be delivered in case <argument
  ZZ                                    which is to be checked> consists only
  ZZ                                    of explicit <carriage-return>-character-
  ZZ                                    tokens of catcode 12(other)>}%
  ZZ                                   {<Tokens to be delivered in case <argument
  ZZ                                    which is to be checked> does not consist
  ZZ                                    only of explicit <carriage-return>-
  ZZ                                    character-tokens of catcode 12(other)>}%
  \newcommand\UD@CheckWhetherOnlyCarriageReturn[1]{Z
    \UD@CheckWhetherLeadingCarriageReturn{#1}{Z
      \expandafter\UD@CheckWhetherOnlyCarriageReturnZ
      \expandafter{\UD@@TrimLeadingCarriageReturn#1}Z
    }{Z
      \UD@CheckWhetherNull{#1}{\@firstoftwo}{\@secondoftwo}Z
    }Z
  }Z
  ZZ-----------------------------------------------------------------------------
  ZZ Remove one leading explicit <carriage-return>-character-token of 
  ZZ catcode 12(other) from _verbatimized_ argument:
  ZZ-----------------------------------------------------------------------------
  \@ifdefinable\UD@@TrimLeadingCarriageReturn{Z
    \long\def\UD@@TrimLeadingCarriageReturn^^M{}Z
  }Z
  ZZ-----------------------------------------------------------------------------
  ZZ Check whether_verbatimized_ argument has a trailing explicit 
  ZZ <carriage-return>-character-token of catcode 12(other):
  ZZ-----------------------------------------------------------------------------
  ZZ \UD@CheckWhetherTrailingCarriageReturn{<Argument which is to be checked>}%
  ZZ                                      {<Tokens to be delivered in case
  ZZ                                       <argument which is to be checked>'s
  ZZ                                       last token is an explicit <carriage-
  ZZ                                       return>-character-token of 
  ZZ                                       catcode 12(other)>}%
  ZZ                                      {<Tokens to be delivered in case
  ZZ                                       <argument which is to be checked>'s
  ZZ                                       last token is not an explicit
  ZZ                                       <carriage-return>-character-token of 
  ZZ                                       catcode 12(other)>}%
  \newcommand\UD@CheckWhetherTrailingCarriageReturn[1]{Z
    \UD@@CheckWhetherTrailingCarriageReturn#1\UD@SelDom^^M\UD@SelDom\UD@@SelDomZ
  }Z
  \@ifdefinable\UD@@CheckWhetherTrailingCarriageReturn{Z
    \long\def\UD@@CheckWhetherTrailingCarriageReturn#1^^M\UD@SelDom#2\UD@@SelDom{Z
      \UD@CheckWhetherNull{#2}{\@secondoftwo}{\@firstoftwo}Z
    }Z
  }Z
  ZZ-----------------------------------------------------------------------------
  ZZ Remove one trailing explicit <carriage-return>-character-token of 
  ZZ catcode 12(other) from _verbatimized_ argument:
  ZZ-----------------------------------------------------------------------------
  \newcommand\UD@TrimTrailingCarriageReturn[1]{Z
     \UD@@TrimTrailingCarriageReturn#1\UD@SelDomZ
  }Z
  \@ifdefinable\UD@@TrimTrailingCarriageReturn{Z
    \long\def\UD@@TrimTrailingCarriageReturn#1^^M\UD@SelDom{#1}Z
  }Z
  ZZ-----------------------------------------------------------------------------
  ZZ Remove one leading and one trailing explicit <carriage-return>-character-
  ZZ token of catcode 12(other) from _verbatimized_ argument if present.
  ZZ In the edge case of the _verbatimized_ argument consisting only of explicit
  ZZ <carriage-return>-character-tokens of catcode 12(other) remove only one of 
  ZZ them.
  ZZ Due to \romannumeral-expansion the result is delivered in 2 expansion-steps:
  ZZ-----------------------------------------------------------------------------
  \newcommand\UD@RemoveLeadingNTrailingCarriageReturn[1]{Z
    \romannumeralZ
    \UD@CheckWhetherLeadingCarriageReturn{#1}{Z
      \UD@CheckWhetherTrailingCarriageReturn{#1}{Z
         \UD@CheckWhetherOnlyCarriageReturn{#1}{Z
           \expandafter\UD@stopromannumeral\UD@@TrimLeadingCarriageReturn#1Z
         }{Z
           \expandafter\expandafter\expandafter\expandafter\expandafterZ
           \expandafter\expandafter\UD@stopromannumeral\expandafterZ
           \UD@TrimTrailingCarriageReturn\expandafter{\UD@@TrimLeadingCarriageReturn#1}Z
         }Z
      }{Z
        \expandafter\UD@stopromannumeral\UD@@TrimLeadingCarriageReturn#1Z
      }Z
    }{Z
      \UD@CheckWhetherTrailingCarriageReturn{#1}{Z
        \expandafter\expandafter\expandafter\UD@stopromannumeralZ
        \UD@TrimTrailingCarriageReturn{#1}Z
      }{\UD@stopromannumeral#1}Z
    }Z
  }Z
  ZZ=============================================================================
  ZZ End of code for removing one leading and one trailing explicit
  ZZ <carriage-return>-character-token of catcode 12(other) from _verbatimized_ 
  ZZ argument
  ZZ=============================================================================
  ZZ Begin of code for replacing explicit <carriage-return>-character-token of 
  ZZ catcode 12(other)(^^M) by explicit <line-feed>-character-token of catcode
  ZZ 12(other) (^^J).
  ZZ In TeX ^^M usually is the endline-character appended at the ends of lines
  ZZ of .tex-input when pre-processing them (by TeX's eyes) right before
  ZZ tokenization.
  ZZ In TeX ^^J usually is the newline-character which is not written but yields
  ZZ a transition from one line to the next line when writing to file/screen, 
  ZZ e.g., while \scantokens does its fake-writing.
  ZZ=============================================================================
  \newcommand\UD@ReplaceCarriageReturnByLineFeed[1]{Z
    \romannumeral\@UDReplaceCarriageReturnByLineFeed{^^J}#1^^M\relax{}Z
  }Z
  \@ifdefinable\@UDReplaceCarriageReturnByLineFeed{Z
    \long\def\@UDReplaceCarriageReturnByLineFeed#1#2^^M#3\relax#4#5{Z
      \UD@CheckWhetherNull{#3}Z
        {\UD@stopromannumeral#5{#4#2}}Z
        {\@UDReplaceCarriageReturnByLineFeed{#1}#3\relax{#4#2#1}{#5}}Z
    }Z
  }Z
  ZZ=============================================================================
  ZZ End of code for replacing explicit <carriage-return>-character-token of 
  ZZ catcode 12(other)(^^M) by explicit <line-feed>-character-token of catcode
  ZZ 12(other) (^^J).
  ZZ=============================================================================
  ZZ Begin of code for environment DefineCodeSnippet
  ZZ=============================================================================
  \newenvironment{DefineCodeSnippet}[1]{Z
    \UD@GatherCodeSnippetLoop{}{}Z
                             {\CsNameToCsToken\endgroup\newcommand*{CodeSnippet#1}}Z
  }{}Z
  ZZ=============================================================================
  ZZ End of code for environment DefineCodeSnippet
  ZZ=============================================================================
  ZZ=============================================================================
  ZZ Begin of code for environment RedefineCodeSnippet
  ZZ=============================================================================
  \newenvironment{RedefineCodeSnippet}[1]{Z
    \UD@GatherCodeSnippetLoop{}{}Z
                             {\CsNameToCsToken\endgroup\renewcommand*{CodeSnippet#1}}Z
  }{}Z
  ZZ=============================================================================
  ZZ End of code for environment RedefineCodeSnippet
  ZZ=============================================================================
  ZZ=============================================================================
  ZZ Begin of code for environment AppendToCodeSnippet
  ZZ=============================================================================
  \newenvironment{AppendToCodeSnippet}[1]{Z
    \UD@GatherCodeSnippetLoop{}{}{\endgroup\UD@AppendToCodeSnippet{#1}}Z
  }{}Z
  \newcommand\UD@AppendToCodeSnippet[2]{Z
    \@ifundefined{CodeSnippet#1}{Z
      \@latex@error{CodeSnippet \string#1 undefined}\@ehcZ
    }{Z
      \CsNameToCsToken\expandafter\renewcommand\expandafter*\expandafter{CodeSnippet#1}Z
      \expandafter{\romannumeral\CsNameToCsToken\expandafter\UD@stopromannumeral{CodeSnippet#1}^^M#2}Z
    }Z
  }Z
  ZZ=============================================================================
  ZZ End of code for environment AppendToCodeSnippet
  ZZ=============================================================================
  ZZ=============================================================================
  ZZ Begin of code for environment PrependToCodeSnippet
  ZZ=============================================================================
  \newenvironment{PrependToCodeSnippet}[1]{Z
    \UD@GatherCodeSnippetLoop{}{}Z
                             {\endgroup\UD@PrependToCodeSnippet{#1}}Z
  }{}Z
  \newcommand\UD@PrependToCodeSnippet[2]{Z
    \@ifundefined{CodeSnippet#1}{Z
      \@latex@error{CodeSnippet \string#1 undefined}\@ehcZ
    }{Z
      \CsNameToCsTokenZ
      \expandafter\renewcommand\expandafter*\expandafter{CodeSnippet#1}Z
      \expandafter{Z
        \romannumeral\expandafter\UD@exchange\expandafter{Z
          \romannumeral\CsNameToCsToken\expandafter\UD@stopromannumeral{CodeSnippet#1}Z
        }{\UD@stopromannumeral#2^^M}Z
      }Z
    }Z
  }Z
  ZZ=============================================================================
  ZZ End of code for environment PrependToCodeSnippet
  ZZ=============================================================================
  \newcommand\ExecuteCodeSnippet[1]{Z
    \expandafter\UD@ReplaceCarriageReturnByLineFeed\expandafter{Z
       \romannumeral\CsNameToCsToken\expandafter\UD@stopromannumeral{CodeSnippet#1}%Z
     }{\scantokens}Z
  }Z
  \newcommand\DeliverCodeSnippetToMacro[2]{Z
    \expandafter\UD@exchange\expandafter{\expandafter{\romannumeralZ
       \CsNameToCsToken\expandafter\UD@stopromannumeral{CodeSnippet#1}Z
    }}{#2}Z
  }Z
  \newcommand\DeliverCodeSnippetToTokens[2]{Z
    \DeliverCodeSnippetBetweenTokens{#1}{#2}{}Z
  }Z
  \newcommand\DeliverCodeSnippetBetweenTokens[3]{Z
    \expandafter\UD@exchange\expandafter{\romannumeralZ
       \CsNameToCsToken\expandafter\UD@stopromannumeral{CodeSnippet#1}Z
    }{#2}#3Z
  }Z
  \newcommand\ShowCodeSnippet[1]{Z
     \CsNameToCsToken\show{CodeSnippet#1}Z
  }Z
  \newcommand\MeaningCodeSnippet[1]{Z
     \CsNameToCsToken\meaning{CodeSnippet#1}Z
  }Z
  \newcommand\ConcatCodeSnippets{Z
    \@ifstar{\ConcatCodeSnippetsAtStarNoStar{\def}}Z
            {\ConcatCodeSnippetsAtStarNoStar{\newcommand*}}Z
  }Z
  \newcommand\ConcatCodeSnippetsAtStarNoStar[4]{Z
    \@ifundefined{CodeSnippet#2}{Z
      \@latex@error{CodeSnippet \string#2 undefined}\@ehcZ
      \@ifundefined{CodeSnippet#3}{Z
        \@latex@error{CodeSnippet \string#3 undefined}\@ehcZ
      }{Z
         \expandafter\UD@exchange\expandafter{\expandafter{Z
           \romannumeral\CsNameToCsToken\expandafter\UD@stopromannumeral{CodeSnippet#3}Z
         }}{\CsNameToCsToken#1{CodeSnippet#4}}Z
      }Z
    }{Z
      \@ifundefined{CodeSnippet#3}{Z
        \@latex@error{CodeSnippet \string#3 undefined}\@ehcZ
        \expandafter\UD@exchange\expandafter{\expandafter{Z
          \romannumeral\CsNameToCsToken\expandafter\UD@stopromannumeral{CodeSnippet#2}Z
        }}{\CsNameToCsToken#1{CodeSnippet#4}}Z
      }{Z
        \expandafter\UD@exchange\expandafter{\expandafter{Z
          \romannumeralZ
          \expandafter\UD@exchange\expandafter{Z
            \romannumeral\CsNameToCsToken\expandafter\UD@stopromannumeral{CodeSnippet#3}Z
          }{Z
            \CsNameToCsToken\expandafter\UD@stopromannumeral{CodeSnippet#2}^^MZ
          }Z
        }}Z
        {\CsNameToCsToken#1{CodeSnippet#4}}Z
      }Z
    }Z
  }Z
}%
%%///////////// End of code for interface \UD@GatherCodeSnippetLoop and ////////
%%///////////// environment DefineCodeSnippet and all suplementary things //////
\makeatother

\documentclass[a4paper]{article}
\pagestyle{empty}
\addtolength\textwidth{7cm}
\addtolength\evensidemargin{-3.5cm}
\addtolength\oddsidemargin{-3.5cm}
\enlargethispage{4cm}
\partopsep=0ex
\topsep=0ex

\begin{document}
\vspace*{-4.75cm}%
\begingroup
\catcode`\^^M=12\relax%
\catcode`\^^J=12\relax%
\leftmargini=\labelsep%
\settowidth\labelwidth{\texttt{\string^^M}}%
\advance\leftmargini\labelwidth%
\begin{itemize}%
\item[\texttt{\string^^M}]in cmtt-font denotes the \textit{\textlangle carriage-return\textrangle}-character %
(code-point-number 13 both in ASCII and in Unicode, \verb|^^M| in TeX's \verb|^^|\hbox{-}notation, ``M'' being %
the \(13^{\hbox{\tiny th}}\) letter of the uppercase-alphabet).\par%
\item[\texttt{\string^^J}] in cmtt-font denotes the \textit{\textlangle line-feed\textrangle}-character %
(code-point-number 10 both in ASCII and in Unicode, \verb|^^J| in TeX's \verb|^^|\hbox{-}notation, ``J'' being %
the \(10^{\hbox{\tiny th}}\) letter of the uppercase-alphabet).%
\end{itemize}%
\endgroup 

\hrule\vfill

\noindent Define a code-snippet \verb|foobar1|:\vfill

\begin{verbatim}
\begin{DefineCodeSnippet}{foobar1}
Text Text    Text
Text
\end{verbatim*}
\end{DefineCodeSnippet}
\end{verbatim}

\begin{DefineCodeSnippet}{foobar1}
Text Text    Text
Text
\end{verbatim*}
\end{DefineCodeSnippet}

\vfill\hrule\vfill

\noindent\verb|\texttt{\MeaningCodeSnippet{foobar1}| yields:\vfill

\noindent\texttt{\MeaningCodeSnippet{foobar1}}%

\vfill\hrule\vfill

\noindent Define a code-snippet \verb|foobar2| with two empty lines:\vfill

\begin{verbatim}
\begin{DefineCodeSnippet}{foobar2}


\end{DefineCodeSnippet}
\end{verbatim}

\begin{DefineCodeSnippet}{foobar2}


\end{DefineCodeSnippet}

\vfill\hrule\vfill

\noindent\verb|\texttt{\MeaningCodeSnippet{foobar2}| yields:\vfill

\noindent\texttt{\MeaningCodeSnippet{foobar2}}%

\vfill\hrule\vfill

\noindent Prepend to code-snippet \verb|foobar1|:\vfill

\begin{verbatim}
\begin{PrependToCodeSnippet}{foobar1}
\begin{verbatim*}
\end{PrependToCodeSnippet}
\end{verbatim}

\begin{PrependToCodeSnippet}{foobar1}
\begin{verbatim*}
\end{PrependToCodeSnippet}

\vfill\hrule\vfill

\noindent Append to code-snippet \verb|foobar1|:\vfill

\begin{verbatim}
\begin{AppendToCodeSnippet}{foobar1}

\noindent\TeX\space is {\huge funny}%
\end{AppendToCodeSnippet}
\end{verbatim}

\begin{AppendToCodeSnippet}{foobar1}

\noindent\TeX\space is {\huge funny}%
\end{AppendToCodeSnippet}

\vfill\hrule\vfill

\noindent\verb|\texttt{\MeaningCodeSnippet{foobar1}| yields:\vfill

\noindent\texttt{\MeaningCodeSnippet{foobar1}}%

\vfill\hrule\vfill

\noindent This yields console-output:\vfill

\noindent\verb|\ShowCodeSnippet{foobar1}|%
\ShowCodeSnippet{foobar1}%

\vfill\hrule\vfill

\noindent\verb|\ExecuteCodeSnippet{foobar1}| yields:\vfill

\ExecuteCodeSnippet{foobar1}%

\vfill\hrule\vfill

\noindent\verb|\DeliverCodeSnippetToTokens{foobar1}{\begin{verbatim}}\end{verbatim}| yields:\vfill

\noindent\DeliverCodeSnippetToTokens{foobar1}{\begin{verbatim}}\end{verbatim}%

\vfill\hrule\vfill

\noindent\verb|\DeliverCodeSnippetToMacro{foobar1}{\texttt}| yields:\vfill

\noindent\DeliverCodeSnippetToMacro{foobar1}{\texttt}%

\vfill\hrule\vfill

\noindent Concatenate code-snippets \verb|foobar1| and \verb|foobar1| as code-snippet \verb|foobarDouble|:\vfill

\noindent\verb|\ConcatCodeSnippets{foobar1}{foobar1}{foobarDouble}|%
\ConcatCodeSnippets{foobar1}{foobar1}{foobarDouble}%

\vfill\hrule\vfill

\noindent This yields console-output:\vfill

\noindent \verb|\ShowCodeSnippet{foobarDouble}|%
\ShowCodeSnippet{foobarDouble}%

\vfill\hrule\vfill

\noindent\verb|\ExecuteCodeSnippet{foobarDouble}| yields:\vfill

\ExecuteCodeSnippet{foobarDouble}%

\vfill

\end{document}

pdf 输出:

在此处输入图片描述

控制台输出:

This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2020-10-01> patch level 4
L3 programming layer <2021-02-18>
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/article.cls
Document Class: article 2020/04/10 v1.4m Standard LaTeX document class
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/size10.clo))
(/usr/local/texlive/2020/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def)
(./test.aux)
Overfull \hbox (3.08784pt too wide) in paragraph at lines 437--440
[]\OT1/cmr/m/n/10 in cmtt-font de-notes the \TS1/cmr/m/it/10 <\OT1/cmr/m/it/10 
line-feed\TS1/cmr/m/it/10 >\OT1/cmr/m/n/10 -character (code-point-number 10 bot
h in ASCII and in Uni-code, [][]\OT1/cmtt/m/n/10 ^^J []\OT1/cmr/m/n/10 in TeX's
 [][]\OT1/cmtt/m/n/10 ^^[][]\OT1/cmr/m/n/10 notation,
> \CodeSnippetfoobar1=macro:
->\begin{verbatim*}^^MText Text    Text^^MText^^M\end{verbatim*}^^M^^M\noindent
\TeX\space is {\huge funny}%.
<argument> \CodeSnippetfoobar1 
                               
l.530 \ShowCodeSnippet{foobar1}
                               %
? 
> \CodeSnippetfoobarDouble=macro:
->\begin{verbatim*}^^MText Text    Text^^MText^^M\end{verbatim*}^^M^^M\noindent
\TeX\space is {\huge funny}%^^M\begin{verbatim*}^^MText Text    Text^^MText^^M\
end{verbatim*}^^M^^M\noindent\TeX\space is {\huge funny}%.
<argument> \CodeSnippetfoobarDouble 
                                    
l.562 \ShowCodeSnippet{foobarDouble}
                                    %
? 
[1{/usr/local/texlive/2020/texmf-var/fonts/map/pdftex/updmap/pdftex.map}]
(./test.aux) ){/usr/local/texlive/2020/texmf-dist/fonts/enc/dvips/cm-super/cm-s
uper-ts1.enc}</usr/local/texlive/2020/texmf-dist/fonts/type1/public/amsfonts/cm
/cmr10.pfb></usr/local/texlive/2020/texmf-dist/fonts/type1/public/amsfonts/cm/c
mr17.pfb></usr/local/texlive/2020/texmf-dist/fonts/type1/public/amsfonts/cm/cmr
5.pfb></usr/local/texlive/2020/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10
.pfb></usr/local/texlive/2020/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt10.
pfb></usr/local/texlive/2020/texmf-dist/fonts/type1/public/cm-super/sfti1000.pf
b>
Output written on test.pdf (1 page, 71882 bytes).
Transcript written on test.log.

答案2


由于答案的字符数限制为 30,000 个,我需要将答案分成两部分。

这是我的回答的第一部分。

我的回答第一部分包含 TeX 工作原理以及第 2 部分中提供的代码/工作示例的一般解释。

我的回答第二部分保存工作示例。


您使用短语“原始内容”,但没有明确说明其含义/表示什么。

我可以提供一个通用接口,该\UD@GatherCodeSnippetLoop接口切换到 verbatim-catcode-régime(特殊字符⟨space-character⟩\{}$&#^_、的类别代码切换到 12(其他))并逐个字符地从 .tex 输入文件中读取并标记和累积事物,直到遇到字符序列,应用于每个累积的非空格字符以将所有内容(例如,由于包 inputenc 通常处于活动状态的字符)转换为 catcode 12(其他)。 (仅适用于非空格字符标记的原因如下:由于切换到 verbatim-catcode-régime,空格字符已经具有类别代码 12。应用会将它们转换为类别代码 10(空格)的显式空格标记,因为空格/空格标记是唯一的例外:将除空格之外的所有内容转换为类别代码 12(其他)并将每个空格转换为类别代码 10(空格)。)%~\end{⟨expansion of \@currenvir⟩}\string\string\string\string\string

\UD@GatherCodeSnippetLoop可用于定义具有不同行为的不同环境,其共同点是环境主体从 .tex 输入文件中收集,作为类别代码 12(其他)的显式字符标记序列。

这样的环境可用于将类别代码 12(其他)的显式字符标记序列存储为宏。

扩展这些宏之后,通过处理类别代码 12(其他)的显式字符标记序列,其\scantokens结果与直接通过 TeX 的眼睛/嘴巴/食道/胃等处理构成环境主体的字符相同,而无需切换 catcode-régime 和保存事物的操作 - 除非在执行此类环境时有效的 catcode-régime 与执行宏时/在\scantokens' 重新标记构成环境主体的单个字符时有效的 catcode-régime 不同。

在第 2 部分的示例中,\UD@GatherCodeSnippetLoop定义了通用接口,并定义了基于的环境、、、。DefineCodeSnippet此外,还定义了用户级宏 、、、RedefineCodeSnippet。所有环境和用户级宏还处理一些表示代码片段 ID 的参数。PrependToCodeSnippetAppendToCodeSnippet\UD@GatherCodeSnippetLoop\ConcatCodeSnippets\ExecuteCodeSnippet\DeliverCodeSnippetToMacro\DeliverCodeSnippetToTokens


\begin{DefineCodeSnippet}{⟨ID⟩}
Text Text    Text
Text
\end{DefineCodeSnippet}

从 .tex 输入文件中读取环境主体作为显式 catcode-12(other)-character-tokens 序列,并定义宏以通过顶层扩展生成该 catcode-12(other)-character-tokens 序列。\CodeSnippet⟨ID⟩


\begin{RedefineCodeSnippet}{⟨ID⟩}
Text Text    Text
Text
\end{RedefineCodeSnippet}

相应地重新定义。\CodeSnippet⟨ID⟩


\begin{PrependToCodeSnippet}{⟨ID⟩}
Text Text    Text
Text
\end{PrependToCodeSnippet}

在 前面添加几行。\CodeSnippet⟨ID⟩


\begin{AppendToCodeSnippet}{⟨ID⟩}
Text Text    Text
Text
\end{AppendToCodeSnippet}

将行附加到。\CodeSnippet⟨ID⟩


\ConcatCodeSnippets{⟨ID of source snippet 1⟩}{⟨ID of source snippet 2⟩}{⟨target ID⟩}

定义为:\CodeSnippet⟨target ID⟩

⟨sequence of explicit catcode-12(other)-character-tokens denoted by ID of source snippet 1⟩
⟨explicit ⟨carriage-return⟩-character-token of category code 12 (other)⟩
⟨sequence of explicit catcode-12(other)-character-tokens denoted by ID of source snippet 2⟩

带星号的变体\ConcatCodeSnippets*允许覆盖/重新定义。\CodeSnippet⟨target ID⟩


\ExecuteCodeSnippet{⟨ID⟩}

\scantokens\expandafter{\CodeSnippet⟨ID⟩%}

,尾随%的 catcode 也是 12(其他),以便它可以通过\scantokens' 假书写 毫无问题地写入,并可以通过\scantokens' 在其通常的 catcode 14(注释)下读回识别,这样它就可以防止由于 TeX 插入结束符而产生虚假标记。

在将东西传递给\scantokens ⟨回车⟩-字符被替换为⟨换行⟩-人物。

为了理解后者的原因,您需要了解 TeX 工作原理的一些细节:

当 TeX 读取 .tex 输入文件中的一行并对其进行预处理时,在标记化之前,会附加一个字符,该字符在 TeX 的内部字符编码方案中的代码点编号(在传统 TeX 中为 ASCII,在 LuaTeX/XeTeX 中为 unicode)等于整数参数 的值\endlinechar
通常 的值为\endlinechar13,而代码点编号 13 表示⟨回车⟩-字符在 ASCII 和 Unicode 中都是如此。
因此通常⟨回车⟩-character 在读取/预处理一行 .tex 输入时附加。
在 TeX 的^^-notation中⟨回车⟩-字符也可以表示为^^MM 是大写字母中的第 13 个字母。

当 TeX 将未展开的显式字符标记写入外部文本文件或屏幕时,TeX 会对每个显式字符标记检查其字符代码(= TeX 内部字符编码方案中相关字符的代码点编号)是否等于整数参数的值\newlinechar
如果是,则不写入相应的字符,但将相关字符标记视为信号,以在外部文本文件或屏幕上创建从当前行到下一行的转换。
结果取决于平台。 在文本文件中,根据平台的不同,⟨回车⟩-字符或⟨换行⟩-字符或两者的组合用于表示从文本的一行到文本的下一行的过渡。
通常的值为\newlinechar10,而代码点号 10 表示⟨换行⟩-字符在 ASCII 和 Unicode 中都是如此。
因此在编写代码时通常⟨换行⟩-character-tokens 是用于创建从文本文件的一行到该文本文件的下一行的转换的信号。
在 TeX 的^^-notation中⟨换行⟩-字符也可以表示为^^JJ 是大写字母中的第 10 个字母。

\scantokens处理一组已标记化的标记如下:
首先 TeX 假装将这些标记未展开写入外部文本文件。
然后 TeX 使用该伪造的外部文件作为 .tex 输入源(就像您已应用 -primitive\input切换到另一个 .tex 输入源一样)并从执行时当前的 catcode 机制中读取/预处理/标记化/摘要它\scantokens

在我们的案例中,已经标记化的标记集在逐字标记码制度下进行标记,因此只会产生(非特殊)显式字符标记,并且每个字符标记都会转换为类别代码 12(其他)。这意味着⟨回车⟩-字符,由于上述\endlinechar机制,在预处理阶段被插入到行尾,被标记为类别代码 12(其他)和字符代码 13 的(非特殊)显式字符标记。
通常 TeX 会将此类标记写入文件中^^文件,即,而不是写入以下某种组合 ⟨回车⟩-字符和/或⟨换行⟩-字符或其他,字符序列^, ^,M会被写入,随后写入的字符也会出现在同一行中。
如果你想让\scantokens' fake-writing 创建一个从一行到下一行的过渡,你可以\newlinechar在应用 , 之前分配值 13 \scantokens,或者记住\newlinechar-parameter 的值通常表示⟨换行⟩-字符(10/^^J ),你可以让 TeX 替换每个显式的 catcode-12(other)-⟨回车⟩-character-token 由明确的 catcode-12(其他)-⟨换行⟩-character-token 在应用 之前\scantokens
我更喜欢后者,因为它不需要在\scantokens执行 时更改 TeX 的任何参数,在极端情况下可能会导致行为偏离被认为是“通常的事情”。
(如果你让 的假写\scantokens只写序列^^M而不是让假写程序从一行到下一行创建过渡,那么在正常的 catcode 机制下输入/读取/标记/消化/处理假文件将是一个问题:^^M将被当作^^M,即^^的 -notation ⟨回车⟩-character。在正常的 catcode-régime 下⟨回车⟩-character 的类别代码为 5(行尾),这意味着它会导致 TeX

  1. (!!!)停止处理该行,这意味着在同一行中通过' 假书写在字符序列^,后面^的每个字符都将丢失(!!!),并且M\santokens

  2. 根据读取装置的当前状态,将没有任何标记附加到流经 TeX 通道的标记流中(状态 S),或者附加显式空格标记(状态 M),或者附加控制字标记\par(状态 N)。


\DeliverCodeSnippetToMacro{⟨ID⟩}{⟨tokens⟩}

交付

⟨tokens⟩{⟨expansion of \CodeSnippet⟨ID⟩⟩}

\DeliverCodeSnippetToTokens{⟨ID⟩}{⟨tokens⟩}

交付

⟨tokens⟩⟨expansion of \CodeSnippet⟨ID⟩⟩

\DeliverCodeSnippetToMacro(和之间的区别\DeliverCodeSnippetToTokens在于, further 传递⟨expansion of \CodeSnippet⟨ID⟩⟩嵌套在 catcode 1(开始组)/catcode 2(结束组)一对花括号之间的内容,而后者传递的内容没有这样的花括号对。)


随着环境的领先⟨回车⟩-字符(如果存在)和一个尾随⟨回车⟩-字符(如果存在)将从中删除⟨环境主体形成的 catcode-12 字符序列⟩在(重新)定义之前。\CodeSnippet⟨ID⟩

这边走

\begin{DefineCodeSnippet}{foobar}
Text Text    Text
Text
\end{DefineCodeSnippet}

结果是相同的

\begin{DefineCodeSnippet}{foobar}Text Text    Text
Text\end{DefineCodeSnippet}

,即:

\newcommand*\CodeSnippetfoobar{Text Text    Text⟨explicit ⟨carriage-return⟩-character-token of catcode 12(other)⟩Text}

\CodeSnippetfoobar;的每个标记⟨定义文本⟩是类别代码 12(其他)的明确字符标记。

在环境主体的边缘情况下,其由以下部分组成⟨回车⟩-字符,只有其中一个被删除,因为在这种情况下,假设有n⟨回车⟩-字符表示(n-1)个空行。(第一个⟨回车⟩-字符表示从第一行“空”到第一行“空”的过渡。)\begin{⟨environment⟩}

请注意,TeX 对 .tex-input 行的预处理在任何情况下都会删除⟨空间⟩- 出现在 .tex-input 行尾的字符。

因此你不能保留⟨空间⟩- 出现在 .tex-input 行尾的字符,可供 TeX 重新处理(例如通过\scantokens)。

环境DefineCodeSnippet等依赖于临时改变类别代码制度,并在改变的类别代码制度生效时通过读取/标记 .tex 输入文件中的内容来获取构成其主体的标记。

因此,环境DefineCodeSnippet等不能在宏定义/宏参数等中使用,因为在收集形成宏定义的标记时/在收集形成宏参数的标记时,内容已经被标记化。

答案3

您可以将内容存储在 中savebox。因此,我向您的 添加了另一个参数\calctree:ID(请注意,它需要反斜杠)。然后您调用\usebox\ID以使用该框。

\documentclass{article}
\usepackage{luacode}

\begin{luacode}
    function calctree(a)
    tex.print("-+-+")
    tex.print("")
    tex.print("$" .. a .. "$")
    tex.print("")
    tex.print("-+-+")
    end    
\end{luacode}

% This fully expands the argument
\newcommand\calctree[2]{% second arg is a macroname to store the content
    \newsavebox{#2}
    \savebox{#2}{\directlua{calctree("#1")}}}%

\newenvironment{multilines}{}{}


\begin{document}    
    \calctree{OOO}{\boxOOO}
    
    \calctree{a (b + 3) - c^2}{\boxabc}
    
    \begin{multilines}
        A
        
        B
        
        C
    \end{multilines}
    
    Bla, bla, ...
    
    % Use the content with \calctree.
    \usebox\boxOOO
    
    Blo, blo, ...
    
    % Use the content another time with \calctree.
    \usebox\boxabc
    
    \usebox\boxOOO
    
\end{document}

相关内容