我正在尝试在一个小的包中实现一个\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}
输出