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.

0

3 Answers 3

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.

5
  • 3
    ...again, what is going on here?
    – Werner
    Apr 28, 2015 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, 2015 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, 2015 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 Apr 28, 2015 at 7:29
  • @Werner words added Apr 28, 2015 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.

5
  • That actually breaks many things, in cases like \blah{\defun ...}
    – Clément
    Apr 28, 2015 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, 2015 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, 2015 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, 2015 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, 2015 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

2
  • 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, 2015 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, 2015 at 14:32

You must log in to answer this question.

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