3

I have a function \Foo that I test against what I think it should expand to for given arguments. Two problems: 1/ I can't 'log' the fully expanded version of \Foo{arg}. 2/ even when the expansion seems right by looking at the pdf output, tests based on \str_if_eq:p fail.

Here is an excerpt of the log file corresponding to Hook 1 in the code:

\l_tmpa_tl=\Foo {A,B,C}.

I expected on the RHS of = an expanded version thereof. Why is it not, and can it be done?

Here's the output corresponding to Hook 2 in the code:

enter image description here

FAIL is the result of testing for the equality of the expansion \Foo {A,B,C}, shown on the LHS of the middle +, against the RHS. Where does the disagreement come from?

\documentclass{l3doc}
\usepackage{xparse}

\ExplSyntaxOn

\cs_new_protected:Nn \foo_str_compare:nn
{
  \exp_args:NNx \tl_set:Nn \l_tmpa_tl {#1}
  \tl_log:N \l_tmpa_tl % Hook 1
  \exp_last_unbraced:Nf % Hook 2
  \str_if_eq:nnTF
  {
    {\l_tmpa_tl}
    {#2}
    {PASS}
    {FAIL+#1+#2+}
  }
}

\NewDocumentCommand{\Foo}
{m}
{
  \seq_gset_from_clist:Nn \foo_seq {#1}
  \seq_use:Nnnn \foo_seq{,~}{,~}{~and~}
}

\NewDocumentCommand{\Test}{}
{
  \foo_str_compare:nn{\Foo{A,B,C}}{A,~B~and~C}
}

\ExplSyntaxOff

\begin{document}

\Test

\end{document}
1
  • 1
    Neither \Foo nor \seq_gset_from_clist:Nn is expandable. Mar 25, 2020 at 8:26

2 Answers 2

6

You can only carry out a string comparison for expandable material. You have a document command, \Foo, which is protected, and also use \seq_gset_from_clist:Nn, which is non-expandable. (In interface3, any function not marked with a star is not expandable; broadly, anything which carries out an assignment or typesetting will no be expandable.)

Re-writing to use an expandable mapping for the comma list leads to

\documentclass{l3doc}
\usepackage{xparse}

\ExplSyntaxOn
\cs_new:Npn \foo_str_compare:nn #1#2
  {
    \str_if_eq:eeTF {#1} {#2}
      { PASS }
      { FAIL+#1+#2+}
  }

\NewExpandableDocumentCommand{\Foo}
{m}
{
  \int_case:nnF { \clist_count:n {#1} }
    {
      { 0 } { }
      { 1 } {#1}
      { 2 } { \__foo_two:w #1 \s_stop }
    }
    { \__foo_list:w #1 \s_stop }
}
\cs_new:Npn \__foo_two:w #1 , #2 \s_stop { #1 ~ and ~ #2 }
\cs_new:Npn \__foo_list:w #1 , #2 \s_stop
  {
    #1 , ~
    \int_compare:nNnTF { \clist_count:n {#2} } > 2
      { \__foo_list:w #2 \s_stop }
      { \__foo_two:w #2 \s_stop }
  }

\NewDocumentCommand{\Test}{}
{
  \foo_str_compare:nn{\Foo{A,B,C}}{A,~B~and~C}
}

\ExplSyntaxOff

\begin{document}

\Test

\end{document}

As noted in egreg's answer, you have various constructs in your code that are not the recommended pattern, and which I've avoided.

10
  • Thanks, Joseph. This recursive algorithm is good to see for learning purposes. Maybe more practically, in case @Erwann didn't know, expl3 already provides easy-to-use functions to do this boring work of enumerating list items that automatically handle the special cases depending on the number of items: \clist_use:Nnnn and \seq_use:Nnnn.
    – frougon
    Mar 25, 2020 at 12:19
  • @frougon Yes, but currently not \clist_use:nnnn, which is what would be needed to stay expandable here
    – Joseph Wright
    Mar 25, 2020 at 12:23
  • Well, I was only thinking about the typesetting aspect; but still, both of the functions I mentioned above are marked as being fully expandable, aren't they?
    – frougon
    Mar 25, 2020 at 12:48
  • @frougon Yes, they are, but the user function here takes an n-type clist, so there would be a set step to use them
    – Joseph Wright
    Mar 25, 2020 at 12:50
  • Ah, okay, this makes sense, thanks!
    – frougon
    Mar 25, 2020 at 12:50
4

You're using a nonexpandable function where expandability is necessary to succeed.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\cs_new_protected:Nn \foo_str_compare:nn
 {
  \tl_log:e { #1 }
  \str_if_eq:eeTF {#1} {#2}
   {PASS}
   {FAIL+#1+#2+}
 }
\cs_generate_variant:Nn \tl_log:n { e }

\NewDocumentCommand{\Test}{m}
 {
  \seq_gset_from_clist:Nn \l_tmpa_seq {#1}
  \foo_str_compare:nn{\seq_use:Nnnn \l_tmpa_seq{,~}{,~}{~and~}}{A,~B~and~C}
 }

\ExplSyntaxOff

\begin{document}

\Test{A,B,C}

\end{document}

Besides, you're abusing \exp_args:... and \exp_last_unbraced:...; for instance,

\exp_args:NNx \tl_set:Nn \l_tmpa_tl { #1 }

is better written as

\tl_set:Nx \l_tmpa_tl { #1 }

and note also \str_if_eq:eeTF, to get full expansion.

You'll get “PASS” and, in the log file,

> A, B and C.

due to forced expansion with \tl_log:e.

You must log in to answer this question.

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