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, 2020 at 3:38
  • @Schrödinger'scat Good point. I mean a macro that accepts arguments. Apr 7, 2020 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, 2020 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... Apr 7, 2020 at 3:49
  • 5
    all macros accept brace groups but not all commands are macros Apr 7, 2020 at 7:08

4 Answers 4

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. Apr 7, 2020 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.

You must log in to answer this question.

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