Your \map
command has one argument delimited by \par
.
When you do
\interviewstart
I: I have a question ...
L: Of course I will answer ...
\interviewstop % line 21
\end{document}
the macro \interviewstart
is replaced by
\linenumbers\let\do\speakify\map
and the argument to \map
is taken to be
I: I have a question ...
Since I
is not equal to \endmap
, the false branch is followed, eventually leading to another \map
macro.
What happens now is that the argument to \map
is
L: Of course I will answer ... \interviewstop
because TeX doesn't expand tokens when absorbing macros and the blank line before \end{document}
provides the \par
delimiter.
Again, L
is not \endmap
, so the false branch is followed and in the course of expanding the tokens TeX comes to \interviewstop
, whose replacement text contains \endmap
, which is not defined.
When you have the explicit \par\endmap\par\nolinenumbers
, there is no problem, because the first \par
is the argument delimiter and the next \map
command will absorb \endmap
as its argument. Since you compare two undefined tokens, the true branch is followed, leading to a happy end.
What you have to do is to absorb the whole text from \interviewstart
to \interviewstop
and then do the work on the absorbed tokens.
\documentclass{article}
\usepackage[left]{lineno}
\def\speakify#1: #2\par{\textit{#1}: #2\par}
\long\def\map#1\par{%
\ifx\interviewstart#1%
\else
\speakify#1\par
\expandafter\map
\fi
}
\long\def\interviewstart#1\interviewstop{%
\linenumbers
\map#1\par\interviewstart\par
\nolinenumbers
}
\begin{document}
\interviewstart
I: I have a question ...
L: Of course I will answer ...
\interviewstop
\end{document}

This version allows any number of blank lines
\documentclass{article}
\usepackage[left]{lineno}
\def\speakify#1: #2\par{\textit{#1}: #2\par}
\long\def\map#1\par{%
\ifx\hfuzz#1\hfuzz
\let\next\map
\else
\ifx\interviewstart#1%
\let\next\relax
\else
\speakify#1\par
\let\next\map
\fi
\fi
\next
}
\long\def\interviewstart#1\interviewstop{%
\linenumbers
\map#1\par\interviewstart\par
\nolinenumbers
}
\begin{document}
\interviewstart
I: I have a question ...
L: Of course I will answer ...
\interviewstop
\end{document}
An environment is, however, much better.
\documentclass{article}
\usepackage[left]{lineno}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentEnvironment{interview}{m}
{
\par
\linenumbers
\keys_set:nn { hkbst/interview } { #1 }
}
{
\par
}
\NewDocumentCommand{\Q}{}{\par\textit{\l_hkbst_questioner_tl}:~}
\NewDocumentCommand{\A}{}{\par\textit{\l_hkbst_answerer_tl}:~}
\keys_define:nn { hkbst/interview }
{
q .tl_set:N = \l_hkbst_questioner_tl,
a .tl_set:N = \l_hkbst_answerer_tl,
}
\ExplSyntaxOff
\begin{document}
\begin{interview}{q=I,a=L}
\Q I have a question ...
\A Of course I will answer ...
\end{interview}
\end{document}
\endmap
is defined. Other than with\newcommand
there is no implicit\end...
command defined when\def
is used, but otherwise line 20 should not work either ;-)\endmap
doesn't have to be defined -- its only use (that I can see) is with\ifx
.