# Macro working over a whole word (plain TeX)

I have spend a good time searching in references and forums for a answer to the question I'm raising here.

I would like to write a macro that gets a word as a parameter without needing to using braces informing this. It would be a smart macro that recognizes what should be a component of a parameter, constructing it token by token until finding a not proper candidate (that will be replaced intact after finishing the macro itself). For examples

\def\km#1{% definition: only accept numbers: writes the unit after the subsequent integer word (parameter)%}
\def\mathstyle#1{% definition: accept anything except point and space: put parameter in mathmode (assumes that is called in "text mode" for simplification)%}
% usage
%% *\km 6300.* expands to 6300 $km$.
The distance between \mathstyle Point-A to \mathstyle Point-B is \km 20 assume that the Earth is plain.
%% puts Point-A and Point-B between math shifts


In short, in order to achieve this, supposedly, I need to check the characters one by one and recalling a handling macro until a not recognizable token is taken by it.

The problem is: in many cases TeX ignores spaces after a macro. So, I wasn't able to create a macro that handles with spaces (concatenating all words separated by spaces).

How can I fix? How to now destroy a space after a macro?

Not sure I quite understand the question (the "How to now destroy a space after a macro?" question seems unrelated to the context above, perhaps an example would be instructive). To answer getting single words as parameters, you can scan token by token using \futurelet and test if a letter appears

\protected\def\getword#1{%
\begingroup \toks0{}\toks1{#1}%
\futurelet\next\getwordA
}
\def\getwordA{\ifcat A\noexpand\next \expandafter\getwordB \else \expandafter\getwordC \fi}
\def\getwordB#1{\toks0\expandafter{\the\toks0 #1}\futurelet\next\getwordA}
\def\getwordC{\edef\tmp{\endgroup \the\toks1{\the\toks0}}\tmp}

\def\textbf#1{{\bf#1}} \def\textit#1{{\it#1\/}} % demo

\getword\textbf Foo bar
{\getword\textit baz} fu

\bye


It is easy to allow dashes, underscores, etc by adding a few extra checks.

• There is some TeXian trick in "\ifcat ...\noexpand", but I can't figure out. Am I wrong? Sep 24 at 13:45
• \ifcat expands everything in the way and compares the next two unexpandable tokens, this can cause unexpected problems if \next had been some macro. Sep 24 at 13:53
• I've just read in The TeX Book something about it (page 209). Thank you too much for completing my lecture. But... \ifcat A\noexpand\next should "return" FALSE (assuming \catcode\A=11), once \next has catcode = 16 (to \ifcat evaluation), isn't it? Sep 24 at 14:06
• Implicit tokens (those produced by \let or \futurelet) are an exception. For example \if{\bgroup (where \bgroup is defined via \let\bgroup= {) is true . Sep 24 at 14:08
• TeX never gets tired of playing weird pranks with me... A peculiar case for all commands (with some exceptions!) Sep 24 at 14:15

There are pathological cases that need to be tested for, such as how to handle a decimal point in \km. Fortunately, logic can be built into a token cycle, and tokcycle can be invoked from plain tex.

\input tokcycle.tex
\def\aborttoks{\tcpush{\empty\endtokcycraw}}
\newif\ifdotless
\xtokcycleenvironment\km
{\tctestifnum{##1<0}{%
\else\aborttoks\fi\else\tcpush{##1}\aborttoks\fi}{%
\tctestifnum{##1>9}{\aborttoks}{\addcytoks{##1}}}}
{\tcpushgroup{##1}\aborttoks}
{\tcpush{##1}\aborttoks}
{\tcpush{##1}\aborttoks}
{\dotlesstrue}

Testing \km3., \km 4000 and \km 2.3. Next sentence.

\bye


If you can use e-TeX extension (that is, pdftex), you can exploit expl3.

\input expl3-generic

\ExplSyntaxOn

\cs_new_protected:Npn \mathstyle
{
\peek_regex_replace_once:nnF
{ (.*?)(\.|\s) } % search anything up to a period or space
{ \c{ensuremath}\cB\{\1\cE\}\2 } % replace with \ensuremath{<tokens>}<period or space>
{} % do nothing if no match
}
\cs_new_protected:Npn \km
{
\peek_regex_replace_once:nnF
{ ([[:digit:]]*) }
{ \c{ensuremath}\cB\{\1\c{kilometers}\cE\} } % replace
{} % do nothing if no match
}

\cs_new_protected:Npn \ensuremath #1
{
\mode_if_math:TF { #1 } { $#1$ }
}

\cs_new_protected:Npn \kilometers { \,\rm km }

\ExplSyntaxOff

The distance between \mathstyle {\rm Point-}A to $\mathstyle {\rm Point-}B$ is \km 20, assume
that the Earth is plain and no wider than $\km 100000$.

\bye
`

As you see, the macros work both in text and in math mode.