1

Once again, I have a macro problem. It works almost. I spend a lot of time googling about \expandafter, \noexpand, \unexpanded and others. I used some of them, but nothing lead to the desired result.

If you find a better title for this problem, change it. I used the best, I could think of.

The following is my code:

\documentclass[12pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[german]{babel}
\usepackage{graphicx}
\usepackage{environ}



\makeatletter
%%%Liederbuch. This works!!!
\NewEnviron{Liederbuch}[1]{
\xdef\LB@my@temp{
\noexpand\newcommand{\csname LH#1\endcsname}[2]{
\unexpanded\expandafter{\BODY}
}
}
\aftergroup\LB@my@temp
}

%%%%%%%%%%%%%%%%%
%The problem starts in the following Environ:

%If I replace `##1` and `##2` with constants, it works, but therefore it fails the basic intention of this effort.

%To see, what should happen, have a look at `\begin{document}`
%%%%%%%%%%%%%%%%%

%%%Lied. This doesn't work!!!
\NewEnviron{Lied}[2]{%
    \ifnum\numexpr#2=\numexpr##2 % ##2 doesn't work; i.e. "1" instead of ##2 works
        \ifnum\pdfstrcmp{#1}{##1}=0 % ##1 doesn't work; same as above
            \BODY
        \fi
    \fi
}
\makeatother %residual of some tries with xdef and \aftergroup

%%%%%%%%%%%%%%%%%    
%Apparently, the following code is super clean. That is the basic idea.
%%%%%%%%%%%%%%%%%    

% This is a Liederbuch
\begin{Liederbuch}{songbook}
\begin{Lied}{t}{1}
Song 1 par t
\end{Lied}
\begin{Lied}{n}{2}
Song 2 par n
\end{Lied}
\end{Liederbuch}


% LHsong calls "the environment" This works!!!
\newcommand{\LHsong}[3][n]{
\csname LH#2\endcsname[#1]{#3}
}


\begin{document}

%This should produce Song 1 par t
\LHsong[t]{songbook}{1}

%This shouldn't produce anything
\LHsong[n]{songbook}{1}

%This should produce Song 2 par n
\LHsong{songbook}{2}

%This shouldn't produce anything
\LHsong[t]{songbook}{2}

%This should also produce Song 1 par t
\LHsongbook{t}{1}

\end{document}

The Liederbuch expands to the following code which isn't part of my code

% it expands to:    %not part of the code
%Liederbuch
\newcommand{\csname LHsongbook\endcsname}[2]{
%Lied 1
    \ifnum1=\numexpr##2 %\numexpr#2 expanded to 1; ##2 should be par2 of newcommand
        \ifnum\pdfstrcmp{t}{##1}=0 %#1 expanded to t
            Song 1 par t
        \fi
    \fi
%Lied 2 - same here different values
    \ifnum2=\numexpr##2
        \ifnum\pdfstrcmp{n}{##1}=0
            Song 2 par n
        \fi
    \fi
}

I stole the Liederbuch-Environ from one of my other questions. The proof, that this works, can be seen by the change of ##x to constants. I suppose, that Lied should look similar, but I couldn't figure out, how.

The intention is, to have environment Liederbuch create a [perhaps series of] custom Lied environment[s] in which an invocation of \LHsong will produce output from Lied if the arguments match certain specifications.

Karlo
  • 3,257
MaestroGlanz
  • 2,348
  • Lots of code snippets are nice, but it would be better to provide a complete A to Z minimum working example that demonstrates the problem. – Steven B. Segletes Apr 14 '16 at 10:13
  • I thought about what would be better. I change it. – MaestroGlanz Apr 14 '16 at 10:18
  • 1
    The definition of \LHsongbookneed not use\csname, of course; the arguments should be#1and#2: using##1` is simply wrong. – egreg Apr 14 '16 at 10:23
  • @egreg Of course. That is not what the code is. That is what it expands to. I kept the ##x to refer to the original code. The problem starts after the last comment line, which is displayed without scrolling down. – MaestroGlanz Apr 14 '16 at 10:25
  • 1
    What you have written makes little sense to me, and you definitely are using ## wrong (also \newcommand and \NewEnviron are not defined to be used that way). Could you expand on what do you want the code to do? – Manuel Apr 14 '16 at 10:36
  • 1
    So that I understand your intention, you are hoping to have environment Liederbuch create a [perhaps series of] custom Lied environment[s] in which an invocation of \LHsong will produce output from Lied if the arguments match certain specifications. Is that right? – Steven B. Segletes Apr 14 '16 at 10:47
  • 1
    @StevenB.Segletes Exactly. – MaestroGlanz Apr 14 '16 at 10:55
  • @Manuel Steven got it. The environments are used in separate Liederbuch.sty files which will be \usepackaged. So I can include several Liederbuch-s and choose specific titles from all of them with only one command. – MaestroGlanz Apr 14 '16 at 10:58
  • Not an answer to your question but: the leadsheets package would allow you to print songs only if they match certain tags. So a separate file could contain the song definitions and in the main file said file is \input and a corresponding setup makes sure only those matching certain tags are actually printed. – cgnieder Apr 14 '16 at 11:10

1 Answers1

3

I don't know if this is precisely what you want, but this does seem to work. I loaded etoolbox for convenience (\csgdef, \csxdef, \csuse).

\documentclass{scrartcl}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{etoolbox}
\usepackage{environ}

\newenvironment{Liederbuch}[1]
  {\def\liederbuchtmp{#1}\csgdef{LH#1}##1##2{\csuse{lied;#1;##1;##2}}}
  {}
\NewEnviron{Lied}[2]{\csxdef{lied;\liederbuchtmp;#1;#2}{\expandonce\BODY}} % perhaps trimming spaces would be important
\newcommand*\LHsong[3][n]{\csuse{lied;#2;#1;#3}}

\begin{Liederbuch}{songbook}
\begin{Lied}{t}{1}
  Song 1 par t
\end{Lied}
\begin{Lied}{n}{2}
  Song 2 par n
\end{Lied}
\end{Liederbuch}

\begin{document}

% This should produce Song 1 par t
\LHsong[t]{songbook}{1}

% This shouldnt produce anything
\LHsong[n]{songbook}{1}

% This should produce Song 2 par n
\LHsong{songbook}{2}

% This shouldnt produce anything
\LHsong[t]{songbook}{2}

% This should also produce Song 1 par t
\LHsongbook{t}{1}

\end{document}
Manuel
  • 27,118
  • Thanks a lot, I will test this as soon as possible. Until now, I avoided the etoolbox due to lack of understanding and for the reason, that I wanted to keep the package (later class) as primitive as possible. – MaestroGlanz Apr 14 '16 at 12:11
  • I used it for simplicity, basically \newcommand*\csxdef[1]{\expandafter\xdef\csname#1\endcsname}, \newcommand*\csgdef[1]{\expandafter\gdef\csname#1\endcsname}, and \newcommand*\csuse[1]{\csname#1\endcsname}. – Manuel Apr 14 '16 at 13:49
  • 1
    I'd use \expandonce\BODY in order to avoid errors due to exhaustive expansion. – cgnieder Apr 16 '16 at 08:54
  • @clemens What means exhaustive expansion? – MaestroGlanz Apr 16 '16 at 15:09
  • @Manuel: Do I get this right: You create for every new song a new command in which the parameters are part of the name. This is by far more elegant than my intended solution. – MaestroGlanz Apr 16 '16 at 15:11
  • @MaestroGlanz Yes, exactly. \expandonce is nice there to stop further expansion in \xdef. – Manuel Apr 16 '16 at 17:40
  • @MaestroGlanz the expansion done bei \edef and friends (\xdef, \write, …). Placing user input in such a context is always dangerous since user input often contains fragile material (i.e., material that leads to errors or erroneous output when placed in an \edef-like context). – cgnieder Apr 17 '16 at 09:45
  • @clemens I assume such a fault would happen, if I use \egroup which would expand in an edef to } and maybe cause a missing \bgroup. Is this correct? – MaestroGlanz Apr 17 '16 at 18:28
  • @MaestroGlanz No. \egroup wouldn't expand. For instance an error might be raised if you have \def\foo{} inside, \def wouldn't expand because it's an unexpandable primitive, but \foo would be expanded, and if it's not defined it would raise an error. Take a look at LaTeX definition of robust and fragile commands. – Manuel Apr 17 '16 at 18:53
  • @clemens I took a look recently, but I didn't understand it. So I postponed this topic in favor of more important things. – MaestroGlanz Apr 17 '16 at 19:07
  • @MaestroGlanz something like \textbf{a} (standard user input, I'd say) already breaks inside an \edef. Try \edef\foo{\textbf{a}} and see what happens. – cgnieder Apr 18 '16 at 11:07