There is no \\
delimiter in the code you're using, so the first argument can be found, but not the second and the file ends during the search for \\
.
In other words, with your definition of \test
the first argument ends with the first period (at brace level 0), the second at the first appearance of \\
which isn't there.
Moreover, in the second call there is no period, so the first argument can't be found.
You have to do it more cautiously; something like this:
\def\test#1{\xtest#1.\\}
\def\xtest#1.#2\\{%
\ifx\\#2\\%
\typeout{<no extension>}%
\else
\typeout{<extension is \removedot#2\\>}
\fi}
\def\removedot#1.\\{#1}
and call it as
\test{file.tex}
\test{file}
Let's see the various cases:
\test{file.tex}
becomes
\xtest file.tex.\\
which results in assigning
#1 = file, #2 = tex.
The final dot will be removed by \removedot
.
The call
\test{file}
will become
\xtest file.\\
resulting in
#1 = file, #2 = <empty>
as desired.
A perhaps easier parsing can be obtained with the help of xparse
:
\usepackage{xparse}
\NewDocumentCommand{\test}{>{\SplitArgument{1}{.}}m}{\xtest #1}
\NewDocumentCommand{\xtest}{mm}
{%
\IfNoValueTF{#2}
{\typeout{No extension}}
{\typeout{Extension is #2}}%
}
This spares from the \removedot
postprocessing.
\test myfile
does not match the macro definition because it must include a dot and `\\`.