[Tex/LaTex] How could LaTeX replace the tokens <= by the command \leq

math-modemath-operatorsparsing

How could LaTeX replace the tokens <= by the command \leq efficiently?

Example 1: I have this code:

\[
   2x <= 4x - 2
\]

And I want to get after the compilation this:

enter image description here

Example 2:

\[
     q --> q 
\]

Output:

enter image description here

Example 3:

\[
    Q ==> Q 
\]

Output:

enter image description here

Best Answer

I assume, the replacements should be done in math mode only. Then the starting characters can be made active via a special value "8000 for \mathcode. The characters behave in text mode as usual, but they became special in math mode.

The following example document provides parsers for the following shorthands:

<< : \ll (latexsym/amsmath)
<> : \neq
<= : \leq
<== : \Leftarrow
<=> : \Leftrightarrow
<-- : \leftarrow
<-> : \leftrightarrow
>> : \gg (latexsym/amsmath)
>= : \geq
--> : \rightarrow
-+ : \pm
+- : \mp
... : \dots (amsmath)
== : \equiv
=. : \doteq
==> : \Rightarrow
=( : \subseteq (latexsym/amsmath)
=) : \supseteq (latexsym/amsmath)
=[ : \sqsubseteq (latexsym/amsmath)
=] : \sqsubseteq (latexsym/amsmath)

Example file:

\documentclass{article}

%\usepackage{latexsym}
% * because of \gg, \ll, \subseteq, \supseteq, \sqsubseteq, \sqsupseteq
% * not needed if amsmath is loaded

\usepackage{amsmath}% because of \dots

\makeatletter
% LaTeX's \@ifnextchar gobbles spaces, therefore
% \msh@ifnextchar is defined that keeps spaces
\newcommand*{\msh@ifnextchar}[3]{%
  \def\msh@temp{\msh@@ifnextchar{#1}{#2}{#3}}%
  \futurelet\msh@token\msh@temp
}
\newcommand*{\msh@@ifnextchar}[1]{%
  \ifx\msh@token#1%
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}

% <<
% <>
% <=
% <==
% <=>
% <--
% <->
% >>
% >=
% -->
% -+
% +-
% ...
% ==
% =.
% ==>
% =(
% =)
% =[
% =]

% Commands that take the original meanings of the special characters
\mathchardef\msh@code@less=\mathcode`\<\relax
\mathchardef\msh@code@greater=\mathcode`\>\relax
\mathchardef\msh@code@minus=\mathcode`\-\relax
\mathchardef\msh@code@plus=\mathcode`\+\relax
\mathchardef\msh@code@equal=\mathcode`\=\relax
\mathchardef\msh@code@dot=\mathcode`\.\relax

% Macro \resetmathshorthands resets the original meaning of the
% special characters by resetting their \mathcode values
\@ifdefinable{\resetmathshorthands}{%
  \edef\resetmathshorthands{%
    \mathcode\number`\<=\msh@code@less
    \mathcode\number`\>=\msh@code@greater
    \mathcode\number`\-=\msh@code@minus
    \mathcode\number`\+=\msh@code@plus
    \mathcode\number`\.=\msh@code@dot
    \mathcode\number`\==\msh@code@equal
  }%
}

% Macro \setmathshorthands activates and defines the special
% characters
\begingroup
  \catcode`\<=\active
  \catcode`\>=\active
  \catcode`\-=\active
  \catcode`\+=\active
  \catcode`\.=\active
  \catcode`\==\active
  \edef={\string=}%
  \@ifdefinable{\setmathshorthands}{%
    \xdef\setmathshorthands{%
      \mathcode\number`\<="8000 %
      \mathcode\number`\>="8000 %
      \mathcode\number`\-="8000 %
      \mathcode\number`\+="8000 %
      \mathcode\number`\.="8000 %
      \mathcode\number`\=="8000 %
      \let\noexpand<\noexpand\msh@less
      \let\noexpand>\noexpand\msh@greater
      \let\noexpand-\noexpand\msh@minus
      \let\noexpand+\noexpand\msh@plus
      \let\noexpand.\noexpand\msh@dot
      \let\noexpand=\noexpand\msh@equal
    }%
  }%
\endgroup

% The parsers for the math shorthands follow:

% <<
% <>
% <=
% <==
% <=>
% <--
% <->
\newcommand*{\msh@less}{%
  \msh@ifnextchar<{%
    \ll\@gobble
  }{%
    \msh@ifnextchar>{%
      \neq\@gobble
    }{%
      \msh@ifnextchar={%
        \expandafter\msh@less@equal\@gobble
      }{%
        \msh@ifnextchar-{%
          \expandafter\msh@less@minus\@gobble
        }{%
          \msh@code@less
        }%
      }%
    }%
  }%
}
\newcommand*{\msh@less@equal}{%
  \msh@ifnextchar={%
    \Leftarrow\@gobble
  }{%
    \msh@ifnextchar>{%
      \Leftrightarrow\@gobble
    }{%
      \leq
    }%
  }%
}
\newcommand*{\msh@less@minus}{%
  \msh@ifnextchar-{%
    \leftarrow\@gobble
  }{%
    \msh@ifnextchar>{%
      \leftrightarrow\@gobble
    }{%
      \msh@code@less\msh@code@minus
    }%
  }%
}

% >>
% >=
\newcommand*{\msh@greater}{%
  \msh@ifnextchar>{%
    \gg\@gobble
  }{%
    \msh@ifnextchar={%
      \geq\@gobble
    }{%
      \msh@code@greater
    }%
  }%
}

% -->
% -+
\newcommand*{\msh@minus}{%
  \msh@ifnextchar-{%
    \expandafter\msh@minus@minus\@gobble
  }{%
    \msh@ifnextchar+{%
      \mp\@gobble
    }{%
      \msh@code@minus
    }%
  }%
}
\newcommand*{\msh@minus@minus}{%
  \msh@ifnextchar>{%
    \rightarrow\@gobble
  }{%
    \msh@code@minus\msh@code@minus
  }%
}

% +-
\newcommand*{\msh@plus}{%
  \msh@ifnextchar-{%
    \pm\@gobble
  }{%
    \msh@code@plus
  }%
}

% ...
\newcommand*{\msh@dot}{%
  \msh@ifnextchar.{%
    \expandafter\msh@dot@dot\@gobble
  }{%
    \msh@code@dot
  }%
}
\newcommand*{\msh@dot@dot}{%
  \msh@ifnextchar.{%
    \expandafter\msh@dot@dot@dot\@gobble
  }{%
    \msh@code@dot
    \msh@code@dot
  }%
}
\newcommand*{\msh@dot@dot@dot}{%
  % remove space after "...", because a space would
  % disturb \dots' auto-positioning feature.
  \expandafter\dots\romannumeral-`\x
}

% ==
% =.
% ==>
% =(
% =)
% =[
% =]
\newcommand*{\msh@equal}{%
  \msh@ifnextchar={%
    \expandafter\msh@equal@equal\@gobble
  }{%
    \msh@ifnextchar.{%
      \doteq\@gobble
    }{%
      \msh@ifnextchar({%
        \subseteq\@gobble
      }{%
        \msh@ifnextchar){%
          \supseteq\@gobble
        }{%
          \msh@ifnextchar[{%
            \sqsubseteq\@gobble
          }{%
            \msh@ifnextchar]{%
              \sqsupseteq\@gobble
            }{%
              \msh@code@equal
            }%
          }%
        }%
      }%
    }%
  }%
}
\newcommand*{\msh@equal@equal}{%
  \msh@ifnextchar>{%
    \Rightarrow\@gobble
  }{%
    \equiv
  }%
}
\makeatother

% Activate math shorthands in the math modes
\everymath{\setmathshorthands}
\everydisplay{\setmathshorthands}

\begin{document}
\centering
\newcommand*{\test}[1]{%
  $#1$%
  \[#1\]%
}
\test{a << b < c <= d >= e > f >> g}
\test{a <> b = c =. d == e}
\test{a <== b <-- c <-> d <=> e --> f ==> g}
\test{a +- b = -(-a -+ +b)}
\test{a, ..., z <> a + ...+ z}
\test{a =( b =) c =[ e =] f}
\end{document}

Result

Remarks:

  • Macro \msh@ifnextchar looks up the next token. In opposite to LaTeX's \@ifnextchar it does not gobble spaces. For example, this is important for a + -b (a - b) that is different from a +- b (a ± b).

  • ... are replaced by \dots of package amsmath, because it has an auto-detection feature. The vertical position of the dots depends on the next token. For example, in a comma separated list, \dots become \ldots; if the next token is a +, then \cdots is used.

    Spaces are gobbled after a command token like \dots, but not after other characters like .... Therefore \msh@dot@dot@dot removes a following space before calling \dots. Otherwise \dots would see the space and become \ldots, even, if the token after the space is a +.

  • The suggested _C for \subseteq looks too ambiguous too me, because it looks like a normal subscript C. Also there is not a good ASCII letter for use in the shorthand of \supseteq. Therefore I have implemented the shorthands =(, =) and the pair =[, =] for the square forms.

    If round or square parentheses follows the equal sign, then the shorthand replacement can be prevented by a space, e.g. a = (b + c).

Related Question