1

I'm trying to rewrite some of the codes I have by following the correct expl3 rules. With the following code I define the scontents environment that encapsulates the filecontentsdefmacro environment of the filecontentsdef package using xparse/expl3. Everything works OK, I get to record the content of scontents in a sequence and then I can access it, the theme is the way I get it. The original package has the \filecontentsexec macro to run the content recorded by the filecontentsdefmacro environment, but, I haven't been able to make this work for me and have had to copy almost directly the definition directly from the package and write it in terms of expl3. Like this:

% Expand \l_scontents_macro_tmp_tl and pass to seq
\cs_new_protected:Nn \_scontents_macro_to_seq:
  {
    \tl_put_right:Nx \l_tmpa_tl
      {
         \tex_newlinechar:D = 13
         \tex_everyeof:D = { \exp_not:N }
         \exp_not:N \scantokens \exp_after:wN { \tl_use:c { l_scontents_macro_tmp_tl } }
         \tex_newlinechar:D = 10 \scan_stop:
       }
    \tl_put_right:Nx \l_tmpb_tl
      {
        \exp_not:N \_scontents_append_contents:nn 
          { 
            \exp_not:V \l_scontents_name_seq_tl 
          } 
          { \exp_not:V \l_tmpa_tl } 
      }
    \l_tmpb_tl % add to seq
  }

The problem is that I use (and abuse) the :D argument that as far as I've read, it shouldn't be occupied, my idea was to be able to use something shorter, similar to this:

% Expand \l_scontents_macro_tmp_tl and pass to seq
\cs_new_protected:Nn \_scontents_macro_to_seq:
  {
    \_scontents_append_contents:nn { \l_scontents_name_seq_tl } 
      { 
        \filecontentsexec \exp_after:wN { \tl_use:c { l_scontents_macro_tmp_tl } }
      }
  }

This is the complete example file:

\documentclass{article}
\usepackage{filecontentsdef}[2019/04/20]
\usepackage{xparse}
\ExplSyntaxOn
\tl_new:c { l_scontents_macro_tmp_tl }

\keys_define:nn { scontents }
  {
    save-env  .tl_set:N   = \l_scontents_name_seq_tl,
    save-env  .initial:n  = contents,
    show-env  .bool_set:N = \l_scontents_show_env_tl,
    show-env  .initial:n  = false  
  }

% Adapted from https://tex.stackexchange.com/a/215571/7832
\cs_new_protected:Npn \_scontents_append_contents:nn #1 #2
  {
    \seq_if_exist:cF { g_scontents_seq_name_#1_seq }
      {
        \seq_new:c { g_scontents_seq_name_#1_seq }
      }
    \seq_gput_right:cn { g_scontents_seq_name_#1_seq } { #2 }
  }

\cs_new_protected:Npn \_scontents_getfrom_seq:nn #1 #2
  {
    \seq_item:cn { g_scontents_seq_name_#2_seq } { #1 }
  }

\ProvideExpandableDocumentCommand{\getstored}{ O{1} m }
  {
    \_scontents_getfrom_seq:nn { #1 } { #2 }
  }

% Define environment ( wrap \filecontentsdefmacro ) 
% Adapted from https://tex.stackexchange.com/a/487746/7832
\ProvideDocumentEnvironment{ scontents }{}
  {
    \char_set_catcode_active:N \^^M
    \scontents_start_environment:w
  }
  {
    \scontents_stop_environment:
    \scontents_atend_environment:
  }

% Delaying [key=val] for environment
\cs_new_protected:Npn \scontents_environment_keys:w [#1]
  {
    \keys_set:nn { scontents } { #1 }
  }

% Star environment
\group_begin:
\char_set_catcode_active:N \^^M
\cs_new_protected:Npn \scontents_start_environment:w #1 ^^M
  {
    \tl_if_blank:nF { #1 } { \scontents_environment_keys:w #1 }
    \group_begin: % open a group for environment
    \use:c { filecontentsdefmacro } { \l_scontents_macro_tmp_tl } ^^M 
  }
\group_end:

% Stop environment
\cs_new_protected:Nn \scontents_stop_environment:
  {
    \endfilecontentsdefmacro 
    \group_end:  % close a group for environment
  }

% Expand \l_scontents_macro_tmp_tl and pass to seq
\cs_new_protected:Nn \_scontents_macro_to_seq:
  {
    \tl_put_right:Nx \l_tmpa_tl
      {
         \tex_newlinechar:D = 13
         \tex_everyeof:D = { \exp_not:N }
         \exp_not:N \scantokens \exp_after:wN { \tl_use:c { l_scontents_macro_tmp_tl } }
         \tex_newlinechar:D = 10 \scan_stop:
       }
    \tl_put_right:Nx \l_tmpb_tl
      {
        \exp_not:N \_scontents_append_contents:nn 
          { 
            \exp_not:V \l_scontents_name_seq_tl 
          } 
          { \exp_not:V \l_tmpa_tl } 
      }
    \l_tmpb_tl % add to seq
  }

% Execution of the code after finishing the scontent environment 
\cs_new_protected:Nn \scontents_atend_environment:
  {
    \_scontents_macro_to_seq:
    \bool_if:NT \l_scontents_show_env_tl
      {
        \_scontents_getfrom_seq:nn { -1 }{ \l_scontents_name_seq_tl }
      }
    \cs_undefine:N \l_scontents_macro_tmp_tl
  }
\ExplSyntaxOff
\setlength{\parindent}{0pt} % just for the example
\begin{document}
\section*{Test environment}
Test \verb+\begin{scontents}+ no \verb+[key=val]+\par

\begin{scontents}
Using \verb+scontents+ env no \verb+[key=val]+, save in 
\verb+contents+ with index 1. \footnote{AND footnotes !!}

\begin{verbatim}
      (A) verbatim environment
\end{verbatim}
\end{scontents}

Test \verb+\begin{scontents}[save-env=other]+\par

\begin{scontents}[save-env=other]
Using \verb+scontents+ env with \verb+[save-env=other]+, save in  
\verb+other+ with index 1.

\begin{verbatim*}
      (B) verbatim environment
\end{verbatim*}
\end{scontents}

\section*{Show stored contents}
\getstored[1]{other}
\getstored[1]{contents}

\end{document}

I have tried to write the code following the "rules" (and what I have learned in the forum), if something is badly written (related to expansion or cs), please correct me accordingly.

Greetings.

0

1 Answer 1

1

As far as I understand, you can't use \filecontentsexec the way you do. What you want is to pass the processed result of that macro to \_scontents_append_contents:nn. As \filecontentsexec is not fully expandable, this is not going to work. You should only use this macro in the normal execution mode, not as part of token list manipulations.

However, you can shorten your \_scontents_macro_to_seq: macro a bit, because the material inside \l_tmpa_tl of your first version is expandable. Using temporary variables is not necessary then, you can just pass the expanded result to \_scontents_append_contents:nn directly:

% Expand \l_scontents_macro_tmp_tl and pass to seq
\cs_gset_protected:Nn \_scontents_macro_to_seq:
  {
    \exp_args:NNx \_scontents_append_contents:nn \l_scontents_name_seq_tl 
      {
        \tex_newlinechar:D = 13
        \tex_everyeof:D = { \exp_not:N }
        \exp_not:N \scantokens \exp_after:wN { \tl_use:c { l_scontents_macro_tmp_tl } }
        \tex_newlinechar:D = 10 \scan_stop:
      }
  }

(If you are not familiar with the set of \exp_args: functions \exp_args:NNx \A \B \C is equivalent to \A \B {<full expansion of \C>}.)

5
  • Thank you very much, it is more less what I was looking for, consultation, is there any way to avoid using :D and \scantokens, that is to say, to place something more in the line of expl3? Jun 18, 2019 at 13:17
  • 1
    What you could use is \tl_set_rescan:Nnn. Looking at the code, this function seems to implement basically the same pattern as your \scantokens code. I'm not sure how well that would work in conjunction with filecontentsdef, though.
    – siracusa
    Jun 18, 2019 at 22:44
  • I think I once tried, but I never really understood how to use \tl_set_rescan:Nnn, if you have some time, could you extend your answer using \tl_set_rescan:Nnn? If expl3 has a function equal to \scantokens I think it would be correct to use it. Saludos Jun 19, 2019 at 0:35
  • I've tried to reimplement that method using \tl_set_rescan but I couldn't make it work. Not sure if this function isn't supposed to work with verbatim commands or I'm just using it wrong. Also for multiline text all spaces seem to be stripped even at the beginning of lines.
    – siracusa
    Jun 19, 2019 at 4:00
  • Now that I remember, I made the attempt some time ago (and a poorly worded question that I then deleted). Apparently you can't set \newlinechar in the {setup} of \tl_set_rescan (I think). Jun 19, 2019 at 11:18

You must log in to answer this question.

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