在水平模式下记录对以内联代码作为参数/宏参数的函数的调用

在水平模式下记录对以内联代码作为参数/宏参数的函数的调用

在下面的代码中\Foo评估内联代码。\Baz是一个“日志版”版本,这意味着它将调用写入\Foo文件。由于“水平模式下的‘宏参数字符 #’”问题,读取文件失败(钩子 2)。通过转换##1-> #1(钩子 3),可以读取文件并生成所需的输出。有没有办法在 LaTeX 中解决这个问题?

PS: 如何使用xparse's ' 参数?(钩子 1)

    \documentclass{l3doc}
    \usepackage{xparse}
    \ExplSyntaxOn

    \iow_new:N \__erw_write_stream
    \AtEndDocument{\iow_close:N \__erw_write_stream}
    \iow_open:Nn \__erw_write_stream{erwlog}
    \cs_new_protected:Nn \__erw_write:n
    {
      \iow_now:Nn \__erw_write_stream{#1}
    }

    \NewDocumentCommand{\Foo}
    {m}
    {
      \cs_gset_protected:Nn \__erw_foo:n {#1}
      \__erw_foo:n{World}
    }
    \NewDocumentCommand{\Baz} % (journaled version of \Foo)
    {
      % s m % Hook 1.a
      o m
    }
    { 
      \Foo{#2}

      \IfValueT{#1}
      {
        (*)
    %    \__erw_write:n{\Foo{#2}} % Hook 2.a
        \__erw_write:n{\Baz{#2}} % Hook 2.b
      }

    }
    \NewDocumentCommand{\Qux} % recover calls to \Foo
    {}
    {  
      \file_input:n{erwlog}
    }

    \NewDocumentCommand\Test
    {}
    {
    %  \Baz*{Hello,~##1!}                   % Hook 1.b % Always (*)
      \Baz[]{Hello,~##1!}                   % OK if Hook 2.c commented out

      \iow_close:N \__erw_write_stream


    %  \Qux                                 % Hook 2.c 
    %ERROR: You can't use `macro parameter character #' in horizontal mode.
    %
    %--- TeX said ---
    %
    %\__erw_foo:n #1->Hello, ##
    %                          1!
    %l.1 \Baz {Hello, ##1!}
    %                      
    %--- HELP ---
    %The special character # has appeared in ordinary text. You probably
    %meant to type \#. 

    % erwlogbis:                         % Hook 3.a
    % \Baz {Hello, #1!}  
      \file_input:n{erwlogbis}

    }

    \ExplSyntaxOff

    \begin{document}

    \Test

    \end{document}

答案1

\write#在写入文件时将参数标记(类别代码为 6)加倍(参见 TeXbook 第 228 页)。我不知道是否有办法避免这种加倍,但您可以通过简单地将有趣的东西包装在临时宏定义中来应对它。当宏展开时,##其替换文本中的类别代码 6- 将变为单个#,因此该技术将执行您想要的操作。换句话说,在您的示例中,日志包含:

\begingroup \def \tmp {\Baz {Hello, ##1!}}\expandafter \endgroup \tmp

\begingroup...对\endgroup仅用于避免我们的\tmp宏造成污染。如果给定一个足够私密的名称,您可以省去它。但有趣的代码是在之后执行的\endgroup,因此用于定义的分组\tmp不应该以任何方式影响您的代码。

您的\__erw_write_stream变量应该有一个\l_\g_前缀和一个_iow后缀(参见 1。命名函数和变量界面3.pdf)。我将其重命名为\g__erw_journal_iow

关于你的问题:

如何使用xparses参数?

我启用了 的星号形式\Baz。您需要\IfBooleanTF(或\IfBooleanT等)来测试星号参数是否存在,而不是 ,\IfValueTF它用于可选参数,例如使用o参数类型声明的参数。

请注意:您的问题是更容易了解您是否直接使用了我们都知道的宏(此处:\cs_gset_protected:Nn,,\iow_now:Nn\file_input:n而不是名称晦涩的自定义包装器。这将使您的代码对我们所有人来说都更短更清晰。当然,您可以在自己的代码中使用所需的包装器,但请在您在此处提出的问题中跳过它们。好的 MWE 简短而切中要害。此外,article除非问题是特定于类的,否则最好使用类。这里,l3doc并不真正相关:所有这些都与类完全相同article

以下是我所做的更改的完整示例:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\iow_new:N \g__erw_journal_iow

\AtEndDocument { \iow_close:N \g__erw_journal_iow }

\iow_open:Nn \g__erw_journal_iow { erwlog }

\cs_new_protected:Nn \__erw_write:n
  {
    \iow_now:Nn \g__erw_journal_iow {#1}
  }

\NewDocumentCommand \Foo { m }
  {
    \cs_gset_protected:Nn \__erw_foo:n {#1}
    \__erw_foo:n { World }
  }

% Journaled version of \Foo
\NewDocumentCommand \Baz { s m }
  {
    \Foo{#2}
    \IfBooleanT {#1}
      {
        (*)
        \__erw_write:n
          { \begingroup \def \tmp { \Baz {#2} } \expandafter \endgroup \tmp }
      }
  }

% Read (execute) the journal
\NewDocumentCommand \Qux { }
  {
    \file_input:n { erwlog }
  }

\NewDocumentCommand \Test { }
  {
    % Define \__erw_foo:n, execute it and write to the journal
    \Baz* { Hello,~##1! }
    \iow_close:N \g__erw_journal_iow
    \Qux                                 % execute the journal
  }
\ExplSyntaxOff

\begin{document}

\Test

\end{document}

在此处输入图片描述

如果您有难以写入文件的标记,您可能会对感兴趣\iow_char:N。我尝试过避免#这里的重复,但显然没有。为了帮助您进一步工作,代码如下:

\cs_new_protected:Nn \__erw_write_wrapper:n
  {
    \__erw_write:n { \Baz {#1} }
  }

\cs_generate_variant:Nn \__erw_write_wrapper:n { x }

...

\NewDocumentCommand \Baz { s m }
  {
    \Foo{#2}
    \IfBooleanT {#1}
      {
        (*)
        \tl_set:Nn \l_tmpa_tl {#2}
        \regex_replace_all:nnN { \cP\# } { \c{iow_char:N} \c{##} } \l_tmpa_tl
        \__erw_write_wrapper:x { \tl_use:N \l_tmpa_tl }
      }

  }

...

\NewDocumentCommand \Test { }
  {
    \Baz* { Hello,~##1! }
  }

相关内容