6

I am working on a LaTeX pretty printer that parses a LaTeX document and then re-renders the source. (I know that this is not possible in all cases, but it should be possible a lot of the time.)

In the spirit of Prettier, I want the code formatter to be opinionated, and I am trying to decide which macros should have their arguments automatically wrapped in a group {}. For example, \mathbb R should be converted to \mathbb{R}, and \frac23 should be converted to \frac{2}{3}.

My question is: (a) Are there LaTeX macros where passing in a group will cause unexpected behavior? and (b) are there macros where it is considered "best practice" not to wrap the arguments in a group?

A follow-up question would be: what would you put on a list of "special" macros that are commonly used. This can include LaTeX macros that don't follow standard conventions (of [...] and {...} style arguments), or commonly-used macros that would need special handling (like almost everything in a tikz environment...).

To rein in the scope, I am not trying to parse complicated TeX programming or macro definitions themselves.

9
  • It depends what you call a macro. For instance, \sffamily should be used as is, and not e.g. like \sffamily{text}. – user194703 Apr 7 '20 at 3:38
  • @Schrödinger'scat Good point. I mean a macro that accepts arguments. – Jason Siefken Apr 7 '20 at 3:42
  • 3
    It really depends on what you call a macro $\left(1\right)$ works but $\left{(}1\right{)}$ doesn't. – user194703 Apr 7 '20 at 3:45
  • \left and \right are exactly the types of exceptions I'm looking for. I have no idea how they work, but I would imagine the macro is something like \left(#1\right)? And so the "argument" is really what's between the two... – Jason Siefken Apr 7 '20 at 3:49
  • 5
    all macros accept brace groups but not all commands are macros – David Carlisle Apr 7 '20 at 7:08
7

All TeX macros take brace groups around their arguments, which may be omitted if the argument is a single token, although in the case of delimited arguments that group may not be where you mean.

\def\foo#1#2{...}

then \foo12, \foo 1 2, \foo{1}{2}, \foo{1} {2} are all equivalent.

\def\foo(#1,#2){...}

then \foo(1,2), \foo({1},{2}), \foo(1,{2}), are all equivalent.


TeX primitives do not have optional braces in this way.

\hbox{a} the braces are mandatory \hbox a is a syntax error, however the brace can be implicit \hbox\bgroup a} is OK.

\hskip 5pt takes no braces, \hskip{5pt} is a syntax error

\def\foo{zzz} must have no brace around the token being defined,


Some commands documented as taking arguments do not, so for example \section is a macro which takes no arguments it just looks ahead for * or [ calling appropriate macros in each case, so

\section*[short]{l} could be written as \section*[{short}] l but not \section{*}[short]{l}


Some packages redefine ^ and _ to be macros and take arguments with optional brace groups as above, but the primitive behaviour is "interesting" as tex expands looking for possibly implicit braces so for example x^\mathrm x is x^{\mathrm{x}} and x^\frac12 is x^{\frac{1}{2}} as the first level expansions of \mathrm and \frac provide the {} group needed for the superscript.

1
  • @UlrichDiez ooh yes thanks for the correction. – David Carlisle Apr 7 '20 at 18:36
4

This is not a complete answer but anything that uses \@ifnextchar can cause problems. I borrowed \CheckMinus from this answer.

\documentclass{article}
\makeatletter
\def\CheckMinus{\@ifnextchar-{got a minus}{no minus}}
\makeatother
\begin{document}
$\left(1\right)$ % works
%$\left{(}1\right{)}$ % doesn't work

\CheckMinus-

\CheckMinus{-}

\end{document}

enter image description here

4

Quite a few TeX "primitives" take arguments that must not be delimited in a group.

  • Text-mode and math-mode kerning commands: \kern and \mkern, which take 1 argument each.

    a\kern1em a
    $a\mkern18mu a$
    

    are both ok, but a\kern{1em} a and $a\mkern{18mu} a$ are not.

  • The commands \raise and \lower, which take 2 arguments each, the first of which must be a length variable and the second of which must be a (TeX) box.

    a\raise1ex\hbox{c}
    

    is ok, but a\raise{1ex}\hbox{c}, a\raise1ex{\hbox{c}}, and a\raise{1ex}{\hbox{c}} are not.

    The LaTeX counterpart to a\raise1ex\hbox{c} is \raisebox{1ex}{c}, which also takes two arguments. Clearly, with \raisebox is allowed -- and, depending on the arguments, may in fact be required.

3

Problems will occur with macros whose last argument is delimited by an opening curly brace.
You can define such macros using the #{-notation with the ⟨parameter text⟩ of their definition.

An example of such a macro is the \name-macro.

The \name-macro is used for avoiding \expandafter-orgies with \csname..\endcsname.

The \name-macro processes an argument that is delimited by an opening curly brace.

It is defined as follows:

\makeatletter
\@ifdefinable\name{%
  \long\def\name#1#{\romannumeral0\innername{#1}}%
}%
\newcommand\innername[2]{%
  \expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\newcommand\exchange[2]{#2#1}%
\makeatother

It is used as follows:

\name⟨stuff without curly braces⟩{macroname}
⟨stuff without curly braces⟩\macroname

E.g.,

\name{macro}\macro
\name\newcommand{macro}...\newcommand\macro...
\name\global\long\def{macro}...\global\long\def\macro...
\name\string{macro}\string\macro
\name\meaning{macro}\meaning\macro
\name\name\global\let{macroA}={macroB}\name\global\let\macroA={macroB}\global\let\macroA=\macroB

Assume ⟨stuff without curly braces⟩ is a single token, something like this:

\name X{Y}X\Y

Further assume that your pretty printer wraps the X in a brace-group. In this case you have:

\name {X}{Y}\X{Y}

, which is slightly different.


When TeX is searching for the tokens that form the delimiter of a delimited macro argument, it will not take things for an argument-delimiter that are nested in a brace-group other than the brace-group wherein the macro in question is nested.

Assume a macro's argument is delimited by the character a:

\def\macro#1a{This is the argument: (#1)}

The sequence

\macro aa

yields:

This is the argument: ()a

Assume the pretty-printer wraps the first a into curly braces:

The sequence

\macro{a}a

yields:

This is the argument: (a)

, which is slightly different.

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.