%D mathlig.tex
%D 
%D (c) 2001 Jules Bean <jules@jellybean.co.uk>
%D Permission is granted to freely use, copy, and distribute
%D without restriction. Permission is also granted to distribute
%D modified versions and incorporate into your own macro packages
%D if a brief acknowledgement is given.
%D 
%D Version 1.0  11 May 2001

%D Define 'mathmode' ligatures, as in 
%D 
%D \definemathligature  [->]  {\rightarrow}
%D \definemathligature  [<-]  {\leftarrow}
%D \definemathligature  [<->] {\leftrightarrow}
%D 
%D Works even if they have common prefixes (takes the longest match,
%D then backtracks).
%D 
%D Can be used to create ligatures even if the second
%D and subsequent characters have weird catcodes, but I don't recommend 
%D it.

\unprotect

%D Let or def an 'active' version of a token.  Thanks to Donald
%D Arseneau and groups.google.com
\def\actively#1#2%
  {\begingroup\uccode`\~=`#2\relax\uppercase{\endgroup#1~}}

%D Eat the next token, and then pass control to mathlig@next@cmd
\def\gobblemathligature
  {\afterassignment\mathlig@next@cmd\let\mathlig@next=}

%D A version of \let <normal cs><computed cs>
\def\mathlig@let@cs#1#2%
  {\expandafter\let\expandafter#1\csname#2\endcsname}

%D Maintain a 'list' of tokens as a macro which expands to them
\def\mathlig@appendcs#1#2%
  {\expandafter\edef\csname#1\endcsname{\csname#1\endcsname#2}}

%D The main externally visible macro.  Defines \type|#1| as a 'ligature' to
%D expand to \type|#2| First uses \tex{startcheckmathligature} to make sure
%D that all initial segments of \type|#1| as set up as ligatures.  Then sets
%D the ligature \type|#1| to expand to \type|#2|.

\def\definemathligature
  {\dosingleargument\dodefinemathligature}

\def\dodefinemathligature[#1]#2%
{\startcheckmathligature#1\stopcheckmathligature
   \setvalue{mathlig@back@#1}{#2}}

%D All the \type|check| macros should be idempotent: they set up the
%D definitions only if it hasn't already been done.

%D Check \type|#1#2| can be a ligature:
%D Check \type|#1| is math-active.
%D If not, save the mathcode, set the macros \type|#1|
%D Now check the suffix tables with \tex{checkrest} (AM??)

\def\startcheckmathligature#1#2\stopcheckmathligature
 {\doifundefined{mathlig@forw@#1}
  {\expandafter\mathchardef\csname mathlig@back@#1\endcsname=\mathcode`#1%
    \mathcode`#1"8000\actively\def#1{\getvalue{mathlig@look@#1}}%
   \dostartmathligature#1\dostopmathligature}
   \startcheckmathligaturesuffix#1#2\stopcheckmathligaturesuffix}

%D Two-part macro.
%D Check suffix tables. If \type|#2| is empty, no suffix!
%D Otherwise, we make sure \type|#2| is a valid suffix for \type|#1|,
%D then recurse for \type|#3| on \type|#1#2|

\def\startcheckmathligaturesuffix#1#2\stopcheckmathligaturesuffix
  {\doifnotempty{#2}
    {\dostartcheckmathligaturesuffix{#1}#2\dostopcheckmathligaturesuffix}}

\def\dostartcheckmathligaturesuffix#1#2#3\dostopcheckmathligaturesuffix%
 {\doifundefined{mathlig@forw@#1#2}
  {\domathligaturesuffix{#1}{#2}}
  \startcheckmathligaturesuffix{#1#2}#3\stopcheckmathligaturesuffix}

%D The do macros should not be called more than once for a given ligature.

%D Add \type|#2| to the list of valid suffixes for \type|#1|
%D Then make the ligature for \type|#1#2| one which only backtracks, for now

\def\domathligaturesuffix#1#2%
 {\mathlig@appendcs{mathlig@toks@#1}{#2}%
  \dostartmathligature{#1}{#2}\dostopmathligature}

%D Setup \type|#1#2| so that ligatures beginning \type|#1#2| work.
%D Such ligatures will look at what's coming next (in case
%D of a longer ligature) and, failing that, 
%D backtrack to just \type|#1|

\def\dostartmathligature#1#2\dostopmathligature
%D The look macro just \futurelets what's coming up and
%D then passes control to forw
 {\setvalue{mathlig@look@#1#2}%
  {\mathlig@let@cs\mathlig@next{mathlig@forw@#1#2}%
   \futurelet\mathlig@next@tok\mathlig@next}%
%D The forw macro uses chck to try all possible suffixes, passing control
%D either to one of those, or to the back macro
 \setvalue{mathlig@forw@#1#2}%
 {\mathlig@let@cs\mathlig@next{mathlig@back@#1#2}%
  \mathlig@let@cs\checkmathligatures{dostartmathligaturecheck#1#2}%
  \mathlig@let@cs\mathligtoks{mathlig@toks@#1#2}%
  \expandafter\doifsomething{\mathligtoks}%
  {\expandafter\checkmathligatures\mathligtoks\dostopmathligaturecheck}%
  \mathlig@next}
%D The toks macro just stores the suffixes
 \resetvalue{mathlig@toks@#1#2}%
%D The chk macro goes through the suffixes one by one
%D tail recursing until it runs out, or finds one.
 \setvalue{dostartmathligaturecheck#1#2}##1##2\dostopmathligaturecheck%
 {%\message{Lig so far '#1#2', checking for '##1'}%
  \ifx\mathlig@next@tok##1%
   \mathlig@let@cs\mathlig@next@cmd{mathlig@look@#1#2##1}%
   \let\mathlig@next\gobblemathligature
  \fi 
  \doifnotempty{##2}
   {\getvalue{dostartmathligaturecheck#1#2}##2\dostopmathligaturecheck}}
%D The back macro, defined only if this is a ligature of at least
%D two characters, is a default fallback: go back to the previous char.
%D (If this is a ligature of only one character, the mathcode will have 
%D been saved and set as the fallback elsewhere)
 \doifnotempty{#2}
   {\setvalue{mathlig@back@#1#2}{\getvalue{mathlig@back@#1}#2}}}

\protect

% NOTE: (AM) It should be possible to replace mathlig@....@ by
% \dosetvalue{mathlig}{...} etc.

% Test:

\definemathligature [->]  {\rightarrow}
\definemathligature [<-]  {\leftarrow}
\definemathligature [<->] {\leftrightarrow}

\starttext
$A -> B$
$A <-> B$
$A <- B$
\stoptext
