2

Here is the MWE:

\documentclass{article}

\begin{document}
\ExplSyntaxOn
\dim_new:N \l__xchoices_coffin_join_horizontal_sep_dim
\str_new:N \l__xchoices_coffin_current_label_pos_str
\str_set:Nn \l__xchoices_coffin_current_label_pos_str {left}
\cs_new:Npn \__xchoices_label_hsep_init:N #1
  {
    \dim_set:Nn \l__xchoices_coffin_join_horizontal_sep_dim
      {
        \str_if_in:NnTF #1 {left}
          { 5pt }
          {
            \str_if_in:NnTF #1 {right}
              {-5pt}
              {0pt}
          }
      }
  }
\__xchoices_label_hsep_init:N
  \l__xchoices_coffin_current_label_pos_str
% \dim_use:N \l__xchoices_coffin_join_horizontal_sep_dim
\ExplSyntaxOff
\end{document}

but it failed to compile, and the fatal message is:

Missing number, treated as zero
Illegal unit of measure (pt inserted)

I don't know what's going on with it.

6

1 Answer 1

5

A very short course on macro expansion

TeX tokens can be expandable or unexpandable. Macros (those defined with \def or variants thereof, such as \cs_new:Npn) are of the first kind and also some primitives.

When TeX processes its input it first builds tokens and then proceeds to expand tokens, if possible. If not, it sends tokens, absorbed according to the syntax rules of the found unexpandable token, to the next stage (which is outside of user's control).

The \dim_set:Nn instruction is, for instance, unexpandable. In legacy TeX terms you have something similar to

\newdimen\foo
\foo=\dimexpr ... \relax

The token \foo is not expandable, so it's sent to the next stage where it's recognized as a <dimen> register, so TeX requests more input according to the syntax rules, which allow for = and then want something that can be interpreted as a (fixed) length. OK, \dimexpr is good, but it's by itself unexpandable. Same story, it's sent up and down it comes a request for more input.

In this case, macro expansion is performed, so if you had \def\baz{640pt} and TeX finds \baz while providing new tokens for \dimexpr, it will be happy. But if it finds something that doesn't fit the syntax rules for \dimexpr, then it will stop looking for more tokens and the next stage has to use what it had.

What's going wrong in your case?

The second argument to \dim_set:Nn must only contain tokens that, at the end of the expansion process only leave unexpandable tokens that fit the syntax of \dimexpr.

Unfortunately, \str_if_in:NNTF doesn't. You can deduce it from the fact that in the interface3 manual it is not followed by a star. So the fact that its processing can (and actually does) not at once provide for one of the last two arguments is documented.

enter image description here

What can you do?

In the picture I added also \str_case:nn(TF) to show that the instructions in this set are expandable.

If your string variable is only expected to contain left or right (so no need to test with \str_if_in:nnTF), you can use \str_case:VnF:

\dim_set:Nn \l__xchoices_coffin_join_horizontal_sep_dim
 {
  \str_case:VnF #1
   {
    {left}{5pt}
    {right}{-5pt}
   }
   {0pt}
 }

Otherwise you need to have the \dim_set:Nn instruction inside the cases

    \str_if_in:NnTF #1 {left}
      {
        \dim_set:Nn \l__xchoices_coffin_join_horizontal_sep_dim {5pt}
      }
      {
        \str_if_in:NnTF #1 {right}
          {
            \dim_set:Nn \l__xchoices_coffin_join_horizontal_sep_dim {-5pt}
          }
          {
            \dim_set:Nn \l__xchoices_coffin_join_horizontal_sep_dim {0pt}
          }
      }

In either case, \cs_new:Npn \__xchoices_label_hsep_init:N #1 should be

\cs_new_protected:Npn \__xchoices_label_hsep_init:N #1

because this function performs unexpandable actions.

1
  • Thanks sooo much for your detailed explanation!
    – xdyy
    Feb 3 at 16:09

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.