Gibt es ein Skript, das eine TeX-Datei liest und jede Instanz eines \newcommand ersetzt?

Gibt es ein Skript, das eine TeX-Datei liest und jede Instanz eines \newcommand ersetzt?

Ich würde gerne wissen, ob es ein Skript gibt, das eine .texDatei liest und jede Instanz eines nicht standardmäßigen TeX-Befehls durch das ersetzt, was es ersetzt. Ich bin nicht sicher, ob klar ist, was ich will, aber lassen Sie mich ein Beispiel geben:

Angenommen, dieEingangIst:

\documentclass{amsart} 
\usepackage{amsmath,amssymb}
\newcommand{\N}{\mathbb{N}}
\DeclareMathOperator{\End}{End}

\begin{document}
In this lecture we'll study the ring of Endomorphisms of an Abelian group $A$.
Let's denote this ring by $\End(A)$. Throughout the lecture, $\N$ will denote
the set of natural numbers.
\end{document} 

Dann eine wünschenswerteAusgabeeines solchen Skripts ist:

\documentclass{amsart}
\usepackage{amsmath, amssymb}
\begin{document}
In this lecture we'll study the ring of Endomorphisms of an Abelian group $A$.
Let's denote this ring by $\operatorname{End}(A)$. Throughout the lecture,  
 $\mathbb{N}$ will denote the set of natural numbers.
\end{document}

PS:Ich glaube, ich habe etwas in dieser Art schon einmal gesehen, aber ich erinnere mich weder an den Ort noch an ein Schlüsselwort, das ich bei Google aufrufen könnte.


Das wollte ich eigentlich schreiben, alle Antworten hier sind super, aber ich habe 2 bei 4 verzählt. :(

Antwort1

Die Info Ich wurde gezwungen durch dieTeX.sx-Chatroom-Mafiaum meine schöne, fehlerhafte, schreckliche, traumatische, post-apokalyptische Arme-Leute-Implementierung eines Ersatzskripts zu posten.:)

Nun, leider wird dies keine TeX-Antwort sein. :)Hier ist mein bescheidener Versuch mit einer Skriptsprache, die ich schrecklich beherrsche.

(Ich schaue dich an, Python!)

import re
import sys

if len(sys.argv) != 3:
    print('We need two arguments.')
    sys.exit()

inputHandler = open(sys.argv[1], 'r')

mathDictionary = {}
commandDictionary = {}

print('Extracting commands...')
for line in inputHandler:
    mathOperator = re.search('\\\\DeclareMathOperator{\\\\([A-Za-z]*)}{(.*)}', line)
    if mathOperator:
        mathDictionary[mathOperator.group(1)] = mathOperator.group(2)
    newCommand = re.search('\\\\newcommand{\\\\([A-Za-z]*)}{(.*)}', line)
    if newCommand:
        commandDictionary[newCommand.group(1)] = newCommand.group(2)

inputHandler.seek(0)

print('Replacing occurrences...')
outputHandler = open(sys.argv[2],'w')
for line in inputHandler:
    current = line
    for x in mathDictionary:
        current = re.sub('\\\\DeclareMathOperator{\\\\' + x + '}{(.*)}', '', current)
        current = re.sub('\\\\' + x + '(?!\w)', '\\operatorname{' + mathDictionary[x] + '}', current)
    for x in commandDictionary:
        current = re.sub('\\\\newcommand{\\\\' + x + '}{(.*)}', '', current)
        current = re.sub('\\\\' + x + '(?!\w)', commandDictionary[x], current)
    outputHandler.write(current)

print('Done.')

inputHandler.close()
outputHandler.close()

Jetzt nennen wir es einfach:

$ python myconverter.py input.tex output.tex
Extracting commands...
Replacing occurrences...
Done.

input.tex

\documentclass{amsart} 
\usepackage{amsmath,amssymb}
\newcommand{\N}{\mathbb{N}}
\DeclareMathOperator{\End}{End}

\begin{document}
In this lecture we'll study the ring of Endomorphisms of an Abelian group $A$.
Let's denote this ring by $\End(A)$. Throughout the lecture, $\N$ will denote
the set of natural numbers.
\end{document} 

output.tex

\documentclass{amsart} 
\usepackage{amsmath,amssymb}



\begin{document}
In this lecture we'll study the ring of Endomorphisms of an Abelian group $A$.
Let's denote this ring by $\operatorname{End}(A)$. Throughout the lecture, $\mathbb{N}$ will denote
the set of natural numbers.
\end{document} 

Einschränkungen:

  • Es ist mein Code, also Vorsicht!:)
  • Es funktioniert nur mit \DeclareMathOperator{...}{...}und \newcommand{...}{...}.
  • Für werden keine optionalen Argumente unterstützt \newcommand.
  • Die Deklaration darf nur aus einer Zeile bestehen.
  • Bitte gleichmäßige geschweifte Klammern.:)

Ich weiß, dass reguläre Ausdrücke nicht zum Parsen von TeX geeignet sind, aber für sehr einfache Ersetzungen sollten sie funktionieren.

Hier ist eine schöne Lektüre über Regex. Viel Spaß.:)

Antwort2

Durch Zufall stieß ich aufde-macro, ein Python-Skript für diesen Zweck. Es ist in TeX Live enthalten.

Einschränkung: Es betrifft nur \newcommand, \renewcommand, \newenvironmentund \renewenvironment. Mit Sternchen markierte Versionen und optionale Argumente werden nicht berücksichtigt. Das folgende Zitat stammt ausdiese Antwort von Willie Wong auf eine andere Frageund enthält weitere Einzelheiten zu den Einschränkungen:

Auf Anregung von Torbjørn T. und cfr habe ich mir das de-macroPaket genauer angesehen. Es funktioniert bis zu einem gewissen Grad. Die folgenden Einschränkungen sind zu beachten:

  • Anders als in der Dokumentation vorgeschlagen, erstellt die von mir installierte Version die Datenbankdatei <filename>statt <filename>.db. Allerdings testet sie offensichtlich nach <filename>.dbals Namen für die Definitionsdatenbank. In ihrer aktuellen Version wird die Definitionsdatenbank also bei jedem einzelnen Durchlauf von Grund auf neu erstellt. Bei kleinen Dokumenten ist das kein Problem. Bei größeren Dokumenten sollte man die Datenbank nach kopieren (nicht verschieben!), um <filename>.dbvon einer möglichen Beschleunigung zu profitieren.

  • Es müssen noch einige Fehler behoben werden. Gelegentlich verstümmelt es Präambeln, indem es falsche Elemente }in den Code einfügt. Ich habe den Grund dafür oder den Auslöser/MWE dafür noch nicht gefunden. Die kleinen Testfälle, an denen ich es ausprobiert habe, haben diesbezüglich alle gut funktioniert.

  • Sehr wichtig: Wie in der Dokumentation beschrieben, müssen alle Definitionen, die Sie austauschen möchten, in einem separaten Paket mit der Endung enthalten sein -private.sty. In der Hauptdatei .texmuss dieses Paket verwendet werden.

  • Ebenfalls sehr wichtig: Das Programm verarbeitet \newcommandund \renewcommand, aber nicht die mit Sternchen versehene Variante \newcommand*(obwohl dies, denke ich, durch eine leichte Änderung des regulären Ausdrucks im Python-Code behoben werden kann). Aus diesem Grundmein erster Versuchfehlgeschlagen. (Ich verwende immerdie mit Sternchen markierte Variante, da ich gelernt habe, dass dies die beste Vorgehensweise ist.)

  • Auch sehr wichtig: Nach dem Entfernen der Sterne hat das Programm einen Fehler ausgegeben. Wie ich schließlich herausfand, liegt das an meiner Angewohnheit, \newcommand\cmdname{<replacement}stattdessen zu schreiben \newcommand{\cmdname}{<replacement>}. Dieses zusätzliche Klammernpaar ist für die Analyse wichtig!

  • Schließlich, sehr enttäuschend für mich, das Programmkann optionale Argumente nicht verarbeiten. \newcommand{\cmdname}[2]{blah #1 blah #2} funktioniert einwandfrei, \newcommand{\cmdname}[2][nothing]{blah #1 blah #2} löst aber eine Ausnahme aus.

Das Problem mit dem Stern und den Klammern kann ich leicht beheben/umgehen, indem ich meine Makrodefinitionen (die, wie Sie sich erinnern, am Ende sowieso weggeworfen werden, da dies der Sinn dieser Übung ist) ohne die Sterne neu schreibe und die zusätzlichen Klammern hinzufüge.

Das Problem mit der Verarbeitung optionaler Argumente macht das Programm für mich derzeit allerdings etwas weniger nützlich. Ich kann es im Moment umgehen, indem ich die optionalen und nicht optionalen Befehle in zwei separate aufteile. Vielleicht werde ich, wenn ich in Zukunft etwas Zeit habe, versuchen, Unterstützung dafür hinzuzufügen, nachdem ich die Logik des ursprünglichen Python-Skripts herausgefunden habe.

Antwort3

Hier ist ein perlSkript, das die gleiche Aufgabe erfüllt. Es hat die gleichen Einschränkungen wie Paulos Code, funktioniert aber in Ihrem Testfall gut. Ich bezweifle nicht, dass es verbessert werden könnte:)

So verwenden Sie es

perl replacenewcommands.plx myfile.tex

die an das Terminal ausgibt, oder

perl replacenewcommands.plx myfile.tex > outputfile.tex

die Ausgabe erfolgt anoutputfile.tex

replacenewcommands.plx

#!/usr/bin/perl

use strict;
use warnings;

# for newcommands
my @newcommandmacro=();
my %newcommandcontent=();

# for DeclareMathoperator
my @declaremathoperator=();
my %declaremathoperatorcontent=();

# for use as an index
my $macro;

# loop through the lines in the INPUT file
while(<>)
{
    # check for 
    #   \newcommand...
    # and make sure not to match
    #   %\newcommand
    # which is commented
    if($_ =~ m/\\newcommand{(.*)}{(.*)}/ and $_ !~ m/^%/)
    {
        push(@newcommandmacro,$1);
        $newcommandcontent{$1}=$2;

        # remove the \newcommand from the preamble
        s/\\newcommand.*//;
    }


    # loop through the newcommands in the 
    # main document
    foreach $macro (@newcommandmacro)
    {
      # make the substitution, making sure to escape the \
      # uinsg \Q and \E for begining and end respectively
      s/\Q$macro\E/$newcommandcontent{$macro}/g;
    }

    # check for 
    #   \DeclareMathOperator...
    # and make sure not to match
    #   %\DeclareMathOperator
    # which is commented
    if($_ =~ m/\\DeclareMathOperator{(.*)}{(.*)}/ and $_ !~ m/^%/)
    {
        push(@declaremathoperator,$1);
        $declaremathoperatorcontent{$1}=$2;

        # remove the \DeclareMathOperator from the preamble
        s/\\DeclareMathOperator.*//;
    }

    # loop through the DeclareMathOperators in the 
    # main document
    foreach $macro (@declaremathoperator)
    {
      # make the substitution, making sure to escape the \
      # uinsg \Q and \E for begining and end respectively
      s/\Q$macro\E(\(.*\))/\\operatorname{$declaremathoperatorcontent{$macro}}$1/g;
    }
    print $_;
}

In Ihrem Testfall

meinedatei.tex (Original)

\documentclass{amsart} 
\usepackage{amsmath,amssymb}
\newcommand{\N}{\mathbb{N}}
\newcommand{\mycommand}{something else}
\DeclareMathOperator{\End}{End}

\begin{document}
In this lecture we'll study the ring of Endomorphisms of an Abelian group $A$.
Let's $\N$ denote this ring by $\End(A)$. Throughout the lecture, $\N$ will denote
the set of natural numbers. \mycommand

and \mycommand again
\end{document} 

outputfile.tex (neu)

\documentclass{amsart} 
\usepackage{amsmath,amssymb}




\begin{document}
In this lecture we'll study the ring of Endomorphisms of an Abelian group $A$.
Let's $\mathbb{N}$ denote this ring by $\operatorname{End}(A)$. Throughout the lecture, $\mathbb{N}$ will denote
the set of natural numbers. something else

and something else again
\end{document} 

Antwort4

Ich habe ein Javascript geschrieben, um Makros zu erweitern, die durch \def, \gdef, \edef, \xdef, \newcommand, \newcommand*, \renewcommand, \renewcommand*, \DeclareMathOperatorund definiert sind \DeclareMathOperator*. Sie können es versuchenHier.

function expandMacros(tex) {
    function nestBrackets(level) {
        var level = level || 5, re = c = "(?:[^\\r\\n\\{\\}]|\\\\[\\{\\}]|\\r?\\n(?!\\r?\\n))*?";
        while (level--) re = c + "(?:\\{" + re + "\}" + c + ")*?";
        return " *(\\{" + re + "\\}|[^\\{])";
    }    
    function getRegExp(name, macro) {
        var num = macro.num, def = macro.def, re = "";
        while (num--) re += nestBrackets();
        re = "\\" + name + "(?![a-zA-Z\\}])" + re;
        return new RegExp(re, "g");
    }
    function trimString(s) {
        return s.replace(/^ +| +$/g, '').replace(/^\{|\}$/g, "");
    }
    function extractMacros() {
        var cs = "\\\\\\w+", re;
        // \def, \gdef, \edef and \xdef
        re = new RegExp("\\\\[gex]?def\\*? *(" + cs + ") *(#\\d)*" + nestBrackets(), "g");
        tex = tex.replace(re, function(match){
            var m = arguments;
            var macro = {
                num:  m[2] ? Math.min(m[2].length / 2, 9) : 0,
                def:  trimString(m[3])
            };
            macros[trimString(m[1])] = macro;
            return "";
        });
        // \newcommand, \newcommand*, \renewcommand and \renewcommand*
        re = new RegExp("\\\\(?:re)?newcommand\\*? *(" + cs + "|\\{" + cs + "\}) *(\\[(\\d)\\])?"
                        + nestBrackets(), "g");
        tex = tex.replace(re, function(match){
            var m = arguments;
            var macro = {
                num:  m[3] || 0,
                def:  trimString(m[4])
            };
            macros[trimString(m[1])] = macro;
            return "";
        });
        // \DeclareMathOperator and \DeclareMathOperator* inside amsmath
        re = new RegExp("\\\\DeclareMathOperator(\\*?) *(" + cs + "|\\{" + cs + "\}) *"
                        + nestBrackets(), "g");
        tex = tex.replace(re, function(match){
            var m = arguments;
            var macro = {
                num:  0,
                def:  "\\operatorname" + m[1] + "{" + trimString(m[3]) + "}"
            };
            macros[trimString(m[2])] = macro;
            return "";
        });
    }
    function replaceMacros() {
        var i = 0, m, re, num;
        for (name in macros) {
            m = macros[name];
            re = getRegExp(name, m), num = m.num;
            //console.log(re);
            tex = tex.replace(re, function(match){
                //console.log(arguments);
                var args = [], result = m.def, k;
                for (k = 1; k <= num; k++) {
                    args[k] = trimString(arguments[k]);
                }
                //console.log(args);
                for (k = 1; k <= num; k++) {
                    result = result.replace(new RegExp("#" + k, "g"), args[k]);
                }
                return result;
            });
        }
    }
    var macros = {};
    extractMacros();
    replaceMacros();
    return tex;
}

document.getElementById("run").onclick = function() {
    var input = document.getElementById("input"),
        output = document.getElementById("output");
    output.value = expandMacros(input.value);
}

verwandte Informationen