lang-mis.mkiv /size: 25 Kb    last modification: 2021-10-28 13:50
1%D \module
2%D   [       file=lang-mis,
3%D        version=1997.03.20, % used to be supp-lan.tex
4%D          title=\CONTEXT\ Language Macros,
5%D       subtitle=Compounds,
6%D         author=Hans Hagen,
7%D           date=\currentdate,
8%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
9%C
10%C This module is part of the \CONTEXT\ macro||package and is
11%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
12%C details.
13
14\writestatus{loading}{ConTeXt Language Macros / Compounds}
15
16%D More or less replaced.
17
18%D \gdef\starttest#1\stoptest{\starttabulate[|l|l|p|]#1\stoptabulate}
19%D \gdef\test     #1{\NC\detokenize{#1}\NC\hyphenatedword{#1}\NC#1\NC\NR}
20
21\unprotect
22
23%D One of \TEX's strong points in building paragraphs is the way hyphenations are
24%D handled. Although for real good hyphenation of non||english languages some
25%D extensions to the program are needed, fairly good results can be reached with the
26%D standard mechanisms and an additional macro, at least in Dutch.
27%D
28%D \CONTEXT\ originates in the wish to typeset educational materials, especially in
29%D a technical environment. In production oriented environments, a lot of compound
30%D words are used. Because the Dutch language poses no limits on combining words, we
31%D often favor putting dashes between those words, because it facilitates reading,
32%D at least for those who are not that accustomed to it.
33%D
34%D In \TEX\ compound words, separated by a hyphen, are not hyphenated at all. In
35%D spite of the multiple pass paragraph typesetting this can lead to parts of words
36%D sticking into the margin. The solution lays in saying \type
37%D {spoelwater||terugwinunit} instead of \type {spoelwater-terugwinunit}. By using a
38%D one character command like \type {|}, delimited by the same character \type {|},
39%D we get ourselves both a decent visualization (in \TEXEDIT\ and colored verbatim
40%D we color these commands yellow) and an efficient way of combining words.
41%D
42%D The sequence \type{||} simply leads to two words connected by a hyphen. Because
43%D we want to distinguish such a hyphen from the one inserted when \TEX\ hyphenates
44%D a word, we use a bit longer one.
45%D
46%D \hyphenation {spoel-wa-ter te-rug-win-unit}
47%D
48%D \starttest
49%D \test {spoelwater||terugwinunit}
50%D \stoptest
51%D
52%D As we already said, the \type{|} is a command. This commands accepts an optional
53%D argument before it's delimiter, which is also a \type{|}.
54%D
55%D \hyphenation {po-ly-meer che-mie}
56%D
57%D \starttest
58%D \test {polymeer|*|chemie}
59%D \stoptest
60%D
61%D Arguments like \type{*} are not interpreted and inserted directly, in contrary to
62%D arguments like:
63%D
64%D \starttest
65%D \test {polymeer|~|chemie}
66%D \test {|(|polymeer|)|chemie}
67%D \test {polymeer|(|chemie|)| }
68%D \stoptest
69%D
70%D Although such situations seldom occur |<|we typeset thousands of pages before we
71%D encountered one that forced us to enhance this mechanism|>| we also have to take
72%D care of comma's.
73%D
74%D  \hyphenation {uit-stel-len}
75%D
76%D  \starttest
77%D  \test {op||, in|| en uitstellen}
78%D  \stoptest
79%D
80%D The next special case (concerning quotes) was brought to my attention by Piet
81%D Tutelaers, one of the driving forces behind rebuilding hyphenation patterns for
82%D the dutch language.\footnote{In 1996 the spelling of the dutch language has been
83%D slightly reformed which made this topic actual again.} We'll also take care of
84%D this case.
85%D
86%D \starttest
87%D \test {AOW|'|er}
88%D \test {cd|'|tje}
89%D \test {ex|-|PTT|'|er}
90%D \test {rock|-|'n|-|roller}
91%D \stoptest
92%D
93%D Tobias Burnus pointed out that I should also support something like
94%D
95%D \starttest
96%D \test {well|_|known}
97%D \stoptest
98%D
99%D to stress the compoundness of hyphenated words.
100%D
101%D Of course we also have to take care of the special case:
102%D
103%D \starttest
104%D \test {text||color and ||font}
105%D \stoptest
106
107%D \macros
108%D   {installdiscretionaries}
109%D
110%D The mechanism described here is one of the older inner parts of \CONTEXT. The
111%D most recent extensions concerns some special cases as well as the possibility to
112%D install other characters as delimiters. The prefered way of specifying compound
113%D words is using \type{||}, which is installed by:
114%D
115%D \starttyping
116%D \installdiscretionary | -
117%D \stoptyping
118%D
119%D Some alternative definitions are:
120%D
121%D \startbuffer
122%D \installdiscretionary * -
123%D \installdiscretionary + -
124%D \installdiscretionary / -
125%D \installdiscretionary ~ -
126%D \stopbuffer
127%D
128%D \typebuffer
129%D
130%D after which we can say:
131%D
132%D \start \getbuffer
133%D \starttest
134%D \test {test**test**test}
135%D \test {test++test++test}
136%D \test {test//test//test}
137%D \test {test~~test~~test}
138%D \stoptest
139%D \stop
140
141%D \macros
142%D   {compoundhyphen}
143%D
144%D Now let's go to the macros. First we define some variables. In the main \CONTEXT\
145%D modules these can be tuned by a setup command. Watch the (maybe) better looking
146%D compound hyphen.
147
148% hm why ex
149
150\ifx\compoundhyphen    \undefined
151    \unexpanded\def\compoundhyphen {\hbox{-\kern-.10775\emwidth-}} % .25\exheight
152\fi
153
154%D The last two variables are needed for subsentences |<|like this one|>| which we
155%D did not yet mention. We want to enable breaking but at the same time don't want
156%D compound characters like |-| or || to be separated from the words. \TEX\ hackers
157%D will recognise the next two macro's:
158
159\ifx\prewordbreak \undefined \unexpanded\def\prewordbreak    {\penalty\plustenthousand\hskip\zeropoint\relax} \fi
160\ifx\postwordbreak\undefined \unexpanded\def\postwordbreak   {\penalty\zerocount      \hskip\zeropoint\relax} \fi
161\ifx\hspaceamount \undefined            \def\hspaceamount#1#2{.16667\emwidth}                                 \fi % language specific
162
163%unexpanded\def\permithyphenation{\ifhmode\prewordbreak\fi} % doesn't remove spaces
164\unexpanded\def\permithyphenation{\ifhmode\wordboundary\fi} % doesn't remove spaces
165
166%D \macros
167%D   {beginofsubsentence,endofsubsentence,
168%D    beginofsubsentencespacing,endofsubsentencespacing}
169%D
170%D In the previous macros we provided two hooks which can be used to support nested
171%D sub||sentences. In \CONTEXT\ these hooks are used to insert a small space when
172%D needed.
173
174% \ifx\beginofsubsentence       \undefined \unexpanded\def\beginofsubsentence{\hbox{\emdash}} \fi
175% \ifx\endofsubsentence         \undefined \unexpanded\def\endofsubsentence  {\hbox{\emdash}} \fi
176% \ifx\beginofsubsentencespacing\undefined \let\beginofsubsentencespacing\relax \fi
177% \ifx\endofsubsentencespacing  \undefined \let\endofsubsentencespacing  \relax \fi
178
179%D The following piece of code is a torture test compound handling. The \type
180%D {\relax} before the \type {\ifmmode} is needed because of the alignment scanner
181%D (in \ETEX\ this problem is not present because there a protected macro is not
182%D expanded. Thanks to Tobias Burnus for providing this example.
183%D
184%D \startformula
185%D   \left|f(x_n)-{1\over2}\right| =
186%D      {\cases{|{1\over2}-x_n| &for $0\le x_n < {1\over2}$\cr
187%D              |x_n-{1\over2}| &for ${1\over2}<x_n\le1$   \cr}}
188%D \stopformula
189
190\installcorenamespace{discretionaryaction}
191\installcorenamespace{discretionarytext}
192\installcorenamespace{discretionarymath}
193\installcorenamespace{discretionaryboth}
194\installcorenamespace{discretionarymode}
195
196\unexpanded\def\installdiscretionary#1#2%
197  {\setevalue{\??discretionarymath\detokenize{#1}}{\detokenize{#1}}% ?
198   \setvalue {\??discretionarytext\detokenize{#1}}{#2}%
199   \setvalue {\??discretionaryboth\detokenize{#1}}{\lang_discretionaries_command#1}%
200   \scratchcounter\expandafter`\detokenize{#1}%
201   \expandafter\uedcatcodecommand\expandafter\ctxcatcodes\expandafter\scratchcounter\csname\??discretionaryboth\detokenize{#1}\endcsname}
202
203\unexpanded\def\handlemathmodediscretionary#1{\ifcsname\??discretionarymath\detokenize{#1}\endcsname\lastnamedcs}
204\unexpanded\def\handletextmodediscretionary#1{\ifcsname\??discretionarytext\detokenize{#1}\endcsname\lastnamedcs}
205
206\unexpanded\def\installdiscretionaries#1#2{\writestatus\m!system{use \string \installdiscretionary}} % obsolete
207
208\setnewconstant\discretionarymode\plusone
209
210\unexpanded\def\ignorediscretionaries{\discretionarymode\zerocount}
211\unexpanded\def\obeydiscretionaries  {\discretionarymode\plusone}
212
213\def\lang_discretionaries_command
214  {% if direct if, we need \relax for lookahead in math mode
215   \csname\??discretionarymode
216     \ifcase\discretionarymode
217       n%
218     \else\ifmmode
219       m%
220     \else
221       t%
222     \fi\fi
223   \endcsname}
224
225\setvalue{\??discretionarymode n}#1%
226  {\detokenize{#1}}
227
228%D The macro \type{\lang_discretionaries_check_before} takes care of loners like
229%D \type{||word}, while it counterpart \type {\lang_discretionaries_check_after} is
230%D responsible for handling the comma.
231
232\newsignal\compoundbreakpoint
233
234\newconditional\punctafterdiscretionary
235\newconditional\spaceafterdiscretionary
236
237\def\lang_discretionaries_check_before %i sused grouped
238  {\ifvmode
239     \dontleavehmode
240   \fi
241   \ifhmode
242    %\begingroup
243    %\setbox\scratchbox\lastbox
244    %\ifzeropt\wd\scratchbox
245    %  \box\scratchbox\relax
246    %  \endgroup
247    %  \let\postwordbreak\prewordbreak
248    %\else
249    %  \box\scratchbox\relax
250    %  \endgroup
251    %\fi
252   \fi}
253
254\def\lang_discretionaries_check_after
255  {\setfalse\punctafterdiscretionary
256   \setfalse\spaceafterdiscretionary
257   \ifx\blankspace\nexttoken \settrue \spaceafterdiscretionary \else
258   \ifx\space     \nexttoken \settrue \spaceafterdiscretionary \else
259   \ifx          .\nexttoken \settrue \punctafterdiscretionary \else
260   \ifx          ,\nexttoken \settrue \punctafterdiscretionary \else
261   \ifx          :\nexttoken \settrue \punctafterdiscretionary \else
262   \ifx          ;\nexttoken \settrue \punctafterdiscretionary \fi\fi\fi\fi\fi\fi}
263
264\letvalue{\??discretionarymode m}\handlemathmodediscretionary
265
266\setvalue{\??discretionarymode t}#1%
267  {\bgroup
268   \let\nextnextnext\egroup
269   \def\next##1#1%
270     {\def\next{\activedododotextmodediscretionary#1{##1}}%
271      \futurelet\nexttoken\next}%
272   \next}
273
274\let\discretionarytoken   \relax
275\let\textmodediscretionary\relax
276
277\unexpanded\def\activedododotextmodediscretionary#1#2%
278  {\edef\discretionarytoken{\detokenize{#2}}%
279   \def\textmodediscretionary{\handletextmodediscretionary{#1}}%
280   \lang_discretionaries_check_after
281   \ifx\discretionarytoken\empty
282     \ifx#1\nexttoken % takes care of ||| and +++ and ......
283       \ifcsname\??discretionaryaction\string#1\endcsname
284         \lastnamedcs
285       \else\ifconditional\spaceafterdiscretionary
286        %\prewordbreak\hbox{\string#1}\relax
287         \wordboundary\hbox{\string#1}\relax
288       \else\ifconditional\punctafterdiscretionary
289        %\prewordbreak\hbox{\string#1}\relax
290         \wordboundary\hbox{\string#1}\wordboundary
291       \else
292        %\prewordbreak\hbox{\string#1}\prewordbreak
293         \wordboundary\hbox{\string#1}\wordboundary
294       \fi\fi\fi
295       \def\nextnextnext{\afterassignment\egroup\let\next=}%
296     \else
297       \lang_discretionaries_check_before
298       % the next line has been changed (20050203)
299       % \prewordbreak\hbox{\textmodediscretionary\nexttoken}\postwordbreak
300       % but an hbox blocks a possible \discretionary
301       \ifcsname\??discretionaryaction\endcsname
302         \lastnamedcs
303       \else\ifconditional\spaceafterdiscretionary
304        %\prewordbreak\textmodediscretionary\relax
305         \wordboundary\textmodediscretionary\relax
306       \else\ifconditional\punctafterdiscretionary
307        %\prewordbreak\textmodediscretionary\relax
308         \wordboundary\textmodediscretionary\relax
309       \else
310        %\prewordbreak\textmodediscretionary\prewordbreak
311         \wordboundary\textmodediscretionary\wordboundary
312       \fi\fi\fi
313     \fi
314   \else\ifcsname\??discretionaryaction\discretionarytoken\endcsname
315     \lastnamedcs
316   \else
317     \lang_discretionaries_check_before
318     \ifconditional\spaceafterdiscretionary
319      %\prewordbreak\hbox{#2}\relax
320       \wordboundary\hbox{#2}\relax
321     \else\ifconditional\punctafterdiscretionary
322      %\prewordbreak\hbox{#2}\relax
323       \wordboundary\hbox{#2}\relax
324     \else
325      %\prewordbreak\discretionary{\hbox{#2}}{}{\hbox{#2}}\postwordbreak
326       \wordboundary\discretionary{\hbox{#2}}{}{\hbox{#2}}\wordboundary
327     \fi\fi
328   \fi\fi
329   \nextnextnext}
330
331%D \macros
332%D   {directdiscretionary}
333%D
334%D In those situations where the nature of characters is less predictable, we can
335%D use the more direct approach:
336
337\unexpanded\def\directdiscretionary
338  {\csname\??discretionarymode
339     \ifcase\discretionarymode
340       n%
341     \else
342       d%
343     \fi
344   \endcsname}
345
346\unexpanded\def\indirectdiscretionary
347  {\csname\??discretionarymode
348     \ifcase\discretionarymode
349       n%
350     \else
351       i%
352     \fi
353   \endcsname}
354
355\setuvalue{\??discretionarymode d}#1%
356  {\edef\discretionarytoken{\detokenize{#1}}%
357   \let\textmodediscretionary\compoundhyphen
358   \ifcsname\??discretionaryaction\discretionarytoken\endcsname
359     \expandafter\lastnamedcs
360   \else
361     \expandafter\indirectdiscretionary
362   \fi{#1}}
363
364\setuvalue{\??discretionarymode i}#1%
365 %{\prewordbreak\discretionary{\hbox{#1}}{}{\hbox{#1}}\postwordbreak}
366  {\wordboundary\discretionary{\hbox{#1}}{}{\hbox{#1}}\wordboundary}
367
368\unexpanded\def\definetextmodediscretionary #1
369  {\setvalue{\??discretionaryaction\detokenize{#1}}}
370
371% \start \hsize 1mm
372% test |||test test|||, test\blank
373% test test|-|, test|-| and test|-|test\blank
374% test test|_|, test|_| and test|_|test\blank
375% test cd|'|tje\blank
376% test |(|test test|)|, test\blank
377% test test test|x|, test\blank
378% test|~|test
379% test|^|test
380% \stop
381
382% x\discretionary{1}{2}{3}xxxxxxx
383% xxxxxxx\discretionary{1}{2}{3}x
384%
385% xxx3xxx
386% xxx1<newline>2xxx
387
388\def\lang_discretionaries_hyphen_like#1#2%
389  {\ifconditional\spaceafterdiscretionary
390     %prewordbreak\hbox{#1}\relax
391     \wordboundary\hbox{#1}\relax
392   \else\ifconditional\punctafterdiscretionary
393     %prewordbreak\hbox{#1}\relax
394     \wordboundary\hbox{#1}\relax
395   \else
396    %\prewordbreak#2\postwordbreak % was prewordbreak
397     \wordboundary#2\wordboundary
398   \fi\fi}
399
400\definetextmodediscretionary {}
401  {\lang_discretionaries_hyphen_like\textmodehyphen\textmodehyphendiscretionary}
402
403\definetextmodediscretionary -
404  {\lang_discretionaries_hyphen_like\normalhyphen\normalhyphendiscretionary}
405
406\definetextmodediscretionary _
407  {\lang_discretionaries_hyphen_like\composedhyphen\composedhyphendiscretionary}
408
409\definetextmodediscretionary )
410  {\lang_discretionaries_hyphen_like{)}{\discretionary{-)}{}{)}}}
411
412\definetextmodediscretionary (
413  {\ifdim\lastskip>\zeropoint
414    %(\prewordbreak
415     (\wordboundary
416   \else
417    %\prewordbreak\discretionary{}{(-}{(}\prewordbreak
418     \wordboundary\discretionary{}{(-}{(}\wordboundary
419   \fi}
420
421\definetextmodediscretionary ~
422 %{\prewordbreak\discretionary{-}{}{\thinspace}\postwordbreak}
423  {\wordboundary\discretionary{-}{}{\thinspace}\wordboundary}
424
425\definetextmodediscretionary '
426 %{\prewordbreak\discretionary{-}{}{'}\postwordbreak}
427  {\wordboundary\discretionary{-}{}{'}\wordboundary}
428
429\definetextmodediscretionary ^
430 %{\prewordbreak\discretionary{\hbox{\normalstartimath|\normalstopimath}}{}{\hbox{\normalstartimath|\normalstopimath}}%
431 % \postwordbreak} % bugged
432  {\wordboundary\discretionary{\hbox{\normalstartimath|\normalstopimath}}{}{\hbox{\normalstartimath|\normalstopimath}}%
433   \wordboundary} % bugged
434
435\definetextmodediscretionary <
436 %{\beginofsubsentence\prewordbreak\beginofsubsentencespacing
437  {\beginofsubsentence\wordboundary\beginofsubsentencespacing
438   \aftergroup\ignorespaces} % tricky, we need to go over the \nextnextnext
439
440\definetextmodediscretionary >
441  {\removeunwantedspaces
442  %\endofsubsentencespacing\prewordbreak\endofsubsentence}
443   \endofsubsentencespacing\wordboundary\endofsubsentence}
444
445\definetextmodediscretionary =
446  {\removeunwantedspaces
447  %\prewordbreak\midsentence\prewordbreak
448   \wordboundary\midsentence\wordboundary
449   \aftergroup\ignorespaces}
450
451% french
452
453%definetextmodediscretionary : {\removeunwantedspaces\prewordbreak\kern\hspaceamount\empty{:}:}
454%definetextmodediscretionary ; {\removeunwantedspaces\prewordbreak\kern\hspaceamount\empty{;};}
455%definetextmodediscretionary ? {\removeunwantedspaces\prewordbreak\kern\hspaceamount\empty{?}?}
456%definetextmodediscretionary ! {\removeunwantedspaces\prewordbreak\kern\hspaceamount\empty{!}!}
457
458\definetextmodediscretionary : {\removeunwantedspaces\wordboundary\kern\hspaceamount\empty{:}:}
459\definetextmodediscretionary ; {\removeunwantedspaces\wordboundary\kern\hspaceamount\empty{;};}
460\definetextmodediscretionary ? {\removeunwantedspaces\wordboundary\kern\hspaceamount\empty{?}?}
461\definetextmodediscretionary ! {\removeunwantedspaces\wordboundary\kern\hspaceamount\empty{!}!}
462
463%definetextmodediscretionary * {\prewordbreak\discretionary{-}{}{\kern.05\emwidth}\prewordbreak}
464\definetextmodediscretionary * {\wordboundary\discretionary{-}{}{\kern.05\emwidth}\wordboundary}
465
466% spanish
467
468%definetextmodediscretionary ?? {\prewordbreak\questiondown}
469%definetextmodediscretionary !! {\prewordbreak\exclamdown}
470
471\definetextmodediscretionary ?? {\wordboundary\questiondown}
472\definetextmodediscretionary !! {\wordboundary\exclamdown}
473
474%D \installdiscretionary | +
475%D \installdiscretionary + =
476
477\def\defaultdiscretionaryhyphen{\compoundhyphen}
478
479\installdiscretionary | \defaultdiscretionaryhyphen % installs in ctx and prt will fall back on it
480
481%D \macros
482%D   {fakecompoundhyphen}
483%D
484%D In headers and footers as well as in active pieces of text we need a dirty hack.
485%D Try to imagine what is needed to savely break the next text across a line and at
486%D the same time make the words interactive.
487%D
488%D \starttyping
489%D \goto{Some||Long||Word}
490%D \stoptyping
491
492\unexpanded\def\fakecompoundhyphen
493  {\def\|{\mathortext\vert\lang_compounds_fake_hyphen}}
494
495\def\lang_compounds_fake_hyphen
496  {\def##1|%
497     {\doifelsenothing{##1}\compoundhyphen{##1}%
498      \kern\compoundbreakpoint\allowbreak}}
499
500%D \macros
501%D   {midworddiscretionary}
502%D
503%D If needed, one can add a discretionary hyphen using \type
504%D {\midworddiscretionary}. This macro does the same as \PLAIN\ \TEX's \type {\-},
505%D but, like the ones implemented earlier, this one also looks ahead for spaces and
506%D grouping tokens.
507
508\unexpanded\def\midworddiscretionary
509  {\futurelet\nexttoken\lang_discretionaries_mid_word}
510
511\def\lang_discretionaries_mid_word
512  {\ifx\nexttoken\blankspace\else
513   \ifx\nexttoken\bgroup    \else
514   \ifx\nexttoken\egroup    \else
515     \discretionary{-}{}{}%
516   \fi\fi\fi}
517
518% As this is rather mkii-ish we keep the mkiv definition around for educational
519% purposes but consider this feature obsolete in post mkii versions.
520
521% %D \macros
522% %D   {installcompoundcharacter}
523% %D
524% %D When Tobias Burnus started translating the dutch manual of \PPCHTEX\ into german,
525% %D he suggested to let \CONTEXT\ support the \type{german.sty} method of handling
526% %D compound characters, especially the umlaut. This package is meant for use with
527% %D \PLAIN\ \TEX\ as well as \LATEX.
528% %D
529% %D I decided to implement compound character support as versatile as possible. As a
530% %D result one can define his own compound character support, like:
531% %D
532% %D \starttyping
533% %D \installcompoundcharacter "a {\"a}
534% %D \installcompoundcharacter "e {\"e}
535% %D \installcompoundcharacter "i {\"i}
536% %D \installcompoundcharacter "u {\"u}
537% %D \installcompoundcharacter "o {\"o}
538% %D \installcompoundcharacter "s {\SS}
539% %D \stoptyping
540% %D
541% %D or even
542% %D
543% %D \starttyping
544% %D \installcompoundcharacter "ck {\discretionary {k-}{k}{ck}}
545% %D \installcompoundcharacter "ff {\discretionary{ff-}{f}{ff}}
546% %D \stoptyping
547% %D
548% %D The support is not limited to alphabetic characters, so the next definition is
549% %D also valid.
550% %D
551% %D \starttyping
552% %D \installcompoundcharacter ". {.\doifnextcharelse{\spacetoken}{}{\kern.125em}}
553% %D \stoptyping
554% %D
555% %D The implementation looks familiar and uses the same tricks as mentioned earlier
556% %D in this module. We take care of two arguments, which complicates things a bit.
557%
558% \installcorenamespace{compoundnormal}
559% \installcorenamespace{compoundsingle}
560% \installcorenamespace{compoundmultiple}
561% \installcorenamespace{compounddefinition}
562%
563% %D When I started working on \MKIV\ code, we needed a different approach for
564% %D defining the active character itself. In \MKII\ as well as in \MKIV\ we now use
565% %D the catcode vectors.
566%
567% \setnewconstant\compoundcharactermode\plusone
568%
569% \newcount\c_lang_compounds_character
570%
571% \def\installcompoundcharacter #1#2#3 #4% {#4} no grouping
572%   {\ifcase\compoundcharactermode
573%       % ignore mode
574%    \else
575%      \chardef\c_lang_compounds_character`#1%
576%      \expandafter\chardef\csname\??compoundnormal\string#1\endcsname\c_lang_compounds_character
577%      \def\!!stringa{#3}%
578%      \expandafter\def\csname\ifx\!!stringa\empty\??compoundsingle\else\??compoundmultiple\fi\detokenize{#1#2#3}\endcsname{#4}%
579%      \setevalue{\??compounddefinition\detokenize{#1}}{\noexpand\lang_compounds_handle_character{\detokenize{#1}}}% beter nr's
580%      \expandafter\letcatcodecommand\expandafter\ctxcatcodes\expandafter\c_lang_compounds_character\csname\??compounddefinition\detokenize{#1}\endcsname
581%    \fi}
582%
583% %D We can also ignore definitions (needed in for instance \XML). Beware, this macro
584% %D is supposed to be used grouped!
585%
586% \def\ignorecompoundcharacter
587%   {\compoundcharactermode\zerocount}
588
589\let\ignorecompoundcharacter\relax
590
591% %D In handling the compound characters we have to take care of \type {\bgroup} and
592% %D \type {\egroup} tokens, so we end up with a multi||step interpretation macro. We
593% %D look ahead for a \type {\bgroup}, \type {\egroup} or \type {\blankspace}. Being
594% %D no user of this mechanism, the credits for testing them goes to Tobias Burnus,
595% %D the first german user of \CONTEXT.
596% %D
597% %D We need to look into the future with \type{\futurelet} to prevent spaces from
598% %D disappearing.
599%
600% \def\lang_compounds_handle_character#1%
601%   {\def\lang_compounds_handle_character_finish{\lang_compounds_handle_character_finish_indeed{#1}}%
602%    \futurelet\nexttoken\xhandlecompoundcharacter}
603%
604% \def\lang_compounds_handle_character_finish_indeed
605%   {\ifx\nexttoken\bgroup
606%     %\expandafter\lang_compounds_handle_character_pickup % handle "{ee} -> \"ee
607%     %\expandafter\gobbleoneargument                          % forget "{ee} -> ee
608%      \expandafter\lang_compounds_handle_character_one    % ignore "{ee} -> "ee
609%    \else\ifx\nexttoken\egroup
610%      \doubleexpandafter\lang_compounds_handle_character_normal
611%    \else\ifx\nexttoken\blankspace
612%      \tripleexpandafter\lang_compounds_handle_character_normal
613%    \else
614%      \tripleexpandafter\lang_compounds_handle_character_pickup
615%    \fi\fi\fi}
616%
617% \def\lang_compounds_handle_character_normal#1%
618%   {\csname\??compoundnormal\string#1\endcsname}
619%
620% \def\lang_compounds_handle_character_pickup#1#2% preserve space
621%   {\def\lang_compounds_handle_character_finish{\lang_compounds_handle_character_finish_indeed#1#2}%
622%    \futurelet\nexttoken\lang_compounds_handle_character_finish}
623%
624% \def\lang_compounds_handle_character_finish_indeed
625%   {\ifx\nexttoken\bgroup
626%      \expandafter\lang_compounds_handle_character_one
627%    \else\ifx\nexttoken\egroup
628%      \doubleexpandafter\lang_compounds_handle_character_one
629%    \else\ifx\nexttoken\blankspace
630%      \tripleexpandafter\lang_compounds_handle_character_one
631%    \else
632%      \tripleexpandafter\lang_compounds_handle_character_two
633%    \fi\fi\fi}
634%
635% %D Besides taken care of the grouping and space tokens, we have to deal with three
636% %D situations. First we look if the next character equals the first one, if so, then
637% %D we just insert the original. Next we look if indeed a compound character is
638% %D defined. We either execute the compound character or just insert the first. So we
639% %D have
640% %D
641% %D \starttyping
642% %D <key><key>  <key><known>  <key><unknown>
643% %D \stoptyping
644% %D
645% %D In later modules we will see how these commands are used.
646%
647% \def\lang_compounds_handle_character_one#1#2%
648%   {\if\string#1\string#2% was: \ifx#1#2%
649%     %\expandafter\let\expandafter\next\csname\??compoundnormal\string#1\endcsname
650%      \csname\??compoundnormal\string#1\expandafter\endcsname
651%    \else\ifcsname\??compoundsingle\string#1\string#2\endcsname
652%     %\expandafter\let\expandafter\next\lastnamedcs
653%      \expandafter\lastnamedcs
654%    \else
655%     %\expandafter\let\expandafter\next\lastnamedcs
656%      \expandafter\lastnamedcs
657%    \fi\fi}
658%
659% \def\lang_compounds_handle_character_two#1#2#3%
660%   {\if\string#1\string#2%
661%      \def\next{\csname\??compoundnormal\string#1\endcsname#3}%
662%    \else\ifcsname\??compoundmultiple\string#1\string#2\string#3\endcsname
663%      \expandafter\let\expandafter\next\lastnamedcs
664%    \else\ifcsname\??compoundsingle\string#1\string#2\endcsname
665%      \expandafter\let\expandafter\next\lastnamedcs
666%    \else
667%      \expandafter\let\expandafter\next\lastnamedcs
668%    \fi\fi\fi
669%    \next}
670%
671% %D For very obscure applications (see for an application \type {lang-sla.tex}) we
672% %D provide:
673%
674% % \def\simplifiedcompoundcharacter#1#2%
675% %   {\ifcsname\??compoundsingle\string#1\string#2\endcsname
676% %      \doubleexpandafter\firstofoneargument\csname\??compoundsingle\string#1\string#2\endcsname
677% %    \else
678% %      #2%
679% %    \fi}
680%
681% \def\simplifiedcompoundcharacter#1#2%
682%   {\ifcsname\??compoundsingle\string#1\string#2\endcsname
683%      \doubleexpandafter\firstofoneargument\lastnamedcs
684%    \else
685%      #2%
686%    \fi}
687
688%D \macros
689%D   {disablediscretionaries,disablecompoundcharacter}
690%D
691%D Occasionally we need to disable this mechanism. For the moment we assume that
692%D \type {|} is used.
693
694\let\disablediscretionaries   \ignorediscretionaries
695\let\disablecompoundcharacters\ignorecompoundcharacter
696
697%D \macros
698%D   {normalcompound}
699%D
700%D Handy in for instance XML. (Kind of obsolete)
701
702\ifdefined\normalcompound \else \let\normalcompound=| \fi
703
704%D \macros
705%D   {compound}
706%D
707%D We will overload the already active \type {|} so we have to save its meaning in
708%D order to be able to use this handy macro.
709%D
710%D \starttyping
711%D so test\compound{}test can be used instead of test||test
712%D \stoptyping
713
714\bgroup
715
716    \catcode\barasciicode\activecatcode
717
718    \unexpanded\gdef\compound#1{|#1|}
719
720    \doglobal \appendtoks
721        \def|#1|{\ifx#1\empty\empty-\else#1\fi}%
722    \to \everysimplifycommands
723
724\egroup
725
726%D Here we hook some code into the clean up mechanism needed for verbatim data.
727
728\appendtoks
729   %\disablecompoundcharacters
730    \disablediscretionaries
731\to \everycleanupfeatures
732
733\protect \endinput
734