10

Is there a way/an existing tool to process a LaTeX source document such that for a given list of macro definitions (using \newcommand), calls to those macros are replaced by their expanded version?

Imagined work-flow:

<program> source.tex macros.tex > output.tex

where

source.tex:

\documentclass{article}
\newcommand\Rbb{\mathbb{R}}
\newcommand\Cbb{\mathbb{C}}
\newcommand\jb[1]{\langle #1\rangle}
\begin{document}
On $\Rbb$ we define $\jb{x} = \sqrt{1 + x^2}$, while on $\Cbb$ we 
define $\jb{x} = \sqrt{1 + |x|^2}$ where the square root is taken 
for the real numbers. 
\end{document}


macros.tex:

\newcommand\Rbb{\mathbb{R}}
\newcommand\Cbb{\mathbb{C}}
\newcommand\jb[1]{\langle #1\rangle}

And the desired output is:

output.tex:

\documentclass{article}
\newcommand\Rbb{\mathbb{R}}
\newcommand\Cbb{\mathbb{C}}
\newcommand\jb[1]{\langle #1\rangle}
\begin{document}
On $\mathbb{R}$ we define $\langle x\rangle = \sqrt{1 + x^2}$, while on $\mathbb{C}$ we 
define $\langle x\rangle = \sqrt{1 + |x|^2}$ where the square root is taken 
for the real numbers. 
\end{document}

Motivation Sometimes my collaborators and I have conflicting items in the personal list of macros that we usually use. To make life easier on myself I would like to prepare my edits using my personal macros to make typing faster. But before merging I would like to flatten out all such macros so that they don't conflict with the ones used by my coauthors.

  • 6
    In general no, this is not possible as macro expansion and execution are interwoven. It's not like a C pre-processor where all macro expansion happens first. (obviously a fixed list of simple macros could just be done a regular expression search/replace in your editor) – David Carlisle Jan 6 '14 at 10:25
  • @DavidCarlisle: which is why I also asked for existing tools. For simple definitions with no arguments, a regexp is easy enough to do by hand. But for definitions with arguments and possibly optional arguments, I wouldn't trust myself to cobble together something in a short amount of time. If someone has a nice perl/python script that can reliably handle the parsing (up to certain known caveats), it would make me very happy. – Willie Wong Jan 6 '14 at 10:31
  • Related/duplicate(?): tex.stackexchange.com/questions/67709 – Torbjørn T. Jan 6 '14 at 10:52
  • @TorbjørnT. Related surely, but it seems that it only treats non-arg macros, doesn't it? – yo' Jan 6 '14 at 11:01
  • @TorbjørnT.: you answered with de-macro in that linked question. The documentation for that package on the CTAN website is very brief. Would you saying a few more words about it? (I've just tested it and the result doesn't look too promising: it introduced an extra brace to the preamble causing a syntax error, and made zero changes to the body. It is likely that I am using it wrong.) – Willie Wong Jan 6 '14 at 11:24
6

Based on the suggestion of Torbjørn T. and cfr, I looked more deeply into the de-macro package. It works to a certain extent. The following are caveats:

  • Unlike the what the documentation suggests, the version I have installed creates the database file <filename> instead of <filename>.db. However evidently it tests for <filename>.db as the name for the definitions database. So in its current incarnation it will recreate the definitions database from scratch every single run. For small documents this is not an issue. For larger documents one should copy (not move!) the database to <filename>.db to take advantage of any potential speed-up.

  • There are still some bugs to be ironed out. Occasionally it mangles preambles by inserting spurious } to the code. I have not yet found the reason why or the trigger/MWE for it. The small test cases I tried it on all worked fine in this regard.

  • Very important: as the documentation suggests, all the definitions you want swapped out must live inside a separate package ending in -private.sty. In the main .tex file one must use that package.

  • Also very important: the program handles \newcommand and \renewcommand, but not the starred variant \newcommand* (though this can be fixed by amending the regular expression a bit in the python code, I suppose). This is why my first attempt failed. (I always use the starred variant since I learned about it being best practice.)

  • Also very important: after removing the stars, the program threw up an error. Which I eventually figured out is due to my habit of writing \newcommand\cmdname{<replacement} instead \newcommand{\cmdname}{<replacement>}. That extra pair of braces is important for the parsing!

  • Lastly, very disappointing for me, the program cannot handle optional arguments. \newcommand{\cmdname}[2]{blah #1 blah #2} works fine, but \newcommand{\cmdname}[2][nothing]{blah #1 blah #2} throws an exception.

The problem with the star and the braces I can easily fix/workaround by rewriting my macro definitions (which as you remember, will be thrown away at the end anyway being the point of this exercise) without the stars and adding the extra braces.

The problem with optional argument handling however, currently makes the program somewhat less useful for me. I can work around it for now by splitting the optional and non-optional commands into two separate ones. Maybe, if I have some time in the future, I will look into adding support for it, after figuring out the logic of the original python script.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.