5

This is basically a question about TeX's error messages.

I am confused by a cryptic error message that TeX gives me when I try to do the above. I have tried to isolate it, but have come no further than the following:

\def\foo#1\par{\vbox{#1}}
\def\bar#1\par{\hbox{#1}}

\let\macro\foo
\macro A paragraph consisting of text.

\let\macro\bar
\macro A paragraph consisting of text.

\everypar={\macro}
A paragraph consisting of text.

\let\macro\foo
A paragraph consisting of text.

\bye

This is all accepted by TeX, except the last example, which gives the error message:

! Argument of \macro has an extra }.
<inserted text> 
            \par 
<to be read again> 
               }
l.16 

"l. 16" points to the line before the \bye, i.e. to the moment where TeX encounters two consecutive newlines (causing it to insert a \par token, I assume).

I am aware that \everypar is inserted after the horizontal list has begun, and that it is not a very wise and clean idea to gobble up the paragraph after it has begun (so "the rest of the paragraph" would be the better description) and to typeset it inside a vbox. But then

\indent\vbox{This is some text.}

is accepted. Of course, if you typeset a whole paragraph inside a vbox inside the paragraph, the whole paragraph is typset to \hsize and shifted to the right by \parindent as a whole, which looks awful. But that is not my point.

What I would like to understand is why it is apparently not possible to a) put a macro in \everypar which b) gobbles up the whole (or rather: rest of the) paragraph and c) puts it into a vbox, while all of these things are apparently possible in isolation? In particular, if we only change c) to an hbox instead of a vbox, the error message disappears. In other words: Why does TeX suddenly see an extra } when we use a vbox?

As I have just typed all of this, something is beginning to dawn on me: Is the problem due to the fact that inside a vbox, TeX starts a new horizontal list, inserting the contents of \everypar once more, triggering an endless recursion? In that case, it would have nothing to do with the \par as an argument delimiter but simply with the fact that within \everypar, one should not start a new paragraph. Indeed, if I say

\def\macro#1\par{\vbox{\noindent}#1}

(assuming \macro is still in \everypar as above), the same error happens when the next paragraph is typeset.

So now it seems to me I have understood where the error comes from, but what does the error message mean? Why does TeX see an extra }? And why does it exactly report it after it has gobbled up the argument to \macro? If I say

\everypar={\vbox{\noindent}}
A paragraph consisting of text.

I get the error message

! TeX capacity exceeded, sorry [semantic nest size=500].
<everypar> \vbox {\noindent 
                            }
[many similar lines left out]
...
l.17 A
       paragraph consisting of text.

which seems clear in the context of what I seem to have figured out above.

Edited in response to egreg's excellent explanations:

[As to the point that my code as given above indeed does NOT cause any error messages; it does so -- and illustrates the problem I had -- if you put two empty lines between the examples instead of one.]

Your tip of putting \everypar={} inside the vbox is great, because that is local to the vbox, right?

What I would still love to know, however, is why in some examples, TeX reports an exceeded "nest size", which I find understandable, and in others (where the reason is the same), that the argument to the macro in everypar has an extra }. I know that TeX's error messages are notoriously cryptic, but I would like to know if it might be possible to make some sense of them anyway.

I will have a try:

When, after \everypar has been set to {\macro}, TeX encounters the next paragraph, it executes and expands \macro, which gobbles up the paragraph (and discards the implicit \par at the end) and then opens a vbox in which it puts the paragraph. Because the paragraph text in the vbox starts a new paragraph, \everypar is executed again, calling \macro again, which again gobbles up the paragraph text, but encounters the closing } of the vbox before any \par token. Thus, it is the second call of \macro which triggers this error message.

Is that the whole story? I would actually have expected that a closing } in a vbox should cause an implicit \par.

  • Welcome to TeX.SX! I get no error from the first code. – egreg Jul 28 '14 at 8:45
5

I get no error with the first example, unless I add a blank line between \let\macro\foo and A:

\def\foo#1\par{\vbox{#1}}
\def\bar#1\par{\hbox{#1}}

\let\macro\foo
\macro A paragraph consisting of text.

\let\macro\bar
\macro A paragraph consisting of text.

\everypar={\macro}
A paragraph consisting of text.

\let\macro\foo

A paragraph consisting of text.

\bye

Why? The \par preceding \let\macro\foo has been gobbled by \bar, so no new paragraph is started, in your example.

Now, why the error? By the same reason! When \foo is executed (under the name of \macro), the \par preceding \bye is gobbled as part of the parameter text, so when A is rescanned in the \vbox a paragraph is started and \foo is expanded again, but a trailing \par is missing. Hitting return will start an infinite loop.

This doesn't happen with \hbox because \everypar is not executed inside it.

“Solution”:

\def\foo#1\par{\vbox{\everypar{}#1}}

Of course, this will produce an overfull box, because of the indentation caused by the start of the paragraph and the fact that the \vbox will be \hsize wide.


You can't “solve” the issue by adding \par to the definition of \foo:

\def\foo#1\par{\vbox{#1\par}}

because this will cause

! TeX capacity exceeded, sorry [semantic nest size=500].
<to be read again> 
                   A
<argument> A

The reason is again that A will trigger a new paragraph, so a call of \foo and hence the opening of a \vbox where the A will be read again, triggering a new paragraph, so a call of \foo

In this case, the program stops when 500 levels of boxing are attempted. The same problem explains the case you are getting the same error.

| improve this answer | |
  • Thank you so much! Indeed the code above is not 100% identical to the code I executed -- I stripped empty lines to make it less long. I did not notice this would make a difference, but now I completely get it. – Florian v. Savigny Jul 28 '14 at 13:01

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.