5

Reading this question and the answer of @Werner made me think about if it is possible to have chains of self-delimiting macros like \XX abc \XX def \XX xyz or lets say

\begin{someenv}
  \XX abc
  \XX def
  \XX xyz
\end{someenv}

The original definition will obviously fail here - insufficent number fo \XXs. So, a first step towards an easy redefinition of \XX could be to say

\def\XX#1\XX{do sth with #1\\ \XX}

The problem now is to terminate this chain in an elegant way. A rather rude and not desiderable way is shown in the MWE below.

\documentclass{article}

\def\XX#1\XX{do sth with #1\\ \XX}

\newenvironment{someenv}{\center}{\endcenter}

\def\abc{def}

\begin{document}
\begin{someenv}
  \XX xyz
  \XX abc
  \XX \abc
  \let\XX\empty %this is the bad guy that needs to be busted
\end{someenv}
\end{document}
3
  • 4
    So what's the question here?
    – Werner
    Nov 29, 2014 at 7:49
  • 1
    @Werner, "The problem now is to terminate this chain in an elegant way."
    – Ruben
    Nov 29, 2014 at 10:16
  • You need to supply some ending constraint, otherwise there's no way to stop it. The ending constraint may be explicit, but mostly its implicit in the form of a environment end, like in your example. It's already elegant (in my opinion) as the user doesn't see it or have to worry about it.
    – Werner
    Nov 29, 2014 at 15:50

3 Answers 3

5

You can't really do it unless you grab the token list you need as an argument.

Here's a possibility using environ; usage of expl3 is not necessary, but it provides a good framework for doing the desired job later.

\documentclass{article}
\usepackage{environ,expl3}

\ExplSyntaxOn
\NewEnviron{collectitems}
 {
  % clear the sequence
  \seq_clear:N \l_ruben_collected_items_seq
  % process the body
  \exp_last_unbraced:No \ruben_collect_item:nw \BODY \item \q_stop
  % do something with the sequence
  \seq_show:N \l_ruben_collected_items_seq
 }
\seq_new:N \l_ruben_collected_items_seq
\cs_new_protected:Npn \ruben_collect_item:nw #1 \item #2 \q_stop
 {
  \seq_put_right:Nx \l_ruben_collected_items_seq { \tl_trim_spaces:n { #1 } }
  \tl_if_empty:nF { #2 }
   {
    \ruben_collect_item:nw #2 \q_stop
   }
 }
\ExplSyntaxOff

\begin{document}

\begin{collectitems}
\item Apple
\item Banana
\item Cherry
\end{collectitems}

\begin{collectitems}
\end{collectitems}

\end{document}

The body is collected in \BODY, which is expanded before prepending \ruben_collect_item:nw and appending \item \q_stop. The function \ruben_collect_item:nw is executed at every \item, until \item is immediately followed by \q_stop. The items are put in a sequence; the first item in the sequence will contain whatever is before the first \item token and can be used to check whether an \item token is actually seen, as the example shows.

The output on the terminal is

The sequence \l_ruben_collected_items_seq contains the items (without outer braces):
>  {}
>  {Apple}
>  {Banana}
>  {Cherry}.
<recently read> }

l.31 \end{collectitems}

? 
The sequence \l_ruben_collected_items_seq contains the items (without outer braces):
>  {}.
<recently read> }

l.34 \end{collectitems}

Of course you will do something more useful with the sequence.


A “translation” into classical commands for a description environment that adapts the left margin to the width of the widest label.

\documentclass{article}
\usepackage{environ,enumitem}

\newlength\rubenwidth
\NewEnviron{xdescription}{%
  \expandafter\rubenprocess\BODY\item\rubenprocess
  \begin{itemize}[labelwidth=\rubenwidth,leftmargin=\rubenwidth,align=left,font=\bfseries]
  \BODY
  \end{itemize}
}
\makeatletter
\def\rubenprocess#1#2\item#3\rubenprocess{% #1 is always \item
  \ruben@item#2\ruben@item
  \if\relax\detokenize{#3}\relax
    \expandafter\@gobble
  \else
    \expandafter\@firstofone
  \fi
  {\rubenprocess\item#3\rubenprocess}%
}
\def\ruben@item[#1]#2\ruben@item{%
  \settowidth\@tempdima{\bfseries#1}%
  \ifdim\rubenwidth<\@tempdima\rubenwidth=\@tempdima\fi
}

\begin{document}

\begin{xdescription}
\item[Apple] a round fruit
\item[Banana] not so round fruit
\item[Strawberry] small fruit with some more words
  to get the text into two lines; will we succeed?
\end{xdescription}

\end{document}

enter image description here

Many variations of the idea are possible.

9
  • Is it :nw or :ww (or :w)?
    – Manuel
    Nov 29, 2014 at 10:34
  • I am still really bad with xpl3. If it isn't too time consuming, could you show in addition how your solution would work without using LaTeX3 stuff?
    – Ruben
    Nov 29, 2014 at 18:57
  • @Ruben It mostly depends on what you want to do with the items.
    – egreg
    Nov 29, 2014 at 18:58
  • Sure! Suppose I want to measure them with \settowidth and store them in a token register.
    – Ruben
    Nov 29, 2014 at 19:00
  • @Ruben Dinner time. Stay tuned.
    – egreg
    Nov 29, 2014 at 19:01
3

Is something like this what you want?

\documentclass{article}
\newenvironment{someenv}{\par\centering}{\par}
\newenvironment{XX}{\endXX\bfseries\itshape Do sth with\ }{\mdseries\upshape\par}
\let\notXX\endXX
\def\abc{ABC}
\begin{document}
before
\begin{someenv}
  \XX xyz 
  \XX abc
  \XX \abc 
  \notXX not under \verb|\XX| control % USE \notXX TO FORCE BREAK FROM \XX CONTROL
  \XX ending line
\end{someenv}
after
\end{document}

enter image description here

You can get fancy and make the "something" that \XX does vary with the iteration of \XX within the environment.

\documentclass{article}
\newcounter{instance}
\newenvironment{someenv}{\par\centering\setcounter{instance}{0}}{\par}
\newenvironment{XX}{\endXX\bfseries\itshape\stepcounter{instance}
    Iteration \theinstance: Do sth with\ }
  {\mdseries\upshape\par}
\let\notXX\endXX
\def\abc{ABC}
\begin{document}
before
\begin{someenv}
  \XX xyz 
  \XX abc
  \XX \abc 
  \notXX not under \verb|\XX| control % USE \notXX TO FORCE BREAK FROM \XX CONTROL
  \XX ending line
\end{someenv}
after
\end{document}

enter image description here

1
  • Yes! To answer your question. (The \notXX function is neat :) Now, I have to check if it is appliable to other cases. For instance, like the one I described in the comments of egregs answer.
    – Ruben
    Nov 29, 2014 at 21:09
2

You can use the code from Macro parameter delimited by more than one delimiter. This code defines the \seplist macro. Its argument is the list of separators. The separators in your example are \XX or \end (with space before this). The definition of \XX looks like:

\def\XXa#1{... macro with normal #1 parameter\par\sepused}
\def\XX{\seplist{{ \XX}{ \end}}\XXa}

\begin{someenv}
   \XX abc
   \XX def
   \XX xyz
\end{someenv}
1
  • 1
    Altough it's a “inside link”, this site prefers to have self contained answers. So it would be better if you include the \seplist definition in this answer.
    – Manuel
    Dec 1, 2014 at 21:50

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.