1

My first code example below runs fine. It does conditional compilation based on whether \flag is defined or not.

\documentclass{article}
\usepackage{listings}
\begin{document}

\ifdefined\flag
\begin{lstlisting}
foo
\end{lstlisting}
\else
\begin{lstlisting}
bar
\end{lstlisting}
\fi

\end{document}

Now I try to create a macro for the conditional compilation like so.

\documentclass{article}
\usepackage{listings}
\def\flag{}
\newcommand{\ifflag}[2]{\ifdefined\flag#1\else#2}
\begin{document}

\ifflag{
\begin{lstlisting}
foo
\end{lstlisting}
}{
\begin{lstlisting}
bar
\end{lstlisting}
}

\end{document}

Compiling this fails like this.

Package Listings Warning: Text dropped after begin of listing on input line 15.

What did I do wrong in the second example?

1
  • You can't have an lstlisting environment in the argument to another command. And even if this problem could be solved, you'd lose the end lines.
    – egreg
    Jun 9, 2019 at 16:34

2 Answers 2

3

I can offer a macro \UDcollectverbarg with the following syntax:

\UDcollectverbarg{⟨^^M-replacement⟩}{⟨Mandatory 1⟩}{⟨Mandatory 2⟩}⟨verbatimized argument⟩

which yields:

⟨Mandatory 1⟩{⟨Mandatory 2⟩{⟨verbatimized argument⟩}}

, with each character ^^M that denotes an end of a line being replaced by the token-sequence ⟨^^M-replacement⟩.

The Mandatory-arguments are mandatory. If they consist of several tokens, they must be nested into catcode-1/2-character-pair / braces.
If reading and tokenizing is necessary, this will take place under unchanged category code régime.
The verbatim-Arg is also mandatory. It is to be read and tokenized under verbatim category code régime. If its first character is a brace, it will be "assumed" that the argument is nested into braces. Otherwise it will be assumed, that the ending of that argument is delimited by that first character—like the argument of \verb.
Empty-lines will not be ignored.

I chose this syntax as with this syntax you can collect verbatimized arguments within the second mandatory argument by nesting calls to \UDcollectverbarg within \UDcollectverbarg's first mandatory argument.

E.g.,

\UDcollectverbarg{<^^M-replacement>}%
                 {\UDcollectverbarg{<^^M-replacement>}{\UDcollectverbarg{<^^M-replacement>}{<actionA>}}}% <- Mandatory 1
                 {<actionB>}%                     <- Mandatory 2
                 <verbatimized argument 1><verbatimized argument 2><verbatimized argument 3>

yields:

\UDcollectverbarg{<^^M-replacement>}{\UDcollectverbarg{<^^M-replacement>}{<actionA>}}% <- Mandatory 1
                 {<actionB>{<verbatimized argument 1>}}%        <- Mandatory 2
                 <verbatimized argument 2><verbatimized argument 3>

yields:

\UDcollectverbarg{<^^M-replacement>}{<actionA>}% <- Mandatory 1
                 {<actionB>{<verbatimized argument 1>}{<verbatimized argument 2>}}% <- Mandatory 2
                 <verbatimized argument 3>

yields:

<actionA>{<actionB>{<verbatimized argument 1>}{<verbatimized argument 2>}{<verbatimized argument 3>}}

Assume <actionA> = \@firstofone:

\@firstofone{<actionB>{<verbatimized argument 1>}{<verbatimized argument 2>}{<verbatimized argument 3>}}

yields:

<actionB>{<verbatimized argument 1>}{<verbatimized argument 2>}{<verbatimized argument 3>}

\documentclass{article}

\makeatletter

%%<-------------------- Code for \UDcollectverbarg -------------------->
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\expandafter\expandafter\@firstoftwo{ }{}%
  \@secondoftwo}{\expandafter\expandafter\@firstoftwo{ }{}\@firstoftwo}%
}%
%%......................................................................
\begingroup
\catcode`\^^M=12 %
\@firstofone{%
  \endgroup%
  \newcommand\UDEndlreplace[2]{\romannumeral0\@UDEndlreplace{#2}#1^^M\relax{}}%
  \newcommand*\@UDEndlreplace{}%
  \long\def\@UDEndlreplace#1#2^^M#3\relax#4#5{%
    \UD@CheckWhetherNull{#3}%
    { #5{#4#2}}{\@UDEndlreplace{#1}#3\relax{#4#2#1}{#5}}%
  }%
}%
\newcommand\UDcollectverbarg[3]{%
  \begingroup
  \let\do\@makeother % <- this and the next line switch to
  \dospecials        %    verbatim-category-code-régime.
  \catcode`\{=1      % <- give opening curly brace the usual catcode so a 
                     %    curly-brace-balanced argument can be gathered in
                     %    case of the first thing of the verbatimized-argument 
                     %    being a curly opening brace.
  \catcode`\ =10     % <- give space and horizontal tab the usual catcode so \UD@collectverbarg
  \catcode`\^^I=10   %    cannot catch a space or a horizontal tab as its 4th undelimited argument.
                     %    (Its 4th undelimited argument denotes the verbatim-
                     %     syntax-delimiter in case of not gathering a
                     %     curly-brace-nested argument.)
  \kernel@ifnextchar\bgroup
  {% seems a curly-brace-nested argument is to be caught:
    \catcode`\}=2    % <- give closing curly brace the usual catcode also.
    \UD@collectverbarg{#1}{#2}{#3}{}%
  }{% seems an argument with verbatim-syntax-delimiter is to be caught:
    \do\{% <- give opening curly brace the verbatim-catcode again.
    \UD@collectverbarg{#1}{#2}{#3}%
  }%
}%
\newcommand\UD@collectverbarg[4]{%
  \do\ %   <- Now that \UD@collectverbarg has the delimiter or
  \do\^^I%    emptiness in its 4th arg, give space and horizontal tab
         %    the verbatim-catcode again.
  \do\^^M% <- Give the carriage-return-character the verbatim-catcode.
  \long\def\@tempb##1#4{%
    %\edef\@tempb{##1}%
    \def\@tempb{##1}%
    \@onelevel@sanitize\@tempb % <- Turn characters into their "12/other"-pendants.
                               %    This may be important with things like the 
                               %    inputenc-package which may make characters 
                               %    active/which give them catcode 13(active).
    \expandafter\UDEndlreplace\expandafter{\@tempb}{#1}{\def\@tempb}% <- this starts 
                               %    the loop for replacing endline-characters.
    \expandafter\UD@@collectverbarg\expandafter{\@tempb}{#2}{#3}% <- this "spits 
                               %    out the result.
  }%
  \@tempb
}%
\newcommand\UD@@collectverbarg[3]{%
  \endgroup
  #2{#3{#1}}%
}%
%%<---------------- End of code for \UDcollectverbarg ----------------->

\usepackage{listings}

% As a usage-example let's now define a macro \ifflag which
% collects two verbatim-arguments and does spit out one of them wrapped into
% \scantokens.
% Basically \ifflag is a wrapper for calling \UDcollectverbarg and
% passing the verbatimized arguments to \@ifflag
\newcommand\ifflag{%
  \UDcollectverbarg{^^J}%
                   {\UDcollectverbarg{^^J}{\@firstofone}}%
                   {\@ifflag}%
}%
\newcommand\@ifflag[2]{%
  \ifdefined\flag\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
  {\scantokens{#1}}%
  {\scantokens{#2}}%
}%
\makeatother

\begin{document}

Listing when flag not defined:

\ifflag{
\begin{lstlisting}
foo1
   foo2
foo3
\end{lstlisting}
}{
\begin{lstlisting}
bar1
   bar2
bar3
\end{lstlisting}
}

\def\flag{}

Listing when flag defined:

\ifflag{
\begin{lstlisting}
foo1
   foo2
foo3
\end{lstlisting}
}{
\begin{lstlisting}
bar1
   bar2
bar3
\end{lstlisting}
}

\end{document}

enter image description here

0

An alternative solution to the one given by the master @Ulrich Diez. The package scontents allows to store content verbatim to be reused, I have only adapted a little the entry of your example:

\documentclass{article}
\usepackage{listings}
\usepackage{scontents}
\def\flag{}
\newcommand{\ifflag}[2]{%
\ifdefined\flag #1
\fi
\unless\ifdefined\flag #2
\fi}
\begin{document}
% foo
\begin{scontents}[store-env=foo]
\begin{lstlisting}
foo
\end{lstlisting}
\end{scontents}
% baz
\begin{scontents}[store-env=baz]
\begin{lstlisting}
foo
\end{lstlisting}
\end{scontents}

\ifflag{\getstored[1]{foo}}{\getstored[1]{baz}}

\end{document}

Saludos

You must log in to answer this question.

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