summaryrefslogtreecommitdiff
path: root/macros/generic/misc/mathlig.tex
blob: 04194b84931fc80a8f78ae9589dd83e8a120b8d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
%
% mathlig.tex
%
% (c) 2001 Jules Bean <jules@jellybean.co.uk>
% Permission is granted to freely use, copy, and distribute
% without restriction. Permission is also granted to distribute
% modified versions and incorporate into your own macro packages
% if a brief acknowledgement is given.
%
% Version 1.0  11 May 2001

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

\count255\catcode`@
\catcode`@=11
\chardef\mathlig@atcode\count255

% Let or def an 'active' version of a token.  Thanks to Donald
% Arseneau and groups.google.com
\def\actively#1#2{\begingroup\uccode`\~=`#2\relax\uppercase{\endgroup#1~}}
% Eat the next token, and then pass control to mathlig@next@cmd
\def\mathlig@gobble{\afterassignment\mathlig@next@cmd\let\mathlig@next= }
% Used to delimit delimited arguments, and ifx tests for emptyness.
\def\mathlig@delim{\mathlig@delim}
% Some macros to help dealing with 'computed names' (csnames).
% Just like def, but first parameter is evaluated as a csname
\def\mathlig@defcs#1{\expandafter\def\csname#1\endcsname}
% A version of \let <normal cs><computed cs>
\def\mathlig@let@cs#1#2{\expandafter\let\expandafter#1\csname#2\endcsname}
% 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}}

% The main externally visible macro. 
% Defines '#1' as a 'ligature' to expand to #2
% First uses \mathlig@checklig to make sure that all initial segments of
% #1 as set up as ligatures.
% Then sets the ligature #1 to expand to #2.
\def\mathlig#1#2{\mathlig@checklig#1\mathlig@end\mathlig@defcs{mathlig@back@#1}{#2}\ignorespaces}

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

%Check #1#2 can be a ligature:
% Check #1 is math-active.
% If not, save the mathcode, set the macros #1
%Now check the suffix tables with \checkrest
\def\mathlig@checklig#1#2\mathlig@end{%
 \expandafter\ifx\csname mathlig@forw@#1\endcsname\relax
 \expandafter\mathchardef\csname mathlig@back@#1\endcsname=\mathcode`#1%
 \mathcode`#1"8000\actively\def#1{\csname mathlig@look@#1\endcsname}%
 \mathlig@dolig#1\mathlig@delim
\fi
\mathlig@checksuffix#1#2\mathlig@end
}

%Two-part macro.
%Check suffix tables. If #2 is empty, no suffix!
%Otherwise, we make sure #2 is a valid suffix for #1,
%then recurse for #3 on #1#2
\def\mathlig@checksuffix#1#2\mathlig@end{%
\ifx\mathlig@delim#2\mathlig@delim\relax\else\mathlig@checksuffix@{#1}#2\mathlig@end\fi
}
\def\mathlig@checksuffix@#1#2#3\mathlig@end{%
\expandafter\ifx\csname mathlig@forw@#1#2\endcsname\relax\mathlig@dosuffix{#1}{#2}\fi
\mathlig@checksuffix{#1#2}#3\mathlig@end
}

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

% Add #2 to the list of valid suffixes for #1
% Then make the ligature for #1#2 one which only backtracks, for now
\def\mathlig@dosuffix#1#2{%
\mathlig@appendcs{mathlig@toks@#1}{#2}%
\mathlig@dolig{#1}{#2}\mathlig@delim
}

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

\def\mathlig@dolig#1#2\mathlig@delim{%
%The look macro just \futurelets what's coming up and
%then passes control to forw
 \mathlig@defcs{mathlig@look@#1#2}{%
 \mathlig@let@cs\mathlig@next{mathlig@forw@#1#2}\futurelet\mathlig@next@tok\mathlig@next}%
%The forw macro uses chck to try all possible suffixes, passing control
%either to one of those, or to the back macro
 \mathlig@defcs{mathlig@forw@#1#2}{%
  \mathlig@let@cs\mathlig@next{mathlig@back@#1#2}%
  \mathlig@let@cs\checker{mathlig@chck@#1#2}%
  \mathlig@let@cs\mathligtoks{mathlig@toks@#1#2}%
  \expandafter\ifx\expandafter\mathlig@delim\mathligtoks\mathlig@delim\relax\else
  \expandafter\checker\mathligtoks\mathlig@delim\fi
  \mathlig@next
 }%
%The toks macro just stores the suffixes
 \mathlig@defcs{mathlig@toks@#1#2}{}%
%The chk macro goes through the suffixes one by one
%tail recursing until it runs out, or finds one.
 \mathlig@defcs{mathlig@chck@#1#2}##1##2\mathlig@delim{%
  %\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\mathlig@gobble
  \fi 
  \ifx\mathlig@delim##2\mathlig@delim\relax\else
   \csname mathlig@chck@#1#2\endcsname##2\mathlig@delim
  \fi
 }%
%
% The back macro, defined only if this is a ligature of at least
% two characters, is a default fallback: go back to the previous char.
% (If this is a ligature of only one character, the mathcode will have 
% been saved and set as the fallback elsewhere)
 \ifx\mathlig@delim#2\mathlig@delim\else
  \mathlig@defcs{mathlig@back@#1#2}{\csname mathlig@back@#1\endcsname #2}%
 \fi
}%

\catcode`@\mathlig@atcode