#!/usr/bin/env perl
use v5.28;
use autodie; # muere si ocurre un error
use File::Basename; # separa el archivo de entrada
## Argumentos ---------------------------------------------------------
@ARGV == 1 or die "Uso: $0 <archivo TeX a procesar>\n";
my $nombre_archivo = shift;
-f $nombre_archivo or die "ERROR: No encuentro [$nombre_archivo]\n";
## Arreglo de la extensión --------------------------------------------
my @SuffixList = ('.tex', '', '.ltx'); # extensión
my ($name, $path, $ext) = fileparse($nombre_archivo, @SuffixList);
$ext = '.tex' if not $ext; # fijamos la extensión
## Abrimos el archivo de entrada a modificar --------------------------
open my $ENTRADA, '<', $nombre_archivo;
my $archivo;
{
local $/;
$archivo = <$ENTRADA>;
}
close $ENTRADA;
## Cambios a realizar
my %cambios = (
'\pspicture' => '\TRICKS',
'\endpspicture' => '\ENDTRICKS',
'\begin{MYexample' => '\begin{MYEXAMPLE',
'\end{MYexample' => '\end{MYEXAMPLE',
'\begin{pspicture' => '\begin{TRICKS',
'\end{pspicture' => '\end{TRICKS',
'\begin{postscript}' => '\begin{POSTRICKS}',
'\end{postscript}' => '\end{POSTRICKS}',
);
## Variables y constantes
my $no_del = "\0";
my $del = $no_del;
## Reglas
my $llaves = qr/\{ .+? \} /x;
my $corchetes = qr/\[ .+? \] /x;
my $no_corchete = qr/(?: $corchetes )? /x;
my $delimitador = qr/\{ (?<del>.+?) \} /x;
my $scontents = qr/Scontents [*]? $no_corchete /ix;
my $verb = qr/verb [*]? /ix;
my $lst = qr/lstinline (?!\*) $no_corchete /ix;
my $mint = qr/mint (?!\*) $no_corchete $llaves /ix;
my $marca = qr/\\ (?:$verb | $scontents | $lst | $mint) (\S) .+? \g{-1} /sx;
my $comentario = qr/^ \s* \%+ .+? $ /mx;
my $definedel = qr/\\ (?: DefineShortVerb | lstMakeShortInline ) $no_corchete $delimitador /ix;
my $indefinedel = qr/\\ (?: UndefineShortVerb | lstDeleteShortInline) $llaves /ix;
my $tcbxverb = qr/\\ (?: tcboxverb | myverb | Scontents [*]?) $no_corchete /ix;
my $mintverb = qr/\\ (?:mint(?:inline)?) $no_corchete $llaves /ix;
my $anidado = qr/(\{(?>[^\{\}\\]++|\\.|(?R))*+\}) /x;
my $tcbxbrace = qr/$tcbxverb $anidado /x;
my $mintbrace = qr/$mintverb $anidado /x;
## Cambiar en comentarios y comandos delimitados <del> contenido <del>
while ($archivo =~
/ $marca
| $comentario
| $definedel
| $indefinedel
| $del .+? $del
/pgmx) {
my($pos_inicial, $pos_final) = ($-[0], $+[0]); # posiciones
my $encontrado = ${^MATCH}; # lo encontrado
if ($encontrado =~ /$definedel/) { # definimos delimitador
$del = $+{del};
$del = "\Q$+{del}" if substr($del,0,1) ne '\\'; # es necesario "escapar" el delimitador
}
elsif ($encontrado =~ /$indefinedel/) { # indefinimos delimitador
$del = $no_del;
}
else { # aquí se hacen los cambios
while (my($busco, $cambio) = each %cambios) {
$encontrado =~ s/\Q$busco\E/$cambio/g; # es necesario escapar $busco
}
substr $archivo, $pos_inicial, $pos_final-$pos_inicial, $encontrado; # insertamos los nuevos cambios
pos($archivo) = $pos_inicial + length $encontrado; # re posicionamos la siguiente búsqueda
}
}
## Cambiar en comandos que utilizan llaves { contenido } balanceadas
while ($archivo =~ /$tcbxbrace | $mintbrace /pgmx) {
my($pos_inicial, $pos_final) = ($-[0], $+[0]); # posiciones
my $encontrado = ${^MATCH}; # lo encontrado
while (my($busco, $cambio) = each %cambios) {
$encontrado =~ s/\Q$busco\E/$cambio/g; # es necesario escapar $busco
}
substr $archivo, $pos_inicial, $pos_final-$pos_inicial, $encontrado; # insertamos los nuevos cambios
pos($archivo)= $pos_inicial + length $encontrado; # re posicionamos la siguiente búsqueda
}
## Divido el archivo por líneas
my @lineas = split /\n/, $archivo;
my $ENTORNO = qr/(?: verbatim\*? | LTXexample | tcblisting | mybox | comment )/xi;
my $DEL;
## Ordenamos las palabras que deseamos cambiar
my %replace = (%cambios);
my $find = join "|", map {quotemeta} sort { length($a)<=>length($b) } keys %replace;
## Cambiar dentro de entornos verbatim
for (@lineas) {
if (/\\begin\{($ENTORNO)(?{ $DEL = "\Q$^N" })\}/ .. /\\end\{$DEL\}/) {
s/($find)/$replace{$1}/g;
}
}
## Escritura del resultado
open my $SALIDA, '>', "$name-out$ext";
print $SALIDA join("\n", @lineas);
close $SALIDA;
__END__