# How can I condition on the last token?

I would like to write a command \foo which, for the sake of simplicity, does just one thing, which is make sure its argument ends with a dot. Thus both foo{hello world} and foo{hello world.} expand to hello world. (or equivalent).

How do I do this? Is there some kind of "if-last-token" conditional I could use?

• Are you perhaps looking for a command that adds punctuation if not explicitly given in the argument? – egreg Jul 18 '18 at 8:19
• For punctuation marks in particular there are good tricks to achieve this, but they may be impractical for other uses. I saw an answer by Ulrike Fischer about this a few days ago, maybe I can find it. Here its is: tex.stackexchange.com/q/4834/35864 – moewe Jul 18 '18 at 8:23

You can define a \stripdot command, which does the opposite of what you asked for - stripping a final "." - in two lines using only TeX primitives:

\def\stripdot#1{\stripdotA#1\end.\end!{#1}}
\def\stripdotA#1.\end#2!#3{\ifx!#2!#3\else#1\fi}

given this command, defining \foo is now trivial:

\def\foo#1{\stripdot{#1}.}
\foo{Hello world.}

\foo{Hello world}

\bye
• Cool! You can use this for implementing a test for trailing dots where \end is the only sentinel-token that must not occur in the argument. ;-) – Ulrich Diez Jul 18 '18 at 20:11
• \stripdot might strip one level of braces: \edef\test{\stripdot{{In Braces?}.}}{\tt\meaning\test}\bye – Ulrich Diez 53 mins ago – Ulrich Diez Jul 18 '18 at 21:35
• @wipet, Do you have any idea why sharelatex's compiler doesn't like this code? – einpoklum Jul 21 '18 at 18:16
• What says log file? Maybe sharelatex is only LaTeX specific... – wipet Jul 21 '18 at 18:21

I suspect this is an XY-question. Anyway, here's an expandable solution (add to the tests as needed).

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewExpandableDocumentCommand{\foo}{m}
{
\str_case_x:nnF { \tl_item:fn { #1 } { -1 } }
{
{.}{Ends~with~period}
{,}{Ends~with~comma}
{?}{Ends~with~question~mark}
}
{No~punctuation}
}
\cs_generate_variant:Nn \tl_item:nn { f }

\ExplSyntaxOff

\newcommand{\withperiod}{This ends with a period.}

\newcommand{\noperiod}{This doesn't end with a period}

\begin{document}

\foo{This ends with a period.} (period)

\foo{\withperiod} (period)

\foo{This ends with a comma,} (comma)

\foo{This ends with a question mark?} (question mark)

\foo{Foo} (no punctuation)

\foo{\noperiod} (no punctuation)

\edef\test{\foo{\withperiod}}\texttt{\meaning\test}

\end{document}

Here's a possibly more useful implementation: if the argument ends with .!?, nothing is added, otherwise a period is added.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewExpandableDocumentCommand{\foo}{m}
{
\str_case_x:nnF { \tl_item:fn { #1 } { -1 } }
{
{.}{#1}
{?}{#1}
{!}{#1}
}
{#1.}
}
\cs_generate_variant:Nn \tl_item:nn { f }

\ExplSyntaxOff

\newcommand{\withperiod}{This ends with a period.}

\newcommand{\noperiod}{This will end with a period}

\begin{document}

\foo{This ends with a period.}

\foo{\withperiod}

\foo{This ends with an exclamation mark!}

\foo{Does this end with a question mark?}

\foo{This will end with a period}

\foo{\noperiod}

\edef\test{\foo{\withperiod}}\texttt{\meaning\test}

\end{document}

There is also a classical way to do this.

\documentclass{article}

\makeatletter
\makeatother

\newcommand{\withperiod}{This ends with a period.}

\newcommand{\noperiod}{This will end with a period}

\begin{document}

\foo{This ends with a period.}

\foo{\withperiod}

\foo{This ends with an exclamation mark!}

\foo{Does this end with a question mark?}

\foo{This will end with a period}

\foo{\noperiod}

\end{document}

Apart from Detect beginning of a sentence in a macro for capitalization you could also use \IfEndWith from the xstring package:

\documentclass{article}
\usepackage{xstring}
\newcommand{\foo}[1]{\IfEndWith{#1}{.}{#1}{#1.}}
\begin{document}
\foo{hello world}

\foo{hello world.}
\end{document}

This uses listofitems to accomplish the task, and can be set up to intercept multiple qualifying end-punctuation (here ., ? and !).

\documentclass{article}
\usepackage{listofitems}
\newcommand\foo[1]{%
\setsepchar{.||?||!}% <-- LIST ALL PUNCTUATION THAT QUALIFES AS ENDING
\foreachitem\x\in\myarg[]{%
\x%
\ifnum\xcnt=\listlen\myarg[]\relax%
\if\relax\x\relax\else.\fi%
\else%
\myargsep[\xcnt]%
\fi%
}%
}
\begin{document}
\foo{Hello world}\par
\foo{Hello world.}\par
\foo{Stop. This. Now}\par
\foo{Stop. This. Now.}\par
\foo{Stop. This. Now!}\par
\foo{well...that is that}\par
\foo{well...that is that.}\par
\foo{well...how is that?}
\end{document}

If you wish to look at the sequence of tokens that forms the argument of \foo unexpanded, you can add a trailing dot to that argument and then run a loop which removes dot-delimited arguments until removing another dot-delimited argument would yield emptiness.

Then examine whether the remaining dot-delimited argument is empty.
If so, the last token of the argument of \foo was a dot.
If not so, the last token of the argument of \foo was not a dot.

\documentclass{article}
\makeatletter
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%%
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\expandafter\expandafter\@firstoftwo{ }{}%
\@secondoftwo}{\expandafter\expandafter\@firstoftwo{ }{}\@firstoftwo}%
}%
%%----------------------------------------------------------------------
%% Check whether argument's last token is a dot:
%%......................................................................
%% \UD@CheckWhetherTrailingDot{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       does have a last token which is a dot>}%
%%                     {<Tokens to be delivered in case that argument
%%                       does not have a last token which is a dot>}%
%%
\newcommand\UD@CheckWhetherTrailingDot[1]{%
\romannumeral0\UD@CheckWhetherNull{#1}%
{\@secondoftwo}%
{\UD@CheckWhetherTrailingDotGobbleToDotLoop{#1.}}%
{\expandafter\expandafter\@firstoftwo{ }{}\@firstoftwo}%
{\expandafter\expandafter\@firstoftwo{ }{}\@secondoftwo}%
}%
%%%%
\newcommand\UD@CheckWhetherTrailingDotGobbleToDotLoop[1]{%
\expandafter\UD@CheckWhetherNull
\expandafter{\UD@CheckWhetherTrailingDotGobbleToDot#1}{%
\UD@CheckWhetherTrailingDotFork{.}#1%
}{%
\expandafter\UD@CheckWhetherTrailingDotGobbleToDotLoop
\expandafter{\UD@CheckWhetherTrailingDotGobbleToDot#1}%
}%
}%
%%%%
\newcommand\UD@CheckWhetherTrailingDotGobbleToDot{}
\long\def\UD@CheckWhetherTrailingDotGobbleToDot#1.{}
%%%%
\newcommand\UD@CheckWhetherTrailingDotFork{}%
\long\def\UD@CheckWhetherTrailingDotFork#1.{%
\expandafter\UD@CheckWhetherNull\expandafter{\@gobble#1}%
}%
%%%%
\newcommand\foo[1]{%
\UD@CheckWhetherTrailingDot{#1}{#1}{#1.}%
}%
\makeatother

\begin{document}

With \verb|\foo| an empty argument is recogniced as an argument
that dose not have a trailing dot:

\verb|\foo{}|: \foo{}

\verb*|X\foo{ }X|: X\foo{ }X

\verb|\foo{.}|: \foo{.}

\verb|\foo{Bla. Bla.}|: \foo{Bla. Bla.}

\verb|\foo{Bla. Bla}|: \foo{Bla. Bla}

\verb|\foo{Bla. {Bla.}}|: \foo{Bla. {Bla.}}

\verb|\foo{hello world}|: \foo{hello world}

\verb|\foo{hello world.}|: \foo{hello world.}

With \verb|\foo| dots nested in braces will not count as trailing dots as
in this case the last token of the argument is not a dot but a closing brace:

\verb|\foo{hello world{.}}|: \foo{hello world{.}}

\end{document}

The gist of the following solution, which is much better, comes from the answer of wipet :

Choose a <sentinel token> which must not occur within the argument to examine (unless nested in braces).

Append <sentinel token>.<sentinel token><delimiter> (<delimiter> other than . and other than <sentinel token> and other than .<sentinel token>) behind the argument to examine before fetching a first argument delimited by .<sentinel token> and fetching a second argument delimited by <delimiter>.

In case the last token of the argument to examine is a dot, that dot and the first <sentinel token> from the appended sequence form the delimiter for that first argument to fetch, and the second argument is not empty but holds the sequence .<sentinel token>.

In case the last token of the argument to examine is not a dot, the dot from the appended sequence and the second <sentinel token> from the appended sequence form the delimiter for that first argument to fetch, and the second argument is empty.

\documentclass{article}

\makeatletter
\newcommand\Wipet@CheckWhetherTrailingDot[1]{%
\Wipet@CheckWhetherTrailingDotA#1\end.\end!%
}%
\newcommand\Wipet@CheckWhetherTrailingDotA{}%
\long\def\Wipet@CheckWhetherTrailingDotA#1.\end#2!{%
\ifx!#2!\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
}%
\newcommand\foo[1]{%
\Wipet@CheckWhetherTrailingDot{#1}{#1}{#1.}%
}%
\makeatother

\begin{document}

With \verb|\foo| an empty argument is recogniced as an argument
that dose not have a trailing dot:

\verb|\foo{}|: \foo{}

\verb*|X\foo{ }X|: X\foo{ }X

\verb|\foo{.}|: \foo{.}

\verb|\foo{Bla. Bla.}|: \foo{Bla. Bla.}

\verb|\foo{Bla. Bla}|: \foo{Bla. Bla}

\verb|\foo{Bla. {Bla.}}|: \foo{Bla. {Bla.}}

\verb|\foo{hello world}|: \foo{hello world}

\verb|\foo{hello world.}|: \foo{hello world.}

With \verb|\foo| dots nested in braces will not count as trailing dots as
in this case the last token of the argument is not a dot but a closing brace:

\verb|\foo{hello world{.}}|: \foo{hello world{.}}

\end{document}

What's embarrassing for me is that when writing my first solution, which is cumbersome, I did not remember the method shown by wipet although it is well-known.

E.g., for several years in the early nineties the late Michael Downes presented a series of TeX macro programming challenges he called Around the Bend.