# Getting the last token of a macro argument

It is easy to get the first token of a macro argument. Ignoring some irrelevant complications, you do something like this:

\def\firsttokof#1{\first(#1)}
\def\first(#1#2){#1}

It's equally easy to get all the remaining tokens after the first one:

\def\nonfirsttoksof#1{\rest(#1)}
\def\rest(#1#2){#2}

As I understand it, this is because TeX does pattern matching from left to right, taking the shortest possible match. In \first and \rest, the shortest possible match for #1 will always be a single token.

But how can you get the last token of a macro argument?

-
Related question: How does one remove material from a token list?. The answers are mostly about removing a single token from the end of a list, so it's more or less the same issue :-) – Stephan Lehmke Jul 18 '12 at 17:55
basically you need to keep taking the first until there is nothing left and then the last is the last one you took off. – David Carlisle Jul 18 '12 at 17:57
This is no doubt one of the irrelevant complications, but I want to point out that this method will only capture the first group in #1; i.e. \firsttokof{{12}34} = 12. Getting rid of those braces is a lot of work. – Ryan Reich Jul 18 '12 at 17:58
Actually, first/last group rather than first/last token is what I want. So you're right as far as terminology goes, but it's not an issue for the problem I'm actually trying to solve. – Dan Velleman Jul 18 '12 at 23:20

Assuming that you need only parameterless macros, here's a way that recognizes also a trailing space or closed brace.

\documentclass{article}
\usepackage{expl3,l3regex,l3str}

\def\A{abc}
\def\B{ab{c}}
\def\C{ab{c} }
\def\D{ab\linebreak}

\ExplSyntaxOn
\cs_new_protected:Npn \velleman_grab_last:N #1
{
\tl_set:Nx \l_velleman_testz_tl { \token_get_replacement_spec:N #1 }
\tl_set_eq:NN \l_velleman_testy_tl \l_velleman_testz_tl
\regex_replace_once:nnN { \A .* (.) \Z } { \1 } \l_velleman_testz_tl
\regex_replace_once:nnN { \A .* (.) . \Z } { \1 } \l_velleman_testy_tl
\prg_case_str:xxn { \l_velleman_testz_tl }
{
{ \c_rbrace_str }{ \tl_set:Nn \l_velleman_last_tl { \c_group_end_token } }
{ \c_space_tl }{ \tl_set:Nn \l_velleman_last_tl { \c_space_token } \velleman_test_last:N #1 }
}
{
\tl_set:Nx \l_velleman_last_tl { \tl_item:Vn #1 { -1 } }
}
\tl_show:N \l_velleman_last_tl
}
\cs_new:Npn \velleman_test_last:N #1
{
\str_if_eq:xxF { \l_velleman_testy_tl } { \c_rbrace_str }
{
\tl_set:Nx \l_velleman_testz_tl { \tl_item:Vn #1 { -1 } }
\token_if_cs:VT \l_velleman_testz_tl { \tl_set:NV \l_velleman_last_tl \l_velleman_testz_tl }
}
}

\cs_generate_variant:Nn \tl_item:nn {V}
\cs_generate_variant:Nn \token_if_cs:NT {V}

\velleman_grab_last:N \A
\velleman_grab_last:N \B
\velleman_grab_last:N \C
\velleman_grab_last:N \D

\ExplSyntaxOff

With the help of \regex_replace_once:nnN we leave in \l_velleman_test the last item in the "meaning" of the control sequence. Then we sort out the cases. In the case the last item is a space, another check has to be done; we keep also the last but one item; if it's a brace then the last item is surely a space; otherwise we look whether it's a control sequence.

There is still some small problem, but this should be enough for a start. For example the macros don't work for \space and the last test is inaccurate if the trailing space follows a control symbol.

-

With all due respect to egreg's impressive answer, I think reversing the token list and grabbing the now-first argument might be slightly easier albeit less general:

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\cs_new:Npn \my_tl_last:N #1
{
\tl_reverse:N #1
}
\tl_set:Nn \l_tmpa_tl {ab{cd}ef}
\my_tl_last:N \l_tmpa_tl
\ExplSyntaxOff
\end{document}

Storing the result might need a slight alteration depending on what you're looking for…

-

This answer is based on egreg's, simplified. If one wants to get the last item (brace group or single non-space non-[begin/end]-group token) in a list of tokens, simply use \tl_item:Nn \foo { -1 }. If one wants to get the last token, the easiest way is to use the (experimental) l3regex module, as egreg noted. Here I define \velleman_get_last:nN, which expects two arguments: some tokens, and a control sequence in which to store the result.

In most cases, \regex_extract_once:nnN { . \Z } { <tokens> } \result will do the trick: the regular expression means "any token (.), followed by the end (\Z) of the input ". The line just below that converts from the result of \regex_extract_once:nnN (currently a sequence) to a token list. The only case that needs to be treated specially is when the last token is an explicit end-group character. This cannot be put into a macro to give the result: we test for that with \regex_match:nnTF { \cE. \Z }, where the regex means "a catcode (\c) end-group (E) token with arbitrary character code (.), followed by the end (\Z) of the input", and in that case, we put \c_group_end_token, an implicit end-group token, into the token list.

\documentclass{article}
\usepackage{expl3,l3regex,l3str}

\def\A{abc}
\def\B{ab{c}}
\def\C{ab{c} }
\def\D{ab\linebreak}

\ExplSyntaxOn
%
\seq_new:N \l_velleman_last_seq
\tl_new:N \l_velleman_last_tl
\cs_new_protected:Npn \velleman_get_last:nN #1#2
{
\regex_match:nnTF { \cE. \Z } {#1}
{ \tl_set:Nn #2 { \c_group_end_token } }
{
\regex_extract_once:nnN { . \Z } {#1} \l_velleman_last_seq
\tl_set:Nx #2 { \seq_item:Nn \l_velleman_last_seq { 1 } }
}
}
\cs_generate_variant:Nn \velleman_get_last:nN { V }
%
\cs_new_protected:Npn \test:N #1
{
\velleman_get_last:VN #1 \l_tmpa_tl
\msg_term:n
{
Last ~ item: ~ ' \tl_item:Nn #1 { -1 } ' \\
Last ~ token: ~ ' \tl_to_str:N \l_tmpa_tl '
}
}
\test:N \A
\test:N \B
\test:N \C
\test:N \D
\ExplSyntaxOff

\stop
-