0

In the code below \Foo evaluates inline code. \Baz is a 'journal-ed' version, meaning it writes calls to \Foo to a file. Reading the file fails (Hook 2) due to a "'macro parameter character #' in horizontal mode." issue. With the transformation ##1->#1 (Hook 3) the file can be read and produces the desired output. Is there a way to solve this problem within LaTeX?

PS: How to use xparse's 's' parameter? (Hook 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 Answer 1

1

\write doubles parameter tokens (# with category code 6) when writing to a file (cf. TeXbook p. 228). I don't know if there is a way to avoid this doubling, but you can cope with it by simply wrapping the interesting things in a temporary macro definition. When the macro is expanded, the catcode 6-## in its replacement text will become single #, thus the technique will do what you intended. In other words, in your example, the journal contains:

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

The \begingroup ... \endgroup pair is only here to avoid polluting with our \tmp macro. Given a private enough name, you could dispense with it. But the interesting code is executed after \endgroup, so the grouping used for the definition of \tmp shouldn't affect your code in any way.

Your \__erw_write_stream variable should have an \l_ or \g_ prefix and an _iow suffix (see 1. Naming functions and variables in interface3.pdf). I renamed it to \g__erw_journal_iow.

Regarding your question:

How to use xparse's s parameter?

I enabled the star form of \Baz. You need \IfBooleanTF (or \IfBooleanT, etc.) to test the presence of the star argument, not \IfValueTF which is for optional arguments such as those declared with the o argument type.

Please note: your questions would be much easier to understand if you directly used the macros we all know (here: \cs_gset_protected:Nn, \iow_now:Nn, \file_input:n) instead of obscurely-named custom wrappers. This would make your code much shorter and clearer for all of us. You can of course use the wrappers you want in your own code, but please skip them in the questions you ask here. A good MWE is short and to the point. Also, better use the article class unless the question is class-specific. Here, l3doc is not really relevant: all this works exactly the same with the article class.

Here is the full example with my changes:

\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}

enter image description here

If you have difficult tokens to write to files, you might be interested in \iow_char:N. I tried it to avoid the # doubling here, but apparently, it doesn't. Just in case it helps you for further work, the code was:

\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! }
  }
2
  • Was the second solution supposed to work? (I get the same error)
    – Erwann
    Apr 3, 2020 at 17:32
  • 1
    No, as I wrote ("I tried it to avoid the # doubling here, but apparently, it doesn't"). I only included this piece of code so that you don't waste time rewriting it to try this technique, and in case it helps you for future, related work.
    – frougon
    Apr 3, 2020 at 20:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .