是否可以自动用其参数包装宏?(或者至少对用定义的宏执行此操作\NewDocumentCommand
,因为我猜对任何宏执行此操作可能都很困难?)我希望我的用户输入:
\wrapMyMacro{somearbitrarymacro}{some options}
\somearbitrarymacro{foo}[bar]
将其翻译成:
\cacheMe[some options]{
\somearbitrarymacro{foo}[bar]
}
我还将添加一个包含在<>
宏中的可选参数,以便输入:
\somearbitrarymacro<more options>{foo}[bar]
翻译过来就是:
\cacheMe[some options, more options]{
\somearbitrarymacro{foo}[bar]
}
可能吗?我想只要我设法将宏的选项列表获取为字符串,例如 |{foo}[bar]|,就可以获得此结果。
梅威瑟:
\documentclass[]{article}
%% My library
\ExplSyntaxOn
\NewDocumentCommand{\cacheMe}{O{}m}{
\str_set:Nn \l_tmp_str {#2}
We ~ will ~ cache ~ \texttt{\l_tmp_str} ~ with ~ options ~ \texttt{#1}.
}
\ExplSyntaxOff
%% Arbitrary macro I don't know the definition
\NewDocumentCommand{\somearbitrarymacro}{mO{}}{
My arbitrary (#2) macro says ``#1''.
}
%% User ca
\begin{document}
\somearbitrarymacro{hey alice}[options]
\cacheMe[cache options1, cache options2]{\somearbitrarymacro{hey alice}[options]}
%%% How to get this same result using:
% \wrapMyMacro{somearbitrarymacro}{cache options1}
% \somearbitrarymacro{hey alice}[options]
% \somearbitrarymacro<cache options2>{hey alice}[options]
\end{document}
编辑我以为这个解决方案有效,但实际上并非如此。值得注意的是,参数发生了奇怪的事情:如果我使用 tokenize,那么我无法使用结果pgfkeys
,而如果没有 tokenize,它会过早直接扩展参数或使哈希值翻倍……但作为参考,这是我的尝试:
我意识到这个很酷的库有这个功能,所以我从这里改编了代码https://github.com/sasozivanovic/memoize。我不知道代码是否可以改进,但现在看来是可行的。
\documentclass[]{article}
\usepackage{xparse-arglist} %% copy .sty from https://github.com/sasozivanovic/memoize/blob/master/xparse-arglist.sty
%% My library
\ExplSyntaxOn
\NewDocumentCommand{\cacheMe}{O{}m}{
\str_set:Nn \l_tmp_str {#2}
We ~ will ~ cache ~ \texttt{\l_tmp_str} ~ with ~ options ~ \texttt{#1}.
}
\ExplSyntaxOff
%% Arbitrary macro I don't know the definition
\NewDocumentCommand{\somearbitrarymacro}{mO{coucou}D<>{ahah}}{
My arbitrary (#2) macro says ``#1'' with a last argument (#3).
}
\begin{document}
\somearbitrarymacro{hey alice}[options]
\cacheMe[cache options1, cache options2]{\somearbitrarymacro{hey alice}[options]}
%%% How to get this same result using:
%% User ca
\makeatletter
%% The idea of the library is that it builds a string like
%% [#2]<#3>{#4}
%% in order to generate something like
%% \NewDocumentCommand{\myfunction}{D<>{}O{coucou}D<>{yes}m}
%% {
%% \cacheMe[#1]{\myfunction[#2]<#3>{#4}}
%% }
%% Since the very first occurrence of D is not forwarded to the function
%% we discard it:
%% The first argument of \xp@arglist@D is the number of the argument
%% so #2#####1#3 reads as #2 = <, #### = #, #1 = > #3 = value if none that can be discarded since it is already part of the argument spec.
\def\xp@arglist@D#1#2#3#4{%
\noexpand\unexpanded{%
\ifnum #1=1 % If it is the first argument D, then we do not add anything to the argument string
\else #2#####1#3\fi}\xp@arglist{#1}%
}
\NewDocumentCommand{\cacheCommand}{mom}{%
%% We get the specification of the command, like "O{}mm"
\IfNoValueTF{#2}{%
\expandafter\GetDocumentCommandArgSpec\csname #1\endcsname%
}{%
\def\ArgumentSpecification{#2}
}%
\edef\ArgumentSpecification{D<>{}\ArgumentSpecification}
\edef\mmz@marshal{%
\noexpand\DeclareDocumentCommand
\expandonce{\csname #1\endcsname}%
{\expandonce{\ArgumentSpecification}}%
{%
% todo: add a hook for users setup; prevent user from changing \MemoizeWrapper?
\edef\noexpand\mmz@marshal{%
\noexpand\noexpand\noexpand\cacheMe[#3,####1]{%
\noexpand\noexpand\expandonce{\csname #1\endcsname}%
\ArgumentList
}%
}\noexpand\mmz@marshal
}%
}\mmz@marshal
}
\makeatother
\ExplSyntaxOff
\cacheCommand{somearbitrarymacro}{global options}
\somearbitrarymacro{hey Bob}[options]
\somearbitrarymacro{Default charlie}
\somearbitrarymacro<local options>{hey alice}[INTERNAL options]<Last arg>
\end{document}
作为参考,.sty 文件包含:
\ProvidesPackage{xparse-arglist}
\RequirePackage{xparse}
\RequirePackage{etoolbox}
\def\ArgumentList{%
\expandafter\xp@arglist\expandafter0\ArgumentSpecification.%
}
\def\xp@arglist#1#2{%
\ifcsname xp@arglist@#2\endcsname
\csname xp@arglist@#2\expandafter\expandafter\expandafter\endcsname
\else
\expandafter\xp@arglist@error
\fi
\expandafter{\the\numexpr#1+1\relax}%
}
% \xp@arglist@...: #1 = the argument number
\def\xp@arglist@m#1{\noexpand\unexpanded{{#####1}}\xp@arglist{#1}}
\def\xp@arglist@r#1#2#3{\noexpand\unexpanded{#2#####1#3}\xp@arglist{#1}}
\def\xp@arglist@R#1#2#3#4{\noexpand\unexpanded{#2#####1#3}\xp@arglist{#1}}
\def\xp@arglist@v#1{{Handled commands with verbatim arguments are not
supported}\xp@arglist{#1}} % error
\def\xp@arglist@b#1{{This is not the way to handle
environment}\xp@arglist{#1}} % error
\def\xp@arglist@o#1{\noexpand\unexpanded{[#####1]}\xp@arglist{#1}}
\def\xp@arglist@d#1#2#3{\noexpand\unexpanded{#2#####1#3}\xp@arglist{#1}}
\def\xp@arglist@O#1#2{\noexpand\unexpanded{[#####1]}\xp@arglist{#1}}
\def\xp@arglist@D#1#2#3#4{\noexpand\unexpanded{#2#####1#3}\xp@arglist{#1}}
\def\xp@arglist@s#1{\noexpand\IfBooleanT{#####1}{*}\xp@arglist{#1}}
\def\xp@arglist@t#1#2{\noexpand\IfBooleanT{#####1}{#2}\xp@arglist{#1}}
\csdef{xp@arglist@+}#1{\expandafter\xp@arglist\expandafter{\the\numexpr#1-1\relax}}%
\csdef{xp@arglist@.}#1{}
% e,E: Embellishments are not supported.
% > Argument processors are not supported. And how could they be?
\def\xp@arglist@error#1.{{Unknown argument type}}
答案1
我最终设法通过将参数包装在\tl_rescan
正确解析的#
...中来避免我的问题,希望这次我得到了所有边缘情况。
\documentclass[]{article}
\usepackage{xparse-arglist} %% copy .sty from https://github.com/sasozivanovic/memoize/blob/master/xparse-arglist.sty
%% My library
\ExplSyntaxOn
\NewDocumentCommand{\cacheMe}{O{}m}{
\str_set:Nn \l_tmp_str {#2}
\str_set:Nn \l_tmp_opt_str {#1}
We ~ will ~ cache ~ \texttt{\l_tmp_str} ~ with ~ options ~ \texttt{\l_tmp_opt_str}.
}
\ExplSyntaxOff
%% Arbitrary macro I don't know the definition
\NewDocumentCommand{\somearbitrarymacro}{mO{coucou}D<>{ahah}}{
My arbitrary (#2) macro says ``#1'' with a last argument (#3).
}
\begin{document}
\newcommand{\myMacroNotDefinedWithNewDocumentCommand}[3][default value]{
Optional argument 1: #1,
Argument 2: #2,
Argument 3: #3 (\fromWhere).
}
% \somearbitrarymacro{hey alice}[options]
% \cacheMe[cache options1, cache options2]{\somearbitrarymacro{hey alice}[options]}
%%% How to get this same result using:
%% User ca
\makeatletter
%% The idea of the library is that it builds a string like
%% [#2]<#3>{#4}
%% in order to generate something like
%% \NewDocumentCommand{\myfunction}{D<>{}O{coucou}D<>{yes}m}
%% {
%% \cacheMe[#1]{\myfunction[#2]<#3>{#4}}
%% }
%% Since the very first occurrence of D is not forwarded to the function
%% we discard it:
%% The first argument of \xp@arglist@D is the number of the argument
%% so #2#####1#3 reads as #2 = <, #### = #, #1 = > #3 = value if none that can be discarded since it is already part of the argument spec.
\def\xp@arglist@D#1#2#3#4{%
\noexpand\unexpanded{%
\ifnum #1=1 % If it is the first argument D, then we do not add anything to the argument string
\else #2#####1#3\fi}\xp@arglist{#1}%
}
\ExplSyntaxOn
\cs_generate_variant:Nn \str_replace_all:Nnn { Nnx }
\cs_generate_variant:Nn \str_replace_all:Nnn { cnx }
\cs_generate_variant:Nn \str_replace_all:Nnn { cxx }
\cs_set:Nn \str_set_hash_robust:Nn {
\str_set:Nn {#1} {#2}
\str_replace_all:Nnx {#1} { ## } { \c_hash_str }
}
\cs_generate_variant:Nn \str_set_hash_robust:Nn { cn }
\cs_generate_variant:Nn \str_set_hash_robust:Nn { cx }
\cs_generate_variant:Nn\tl_rescan:nn { nv }
\NewDocumentCommand{\robExtRescanAndHalveHashes}{m}{
\str_set_hash_robust:Nn \l_robExt_tmp_str {#1}
\show\l_robExt_tmp_str
\tl_rescan:nv {}{ l_robExt_tmp_str }
}
\ExplSyntaxOff
%% After the first expansion, this will look like:
% \DeclareDocumentCommand \myMacroNotDefinedWithNewDocumentCommand {D<>{}O{default value}mm}{
% \edef \mmz@marshal {
% \noexpand \cacheMe [latex,#1]{
% \noexpand \myMacroNotDefinedWithNewDocumentCommand \unexpanded {}\unexpanded {[#2]}\unexpanded {{#3}}\unexpanded {{#4}}
% }
% }
% \show\mmz@marshal
% % <--- this will be equal after the second expansion to:
% % \cacheMe [latex,]{ \myMacroNotDefinedWithNewDocumentCommand [default value]{Second arg}{Last arg} }
% \mmz@marshal
% }
\NewDocumentCommand{\cacheCommand}{mom}{%
%% We get the specification of the command, like "O{}mm"
\IfNoValueTF{#2}{%
\expandafter\GetDocumentCommandArgSpec\csname #1\endcsname%
}{%
\def\ArgumentSpecification{#2}%
}%
\edef\ArgumentSpecification{D<>{}\ArgumentSpecification}%
\edef\mmz@marshal{%
\noexpand\DeclareDocumentCommand%
\expandonce{\csname #1\endcsname}%
{\expandonce{\ArgumentSpecification}}%
{%
% todo: add a hook for users setup; prevent user from changing \MemoizeWrapper?
\edef\noexpand\mmz@marshal{%
\noexpand\noexpand\noexpand\robExtRescanAndHalveHashes{\noexpand\noexpand\noexpand\cacheMe[\detokenize{#3}, \noexpand\detokenize{####1}]{%
\noexpand\noexpand\expandonce{\csname #1\endcsname}%
\ArgumentList%
}%
}%
}%
% For debug
%\noexpand\show\noexpand\mmz@marshal
\noexpand\mmz@marshal%
}%
}%
% for debug
%\show\mmz@marshal%
\mmz@marshal%
}
\makeatother
\ExplSyntaxOff
\cacheCommand{somearbitrarymacro}{global options}
\somearbitrarymacro{hey Bob}[options]
\somearbitrarymacro{Default charlie}
\somearbitrarymacro<local options>{hey alice}[INTERNAL options]<Last arg>
\cacheCommand{myMacroNotDefinedWithNewDocumentCommand}[O{default value}mm]{latex, \latex}
\myMacroNotDefinedWithNewDocumentCommand{Second arg}{Last arg}\\
\myMacroNotDefinedWithNewDocumentCommand<disable externalization>{second arg}{last arg}
\end{document}
作为参考,.sty 文件包含:
\ProvidesPackage{xparse-arglist}
\RequirePackage{xparse}
\RequirePackage{etoolbox}
\def\ArgumentList{%
\expandafter\xp@arglist\expandafter0\ArgumentSpecification.%
}
\def\xp@arglist#1#2{%
\ifcsname xp@arglist@#2\endcsname
\csname xp@arglist@#2\expandafter\expandafter\expandafter\endcsname
\else
\expandafter\xp@arglist@error
\fi
\expandafter{\the\numexpr#1+1\relax}%
}
% \xp@arglist@...: #1 = the argument number
\def\xp@arglist@m#1{\noexpand\unexpanded{{#####1}}\xp@arglist{#1}}
\def\xp@arglist@r#1#2#3{\noexpand\unexpanded{#2#####1#3}\xp@arglist{#1}}
\def\xp@arglist@R#1#2#3#4{\noexpand\unexpanded{#2#####1#3}\xp@arglist{#1}}
\def\xp@arglist@v#1{{Handled commands with verbatim arguments are not
supported}\xp@arglist{#1}} % error
\def\xp@arglist@b#1{{This is not the way to handle
environment}\xp@arglist{#1}} % error
\def\xp@arglist@o#1{\noexpand\unexpanded{[#####1]}\xp@arglist{#1}}
\def\xp@arglist@d#1#2#3{\noexpand\unexpanded{#2#####1#3}\xp@arglist{#1}}
\def\xp@arglist@O#1#2{\noexpand\unexpanded{[#####1]}\xp@arglist{#1}}
\def\xp@arglist@D#1#2#3#4{\noexpand\unexpanded{#2#####1#3}\xp@arglist{#1}}
\def\xp@arglist@s#1{\noexpand\IfBooleanT{#####1}{*}\xp@arglist{#1}}
\def\xp@arglist@t#1#2{\noexpand\IfBooleanT{#####1}{#2}\xp@arglist{#1}}
\csdef{xp@arglist@+}#1{\expandafter\xp@arglist\expandafter{\the\numexpr#1-1\relax}}%
\csdef{xp@arglist@.}#1{}
% e,E: Embellishments are not supported.
% > Argument processors are not supported. And how could they be?
\def\xp@arglist@error#1.{{Unknown argument type}}