9

I am trying to set things up so that occurrences of $$ are replaced by \] if in math mode, \[ otherwise. My strategy is to make $ active and have it behave differently depending on whether the following token is also a $. However, I cannot seem to make this work. Here is my attempt:

\documentclass{article}
\usepackage{lipsum}
\def\unactiveDollar{$}

\catcode`\$=\active
\def\activeDollar{$}

\def\executedollar{%
        \if\isDollar\activeDollar%
            \let\next=\executeDDollar%
        \else%
            \let\next=\unactiveDollar%
        \fi%
        \next%
    }
\def\executeDDollar{%
        \ifmmode%
            \let\next=\]%
        \else%
            \let\next=\[%
        \fi%
        \next%
        \let\absorbnext=
    }
\def${\futurelet\isDollar\executedollar}

\begin{document}
\lipsum*[7]
$$\int x^2 \; dx = \frac{1}{3}x^3 + C$$
\lipsum[9]
%Make sure $ $ is not mistaken for $$:
It follows that $E = m c^2$ $\forall x \in X$.
\end{document}

The resulting error is

./doubledollar.tex:29: TeX capacity exceeded, sorry [input stack size=5000].
\isDollar ->\if \isDollar 
                          \activeDollar \let \next =\executeDDollar \else \l...
l.29 $$
       \int x^2 \; dx = \frac{1}{3}x^3 + C$$
./doubledollar.tex:29:  ==> Fatal error occurred, no output PDF file produced!

Other variations (e.g., substituting \ifx for \if) give different errors or simply incorrect output, but nothing gets it right. And as far as I can tell, the key problem is that my method for determining whether the following token is a $ (and absorbing it if so) simply does not work.

What am I doing wrong, and how can I fix it?

Additional note: An answer that throws an error whenever $$ is used instead of other macros/environments (which must be able to use it internally) would also be acceptable, since it answers the titular question.

lockstep
  • 250,273

4 Answers4

6

You should use \ifx and not \if, that just compares character codes and does full expansion.

\documentclass{article}
\usepackage{lipsum}

\let\unactiveDollar=$
\catcode`\$=\active

\def\executedollar{%
  \ifx\isDollar$%
    \let\next=\executeDDollar
  \else
    \let\next=\unactiveDollar
  \fi
  \next
}
\def\executeDDollar{%
  \ifmmode
    \let\next=\]%
  \else
    \let\next=\[%
  \fi
  \next
  \let\absorbnext=
}
\def${\futurelet\isDollar\executedollar}

\begin{document}
\lipsum*[7]
$$\int x^2 \; dx = \frac{1}{3}x^3 + C$$
\lipsum[9]
%Make sure $ $ is not mistaken for $$:
It follows that $E = m c^2$ $\forall x \in X$.
\end{document}

Of course input such as

It follows that $E = m c^2$$\forall x \in X$.

which will produce weird errors, which might be cured with an \ifinner test.

Here's a similar version that does the check for a legal (albeit disputable) $$:

\documentclass{article}
\usepackage{lipsum}

\makeatletter
\let\staats@inactivedollar=$
\catcode`\$=\active

\def\staats@execdollar{%
  \ifx\let@token$%
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  \staats@execddollar
  \staats@inactivedollar
}
\def\staats@execddollar{%
  \ifmmode
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\ifinner
     \expandafter\@firstoftwo
   \else
     \expandafter\@secondoftwo
   \fi
   {\staats@inactivedollar}%
   {\]\let\staats@absorbnext= }}%
  {\[\let\staats@absorbnext= }%
}
\protected\def${\futurelet\let@token\staats@execdollar}

\begin{document}
\lipsum*[7]
$$\int x^2 \; dx = \frac{1}{3}x^3 + C$$
\lipsum[9]
%Make sure $ $ is not mistaken for $$:
It follows that $E = m c^2$ $\forall x \in X$.

It follows that $E = m c^2$$\forall x \in X$.
\end{document}

I fear that there are several things that can make this go wrong. The best is simply insisting that $$ should never be used. Physical coercion might be fruitful. ;-)

egreg
  • 1,121,712
6

I believe the easiest way, though fairly draconian, is to place

\RequirePackage[l2tabu,orthodox]{nag}

in your document class, or

\usepackage[l2tabu,orthodox]{nag}

in whatever auxiliary package you may be providing.

This will teach your users many things to be avoided, including $$...$$.

2

Based on this answer by egreg, together with the \new@nextchar macro from the amsmath package, I have an answer that seems to work, at least for this example:

\documentclass{article}
\usepackage{lipsum}

\RequirePackage{amsmath}

\makeatletter
\def\unactiveDollar{$}
\catcode`\$=\active
\def\@DDollar${%
        \ifmmode%
            \let\next=\]%
        \else%
            \let\next=\[%
        \fi%
        \next%
    }
\protected\def${\new@ifnextchar$\@DDollar\unactiveDollar}
\makeatother

\begin{document}
\lipsum*[7]
$$\int x^2 \; dx = \frac{1}{3}x^3 + C$$
\lipsum[9]
%Make sure $ $ is not mistaken for $$:
It follows that $E = m c^2$ $\forall x \in X$.
\end{document}

Question: Why is the definition of $ protected? I copied this behavior because I assume there's a good reason for it, but this example works fine without it.

  • It's protected because \new@ifnextchar is not expandable, I guess. Also \@DDollar is not expandable. Place the unprotected version in an \edef and see what's happens... – cgnieder Jul 30 '14 at 19:39
  • 1
    It needs to be protected otherwise it'll go very wrong at the start of table cells, or if you go \caption{ foo $x$} or ... – David Carlisle Jul 30 '14 at 20:20
0

Here it is another answer. Instead of testing many things, after deciding if it starts inline or display math the $s are gobbled.

\documentclass{scrartcl}
\usepackage{fixltx2e}
\usepackage{mathtools}

\makeatletter
\catcode`\$=\active
\protected\def${\new@ifnextchar$\staats@dmath\staats@math}
\def\staats@math#1${\(#1\)}
\def\staats@dmath$#1$${\[#1\]}
\makeatother

\begin{document}
Some math $x^2$

and a little more
$$ x^2 $$$x^2$$24$

\begin{figure}[h]
    \rule{2cm}{2cm}
    \caption{$x^2 +1$}
\end{figure}
\end{document}
Manuel
  • 27,118