2

i try to write a macro which must put one or two label depending of the input parameter: input=⟨test1⟩ must write one label: \label{sec:test1}

input=⟨test1,test2⟩ must write two labels: \label{sec:test1}\label{test2}

For one label it's OK but for two the compilation return:

TeX capacity exceeded, sorry [input stack size=5000]. 
...bsection{Test}\ecriturelabel{test1,test2}

The macro is write as follow:

\usepackage{xifthen}

\usepackage{xstring}

\newcommand{\ecriturelabel}[1]{

\ifthenelse{\isin{,}{#1}}{\def\labelone{sec:\StrBefore{#1}{,}}}{\def\labelone{sec:#1}}
\ifthenelse{\isin{,}{#1}}{\def\labeltwo{sec:\StrBehind{#1}{,}}}{\def\labeltwo{}}
\ifthenelse{\isin{,}{#1}}{\label{\labelone}\label{\labeltwo}}{\label{\labelone}}
}
  • Welcome to TeX.SX! Please help us help you and add a minimal working example (MWE) that illustrates your problem. Reproducing the problem and finding out what the issue is will be much easier when we see compilable code, starting with \documentclass{...} and ending with \end{document}. – Alessandro Cuttin Sep 30 at 14:47
  • This might be a circular reference: en.wikipedia.org/wiki/Circular_reference – CampanIgnis Sep 30 at 15:30
  • According to the text in your question you wish the second label to be test2 while according to the code-snippet in your question you want it to be preceded by sec:, i.e., sec:test2. Please clarify! – Ulrich Diez Sep 30 at 15:53
  • 1
    @CampanIgnis No circular reference. Just two \labels are placed. Thus the same referencing-data will be available for cross-referencing under two names. The problem is: The macros of the xstring package are not expandable. Therefore they cannot be placed into the argument of the \label-command. – Ulrich Diez Sep 30 at 15:58
  • Be aware that when writing questions, in normal text, i.e., outside code-snippets, you cannot use "<" and ">" directly as these characters will be taken for something that introduces/ends a html-tag. If the html-tag is unknown, "<" and ">" and everything in between will just not be displayed. Instead use, e.g., the html-entities &langle; (yields: ⟨) and &rangle; (yields: ⟩) or &lt;(yields: <) and &gt;(yields: >). – Ulrich Diez Sep 30 at 16:11
1

As far as I can see, you'd like to do

\ecriturelabel{a,b}

so that this issues \label{sec:a} and \label{b}, but \ecriturelabel{a} only does \label{sec:a}.

There's no way that \def\labelone{sec:\StrBefore{#1}{,}} can work, because \label needs a fully expandable argument.

You can do it much more easily:

\makeatletter
\newcommand{\ecriturelabel}[1]{%
  \def\el@perhaps{sec:}%
  \@for\next:=#1\do{\label{\el@perhaps\next}\def\el@perhaps{}}%
}
\makeatother

which will work for any number of comma separated items in the argument to \ecriturelabel.

Full example:

\documentclass{article}

\makeatletter
\newcommand{\ecriturelabel}[1]{%
  \def\el@perhaps{sec:}%
  \@for\next:=#1\do{\label{\el@perhaps\next}\def\el@perhaps{}}%
}
\makeatother

\begin{document}

\section{a}\ecriturelabel{a}

\section{bc}\ecriturelabel{b,c}

\end{document}

Here's the contents of the .aux file.

\relax 
\@writefile{toc}{\contentsline {section}{\numberline {1}a}{1}\protected@file@percent }
\newlabel{sec:a}{{1}{1}}
\@writefile{toc}{\contentsline {section}{\numberline {2}bc}{1}\protected@file@percent }
\newlabel{sec:b}{{2}{1}}
\newlabel{c}{{2}{1}}

A more flexible solution using xparse and expl3:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\ecriturelabel}{O{sec}m}
 {
  \poulot_labels:nn { #1 } { #2 }
 }

\seq_new:N \l_poulot_labels_seq
\tl_new:N \l_poulot_label_first_tl

\cs_new_protected:Nn \poulot_labels:nn
 {
  \seq_set_from_clist:Nn \l_poulot_labels_seq { #2 }
  \seq_pop_left:NN \l_poulot_labels_seq \l_poulot_label_first_tl
  \label{#1:\l_poulot_label_first_tl}
  \seq_map_function:NN \l_poulot_labels_seq \label
 }
\ExplSyntaxOff

\newtheorem{theorem}{Theorem}

\begin{document}

\section{a}\ecriturelabel{a}

\section{bc}\ecriturelabel{b,c}

\begin{theorem}\ecriturelabel[theorem]{d,e}
Statement
\end{theorem}

\end{document}

The .aux file:

\relax 
\@writefile{toc}{\contentsline {section}{\numberline {1}a}{1}\protected@file@percent }
\newlabel{sec:a}{{1}{1}}
\@writefile{toc}{\contentsline {section}{\numberline {2}bc}{1}\protected@file@percent }
\newlabel{sec:b}{{2}{1}}
\newlabel{c}{{2}{1}}
\newlabel{theorem:d}{{1}{1}}
\newlabel{e}{{1}{1}}

The command has an optional argument for the prefix to be added to the first label.

The idea is to form a sequence of items from the given argument; the first item is detached and the label with the prefix is generated; the remaining items are passed to \label one by one.

1

Quote from the manual of the xstring package:

3.2 Expansion of macros, optional argument

The macros of this package are not purely expandable, i.e. they cannot be put in the argument of an \edef. Nestling macros is not possible neither.

For this reason, all the macros returning a result (i.e. all excepted the tests) have an optional argument in last position. The syntax is [⟨name⟩], where [⟨name⟩] is the name of the control sequence that will receive the result of the macro: the assignment is made with an \edef which make the result of the macro [⟨name⟩] purely expandable. Of course, if an optional argument is present, the macro does not display anything.

Thus, this structure not allowed, supposed to assign to \Result the 4 chars on the left of xstring:
\edef\Result{\StrLeft{xstring}{4}}
is equivalent to :
\StrLeft{xstring}{4}[\Result]

And this nested structure, not allowed neither, supposed to remove the first and last char of xstring:
\StrGobbleLeft{\StrGobbleRight{xstring}{1}}{1}
should be written like this:
\StrGobbleRight{xstring}{1}[\mystring]
\StrGobbleleft{\mystring}{1}

Being not purely expandable does not only mean that things cannot be put in "the argument" of an \edef. It also means that things can generally not be used in full expansion contexts which means that they also cannot be put in the argument of a \label-command.

Perhaps the following approach is helpful:

\documentclass{article}

\usepackage{xifthen}
\usepackage{xstring}

\newcommand{\ecriturelabel}[1]{%
  \ifthenelse{\isin{,}{#1}}{%
    \StrBefore{#1}{,}[\labelname]%
    \label{sec:\labelname}%
    \StrBehind{#1}{,}[\labelname]%
    \label{\labelname}%
  }{%
    \label{sec:#1}%
  }%
}%

\begin{document}

\section{Test}

\subsection{Test}

\ecriturelabel{test1,test2}

\noindent\ref{sec:test1}

\noindent\ref{test2}

\end{document}

The code above does neither take into account the case of more than one comma nor take into account the case of spaces surrounding elements of the comma-list of labels nor take into account cases like \ecriturelabel{,}, \ecriturelabel{test1,}, \ecriturelabel{,test2} etc.

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.