7

I have two files. Basically, the main file just loads the environ package and inputs the secondary file. The secondary file creates an environment that basically does nothing, and creates one instance of that environment:

main.tex:

\documentclass{scrartcl}
\usepackage{environ}
\begin{document}
  \input{secondary.tex}
\end{document}

secondary.tex:

\NewEnviron{myenv}{\BODY}
\begingroup
  \myenv
    Hello
  \endmyenv
\endgroup

I get the following error message:

Runaway argument?
Hello \endmyenv \endgroup \par
! File ended while scanning use of \document.
<inserted text>
                \par
l.5 \input{secondary.tex}

The example works as expected when I make any (!) of the following changes:

  • Use \newenvironment instead of \NewEnviron
  • Use \begin{myenv}...\end{myenv} instead of \begingroup\myenv...\endmyenv\endgroup
  • Place the contents of secondary.tex directly in main.tex instead of inputting secondary.tex

Why does this not work?

2 Answers 2

7

environ creates an environment that uses TeX parameter text in definitions to capture the environment contents. This parameter-text style definition allows the package to capture the environment contents, but it also requires a very strict usage of the macros/definitions... something you're not adhering to when using a command-style environment (\myenv...\endmyenv rather than \begin{myenv}...\end{myenv}).

Broadly speaking, \NewEnviron{myenv} creates a macro \myenv which designates the start of the environment, and this macro is on the lookout for exactly \end{myenv}... nothing else.

The example fails when using \input since there's no explicit \end{<something>} that \myenv is searching for. It reaches the end of secondary.tex before it ever finds any resemblance of \end. It's actually looking for \end{document} (see below).

Let's look at the three instances you mention that corrected the problem and see why that's the case:

  • Use \newenvironment instead of \NewEnviron

    It should be obvious why this works. \newenvironment{myenv} defines both \myenv and \endmyenv, so using the command-definition of the environment should work without problem;

  • Use \begin{myenv}...\end{myenv} instead of \begingroup\myenv...\endmyenv\endgroup

    This is exactly what environ expects you should use when you define an environment using \NewEnviron. So, this works out-of-the-box, since you've explicitly used the environment termination \end{myenv};

  • Place the contents of secondary.tex directly in main.tex instead of inputting secondary.tex

    Since you're using the command-form of an environment, the "current environment" (known to LaTeX as \@currenvir) never really changes. That is, it remains as being inside the document environment (due to a call \begin{document}). So, your end-of-environment capture is actually \end{document} according to newenviron's setup. Your environment "starts" with \myenv and ends with \end{document}. Weird, but that's how it works when you don't use the expected environment scoping \begin{...}...\end{...}.

For it to work properly within the context of newenviron, use the scoping \begin{myenv}...\end{myenv}. This scoping provides the necessary grouping you implement through \begingroup...\endgroup.

Alternatively, define \myenv to capture stuff until it reaches \endmyenv:

\documentclass{scrartcl}
\usepackage{filecontents}
\begin{filecontents*}{secondary.tex}
\long\def\myenv #1\endmyenv{#1}% Fake myenv environment that captures its body
\begingroup
  \myenv
    Hello
  \endmyenv
\endgroup
\end{filecontents*}
\begin{document}
\input{secondary.tex}
\end{document}
5
  • Thanks for the comprehensive explanation. On part that is still not clear is that first you say that the "macro is on the lookout for exactly \end{myenv}...nothing else", and then you say that the "end-of-environment capture is actually \end{document}". I get the first part, thanks to your alternative example, but the second part seems to contradict the first part.
    – Martin
    Dec 18, 2014 at 18:57
  • Would there be a problem if \myenv checked \@currenv and then if \@currenv = myenv => look for \end{myenv}; and if \@currenvmyenv then look for \endmyenv?
    – Manuel
    Dec 18, 2014 at 18:59
  • @Manuel it would be very expensive in terms of complexity, currently tex can use a primitive delimited argument to skip to the next \end and then it just has to check the argument to check it is the right thing. To stop at \endmyenv it would have to read every token one by one. Dec 18, 2014 at 19:05
  • @DavidCarlisle I mean, define 2 auxiliar macros to pass the “content of the environment” to a third one. And then \myenv checks \@currenv and calls either one (which looks for \end{myenv}) or the other (which looks for \endmyenv) and then “the content” would be passed to a third one. I was asking more of trivial problems that would be created because of this (which I can't think of), I think it's “easy task” to define those macros.
    – Manuel
    Dec 18, 2014 at 19:08
  • @Manuel ok but it would break the common use of using it in another macro, eg tabularx does similar and you can use \tabularx \endtabularx in the definition of mytable because the scanner knows to look for the end of the current environment Dec 18, 2014 at 19:11
4

It does not work if you place the content in the main file. It only don't give an error. Try this:

\documentclass{scrartcl}
\usepackage{environ}
\begin{document}
\NewEnviron{myenv}{BEGIN\BODY END}
\begingroup
  \myenv
    Hello
  \endmyenv
\endgroup

World

\end{document}

Environ needs to find \end{myenv} to stop the parsing.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .