# \def taking next word as argument?

This question is a variant of my previous question requesting a macro that takes the rest of the line as argument. I would like to write a macro that would take the next word as argument, where word boundary is defined as a space or punctuation (say anything which is not a letter, a digit, or the colon character).

The difficulty lies with the fact that a normal \def can be set to expect a specific character, not a set of characters.

This is not a theoretical challenge---the concrete application is a macro that will do better references, so instead of writing Lemma~\cite{Lemma:Kantor}, one could write \cf Lemma Kantor

An approximation is

\def\cf #1 #2 {#1~\ref{#1:#2}}

but of course it would fail in cases it is used just before punctuation.

• A similar problem, but not quite the same, was discussed here. – Juan A. Navarro Feb 24 '11 at 10:05

You need to read every character on its own using a loop. Actually you need to look ahead using \futurelet because a space would otherwise be removed by the argument grapper.

The following code works and treats line breaks as spaces. One colon is allowed as separator between the two words instead of a space. If you want to allow multiple colons you have to add them into the if list in \cf@@.

\documentclass{article}

\makeatletter
\def\cf{%
\begingroup
\def\name{}%
\cf@
}

\def\cf@{%
\futurelet\ntoken\cf@@
}

\def\cf@@{%
\ifcase 0%
\ifx\ntoken\@sptoken 0\else
\ifcat a\ntoken 1\else
\ifcat 0\ntoken 2\fi% test of token is catcode "other"
\fi\fi
\relax
\expandafter\cf@end
\or
\else
\expandafter\cf@checknum
\fi
}

% Checks if token is a number (ASCII 48-57)
\def\cf@checknum#1{%
\ifcase 0%
\ifnum#1>47
\ifnum#1<58 1\fi\fi
\relax
\def\next{\cf@end#1}%
\else
\fi
\next
}

\edef\name{\name#1}%
\cf@
}

\def\cf@end{%
\let\type\name
\edef\name{\name:}%
\def\cf@end{%
\edef\@tempa{%
\endgroup
\type\noexpand~%
\noexpand\ref{\name}%
}%
\@tempa
}%
% Remove colon
% support active colons (e.g. [french]{babel})
\scantokens{\expandafter\let\csname cf@colon\endcsname=:}%
\@ifnextchar\cf@colon%
{\expandafter\cf@\@gobble}%
{\cf@}%
}
\makeatother

\begin{document}

\section{test}\label{sec:test}
\section{test}\label{sec:tes1}
\section{other}

\cf sec test it out
\cf sec:test it out

\cf sec
tes1 it out  % numbers and line break work

\end{document}
• @Martin: Wow! Don't you think this ought to be packaged and distributed? It would make the life of so many people sweeter. It would also be cool also to add a flag that would either produce the long version, i.e., Section~1, or the short version Sect.~1... More later. – Yossi Gil Feb 24 '11 at 9:32
• @Yossi: I personally think people should stick with explicit \ref{sec:name}. Feel free to turn it into a package. Just mention me somewhere in the manual. – Martin Scharrer Feb 24 '11 at 9:33
• @Martin: I like the use of \ifcase 0, which expands the following tokens to see whether 0 will be followed by a digit or not. And that next digit continues the expansion until reaching \relax, so the conditionals are fully expanded (no spurious \fi) before \ifcase is evaluated. Neat. – Bruno Le Floch Feb 24 '11 at 10:03
• @Bruno: I didn't came up with this technique by myself but learned it somewhere (on comp.text.tex I think) – Martin Scharrer Feb 24 '11 at 10:42
• @Martin: your colon handling mechanism will fail with \usepackage[frenchb]{babel} which makes : active (the second reference will always remain ??). – Philippe Goutet Feb 24 '11 at 10:43

Here is a rewrite of Martin's code. First the use case:

\documentclass{article}
\usepackage{cf}
\begin{document}
\section{Introduction}
\paragraph{Outline.}
In \cf Section things we shall discuss things, and then continue to discuss
stuff in \cf Section stuff; \cf Section final concludes.
\section{Things are so Important}\label{Section:things}
\section{Stuff is also Important}\label{Section:stuff}
\section{Conclusions}\label{Section:final}
\end{document

which produces

now, the rudimentary style file is

\def\cf #1 {%
\def\kind@cf{#1}%
\beginIteration@cf
}

\def\beginIteration@cf{%
\begingroup
\def\idAccumulator@cf{}%
\iterateOnNextToken@cf
}

\def\iterateOnNextToken@cf{%
\futurelet\nextToken@cf
\examineNextToken@cf
}

\def\examineNextToken@cf{%
\ifcase 0% Trick: will expand the next tokens to see if more digits follow.
\ifx\nextToken@cf\@sptoken 0\else
\ifcat a\nextToken@cf 1\else
\ifcat 0\nextToken@cf 2\fi% test of token is catcode "other"
\fi\fi
\relax
\expandafter\endIteration@cf
\or
\expandafter\accumulateCharacter@cf
\else
\expandafter\cf@checknum
\fi
}

\def\accumulateCharacter@cf#1{%
\edef\idAccumulator@cf{\idAccumulator@cf#1}%
\iterateOnNextToken@cf
}

% Checks whether the parameter is a number (ASCII 48-57)
\def\cf@checknum#1{%
\ifcase 0%
\ifnum#1>47
\ifnum#1<58 1\fi\fi
\relax
\def\next{\endIteration@cf#1}%
\else
\def\next{\accumulateCharacter@cf{#1}}%
\fi
\next
}

\def\endIteration@cf{%
\edef\label@cf{\kind@cf:\idAccumulator@cf}%
\kind@cf~\expandafter\ref\expandafter{\label@cf}%
\endgroup
}