1

I am running TeX Live 2018 on macOS. It comes with a python script de-macro that should replace personal macros. Typing de-macro test.tex where test.tex is

\documentclass{amsart}

\newcommand{\e}{\mathrm{e}}
\renewcommand{\i}{\mathrm{i}}

\begin{document}
\[
\e^{\i\pi}=-1
\]
\end{document}

results in a file test-clean.tex which is identical to test.tex. Am I using de-macro incorrectly or if not, can anyone confirm this behaviour?

Edit: Meanwhile I have learned that I am supposed to write my macros into a *-private.sty file and load this file. Even when doing so, nothing seems to happen except that \usepackage{*-private.sty} is removed.

4
  • 2
    No idea, but in Linux pandoc -f latex -t latex test.tex make what you are looking, for the body of the document only. With -s is possible make a complete document, but with a different preamble unless using a simplified template. (it should not hurt, anyway)
    – Fran
    Commented Jul 7, 2021 at 12:42
  • @Fran Thanks! Works indeed.
    – 220284
    Commented Jul 7, 2021 at 12:50
  • Regarding your "edit": it works for me. Can you edit to show exactly what you used for the .sty file and what is the content of the tex file? Commented Jul 7, 2021 at 15:47
  • @WillieWong I'd rather go with the modification below and/or pandoc instead of messing around with the additional .sty file. Many thanks for your feedback though.
    – 220284
    Commented Jul 8, 2021 at 8:43

1 Answer 1

1

By default de-macro seems not to process \newcommand etc. in the main file. Below an experimental attempt to include in-place processing. It works for the example file, but it can be expected to fail on more complex files.

The Python source has a function scan_defs() which stores the definitions of commands and environments. The idea is to call this function as part of the regular file processing, before performing the actual substitution function. This is done by adding one line in the function process_file() around line 1030.

def process_file(self, file):
    """Returns the new defs.
    """
    file = cut_extension(file, ".tex")
    source_file = "%s.tex" % (file)
    print("File %s [" % (source_file))
    source_fp = open(source_file, "r")
    text_str = source_fp.read()
    source_fp.close()

    self.smart_tokenize(text_str, handle_inputs=True)
    if not self.data:
        raise Error("Empty tokenization result.")
    self.reset()

    if self.debug:
        source_seen_fname = "%s-seen.tex" % (file)
        source_seen_fp = open(source_seen_fname, "w")
        source_seen_fp.write(detokenize(self.data))
        source_seen_fp.close()
    
    self.scan_defs() ### ADDED THIS FUNCTION CALL
    self.data = self.apply_all_recur(self.data, report=True)
    
    result_fname = "%s-clean.tex" % (file)
    print("Writing %s [" % (result_fname))
    result_fp = open(result_fname, "w")
    result_fp.write(self.smart_detokenize())
    result_fp.close()
    print("] file %s" % (result_fname))
    print("] file %s" % (source_file))

This is simple enough, and it works to perform the substitutions. However, the private macro/environment definitions are still part of the file, so this results in

\documentclass{amsart}

\newcommand{\e}{\mathrm{e}}
\renewcommand{\i}{\mathrm{i}}

\begin{document}
\[
\mathrm{e}^{\mathrm{i}\pi}=-1
\]
\end{document}

which is rather redundant and it might still cause problems with conversion software, publishers etc.

To also remove the now-unused definitions from the file, the function scan_defs() (around line 750) can be modified as well. The self.data object is a list of tokens, so once you know the start and the end of the definition you can remove all these tokens with the Python del function in a loop. At the end of the loop you should adjust the list position and current token to take into account the tokens that were just deleted.

def scan_defs(self):
    if not self.legal():
        raise Error("No definitions to scan.")
    self.reset()
    command_defs, env_defs = self.defs
    while self.uplegal():
        if (esc_str_ty == self.item.type
            and self.item.val in ["newcommand", "renewcommand"]):
            def_start_pos = self.pos
            command_def = self.scan_command_def()
            command_defs[command_def.name] = command_def
            def_end_pos = self.pos
            for del_pos in range(def_start_pos,def_end_pos):
                del self.data[def_start_pos]
            self.pos = def_start_pos
            self.item = self.data[self.pos]
        elif (esc_str_ty == self.item.type and self.item.val
              in ["newenvironment", "renewenvironment"]):
            def_start_pos = self.pos
            env_def = self.scan_env_def()
            env_defs[env_def.name] = env_def
            def_end_pos = self.pos
            for del_pos in range(def_start_pos,def_end_pos):
                del self.data[def_start_pos]
            self.pos = def_start_pos
            self.item = self.data[self.pos]
        else:
            self.next()

This results in the following LaTeX file:

\documentclass{amsart}




\begin{document}
\[
\mathrm{e}^{\mathrm{i}\pi}=-1
\]
\end{document}

Note that the newlines are not removed as they are not part of the command definitions.

As mentioned in the comments Pandoc can do this as well, and undoubtedly much more robust, so I recommend using that instead of this answer :)

1
  • Many thanks, that's great! I will give your modification a shot.
    – 220284
    Commented Jul 8, 2021 at 8:41

You must log in to answer this question.

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