将参数传递给隐藏在文本中的宏

将参数传递给隐藏在文本中的宏

语境:

我正在尝试编写一个行为类似于 Cprintf函数系列的宏。例如,以下 C 代码:

#include <stdio.h>
int main()
{
    printf("Hello %s! Today's a good day to write obscure %s macros.\n",
           "World","TeX");
}

印刷

Hello World! Today's a good day to write obscure TeX macros.

将第一个替换%s"World",将第二个%s替换为"TeX"

我正在尝试提供类似的语法:

\Printf{Hello \%! Today's a good day to write obscure \%\ macros.}
       {world,\TeX}

这应该会在 TeX 中打印相同的句子。现在我失败了,因为 TeX 不是 C:)


问题:

我找不到方法来传递第二个参数中的项目,\Printf因为\%\%可以隐藏在文本中间的任何位置。

\Printf和 的简单定义\%是:

\def\Printf#1#2{%
  #1% What do I do with #2?
}
\def\%{% How do I define this macro?
  :(%
}
\Printf{Hello \%! Today's a good day to write obscure \%\ macros.}
       {world,\TeX}
\bye

那么输出将是:在此处输入图片描述

问题是:我该如何定义\Printf\%使其打印正确的句子?


要求(因为我很挑剔:P):

  • 最强烈的要求是我希望该\Printf功能可扩展,以便:

    \edef\tempa{\Printf{Obscure \%\space Macros.}{TeX}}
    \show\tempa
    

    将打印:

    > \tempa=macro:
    ->Obscure TeX Macros..
    
  • 它必须在括号组内工作。我尝试了一种分隔宏方法,这种方法有点奏效,但如果出现\%在括号对1内,就会失败。

  • 我还希望引擎独立。

  • 另一方面,语法可以稍微改变。也就是说\Printf{text}{replacement}。我想保留这个\%东西:)


我尝试过的:

1这是我使用\%分隔宏的尝试。我更喜欢这种方法,因为我不必\%在任何地方重新定义控制字符。缺点是当\%标记出现在括号组内时,它会失败。多么大胆...

\input expl3-generic.tex
\ExplSyntaxOn
\cs_generate_variant:Nn \clist_item:nn { nf }
\cs_set_eq:NN \clistItem \clist_item:nf
\cs_new:Npn \Printf #1 #2
  { \__printf_step:nnn { 0 } { #2 } { #1 } }
\cs_new:Npn \__printf_step:nnn #1 #2 #3
  { \__printf_step:nnw { #1 } { #2 } #3 \% \q_nil }
\cs_new:Npn \__printf_step:nnw #1 #2 #3 \% #4
  {
    #3
    \quark_if_nil:NF #4
      {
        \exp_args:Nf \__printf_percent:nn
          { \int_eval:n { #1 + 1 } } { #2 }
        \exp_args:Nf \__printf_step:nnw
          { \int_eval:n { #1 + 1 } } { #2 }  #4
      }
  }
\cs_new:Npn \__printf_percent:nn #1 #2
  { \clist_item:nn { #2 } { #1 } }
\ExplSyntaxOff

\Printf{Hello \%! Today's a good day to write obscure \%\ macros.}
       {world,\TeX}

\Printf{Oh, \%, my macro doesn't work {\bf with \%\ text}. What a \%\dots}
       {drat,bold,shame}

\bye

这将打印:

在此处输入图片描述

答案1

当然,这完全是疯了,但可行!在这里,我使用与例如相同的方法\text_lowercase:n。我们逐步检查标记,检查每个标记。有三种情况,一个括号组、一个空格和一个“正常”标记。在后一种情况下,我们将\%和其他任何东西分开。唯一棘手的部分是跟踪括号组内的替换号。我通过将跟踪号保持在“顶层”并在将括号组添加到“输出”之前从任何括号组内“传回”该数字来实现这一点。

\input expl3-generic.tex
\ExplSyntaxOn
\cs_new:Npn \Printf #1#2
  {
    \exp:w \exp_not:o
      {
        \exp_after:wN \exp_end: \exp:w
        \exp_args:Nf \__printf_outer:n { \__printf:nnn { 1 } {#1} {#2} }
      }
  }
\cs_new:Npn \__printf_outer:n #1
  { \__printf_outer:nn #1 }
\cs_new:Npn \__printf_outer:nn #1#2
  {
    \exp_end:
    #1
  }
\cs_new:Npn \__printf:nnn #1#2#3
  {
    \group_align_safe_begin:
    \__printf_loop:w
      #2 \q_recursion_tail \q_recursion_stop {#3}
    \__printf_result:nn { } {#1}
  }
\cs_new:Npn \__printf_loop:w #1 \q_recursion_stop
  {
    \tl_if_head_is_N_type:nTF {#1}
      { \__printf_N_type:N }
      {
        \tl_if_head_is_group:nTF {#1}
          { \__printf_group:nw }
          { \__printf_space:w }
      }
    #1 \q_recursion_stop
  }
\cs_new:Npn \__printf_N_type:N #1
  {
    \quark_if_recursion_tail_stop_do:Nn #1
      { \__printf_end:w }
    \token_if_eq_meaning:NNTF #1 \%
      { \__printf_N_type:w }
      {
        \__printf_output:nw {#1}
        \__printf_loop:w
      }
  }
\cs_new:Npn \__printf_N_type:w #1 \q_recursion_stop #2 \__printf_result:nn #3#4
  {
    \exp_args:Nff \__printf_N_type:nnnnn
      { \clist_item:nn {#2} {#4} }
      { \int_eval:n { #4 + 1 } }
      {#1} {#2} {#3}
  }
\cs_new:Npn \__printf_N_type:nnnnn #1#2#3#4#5
   {
    \__printf_loop:w #3 \q_recursion_stop
      {#4}
      \__printf_result:nn { #5 #1 } {#2}
  }
\cs_new:Npn \__printf_group:nw #1#2 \q_recursion_stop #3 \__printf_result:nn #4#5
  {
    \exp_args:Nf \__printf_group:nnnn
      { \__printf:nnn {#5} {#1} {#3} }
      {#2} {#3} {#4}
  }
\cs_new:Npn \__printf_group:nnnn #1#2#3#4
  { \__printf_group:nnnnn #1 {#2} {#3} {#4} }
\cs_new:Npn \__printf_group:nnnnn #1#2#3#4#5
  {
    \__printf_loop:w #3 \q_recursion_stop
      {#4}
      \__printf_result:nn { #5 {#1} } {#2}
  }
\exp_last_unbraced:NNo \cs_new:Npn \__printf_space:w \c_space_tl
  {
    \__printf_output:nw { ~ }
    \__printf_loop:w
  }
\cs_new:Npn \__printf_output:nw #1#2 \__printf_result:nn #3
  { #2 \__printf_result:nn { #3 #1 } }
\cs_new:Npn \__printf_end:w #1 \__printf_result:nn
  {
    \group_align_safe_end:
  }

\ExplSyntaxOff

\Printf{Hello \%! Today's a good day to write obscure \%\ macros.}
       {world,\TeX}

\Printf{Oh, \%, my macro doesn't work {\bf with \%\ text}. What a \%\dots}
       {drat,bold,shame}

\bye

答案2

常用表达是你的朋友!你可以逐步检查clist参数,并使用\regex_replace_once:nnN在标记列表中依次替换每个参数,以生成打印的文本:

在此处输入图片描述

这种方法也更短!代码如下:

\input expl3-generic.tex

\ExplSyntaxOn
\clist_new:N \l_printf_args_clist
\tl_new:N \l_printf_tl
\cs_new:Npn \Printf #1 #2
{
  \tl_set:Nn \l_printf_tl {#1}
  \clist_set:Nn \l_printf_args_clist {#2}
  \clist_map_inline:Nn \l_printf_args_clist {
    \regex_replace_once:nnN { \c{\%} } {##1} \l_printf_tl
  }
  \tl_use:N \l_printf_tl
}
\ExplSyntaxOff

\Printf{Hello \%! Today's a good day to write obscure \%\ macros.}
       {world,\TeX}

\Printf{Oh, \%, my macro doesn't work {\bf with \%\ text}. What a \%\dots}
       {drat,bold,shame}

\bye

嗯,虽然这可能不符合可扩展性要求……

相关内容