4

Problem

I would like to use an expl3 function inside a \label{...}.

%!TEX program=xelatex
\documentclass{article}

\usepackage{l3regex}

\ExplSyntaxOn
    \tl_new:N \l_slugify_tl
    \cs_new:Npn \slugify #1 {
        \tl_set:Nf \l_slugify_tl { \tl_lower_case:n { #1 } } %lower case
        \regex_replace_all:nnN { [^a-z]+ }{ - }\l_slugify_tl % remove non-letters
        \regex_replace_all:nnN { (^-|-$$$$) }{ } \l_slugify_tl % clean up leading/trailing dashes
        \tl_use:N \l_slugify_tl
    }
\ExplSyntaxOff


\begin{document}

\subsection{The Explanation}
    \label{wiki:\slugify{The Explanation}}

\end{document}

When typesetting the document, I get a

) (./test-regex-slug.aux
! Undefined control sequence.
<argument> r@wiki:\tl 
                      _set:Nf {the explanation}\regex _replace_all:nnN {[^a-...
l.3 ...x_replace_all:nnN {(^-|-$$$$)}{}}{{0.1}{1}}

Apparently, the contents of \slugify are not expanded and the expl3-code is written directly to the aux file. How can I prevent that and make my code work?

Background

I'd like to simplify the usage of cross-references inside my (huge) document. I would type something like

... e.g. using a \linkR{Time of Flight} detector.
That \linkR{TOF} would ...

% ...

\section{\anchorR{Time of Flight} Detector (\aliasR{TOF})}
% ...

instead of

...e.g. using a \hyperref[time-of-flight]{Time of Flight} detector.
That \hyperref[time-of-flight]{TOF} would...

% ...

\section{Time of Flight Detector (TOF)} \label{time-of-flight} \label{tof}
% ...
  • I can't call your \slugify outside of \label, too. \tl_use:V seems to be the problem (undefined). If you remove the bottom most line from your macro it runs without an error (outside of \label). – Skillmon Jul 04 '17 at 23:23
  • 1
    What you want to do is simply not possible. Regex replace and assignments are just not expandable and can thus never be used in a full expansion context of, e.g., \label. – Henri Menke Jul 04 '17 at 23:33
  • 1
    In general it is a bad idea to use macros or active characters in \label and \ref. – Henri Menke Jul 04 '17 at 23:44
  • @HenriMenke Sometimes it is really useful, though ;). To be clear, I wouldn't do it here. But sometimes you want to create a unique label using a counter or something. – cfr Jul 05 '17 at 01:02
  • 3
    Your code would put \label{} in \section{} i.e. \section{ \label{} }. That will end in tears, even setting the expansion issues mentioned by @HenriMenke aside. – cfr Jul 05 '17 at 01:03
  • 2
    Note that you should not be creating user-level commands with \cs_new. If you want something like \slugify, use \NewDocumentCommand or whatever as a wrapper for the lower level function \ralfix_slugify:n. – cfr Jul 05 '17 at 01:05
  • Your example looks as if you want to use glossaries. – Ulrike Fischer Jul 05 '17 at 06:40
  • @Skillmon sorry, you're right. It should be \tl_use:N. Changed it. – Peater de Xel Jul 05 '17 at 09:22
  • @cfr I see no problem in using \label inside \section... https://tex.stackexchange.com/a/32327 – Peater de Xel Jul 05 '17 at 10:12
  • @ralfix Putting it inside \caption is not the same as putting it inside \section, as the comments on that answer point out. – cfr Jul 05 '17 at 13:27
  • @cfr I have no better source, but one comment by A. Sommerfeldt says „And yes, one can put \label inside \section etc., and it is documented (by Laslie Lamport) that you are allowed to do it.“ — is there some good material on this topic, or would the LaTeX source be the only reference? – Peater de Xel Jul 06 '17 at 15:46

1 Answers1

7

What you want to do is simply not possible. Regex replace and assignments are just not expandable and can thus never be used in a full expansion context of, e.g., \label.

However, you can do things like save the processed label in a macro and pass that on or just make your \slugify macro immediately place the label, etc.

Whenever you are doing such things, keep this quote in mind:

»This is not as productive as you would think in the long run.« — percusse

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\tl_new:N \l_slugify_tl

\NewDocumentCommand \slugify { m m m }
{
  \tl_set:Nf \l_slugify_tl { \tl_lower_case:n { #3 } } %lower case
  \regex_replace_all:nnN { [^a-z]+ }{ - }\l_slugify_tl % remove non-letters
  \regex_replace_all:nnN { (^-|-$$$$) }{ } \l_slugify_tl % clean up leading/trailing dashes
  #1 { #2 \tl_use:N \l_slugify_tl }
}

\ExplSyntaxOff

\begin{document}

\subsection{The Explanation}
    \slugify{\label}{wiki:}{The Explanation}

\end{document}
Henri Menke
  • 109,596
  • My goal is to not have to memorize the exact labels, as there will be hundreds, created by a work group, think something wiki-like. An awesome quote, nevertheless :) – Peater de Xel Jul 05 '17 at 09:08
  • In my world, at a certain point, the regex replacements have to be fully expanded, as they are typeset in the document, aren't they? Do you have some additional material concerning your first point? Is this generally the case with the expl3 functions? – Peater de Xel Jul 05 '17 at 09:10
  • @ralfix No, typesetting isn't an expansion context: stuff can also be executed (for example assignments). The interface3 documentation marks every expandable command with a star: no star, not expandable. – Joseph Wright Jul 05 '17 at 10:34