10

I am trying to make adjustments to a large document that uses TeX-style macros, like this:

{\defun SomeFunctionName arg1 arg2}

The \defun macro is defined as \newcommand{\defun}{\tt}.

Can I adjust this definition to retrieve the SomeFunctionName arg1 arg2 part as an argument, to allow for a more complex definition? Fox example, if defun was a proper LaTeX macro, I could put a box around the text, or put something before and after, etc.

Minimal example:

\documentclass{article}
\newcommand{\defun}{\tt}

\begin{document}
\section{\defun SomeFunctionName arg1 arg2}
Call {\defun SomeFunctionName} to foo the bar.
\end{document}

How could I (for example) add some text before and after every invocation of defun? What is the general approach to handle the contents of {\defun ...} as an argument, short of editing all occurences?

To clarify: this is a large, two decades old document. I didn't write it.

18

enter image description here

\documentclass{article}

\protected\def\defun{\expandafter\zdefun\expandafter{\iffalse}\fi}

\def\zdefun#1{A \fbox{#1} B\egroup}

\begin{document}
\section{\defun SomeFunctionName arg1 arg2}
Call {\defun SomeFunctionName} to foo the bar.
\end{document}

Here

\iffalse}\fi

expands to nothing so

\expandafter{\iffalse}\fi

expands to a single unmatched { but is a matching {} so can be included in a definition.

So given

 {\defun SomeFunctionName}

The { starts a group and

\defun SomeFunctionName}

expands in one step to

\expandafter\zdefun\expandafter{\iffalse}\fi SomeFunctionName}

and in the next step to

\zdefun{SomeFunctionName}

\zdefun has a normal #1 argument which is delimited by the { we just inserted and the } that was originally in the file. (Note this } was originally a group close delimiter, it is now used as an argument delimiter so doesn't close a group.)

So this expands in one step to

A \fbox{SomeFunctionName} B\egroup

and the

A \fbox{SomeFunctionName} B

gets typeset, and finally the

\egroup

closes the group started by the { in the original file.

  • 3
    ...again, what is going on here? – Werner Apr 28 '15 at 0:54
  • 1
    Wow, that's really cool. I never really understood why \iffalse and \iftrue were useful. Now I've got an idea. So, cool. – A.Ellett Apr 28 '15 at 1:40
  • Out of curiousity, why not write \iffalse{\else}\fi instead of \egroup. It seems to work when I try that too. – A.Ellett Apr 28 '15 at 4:05
  • @A.Ellett The iffalse trick is needed at the start but at the end it just takes 4 more tokens and is slower, even if more symmetric – David Carlisle Apr 28 '15 at 7:29
  • @Werner words added – David Carlisle Apr 28 '15 at 8:44
10

My preferred approach would be: use the find-and-replace function of your editor of choice and replace {\defun with \textdefun{.

EDIT: As @Clément points out, this does not work in cases like \section{\defun abc def}. I am afraid those cases would elude even a more sophisticated regexp-based approach, because one would have to add an additional closing brace.

  • That actually breaks many things, in cases like \blah{\defun ...} – Clément Apr 28 '15 at 12:01
  • 1
    I'm apparently too dumb for sed, but in Ruby it would be text.gsub(/\{\\defun\s+([^\}]*)\}/, "{\\defun{\\1}}"); see here. Of course, you will never be able to catch all (La)TeX intricacies with a single (readable) regexp (which is one indicator that (La)TeX sucks as a programming language) but if the original author adhered to some coding standard, you'll need only a few runs to get most things right. It definitely beats loading even more wicked (La)TeX on top. – Raphael Apr 29 '15 at 9:34
  • @Raphael This breaks for simple things like \{ in the source, or just {\defun abc \emph{def} ghi} Also, the document is still being edited from time to time, and I can't merge my changes back in, so re-syncing with the master branch would be a pain. – Clément Apr 29 '15 at 17:12
  • 1
    @Raphael I don't know of a single significant programming language that can be easily parsed by a regexp, at least without recursive regexps, due to the nested parentheses problem. – Clément Apr 29 '15 at 17:15
  • 1
    @Clément That's true, but many refactoring tasks don't need a full parse. (Oh wait, you can't even parse (La)TeX!) – Raphael Apr 29 '15 at 17:59
8

You can do it with \aftergroup:

\documentclass{article}

\protected\def\defun{\aftergroup\newdefun\aftergroup{}}
\newcommand\newdefun[1]{\fbox{#1}}

\begin{document}

\tableofcontents

\section{Here {\defun SomeFunctionName arg1 arg2}}
Call {\defun SomeFunctionName} to foo the bar.

\end{document}

The same limitation as David Carlisle's answer holds: calls of \defun that don't appear inside a pair of braces will break. In the case of

\section{\defun Whatever}

the error will be when processing \tableofcontents. But \section{{\defun Whatever}} will behave well.

enter image description here

  • I'm a bit confused about how this works. It seems that the bracket after \aftergroup is serving double duty. Shouldn't there be a problem with its closing bracket (the one immediately following it in the definition of \defun) being mismatched at expansion time? – A.Ellett Apr 28 '15 at 14:29
  • @A.Ellett The { brace following \aftergroup is put aside, the following one balances the one before \defun and makes the tokens \newdefun{ put aside by \aftergroup to be reinserted. It's good luck that the braces also keep happy the scanner of the replacement text. The fact that this works also in \section is just a coincidence. – egreg Apr 28 '15 at 14:32

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.