2

Let's assume that you have a single LaTeX source file that can compile two arts of documents: a main conference paper and its extended long version. Inside, you use \ifthenese to produce the two versions. Example:

\RequirePackage{ifthen}
\documentclass{article}
\newboolean{techrep}
\setboolean{techrep}{false}
\begin{document}
\ifthenelse{\boolean{techrep}}{Stuff for the TR}{Stuff for the main paper}
\end{document}

Is it possible, say, via some script (sed, elisp, awk, m4, ...), to produce the source LaTeX file for the main conference paper out of such a source? Example:

\RequirePackage{ifthen}
\documentclass{article}
\newboolean{techrep}
\setboolean{techrep}{false}
\begin{document}
Stuff for the main paper
\end{document}

You may assume that every occurence of an \ifthenelse starts \ifthenelse{\boolean{techrep}} (i.e., the macro is used for a single purpose only), that these occurences may be nested up to two levels, and that the two arguments of the command can be of any size: empty, a word, several words, or many lines, including empty lines. The command \ifthenelse{...}{...}{...} itself may start anywhere in a line and end anywhere in the same or in a different line. You may assume that all the braces { } are properly nested, that this nesting may go arbitrarily deep anywhere in the LaTeX source (including both branches of the \ifthenelse), that all the comments %... have been already emptied, that there are neither \includes nor \inputs left, and that the end-of-line mark follows the UNIX convention.

Ideally, your script should throw an error if a wrong number of arguments is given to \ifthenelse, such as in

\ifthenelse{\boolean{techrep}}{A}%

More text
  • Do you know docstrip? See: https://tex.stackexchange.com/a/461982/134574 – Phelype Oleinik Sep 16 '19 at 12:03
  • No, with docstrip the guard (which acts as the conditional) must be at the beginning of the line. But it produces each version of the document out of the box. You \ifthenelse could be replaced by %<techrep>Stuff for the TR[imagine a line break here]%<paper>Stuff for the main paper. And things common to both would be delimited by %<*techrep|paper> and %</techrep|paper>. Adding a third version would be almost trivial. I know your question is not about docstrip. I was just showing the tool :-) Anyhow, I think you would find more sed/awk experts at stackoverflow or unix & linux... – Phelype Oleinik Sep 16 '19 at 12:10

2 Answers2

1

Following the idea of PhelypeOleinik, but without having to comment all the lines, perhaps the perl script srcredact (part of TeXLive 2019) can be of help. This is my modified example file (MdAyq6-doc.tex):

%<*ALL>
\documentclass{article}
\begin{document}
Common text
%</ALL>
%<*techrep>
Stuff for the TR
%</techrep>
%<*maindoc>
Stuff for the main paper
%</maindoc>
%<*ALL>
\end{document}
%</ALL>

Now from the command line:

[pablo@worktex forum] $ srcredact -l MdAyq6-doc.tex 
ALL
maindoc
techrep
[pablo@worktex forum] $ srcredact -e techrep MdAyq6-doc.tex 
\documentclass{article}
\begin{document}
Common text
Stuff for the TR
\end{document}
[pablo@worktex forum] $ srcredact -e maindoc MdAyq6-doc.tex 
\documentclass{article}
\begin{document}
Common text
Stuff for the main paper
\end{document}
[pablo@worktex forum] $ srcredact -e ALL MdAyq6-doc.tex 
\documentclass{article}
\begin{document}
Common text
\end{document}

Here we see the output on screen, if we want to generate the output file just add > file.tex at the end of the line. As follows:

$ srcredact -e techrep MdAyq6-doc.tex > techrep.tex

and the file techrep.tex will be created.

\documentclass{article}
\begin{document}
Common text
Stuff for the TR
\end{document}

It's not as powerful as docstript, but it does a great job.

EDIT If the idea is to remove using regular expressions, it's not that easy, the escaped braces inside are a mess, but, something can be done using Perl :). Assuming that the syntax of your input file is valid, with the following rmifthen.pl script it is possible:

#!/usr/bin/env perl
use v5.26;
use autodie;
use File::Basename;
use open ':locale';

## Args
@ARGV == 1  or die "Use: $0 <file (La)TeX to be processed>\n";
my $input_file = shift;
-f $input_file or die "ERROR: Can't find [$input_file]\n";

## Extension
my @SuffixList = ('.tex', '', '.ltx');
my ($name, $path, $ext) = fileparse($input_file, @SuffixList);
$ext = '.tex' if not $ext;

## Read file
open my $TEXINPUT, '<', $input_file;
my $ltxfile;
 {
    local $/;
    $ltxfile = <$TEXINPUT>;
 }
close   $TEXINPUT;

## Change escaped braces to a character that is not in the text
$ltxfile =~ s/\\[{]/«/g;
$ltxfile =~ s/\\[}]/»/g;

## Two groups of captures: the complete ifthenelse, and the true ifthenelse.
my $ifthenelse = qr/
    (
        \\ifthenelse (?&condicion_rx)\s*
            (?<expVerdadero> (?&expVerdadero_rx)\s*)
            (?&expFalsa_rx)
    )

    (?(DEFINE)
        (?<condicion_rx>    (?&texto_rx))
        (?<expVerdadero_rx> (?&texto_rx))
        (?<expFalsa_rx>     (?&texto_rx))
        (?<texto_rx>  ( [{] (?: [^{}]++ | (?-1) )*+ [}]  ) )
    )
/sx;

## We look for ifthenelse throughout the document
my @posiciones;

# Every time we find an ifthenelse mark, we keep the initial, final
# position of the whole mark, And the text of the true clause
while ($ltxfile =~ /$ifthenelse/g) {
    push @posiciones, [ $-[1], $+[1], $+{expVerdadero} ];
}

## We replace the whole ifthenelse brand, with the true expression, except the braces
for my $pos_ref (reverse @posiciones) {
    substr($ltxfile, $pos_ref->[0], $pos_ref->[1] - $pos_ref->[0])
        = substr $pos_ref->[2], 1, -2;
}

## We recovered the escaped braces.
$ltxfile =~ s/«/\\{/g;
$ltxfile =~ s/»/\\}/g;

## Write file
open my $SALIDA, '>', "$name-out$ext";
print   $SALIDA $ltxfile;
close   $SALIDA;

__END__

With this example file:

\RequirePackage{ifthen}
\newboolean{techrep}
\setboolean{techrep}{false}
\begin{document}
\ifthenelse{\boolean{techrep}}{Este es texto
con el que {deseo quedarme} incluidas las llaves \{ que no están
escapadas dentro de el, pero sin las llaves exteriores
}{Este es texto NO lo deseo, también {puede llevar llaves}  \}
que no están escapadas} %
TEXTO TEXTO
TEXTO TEXTO \ifthenelse{\boolean{techrep}}{Otro texto
con el que \{deseo quedarme\} incluidas las llaves \{ \}
que no están escapadas
}{Este es texto {NO lo deseo}, también puede llevar llaves \{
que no están \} escapadas}
TEXTO TEXTO TEXTO
\end{document}

running:

perl rmifthen.pl file.tex

we get:

\RequirePackage{ifthen}
\newboolean{techrep}
\setboolean{techrep}{false}
\begin{document}
Este es texto
con el que {deseo quedarme} incluidas las llaves \{ que no están
escapadas dentro de el, pero sin las llaves exteriores %
TEXTO TEXTO
TEXTO TEXTO Otro texto
con el que \{deseo quedarme\} incluidas las llaves \{ \}
que no están escapadas
TEXTO TEXTO TEXTO
\end{document}

With your MWE the output is what you propose. I think this is more than you were looking for.

Greetings and good luck with your next conference.

1

Regular expressions in Vim, probably similar in other programs:

:%s/\\ifthenelse{[^}]*}{\([^}]*\)}{\([^}]*\)}/\1

That keeps the first branch; if you want to keep the second, change \1 to \2.

musarithmia
  • 12,463