在下面的代码中\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
。
关于你的问题:
如何使用
xparse
的s
参数?
我启用了 的星号形式\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! }
}