# Non-trivial nested environment expansions with lualatex dependent packages

In the following MWE I have an environment that needs lualatex to compile.

In brief, I'm trying to create an environment that creates a very specific instance of another environment, i.e. with a bunch of default code templated.

\documentclass{article}

\usepackage{xparse}         % can exclude expl3, xparse loads it
\usepackage{xpatch}         % can exclude etoolbox, xpatch loads it

\usepackage{luatexbase}
\usepackage[program=lilypond]{lyluatex}

\DeclareDocumentCommand{\tune}{}
{%
\begin{lilypond}
% templated code will go here
}
\def\endtune
{%
% templated code will go here
\end{lilypond}
}

% this structure doesn't work either when identical code is inserted
%\NewDocumentEnvironment{tune}{}
%{%
%}
%{%
%}

\begin{document}

\begin{tune}
c'4 d' e' f' g' a' b' c''
\end{tune}

\end{document}


I know not many will have lyluatex or lilypond installed, and I know this problem isn't reproducible with every kind of nested environments (i.e. replace lilypond with itemize or figure, etc. and this will work fine), but I think I've got an expansion problem which this particular environment is sensitive to.

When I run the above MWE I get the following errors:

%\begin{lilypond} on input line 28 ended by \end{tune}. \end{tune}

%\begin{tune} on input line 28 ended by \end{document}. \end{document}

which is telling me, that \end{tune} isn't being expanded in time to prove that \end{lilypond} actually exists.

If I'm right, my question is:

Can I force this expansion somehow?

This simple example will successfully compile if the complete environment is within the macro/command, which I think confirms that there's an expansion problem with the end part. Note: I say this simple example is successful because I've seen questions warning that lilypond commands with square brackets nested in a latex macro have difficulties. I have not explored this as I don't yet have a working modular environment to explore with.

I also suspect that the extra scripting execution layer may be part of the problem, but I'm brand new to lua, and have just started reading some introductory level material.

EDIT

As it also appears that the complexity of the lilypond code will affect the approach, here is a sample lilypond block that successfully compiles in latex with lyluatex.

\documentclass{article}

\usepackage{xparse}         % can exclude expl3, xparse loads it
\usepackage{xpatch}         % can exclude etoolbox, xpatch loads it

\usepackage{luatexbase}
\usepackage[program=lilypond]{lyluatex}

\begin{document}
\noindent

\begin{lilypond}
\version "2.16"
\language "français"

tagline = ""
composer = ""
}

MetriqueArmure = {
\tempo 4=70
\time 3/4
\key do \major
}

italique = { \override Score . LyricText #'font-shape = #'italic }

roman = { \override Score . LyricText #'font-shape = #'roman }

MusiqueTheme = \relative do' {
\partial 4 do4
fa4 fa la
fa4 fa la
sol4 sol la8[( sol])
fa2 do4
fa4 fa la
fa4 fa la
sol4 sol la8[( sol])
fa2 \breathe fa4^"Refrain"
sib2 sib4
la2 la4
sol4 sol sol
do2 la4
sib2 sib4
la2 la4
sol4 sol la8[( sol])
fa2. \bar "|."
}

Paroles = \lyricmode {
L'heure e -- tait ve -- nu -- e,
ou l'ai -- rain sa -- cre,
de sa voix con -- nu -- e,
an -- non -- cait l'A -- ve.

\italique
A -- ve, a -- ve, a -- ve Ma -- ri -- a_!
A -- ve, a -- ve, a -- ve Ma -- ri -- a_!
}

\score{
<<
\new Staff <<
\set Staff.midiInstrument = "flute"
\set Staff.autoBeaming = ##f
\new Voice = "theme" {
\override Score.PaperColumn #'keep-inside-line = ##t
\MetriqueArmure
\MusiqueTheme
}
>>
\new Lyrics \lyricsto theme {
\Paroles
}
>>
\layout{}
\midi{}
}
\end{lilypond}
\end{document}


The lilypond environment uses LuaTeX callbacks to grab its contents. Inside the \lilypond command there is a switch which lets it act as an environment or as a command depending on how it is called. This screws thing up for you here. Instead you can simply use the low level commands \ly and \endly which \lilypond uses under the hood.

\documentclass{article}
\pagestyle{empty}

\usepackage{xparse}         % can exclude expl3, xparse loads it
\usepackage{xpatch}         % can exclude etoolbox, xpatch loads it

\usepackage{luatexbase}
\usepackage[program=lilypond]{lyluatex}

\NewDocumentEnvironment{tune}{}{\ly}{\endly}

\begin{document}

\begin{tune}
c'4 d' e' f' g' a' b' c''
\end{tune}

\end{document}


• Thanks for such a rapid answer! This is really really neat. What kind of limitations will there be for the contents of BODY or within the templated sections that will surround BODY. I pulled a more complex example from the lyluatex test.tex page and saw that this extra level of complexity resulted in Undefined Control Sequence Errors. I still have a ways to go in troubleshooting down to the exact syntax bits that cause the failure. There's no shortage of possibilities I anticipate, seeing how lilypond uses <<>> encapsulations and \score, \midi{}, \layout{} type commands. – EngBIRD May 11 '18 at 3:30
• @EngBIRD Actually my answer was just wrong. Please see the updated answer for a working and robust solution. – Henri Menke May 11 '18 at 4:58
• Thanks, This is really handy information about the under the hood code. It indeed works for even the more complex example I updated my question with. In playing around with moving code to the template area, I've noticed that special character sequences, based on # in particular will cause problems because they are expected to be used when providing arguments, which I will likely want to preserve. I'll play around with catcode and input/include commands to see if any of these provide an easy fix, but your answer takes care of the hard part. THANKS! – EngBIRD May 12 '18 at 0:16
• Because I originally thought that my question was generic, and wouldn't have a lilypond based solution, if you want to include a second answer with your environ package information, I would also upvote that one as well. It contained useful information that could get hits from the broader question title. – EngBIRD May 12 '18 at 0:16