自动换行宏

自动换行宏

是否可以自动用其参数包装宏?(或者至少对用定义的宏执行此操作\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}}

相关内容