10

The following code works, but if you comment line 20 and uncomment line 21 LaTeX complains about \endmap being undefined. I do not really understand why that is and even less what to do about it.

\documentclass{article}
\usepackage[]{a4wide}
\usepackage[left]{lineno}

\newcommand{\define}{\newcommand}

\def\speakify#1:#2\par{{\itshape#1}: #2\par}
\def\map#1\par{\ifx\endmap#1\else\do#1\par\expandafter\map\fi}

\def\interviewstart{\linenumbers\let\do\speakify\map}
\def\interviewstop{\par\endmap\par\nolinenumbers}

\begin{document}

%\linenumbers\let\do\speakify\map
\interviewstart
I: I have a question for you that I would really like to know the answer to. Could you please explain it in your own words while the moon is still high in the sky? That would be lovely thank you very much!

L: Of course I will answer gladly all questions you might have no matter how silly I feel they may be provided payment is made in full before commencement of the answering phase.
\par\endmap\par\nolinenumbers % line 20
%\interviewstop % line 21

\end{document}
  • I don't see where \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 ;-) – user31729 Jul 6 '15 at 18:28
  • @ChristianHupfer I would think \endmap doesn't have to be defined -- its only use (that I can see) is with \ifx. – Sean Allred Jul 6 '15 at 18:50
  • @SeanAllred: Yes, agreed. But it still complains about the undefined cs then – user31729 Jul 6 '15 at 18:54
  • why are you using primitive tex delimited argument definitions in what purports to be a latex document? Why not define interview to be an environment? – David Carlisle Jul 6 '15 at 22:01
  • @David: Using an environment it fails in the same way and if you then provide the substitution manually the environment would be unclosed. I admit I did not try this. I am using latex because the lineno package does not seem to work under tex, so my use of primitive tex macros stems from starting this as a tex document. – hkBst Jul 8 '15 at 5:08
8

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}

enter image description here

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}
  • thanks for your answer. Your thorough explanation of the expansion process has helped me understand why this specific error comes up. And in case it turns out I cannot use \everypar after all it will be very useful to have your solution. In effect \interviewstart does the expansion of \interviewstop in your solution. I only wonder if I should worry that scooping up the entire interview in a macro argument will cause tex to exhaust some of its buffers for very long interviews. – hkBst Jul 8 '15 at 5:33
  • A refinement that I am interested in would be to allow any number of newlines between the final paragraph and \interviewstop and if possible also between paragraphs. – hkBst Jul 8 '15 at 10:01
  • @hkBst Add the check whether the argument is blank and, in this case, discard it and resume the recursion. – egreg Jul 8 '15 at 10:07
8

The normal latex syntax for this would be an environment:

enter image description here

\documentclass{article}
\usepackage[]{a4wide}
\usepackage[left]{lineno}


\newenvironment{interview}{\linenumbers\everypar{\additalic}}{\par}
\def\additalic#1:{\textit{#1}:}

\begin{document}


\begin{interview}
I: I have a question for you that I would really like to know the answer to. Could you please explain it in your own words while the moon is still high in the sky? That would be lovely thank you very much!

L: Of course I will answer gladly all questions you might have no matter how silly I feel they may be provided payment is made in full before commencement of the answering phase.
\end{interview}

\end{document}

Actually I would have used a syntax such as

\item[L] I have a question....

then you would not have needed any low level delimited macros or \everpar tricks.


In comments you ask for a variant that sets the paragraph in italic and the label in bold.

\newenvironment{interview}{\linenumbers\itshape\everypar{\additalic}}{\par}
\def\additalic#1:{\textup{\textbf{#1}}:}
  • thanks for your answer. It seems I stared myself blind trying to reimplement \everypar without realizing it. Thanks for opening my eyes. The reason I did not use \item is that I am exploring something called minimal markup which seems useful to aspire to and should hopefully make it easier for my wife to start using (la)tex. – hkBst Jul 8 '15 at 5:12
  • @hkBst actually I believe it's easier to learn a language that uses consistent syntax rather than ad hoc syntax, if every package ignores the language conventions and just implements its own micro-parser then the document structure as a whole is far harder to learn. – David Carlisle Jul 8 '15 at 7:22
  • It seems you skirted part of the problem by simplifying my speakify macro. Can you change your answer to use \def\speakify#1:#2\par{{\bfseries#1}:{\itshape#2}\par} or similar without adding an explicit \par or newline before \end{interview}? – hkBst Jul 8 '15 at 7:30
  • @hkBst I can make it do that (will update in a bit) but that isn't what your original code did. In your original, egreg's answer and mine; the body of the paragraph was not styled, and the word before the colon is in italic. – David Carlisle Jul 8 '15 at 8:16
  • @hkBst added to the answer. – David Carlisle Jul 8 '15 at 8:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.