将宏从 Latex2e 转换为 expl3

将宏从 Latex2e 转换为 expl3

我正在尝试在一个小的包中实现一个\Scontents以两种不同方式运行的命令:

\Scontents*[opt]<del>argument<del>
\Scontents[opt] { argument }

有了星号,它的行为\verb就如同接收限定参数,或者{...}说它们是平衡的。

如果没有星号,它的行为就像使用 的常规命令一样{...}

在这两种情况下,argument都会按顺序保存,然后使用。该命令完全符合我的要求,问题在于,为了实现它,我使用了 Ulrich Diez 提供的一个漂亮的宏(https://tex.stackexchange.com/a/472044/7832) 并详细解释了它的工作原理,但是,我想知道您是否可以迁移到更具expl3风格的代码,该代码非常obfuscated适合我的水平。这是示例文件:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\keys_define:nn { scontents }
  {
    store-cmd .tl_set:N  = \l__scontents_name_seq_cmd_tl,
    store-cmd .initial:n = contents,
  }
\cs_new_protected:Npn \__scontents_append_contents:nn #1#2
  {
    \seq_if_exist:cF { g__scontents_seq_name_#1_seq }
      { \seq_new:c { g__scontents_seq_name_#1_seq } }
    \seq_gput_right:cn { g__scontents_seq_name_#1_seq } {#2}
  }
\cs_new:Npn \__scontents_getfrom_seq:nn #1#2
  { \seq_item:cn { g__scontents_seq_name_#2_seq } {#1} }
\ExplSyntaxOff
% From Ulrich Diez https://tex.stackexchange.com/a/472044/7832
\makeatletter
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\expandafter\expandafter\@firstoftwo{ }{}%
  \@secondoftwo}{\expandafter\expandafter\@firstoftwo{ }{}\@firstoftwo}%
}%
\begingroup
\catcode`\^^M=12 %
\@firstofone{%
  \endgroup%
  \newcommand\UDEndlreplace[2]{\romannumeral0\@UDEndlreplace{#2}#1^^M\relax{}}%
  \newcommand*\@UDEndlreplace{}%
  \long\def\@UDEndlreplace#1#2^^M#3\relax#4#5{%
    \UD@CheckWhetherNull{#3}%
    { #5{#4#2}}{\@UDEndlreplace{#1}#3\relax{#4#2#1}{#5}}%
  }%
}%
\newcommand\UDcollectverbarg[3]{%
  \@bsphack
  \begingroup
  \let\do\@makeother % <- this and the next line switch to
  \dospecials        %    verbatim-category-code-régime.
  \catcode`\{=1      % <- give opening curly brace the usual catcode so a
                     %    curly-brace-balanced argument can be gathered in
                     %    case of the first thing of the verbatimized-argument
                     %    being a curly opening brace.
  \catcode`\ =10     % <- give space the usual catcode so \UD@collectverbarg
                     %    cannot catch a space as its 4th undelimited argument.
                     %    (Its 4th undelimited argument denotes the verbatim-
                     %     syntax-delimiter in case of not gathering a
                     %     curly-brace-nested argument.)
  \kernel@ifnextchar\bgroup
  {% seems a curly-brace-nested argument is to be caught:
    \catcode`\}=2    % <- give closing curly brace the usual catcode also.
    \UD@collectverbarg{#1}{#2}{#3}{}%
  }{% seems an argument with verbatim-syntax-delimiter is to be caught:
    \do\{ % <- give opening curly brace the verbatim-catcode again.
    \UD@collectverbarg{#1}{#2}{#3}%
  }%
}%
\newcommand\UD@collectverbarg[4]{%
  \do\ %             % <- Now that \UD@collectverbarg has the delimiter or
                     %    emptiness in its 4th arg, give space the
                     %    verbatim-catcode again.
  \catcode`\^^M=12   % <- Give the carriage-return-character the verbatim-catcode.
  \long\def\@tempb##1#4{%
    \edef\@tempb{##1}%
    \@onelevel@sanitize\@tempb % <- Turn characters into their "12/other"-pendants.
                               %    This may be important with things like the
                               %    inputenc-package which may make characters
                               %    active/which give them catcode 13(active).
    \expandafter\UDEndlreplace\expandafter{\@tempb}{#1}{\def\@tempb}% <- this starts
                               %    the loop for replacing endline-characters.
    \expandafter\UD@@collectverbarg\expandafter{\@tempb}{#2}{#3}% <- this "spits
                               %    out the result.
  }%
  \@tempb
}%
\newcommand\UD@@collectverbarg[3]{%
  \endgroup
  \@esphack
  #2{#3{#1}}%
}%
\makeatother
\ExplSyntaxOn % Back to |expl3| programing
\cs_new_eq:Nc \__scontents_UD_firstofone: { @firstofone }
\exp_args_generate:n { Vx }
\ProvideDocumentCommand{ \Scontents }{!s O{} }
  {
    \group_begin:
    \IfNoValueF {#2} { \keys_set_known:nn { scontents } {#2} }
    \IfBooleanTF{#1}
      { \UDcollectverbarg{^^J}{\__scontents_UD_firstofone:}{\__scontents_append_vercmd:n} }
      { \__scontents_append_stdcmd:n }
  }
% No starred
\cs_new_protected:Npn \__scontents_append_stdcmd:n #1
  { 
    \exp_args:NV  \__scontents_append_contents:nn \l__scontents_name_seq_cmd_tl {#1}
    \group_end:
  }
% Starred
\cs_new_protected:Npn \__scontents_append_vercmd:n #1
  {
    \exp_args:NV \__scontents_append_contents:nn \l__scontents_name_seq_cmd_tl { \tex_scantokens:D {#1} }
    \group_end:
  }
% get
\ProvideExpandableDocumentCommand { \getstored } { O{1} m }
  { \__scontents_getfrom_seq:nn {#1} {#2} }
\ExplSyntaxOff
\begin{document}

\Scontents[store-cmd=nostarred]{Using Scontents command, 
no verbatim suport, save in seq nostarred with index $1$.}

\Scontents*[store-cmd=starred]|Using \verb+\Scontents*+ command, 
delimited by tbar, save in seq starred with index $1$.
\begin{verbatim*}
      verbatim environment
\end{verbatim*}|

\noindent\hrulefill

\getstored[1]{nostarred}\par
\getstored[1]{starred}
\end{document}

这个宏能用 ? 的形式来写吗expl3?还是最好保持原样,我的想法是尽可能保留代码expl3

问候

答案1

这是一个纯粹的expl3重新实现。最显著的变化是:

  • \Scontents现在具有参数类型O{} +v。通过使用xparse逐字参数类型v,不再需要使用星号,因为此类型会自动检测后面是否跟着括号参数或分隔符。
  • \UDcollectverbarg删除了,因为它似乎做了该v类型已经做的事情。
  • \tex_scantokens:D与序列中的逐字文本一起存储\g__scontents_seq_name_*_seq,并在查询最终序列时执行。\tex_newlinechar:D在那里也设置为 13。我认为这与宏中发生^^J的替换基本相同。^^M\UDcollectverbarg

完整代码:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\keys_define:nn { scontents }
  {
    store-cmd .tl_set:N  = \l__scontents_name_seq_cmd_tl,
    store-cmd .initial:n = contents,
  }
\cs_new_protected:Npn \__scontents_append_contents:nn #1#2
  {
    \seq_if_exist:cF { g__scontents_seq_name_#1_seq }
      { \seq_new:c { g__scontents_seq_name_#1_seq } }
    \seq_gput_right:cn { g__scontents_seq_name_#1_seq } {#2}
  }
\cs_new_protected:Npn \__scontents_getfrom_seq:nn #1#2
  {
    \seq_item:cn { g__scontents_seq_name_#2_seq } {#1}
  }

\ProvideDocumentCommand{ \Scontents }{ O{} +v }
  {
    \IfNoValueF {#1} { \keys_set_known:nn { scontents } {#1} }
    \exp_args:NV \__scontents_append_contents:nn \l__scontents_name_seq_cmd_tl
      {
        \group_begin:
        \tex_newlinechar:D = 13 \scan_stop:
        \tex_scantokens:D { #2 }
        \group_end:
      }
  }
% get
\ProvideDocumentCommand { \getstored } { O{1} m }
  { \__scontents_getfrom_seq:nn {#1} {#2} }
\ExplSyntaxOff

\begin{document}
\Scontents[store-cmd=nostarred]{Using Scontents command, 
no verbatim support, save in seq nostarred with index $1$.}

\Scontents[store-cmd=starred]|Using \verb+\Scontents*+ command, 
delimited by tbar, save in seq starred with index $1$.
\begin{verbatim*}
      verbatim environment
      with unbalanced } braces }
\end{verbatim*}|

\noindent\hrulefill

\getstored[1]{nostarred}\par
\getstored[1]{starred}
\end{document}

输出

在此处输入图片描述

相关内容