From 56bd1f5b8377f54b4af39a114362a7762d27f13c Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Thu, 7 May 2020 03:01:09 +0000 Subject: CTAN sync 202005070301 --- macros/luatex/optex/base/makeindex.opm | 460 +++++++++++++++++++++++++++++++++ 1 file changed, 460 insertions(+) create mode 100644 macros/luatex/optex/base/makeindex.opm (limited to 'macros/luatex/optex/base/makeindex.opm') diff --git a/macros/luatex/optex/base/makeindex.opm b/macros/luatex/optex/base/makeindex.opm new file mode 100644 index 0000000000..5904b494b4 --- /dev/null +++ b/macros/luatex/optex/base/makeindex.opm @@ -0,0 +1,460 @@ +%% This is part of OpTeX project, see http://petr.olsak.net/optex + +\_codedecl \makeindex {Makeindex and sorting <2020-04-26>} % loaded in format + + \_doc ----------------------------- + \^`\makeindex` implements sorting algorithm at \TeX/ macro-language level. + You need not any external program. + + There are two passes in sorting algorithm. Primary pass does not + distinguish between a group o letters (typically non-accented and + accented). If the result of comparing two string is equal in primary pass + then secondary pass is started. It distinguish between variously accented + letters. Czech rules, for example says: not accented before dieresis + before acute before circumflex before ring. At less priority: lowercase + letters must be before uppercase letters. + + The \`\_sortingdata``` implements these rules for the language + . The groups between commas are not distinguished in the first + pass. The second pass distinguishes all characters mentioned in the + `\_sortingdata` (commas are ignored). The order of letters + in the `\_sortingdata` macro is significant for sorting algorithm. + The Czech rules (`cs`) are implemented here: + \_cod ----------------------------- + +\_def \_sortingdatacs {% + /,{ },-,&,@,% + aAäÄáÁ,% + bB,% + cC,% + čČ,% + dDďĎ,% + eEéÉěĚ,% + fF,% + gG,% + hH,% + ^^T^^U^^V,% ch Ch CH + iIíÍ,% + jJ,% + kK,% + lLĺĹľĽ,% + mM,% + nNňŇ,% + oOöÖóÓôÔ,% + pP,% + qQ,% + rRŕŔ,% + řŘ,% + sS,% + šŠ,% + tTťŤ,% + uUüÜúÚůŮ,% + vV,% + wW,% + xX,% + yYýÝ,% + zZ,% + žŽ,% + 0,1,2,3,4,5,6,7,8,9,'% +} + + \_doc ----------------------------- + Characters ignored by sorting algorithm are declared in \`\_ignoredchars```. + The compound characters (two or more characters interpreted as one + character in sorting algorithm) is mapped to single invisible characters + in \`\_compoundchars```. Czech rules declares ch or Ch or CH as + a single letter sorted between H and I. See \`\_sortingdatacs` above where + these declared characters are used. + + The characters declared in `\_ignoredchars` are ignored in first pass + without additional condition. All characters are taken into account in + second pass: ASCII characters with code $\lq65$ are sorted first if they + are not mentioned in the `\_sortingdata` macro. + Others not mentioned characters have undefined behavior during sorting. + \_cod ----------------------------- + +\_def \_ignoredcharscs {.,;?!:'"|()[]<>=+} +\_def \_compoundcharscs {ch:^^T Ch:^^U CH:^^V} % DZ etc. are sorted normally + + \_doc ----------------------------- + Slovak sorting rules are the same as Czech. The macro `\_sortingdatacs` + includes Slovak letters too. Compound characters are the same. + English sorting rules can be defined by `\_sortingdatacs` too because + English alphabet is subset of Czech and Slovak alphabets. Only + difference: \`\_compoundcharsen` is empty in English rules. + + You can declare these macros for more languages, if you wish to use + `\makeindex` with sorting rules in respect to your language. + Note: if you need to map compound characters to a character, don't use + `^^I` or `^^M` because these characters have very specific category code. + And use space to separate more mappings, like in \`\_compoundcharscs` above. + \_cod ----------------------------- + +\_let \_sortingdatask = \_sortingdatacs +\_let \_compoundcharssk = \_compoundcharscs +\_let \_ignoredcharssk = \_ignoredcharscs +\_let \_sortingdataen = \_sortingdatacs +\_def \_compoundcharsen {} +\_let \_ignoredcharsen = \_ignoredcharscs + + \_doc ----------------------------- + Preparing to primary pass is implemented by the \`\_setprimarysorting` macro. + It is called from `\makeindex` macro and all processing of sorting is in a group. + \_cod ----------------------------- + +\_def\_setprimarysorting {% + \_ea\_let \_ea\_sortingdata \_csname _sortingdata\_sortinglang\_endcsname + \_ea\_let \_ea\_compoundchars \_csname _compoundchars\_sortinglang\_endcsname + \_ea\_let \_ea\_ignoredchars \_csname _ignoredchars\_sortinglang\_endcsname + \_ifx \_sortingdata\_relax \_addto\_nold{ sortingdata}% + \_let \_sortingdata = \_sortingdataen \_fi + \_ifx \_compoundchars\_relax \_addto\_nold{ compoundchars}% + \_let \_compoundchars = \_compoundcharsen \_fi + \_ifx \_ignoredchars\_relax \_addto\_nold{ ignoredchars}% + \_let \_ignoredchars = \_ignoredcharsen \_fi + \_ifx \_compoundchars\_empty \_else + \_edef \_compoundchars {\_detokenize\_ea{\_compoundchars} }\_fi % all must be catcode 12 + \_def \_act ##1{\_ifx##1\_relax \_else + \_ifx##1,\_advance\_tmpnum by1 + \_else \_lccode`##1=\_tmpnum \_fi + \_ea\_act \_fi}% + \_tmpnum=65 \_ea\_act \_sortingdata \_relax + \_def \_act ##1{\_ifx##1\_relax \_else + \_lccode`##1=`\^^I + \_ea\_act \_fi}% + \_ea\_act \_ignoredchars \_relax +} + + \_doc ----------------------------- + Preparing to secondary pass is implemented by the \`\_setsecondarysorting` macro. + \_cod ----------------------------- + +\_def\_setsecondarysorting {% + \_def \_act ##1{\_ifx##1\_relax \_else + \_ifx##1,\_else \_advance\_tmpnum by1 \_lccode`##1=\_tmpnum \_fi + \_ea\_act \_fi}% + \_tmpnum=65 \_ea\_act \_sortingdata \_relax +} + + \_doc ----------------------------- + Strings to be sorted are prepared in `\,` control sequences + (in order to save `\TeX` memory). + The \`\_preparesorting` `\,` converts `` to `\_tmpb` + with respect to the data initialized in \^`\_setprimarysorting` or + \^`\_setsecondarysorting`.\nl + The compoud characters are converted to single characters by the + \`\_docompound` macro. + \_cod ----------------------------- + +\_def \_preparesorting #1{% + \_edef \_tmpb {\_ea\_ignorefirst\_csstring #1}% \, -> + \_ea \_docompound \_compoundchars \_relax:{} % replace compound characters + \_lowercase \_ea{\_ea\_def \_ea\_tmpb \_ea{\_tmpb}}% convert in respect to \_sortingdata + \_ea\_replstring \_ea\_tmpb \_ea{\_csstring\^^I}{}% remove ignored characters +} +\_def \_docompound #1:#2 {% + \_ifx\_relax#1\_else \_replstring\_tmpb {#1}{#2}\_ea\_docompound \_fi +} +\_def \_ignorefirst#1{} + + \_doc ----------------------------- + Macro \`\_isAleB` `\, \,` returns the result of comparison + of given two strings to \`\_ifAleB` control sequence. Usage: + `\isAleB \, \, \_ifAleB ... \_else ... \_fi` + The converted strings (in respect of the data prepared for first pass) + must be saved as values of `\,` and `\,` macros. + The reason is speed: we don't want to convert them repeatedly in each + comparison. + \nl + The macro + \`\_testAleB` `&\_relax\_relax \,\,`\nl + does the real work. It reads first character from both converted strings, compares them + and if it is equal then calls itself recursively else gives result. + \_cod ----------------------------- + +\_newifi \_ifAleB + +\_def\_isAleB #1#2{% + \_edef\_tmpb {#1&\_relax#2&\_relax}% + \_ea \_testAleB \_tmpb #1#2% +} +\_def\_testAleB #1#2\_relax #3#4\_relax #5#6{% + \_if #1#3\_if #1&\_testAleBsecondary #5#6% goto to the second pass:: + \_else \_testAleB #2\_relax #4\_relax #5#6% + \_fi + \_else \_ifnum `#1<`#3 \_AleBtrue \_else \_AleBfalse \_fi + \_fi +} +\_def\_testAleBsecondary#1#2{% + \_bgroup + \_setsecondarysorting + \_preparesorting#1\_let\_tmpa=\_tmpb \_preparesorting#2% + \_edef\_tmpb{\_tmpa0\_relax\_tmpb1\_relax}% + \_ea\_testAleBsecondaryX \_tmpb + \_egroup +} +\_def\_testAleBsecondaryX #1#2\_relax #3#4\_relax {% + \_if #1#3\_testAleBsecondaryX #2\_relax #4\_relax + \_else \_ifnum `#1<`#3 \_global\_AleBtrue \_else \_global \_AleBfalse \_fi + \_fi +} + + \_doc ----------------------------- + Merge sort is very effectively implemented by \TeX/ macros. The following + code is created by my son Miroslav. + The \`\_mergesort` macro expects that all items in `\_iilist` are separated + by comma when it starts. It ends with sorted items in `\_iilist` without commas. + So `\_dosorting` macro must prepare commas between items. + \_cod ----------------------------- + +\_def\_mergesort #1#2,#3{% by Miroslav Olsak + \_ifx,#1% % prazdna-skupina,neco, (#2=neco #3=pokracovani) + \_addto\_iilist{#2,}% % dvojice skupin vyresena + \_sortreturn{\_fif\_mergesort#3}% % \mergesort pokracovani + \_fi + \_ifx,#3% % neco,prazna-skupina, (#1#2=neco #3=,) + \_addto\_iilist{#1#2,}% % dvojice skupin vyresena + \_sortreturn{\_fif\_mergesort}% % \mergesort dalsi + \_fi + \_ifx\_end#3% % neco,konec (#1#2=neco) + \_ifx\_empty\_iilist % neco=kompletni setrideny seznam + \_def\_iilist{#1#2}% + \_sortreturn{\_fif\_fif\_gobbletoend}% % koncim + \_else % neco=posledni skupina nebo \end + \_sortreturn{\_fif\_fif % spojim \indexbuffer+necoa cele znova + \_edef\_iilist{\_ea}\_ea\_mergesort\_iilist#1#2,#3}% + \_fi\_fi % zatriduji: p1+neco1,p2+neco2, (#1#2=p1+neco1 #3=p2) + \_isAleB #1#3\_ifAleB % p1p2 + \_addto\_iilist{#3}% % p2 do bufferu + \_sortreturn{\_fif\_mergesort#1#2,}% % \mergesort p1+neco1,neco2, + \_fi + \_relax % zarazka, na ktere se zastavi \sortreturn +} +\_def\_sortreturn#1#2\_fi\_relax{#1} \_def\_fif{\_fi} +\_def\_gobbletoend #1\_end{} + + \_doc ----------------------------- + The \`\_dosorting` `\list` macro redefines `\list` as sorted `\list`. + The `\list` have to include control sequences in the form `\`. + These control sequences will be sorted in respect to without + change of meanings of these control sequences. Their meanings are + irrelevant when sorting. The first character in `\` should + be whatever. It does not influence the sorting. \OpTeX/ uses comma at + this place for sorting indexes: `\, \, \, ...`. + + The actual language (chosen for hyphenation patterns) is used for + sorting data. If the `\_sortinglang` macro + is defined as ``(for example `\def\sortinglang{de}`) + then this has precedence and actual language is not used. + Moreover, if you specify \`\_asciisortingtrue` then ASCII + sorting will be processed and all language sorting data will be ignored. + \_cod ----------------------------- + +\_newifi \_ifasciisorting \_asciisortingfalse +\_def\_dosorting #1{% + \_begingroup + \_def\_nold{}% + \_ifx\_sotringlang\_undefined \_edef\_sortinglang{\_cs{_lan:\_the\_language}}\_fi + \_ifasciisorting + \_edef\_sortinglang{ASCII}% + \_def \_preparesorting##1{\_edef\_tmpb{\_ea\_ignorefirst\_csstring##1}}% + \_let \_setsecondarysorting=\_relax + \_else + \_setprimarysorting + \_fi + \_message{OpTeX: Sorting \_string#1 (\_sortinglang) ...^^J}% + \_ifx\_nold\_empty\_else \_opwarning{Missing\_nold\_space for language (\_sortinglang)}\_fi + \_def \_act##1{\_preparesorting ##1\_edef##1{\_tmpb}}% + \_ea\_xargs \_ea\_act #1;% + \_def \_act##1{\_addto #1{##1,}}% + \_edef #1{\_ea}\_ea\_xargs \_ea\_act #1;% + \_edef \_iilist{\_ea}\_ea\_mergesort #1\_end,\_end + \_ea\_endgroup + \_ea\_def\_ea#1\_ea{\_iilist}% +} + + \_doc ----------------------------- + The \`\makeindex` prints the index. First, it sorts the `\_iilist` + second, it prints the sorted `\_iilist`, each item is printed + using \^`\_printindexitem`. + \_cod ----------------------------- + +\_def\_makeindex{\_par + \_ifx\_iilist\_empty \_opwarning{index data-buffer is empty. TeX me again} + \_else + \_dosorting \_iilist % sorting \_iilist + \_bgroup + \_rightskip=0pt plus1fil \_exhyphenpenalty=10000 \_leftskip=\_iindent + \_ea\_xargs \_ea\_printindexitem \_iilist ;\_par + \_egroup + \_fi +} +\_public \makeindex ; + + \_doc ----------------------------- + The \`\_printindexitem` `\,` prints one item to the index. + If `\_,` is defined then this is used instead real + (this exception is declared by `\iis` macro). Else is printed by + \^`\_printii`. Finally, \^`\_printiipages` prints the value of `\,`, + i.e. the list of pages. + \_cod ----------------------------- + +\_def\_printindexitem #1{% + \_ifcsname _\_csstring #1\_endcsname + \_ea\_ea\_ea \_printii \_csname _\_csstring #1\_endcsname &% + \_else + \_ea\_ea\_ea\_printii \_ea\_ignorefirst \_csstring #1&% + \_fi + \_ea\_printiipages #1& +} + + \_doc ----------------------------- + \`\_printii` `&` does more intelligent work because we are working with + words in the form `//`. + The \^`\everyii` tokens register is applied before `\noindent`. User can + declare something special here. + + The \`\_newiiletter``{}` macro is empty by default. It is invoked if first + letter of index entries is changed. You can declare a design between + index entries here. You can try, for example: + \begtt + \def\_newiiletter#1#2{% + \bigskip \hbox{\setfontsize{at15pt}\bf\uppercase{#1}}\medskip} + \endtt + \_cod ----------------------------- + +\_def\_printii #1#2&{% + \_ismacro\_lastii{#1}\_iffalse \_newiiletter{#1}{#2}\_def\_lastii{#1}\_fi + \_gdef\_currii{#1#2}\_the\_everyii\_noindent + \_hskip-\_iindent \_ignorespaces\_printiiA#1#2//} +\_def\_printiiA #1/{\_if^#1^\_let\_previi=\_currii \_else + \_ea\_scanprevii\_previi/&\_edef\_tmpb{\_detokenize{#1}}% + \_ifx\_tmpa\_tmpb \_iiemdash \_else#1 \_gdef\_previi{}\_fi + \_expandafter\_printiiA\_fi +} +\_def\_iiemdash{\_kern.1em---\_space} +\_def\_lastii{} +\_def\_newiiletter#1#2{} + +\_def\_scanprevii#1/#2&{\_def\_previi{#2}\_edef\_tmpa{\_detokenize{#1}}} +\_def\_previi{} % previous index item + + \_doc ----------------------------- + \`\_printiipages` `&` gets `` in the form + `:,:,...:` and it converts them to + `, , --, ` etc. The same pages must be printed only once + and continuous consequences of pages must be compressed to the form -. + Moreover, the consequence is continuous only if all pages have the same . + Empty is most common, pages with `b` must be printed as bold + and with `i` `` as italics. + Moreover, the `` mentioned here are , but we have to print + . The following macros solves these tasks. + \_cod ----------------------------- + +\_def\_printiipages#1&{\_let\_pgtype=\_undefined \_tmpnum=0 \_printpages #1,:,\_par} +\_def\_printpages#1:#2,{% state automaton for compriming pages + \_ifx,#1,\_uselastpgnum + \_else \_def\_tmpa{#2}% + \_ifx\_pgtype\_tmpa \_else + \_let\_pgtype=\_tmpa + \_uselastpgnum \_usepgcomma \_pgprint#1:{#2}% + \_tmpnum=#1 \_returnfi \_fi + \_ifnum\_tmpnum=#1 \_returnfi \_fi + \_advance\_tmpnum by1 + \_ifnum\_tmpnum=#1 \_ifx\_lastpgnum\_undefined \_usepgdash\_fi + \_edef\_lastpgnum{\_the\_tmpnum:{\_pgtype}}% + \_returnfi \_fi + \_uselastpgnum \_usepgcomma \_pgprint#1:{#2}% + \_tmpnum=#1 + \_relax + \_ea\_printpages \_fi +} +\_def\_returnfi #1\_relax{\_fi} +\_def\_uselastpgnum{\_ifx\_lastpgnum\_undefined + \_else \_ea\_pgprint\_lastpgnum \_let\_lastpgnum=\_undefined \_fi +} +\_def\_usepgcomma{\_ifnum\_tmpnum>0, \_fi} % comma+space between page numbers +\_def\_usepgdash{\_hbox{--}} % dash in the -- form + + \_doc ----------------------------- + You can re-define \`\_pgprint` `:{}` + if you need to implement more . + \_cod ----------------------------- + +\_def\_pgprint #1:#2{% + \_ifx ,#2,\_pgprintA{#1}\_returnfi \_fi + \_ifx b#2{\_bf \_pgprintA{#1}}\_returnfi \_fi + \_ifx i#2{\_it \_pgprintA{#1}}\_returnfi \_fi + \_ifx u#2\_pgu{\_pgprintA{#1}}\_returnfi \_fi + \_pgprintA{#1}\_relax +} +\_def\_pgprintA #1{\_ilink[pg:#1]{\_cs{_pgi:#1}}} % \ilink[pg:]{} +\_def\_pgu#1{\_leavevmode\_vtop{\_hbox{#1}\kern.3ex\_hrule}} + + \_doc ----------------------------- + The \`\iindex``{}` puts one to the index. It writes + \^`\_Xindex``{}{}` to the `.ref` file. + All othes variants of indexing macros expands internally to `\iindex`. + \_cod ----------------------------- + +\_def\_iindex#1{\_isempty{#1}\_iffalse\_openref{\def~{ }% + \edef\_act{\_noexpand\_wref\_noexpand\_Xindex{{#1}{\_iitypesaved}}}\_act}\_fi} +\_public \iindex ; + + \_doc ----------------------------- + The \`\_Xindex``{}{}` stores `\,` to the `\_iilist` if + there is first occurrence of the . The list of pages where `` + occurs, is the value of the macro `\,`, so the `:` + is appended to this list. + Moreower, we need a mapping from to ``, because we print + `` in the index, but hyperlinks are implemented by ``. + So, the macro `\_pgi:` is defined as ``. + \_cod ----------------------------- + +\_def \_iilist {} +\_def \_Xindex #1#2{\_ea\_XindexA \_csname ,#1\_ea\_endcsname \_currpage {#2}} +\_def \_XindexA #1#2#3#4{% #1=\, #2= #3= #4= + \_ifx#1\_relax \_global\_addto \_iilist {#1}% + \_gdef #1{#2:#4}% + \_else \_global\_addto #1{,#2:#4}% + \_fi + \_sxdef{_pgi:#2}{#3}% +} + + \_doc ----------------------------- + The implementation of macros \`\ii`, \`\iid`, \`\iis` follows. + Note that `\ii` works in horizontal mode on order to the `\write` whatsit + is not broken from the following word. If you need to keep vertical mode, + use \^`\iindex``{}` directly. + \nl + The \`\iitype` `{}` saves the `` to the \`\_iitypesaved` macro. It is + used in the \^`\iindex` macro. + \_cod ----------------------------- + +\_def\_ii #1 {\_leavevmode\_def\_tmp{#1}\_iiA #1,,\_def\_iitypesaved{}} + +\_def\_iiA #1,{\_if$#1$\_else\_def\_tmpa{#1}% + \_ifx\_tmpa\_iiatsign \_ea\_iiB\_tmp,,\_else\_iindex{#1}\_fi + \_ea\_iiA\_fi} +\_def\_iiatsign{@} + +\_def\_iiB #1,{\_if$#1$\_else \_iiC#1/\_relax \_ea\_iiB\_fi} +\_def\_iiC #1/#2\_relax{\_if$#2$\_else\_iindex{#2#1}\_fi} + +\_def\_iid #1 {\_leavevmode\_iindex{#1}#1\_futurelet\_tmp\_iiD\_def\_iitypesaved{}} +\_def\_iiD{\_ifx\_tmp,\_else\_ifx\_tmp.\_else\_space\_fi\_fi} + +\_def\_iis #1 #2{{\_def~{ }\_global\_sdef{_,#1}{#2}}\_ignorespaces} + +\_def\_iitypesaved{} +\_def\_iitype #1{\_def\_iitypesaved{#1}\_ignorespaces} + +\_public \ii \iid \iis \iitype ; + +\_endcode % ------------------------------------- + +2020-04-21 \isempty \iffalse ... \fi added to \iindex +2020-03-26 introduced -- cgit v1.2.3