2

I'm trying to implement a \Scontents command inside a small package that behaves in two different ways:

\Scontents*[opt]<del>argument<del>
\Scontents[opt] { argument }

With the star it behaves like \verb and receives the delimited argument or with {...} that they are balanced.

Without the star it behaves like a regular command using {...}.

In both cases the argument is saved in a sequence and then used. The command works perfectly for what I want, the issue is that to implement it I have used a beautiful macro given by Ulrich Diez (https://tex.stackexchange.com/a/472044/7832) with an extensive explanation of how it works, but, I would like to know if you can migrate to a more expl3 style code, the code is quite obfuscated for my level. This is the example file:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\keys_define:nn { scontents }
  {
    store-cmd .tl_set:N  = \l__scontents_name_seq_cmd_tl,
    store-cmd .initial:n = contents,
  }
\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:Npn \__scontents_getfrom_seq:nn #1#2
  { \seq_item:cn { g__scontents_seq_name_#2_seq } {#1} }
\ExplSyntaxOff
% From Ulrich Diez https://tex.stackexchange.com/a/472044/7832
\makeatletter
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\expandafter\expandafter\@firstoftwo{ }{}%
  \@secondoftwo}{\expandafter\expandafter\@firstoftwo{ }{}\@firstoftwo}%
}%
\begingroup
\catcode`\^^M=12 %
\@firstofone{%
  \endgroup%
  \newcommand\UDEndlreplace[2]{\romannumeral0\@UDEndlreplace{#2}#1^^M\relax{}}%
  \newcommand*\@UDEndlreplace{}%
  \long\def\@UDEndlreplace#1#2^^M#3\relax#4#5{%
    \UD@CheckWhetherNull{#3}%
    { #5{#4#2}}{\@UDEndlreplace{#1}#3\relax{#4#2#1}{#5}}%
  }%
}%
\newcommand\UDcollectverbarg[3]{%
  \@bsphack
  \begingroup
  \let\do\@makeother % <- this and the next line switch to
  \dospecials        %    verbatim-category-code-régime.
  \catcode`\{=1      % <- give opening curly brace the usual catcode so a
                     %    curly-brace-balanced argument can be gathered in
                     %    case of the first thing of the verbatimized-argument
                     %    being a curly opening brace.
  \catcode`\ =10     % <- give space the usual catcode so \UD@collectverbarg
                     %    cannot catch a space as its 4th undelimited argument.
                     %    (Its 4th undelimited argument denotes the verbatim-
                     %     syntax-delimiter in case of not gathering a
                     %     curly-brace-nested argument.)
  \kernel@ifnextchar\bgroup
  {% seems a curly-brace-nested argument is to be caught:
    \catcode`\}=2    % <- give closing curly brace the usual catcode also.
    \UD@collectverbarg{#1}{#2}{#3}{}%
  }{% seems an argument with verbatim-syntax-delimiter is to be caught:
    \do\{ % <- give opening curly brace the verbatim-catcode again.
    \UD@collectverbarg{#1}{#2}{#3}%
  }%
}%
\newcommand\UD@collectverbarg[4]{%
  \do\ %             % <- Now that \UD@collectverbarg has the delimiter or
                     %    emptiness in its 4th arg, give space the
                     %    verbatim-catcode again.
  \catcode`\^^M=12   % <- Give the carriage-return-character the verbatim-catcode.
  \long\def\@tempb##1#4{%
    \edef\@tempb{##1}%
    \@onelevel@sanitize\@tempb % <- Turn characters into their "12/other"-pendants.
                               %    This may be important with things like the
                               %    inputenc-package which may make characters
                               %    active/which give them catcode 13(active).
    \expandafter\UDEndlreplace\expandafter{\@tempb}{#1}{\def\@tempb}% <- this starts
                               %    the loop for replacing endline-characters.
    \expandafter\UD@@collectverbarg\expandafter{\@tempb}{#2}{#3}% <- this "spits
                               %    out the result.
  }%
  \@tempb
}%
\newcommand\UD@@collectverbarg[3]{%
  \endgroup
  \@esphack
  #2{#3{#1}}%
}%
\makeatother
\ExplSyntaxOn % Back to |expl3| programing
\cs_new_eq:Nc \__scontents_UD_firstofone: { @firstofone }
\exp_args_generate:n { Vx }
\ProvideDocumentCommand{ \Scontents }{!s O{} }
  {
    \group_begin:
    \IfNoValueF {#2} { \keys_set_known:nn { scontents } {#2} }
    \IfBooleanTF{#1}
      { \UDcollectverbarg{^^J}{\__scontents_UD_firstofone:}{\__scontents_append_vercmd:n} }
      { \__scontents_append_stdcmd:n }
  }
% No starred
\cs_new_protected:Npn \__scontents_append_stdcmd:n #1
  { 
    \exp_args:NV  \__scontents_append_contents:nn \l__scontents_name_seq_cmd_tl {#1}
    \group_end:
  }
% Starred
\cs_new_protected:Npn \__scontents_append_vercmd:n #1
  {
    \exp_args:NV \__scontents_append_contents:nn \l__scontents_name_seq_cmd_tl { \tex_scantokens:D {#1} }
    \group_end:
  }
% get
\ProvideExpandableDocumentCommand { \getstored } { O{1} m }
  { \__scontents_getfrom_seq:nn {#1} {#2} }
\ExplSyntaxOff
\begin{document}

\Scontents[store-cmd=nostarred]{Using Scontents command, 
no verbatim suport, save in seq nostarred with index $1$.}

\Scontents*[store-cmd=starred]|Using \verb+\Scontents*+ command, 
delimited by tbar, save in seq starred with index $1$.
\begin{verbatim*}
      verbatim environment
\end{verbatim*}|

\noindent\hrulefill

\getstored[1]{nostarred}\par
\getstored[1]{starred}
\end{document}

Can this macro be written in terms of expl3? or is it better to leave it as it is, my idea is to try to leave the code in favor of expl3 if possible.

Greetings

0
5

Here is a pure expl3 reimplementation. The most notable changes are:

  • \Scontents now has argument types O{} +v. By using xparse's verbatim argument type v, the use of the star isn't necessary anymore, as this type automatically detects if a braced argument or a delimiter character follows.
  • The \UDcollectverbarg was removed, because it seems to do what the v type already does.
  • \tex_scantokens:D is stored along with the verbatim text in the \g__scontents_seq_name_*_seq sequences and executed when the final sequence is queried. Also \tex_newlinechar:D is set to 13 there. I think this is essentially the same as the replacement of ^^J by ^^M that happened in your \UDcollectverbarg macro.

The full code:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\keys_define:nn { scontents }
  {
    store-cmd .tl_set:N  = \l__scontents_name_seq_cmd_tl,
    store-cmd .initial:n = contents,
  }
\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}
  }

\ProvideDocumentCommand{ \Scontents }{ O{} +v }
  {
    \IfNoValueF {#1} { \keys_set_known:nn { scontents } {#1} }
    \exp_args:NV \__scontents_append_contents:nn \l__scontents_name_seq_cmd_tl
      {
        \group_begin:
        \tex_newlinechar:D = 13 \scan_stop:
        \tex_scantokens:D { #2 }
        \group_end:
      }
  }
% get
\ProvideDocumentCommand { \getstored } { O{1} m }
  { \__scontents_getfrom_seq:nn {#1} {#2} }
\ExplSyntaxOff

\begin{document}
\Scontents[store-cmd=nostarred]{Using Scontents command, 
no verbatim support, save in seq nostarred with index $1$.}

\Scontents[store-cmd=starred]|Using \verb+\Scontents*+ command, 
delimited by tbar, save in seq starred with index $1$.
\begin{verbatim*}
      verbatim environment
      with unbalanced } braces }
\end{verbatim*}|

\noindent\hrulefill

\getstored[1]{nostarred}\par
\getstored[1]{starred}
\end{document}

outputs

enter image description here

3
  • Thank you very much for the answer, I've been a bit late (it's the end of the academic semester in my country). A query, you can move \tex_newlinechar:D = 13and \tex_scantokens:D into \Scontents, I think if possible I can adapt a project I have in mind. Greetings – Pablo González L Jul 19 '19 at 3:40
  • Okay, I moved it back – siracusa Jul 19 '19 at 4:06
  • Thanks again, it works perfectly. – Pablo González L Jul 19 '19 at 4:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.