%D \module %D [ file=syst-ext, %D version=1995.10.10, %D title=\CONTEXT\ System Macros, %D subtitle=Extras, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA / Hans Hagen \& Ton Otten}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. \writestatus{loading}{Context System Macro's / Extras} %D In this second system module, we continue the definition of %D some handy commands. \unprotect %D \macros %D {rawgetparameters} %D %D A raw and dirty alternative for \type {\getparameters}; no %D checking is done! % \def\rawgetparameters[#1][#2]% scheelt 5\% % {\def\rawparameterprefix{#1}% % \expandafter\rawsetparameter#2,]=,} \def\rawsetparameter#1=#2,% {\if]#1\else \expandafter\def\csname\rawparameterprefix#1\endcsname{#2}% \expandafter\rawsetparameter \fi} % the next one handles empty #1 okay: \def\rawgetparameters[#1][#2% some 5-10% faster {\ifx#2]% test is needed, else bomb on [#1][] \expandafter\gobbleoneargument \else \def\rawparameterprefix{#1}% \expandafter\dorawgetparameters \fi#2} \def\dorawgetparameters#1]% {\expandafter\rawsetparameter#1,]=,} %D \macros %D {doglobal, %D redoglobal,dodoglobal,resetglobal} %D %D The two macros \type {\redoglobal} and \type{\dodoglobal} are %D used in this and some other modules to enforce a user %D specified \type {\doglobal} action. The last and often only %D global assignment in a macro is done with %D \type {\dodoglobal}, but all preceding ones with %D \type {\redoglobal}. When using only alternatives, one can %D reset this mechanism with \type {\resetglobal}. \def\doglobal {\let\redoglobal\global \def\dodoglobal{\resetglobal\global}} \def\resetglobal {\let\redoglobal\relax \let\dodoglobal\relax} \resetglobal %D New: \def\doglobal {\ifx\redoglobal\relax \let\redoglobal\global \let\dodoglobal\@@dodoglobal %\else % \writestatus{system}{global not reset, warn me!}% \fi} \def\@@dodoglobal {\resetglobal\global} \def\saveglobal {\let\@@dodoglobal\dodoglobal \let\@@redoglobal\redoglobal} \def\restoreglobal {\let\redoglobal\@@redoglobal \let\dodoglobal\@@dodoglobal} %D A very useful application of this macro is \type {\newif}, %D \TEX's fake boolean type. Not being a primitive, %D \type {\global} hopelessly fails here. But a slight %D adaption of Knuth's original macro permits: %D %D \starttyping %D \doglobal\newif\iftest %D \stoptyping %D %D Of course one can still say: %D %D \starttyping %D \global\testtrue %D \global\testfalse %D \stoptyping %D %D Apart from the prefixes, a few more \type{\expandafters} %D are needed: \def\newif#1% {\count@\escapechar \escapechar\m@ne \expandafter\expandafter\expandafter \redoglobal\expandafter\expandafter\expandafter \edef\@if#1{true}{\let\noexpand#1\noexpand\iftrue}% \expandafter\expandafter\expandafter \redoglobal\expandafter\expandafter\expandafter \edef\@if#1{false}{\let\noexpand#1\noexpand\iffalse}% \dodoglobal\@if#1{false}% \escapechar\count@} %D Also new: \def\define#1% {\ifx#1\undefined \expandafter\def \else \message{[\noexpand#1is already defined]}% \expandafter\def\expandafter\gobbleddefinition \fi#1} \def\redefine#1% {\ifx#1\undefined\else \message{[\noexpand#1is redefined]}% \fi \def#1} % \define\hans{hans} % \redefine\hans{hans} % \define\hans#1[]#2#3{hans} %D \macros %D {newcounter, %D increment,decrement} %D %D Unfortunately the number of \COUNTERS\ in \TEX\ is limited, %D but fortunately we can store numbers in a macro. We can %D increment such pseudo \COUNTERS\ with \type{\increment}. %D %D \starttyping %D \increment(\counter,20) %D \increment(\counter,-4) %D \increment(\counter) %D \increment\counter %D \stoptyping %D %D After this sequence of commands, the value of %D \type{\counter} is 20, 16, 17 and~18. Of course there is %D also the complementary command \type{\decrement}. %D %D Global assignments are possible too, using \type{\doglobal}: %D %D \starttyping %D \doglobal\increment\counter %D \stoptyping %D %D When \type{\counter} is undefined, it's value is initialized %D at~0. It is nevertheless better to define a \COUNTER\ %D explicitly. One reason could be that the \COUNTER\ can be %D part of a test with \type{\ifnum} and this conditional does %D not accept undefined macro's. The \COUNTER\ in our example %D can for instance be defined with: %D %D \starttyping %D \newcounter\counter %D \stoptyping %D %D The command \type{\newcounter} must not be confused with %D \type{\newcount}! Of course this mechanism is much slower %D than using \TEX's \COUNTERS\ directly. In practice %D \COUNTERS\ (and therefore our pseudo counters too) are %D seldom the bottleneck in the processing of a text. Apart %D from some other incompatilities we want to mention a pitfal %D when using \type{\ifnum}. %D %D \starttyping %D \ifnum\normalcounter=\pseudocounter \doif \else \doelse \fi %D \ifnum\pseudocounter=\normalcounter \doif \else \doelse \fi %D \stoptyping %D %D In the first test, \TEX\ continues it's search for the %D second number after reading \type{\pseudocounter}, while %D in the second test, it stops reading after having %D encountered a real one. Tests like the first one therefore %D can give unexpected results, for instance execution %D of \type{\doif} even if both numbers are unequal. \def\zerocountervalue{0} \def\newcounter#1% {\dodoglobal\let#1\zerocountervalue} % This is the original implementation: % % \def\dodododoincrement(#1,#2)% % {\ifx#1\undefined % \redoglobal\let#1\zerocountervalue % \else\ifx#1\relax % \csname...\endcsname % \redoglobal\let#1\zerocountervalue % \fi\fi % \scratchcounter=#2\relax % \scratchcounter=\incrementsign\scratchcounter % \advance\scratchcounter #1\relax % \dodoglobal\edef#1{\the\scratchcounter}} % % \def\dododoincrement#1% % {\dodododoincrement(#1,1)} % % \def\dodoincrement(#1% % {\doifnextcharelse,% % {\dodododoincrement(#1}{\dodododoincrement(#1,1}} % % \def\doincrement#1% % {\def\incrementsign{#1}% % \doifnextcharelse(\dodoincrement\dododoincrement} % % \def\increment{\doincrement+} % \def\decrement{\doincrement-} % % And this is the one optimized for speed: % maxcounter = 2\maxdimen=1 \def\!!zerocount {0} % alongside \zerocount \def\!!minusone {-1} % alongside \minusone \def\!!plusone {1} % alongside \plusone \beginTEX \def\dodoindecrement#1(#2,#3)% {\ifx#2\undefined \redoglobal\let#2\zerocountervalue \else\ifx#2\relax % \csname...\endcsname \redoglobal\let#2\zerocountervalue \fi\fi \scratchcounter#3\relax \scratchcounter#1\scratchcounter \advance\scratchcounter#2\relax \dodoglobal\edef#2{\the\scratchcounter}} \def\dodoincrement(#1% {\doifnextcharelse,{\dodoindecrement+(#1}{\dodoindecrement+(#1,1}} \def\dododecrement(#1% {\doifnextcharelse,{\dodoindecrement-(#1}{\dodoindecrement-(#1,1}} \def\doincrement#1% 10% faster alternative {\ifx#1\undefined \dodoglobal\let#1\!!plusone \else\ifx#1\relax % \csname...\endcsname \dodoglobal\let#1\!!plusone \else \fastincrement#1% \fi\fi} \def\dodecrement#1% 10% faster alternative {\ifx#1\undefined \dodoglobal\let#1\!!minusone \else\ifx#1\relax % \csname...\endcsname \dodoglobal\let#1\!!minusone \else \fastdecrement#1% \fi\fi} \def\fastdecrement#1% 50% faster alternative {\scratchcounter#1\advance\scratchcounter\minusone \dodoglobal\edef#1{\the\scratchcounter}} \def\fastincrement#1% 50% faster alternative {\scratchcounter#1\advance\scratchcounter\plusone \dodoglobal\edef#1{\the\scratchcounter}} \endTEX \beginETEX \numexpr % \def\doindecrement#1#2% % {\dodoglobal\edef#2% % {\the\numexpr(\ifx#2\undefined\else\ifx#2\relax\else#2\fi\fi#11)}} % % \def\doincrement{\doindecrement+} % \def\dodecrement{\doindecrement-} % % some 3\% faster: \def\doindecrement#1#2% {\dodoglobal\edef#2% {\the\numexpr\ifx#2\undefined\else\ifx#2\relax\else#2\fi\fi+#1\relax}} \def\doincrement{\doindecrement\plusone } \def\dodecrement{\doindecrement\minusone} \def\dodoindecrement#1#2,#3)% {\dodoglobal\edef#2% {\the\numexpr\ifx#2\undefined\else\ifx#2\relax\else#2\fi\fi#1#3\relax}} \def\dodoincrement(#1% {\doifnextcharelse,{\dodoindecrement+#1}{\dodoindecrement+#1,\plusone}} \def\dododecrement(#1% {\doifnextcharelse,{\dodoindecrement-#1}{\dodoindecrement-#1,\plusone}} \def\fastincrement#1{\dodoglobal\edef#1{\the\numexpr#1+\plusone \relax}} \def\fastdecrement#1{\dodoglobal\edef#1{\the\numexpr#1+\minusone\relax}} \endETEX \def\increment{\doifnextcharelse(\dodoincrement\doincrement} \def\decrement{\doifnextcharelse(\dododecrement\dodecrement} %D \macros %D {newsignal} %D %D When writing advanced macros, we cannot do without %D signaling. A signal is a small (invisible) kern or penalty %D that signals the next macro that something just happened. %D This macro can take any action depending on the previous %D signal. Signals must be unique and the next macro takes care %D of that. %D %D \starttyping %D \newsignal\somesignal %D \stoptyping %D %D Signals old dimensions and can be used in skips, kerns and %D tests like \type{\ifdim}. \newdimen\maximumsignal % step is about 0.00025pt \def\newsignal#1% {\ifx#1\undefined \advance\maximumsignal 2sp % to be save in rounding \edef#1{\the\maximumsignal}% \fi} %D \macros %D {newskimen} %D %D \TEX\ offers 256 \DIMENSIONS\ and \SKIPS. Unfortunately this %D amount is too small to suit certain packages. Therefore when %D possible one should use: %D %D \starttyping %D \newskimen\tempskimen %D \stoptyping %D %D This commands allocates a \DIMENSION\ or a \SKIP, depending %D on the availability. One should be aware of the difference %D between both. When searching for some glue \TEX\ goes on %D searching till it's sure that no other glue component if %D found. This search can be canceled by using \type{\relax} %D when possible and needed. %D %D \starttyping %D \def\newskimen#1% %D {\ifx#1\undefined %D \ifnum\count11>\count12 %D \newskip#1\relax %D \else %D \newdimen#1\relax %D \fi %D \fi} %D \stoptyping %D %D In order to make this macro work in plain \TEX\ too, we %D use the following alternative, which fools \TEX\ about %D the new commands being \type {\outer} ones. \def\newskimen#1% {\ifx#1\undefined \csname new\ifnum\count11>\count12 skip\else dimen\fi\endcsname#1% \fi} %D \macros %D {strippedcsname} %D %D The next macro can be very useful when using \type{\csname} %D like in: %D %D \starttyping %D \csname if\strippedcsname\something\endcsname %D \stoptyping %D %D This expands to \type{\ifsomething}. %D %D \starttyping %D \def\strippedcsname %D {\expandafter\gobbleoneargument\string} %D \stoptyping %D %D Slower but better: {\catcode`.=\@@escape .catcode`.\ 12 .xdef.letterbackslash{.string\}} % hack \def\strippedcsname {\expandafter\dostrippedcsname\string} \def\dostrippedcsname#1% {\if\noexpand#1\letterbackslash\else#1\fi} %D \macros %D {savenormalmeaning} %D %D We will use this one in: \def\savenormalmeaning#1% {\ifundefined{normal\strippedcsname#1}% \letvalue{normal\strippedcsname#1}#1% \fi} %D \macros %D {newconditional, %D settrue, setfalse, %D ifconditional} %D %D \TEX's lacks boolean variables, although the \PLAIN\ format %D implements \type{\newif}. The main disadvantage of this %D scheme is that it takes three hash table entries. A more %D memory saving alternative is presented here. A conditional %D is defined by: %D %D \starttyping %D \newconditional\doublesided %D \setfalse %D %D Setting a conditional is done by \type{\settrue} and %D \type{\setfalse}: %D %D \starttyping %D \settrue\doublesided %D \setfalse %D %D while testing is accomplished by: %D %D \starttyping %D \ifconditional\doublesided ... \else ... \fi %D \setfalse %D %D We cannot use the simple scheme: %D %D \starttyping %D \def\settrue#1{\let#1=\iftrue} %D \def\settrue#1{\let#1=\iffalse} %D \stoptyping %D %D Such an implementation gives problems with nested %D conditionals. The next implementation is abaou as fast %D and just as straightforward: \def\settrue #1{\chardef#1\zerocount} \def\setfalse#1{\chardef#1\plusone} \let\newconditional = \setfalse \let\ifconditional = \ifcase %D \macros %D {ifzeropt} %D %D The next macro is both cosmetic and byte saving. It is %D pretty \type{\if}||safe too. It can be used in cases %D like: %D %D \starttyping %D \ifzeropt \somedimen ... \else ... \fi %D \stoptyping \let\ifzeropt\ifcase %D \macros %D {dorecurse,recurselevel,recursedepth, %D dostepwiserecurse, %D for} %D %D \TEX\ does not offer us powerfull for||loop mechanisms. On %D the other hand its recursion engine is quite unique. We %D therefore identify the for||looping macros by this method. %D The most simple alternative is the one that only needs a %D number. %D %D \starttyping %D \dorecurse {n} {whatever we want} %D \stoptyping %D %D This macro can be nested without problems and therefore be %D used in situations where \PLAIN\ \TEX's \type{\loop} macro %D ungracefully fails. The current value of the counter is %D available in \type{\recurselevel}, before as well as after %D the \typ{whatever we wat} stuff. %D %D \starttyping %D \dorecurse % inner loop %D {10} %D {\recurselevel: % outer value %D \dorecurse % inner loop %D {\recurselevel} % outer value %D {\recurselevel} % inner value %D \dorecurse % inner loop %D {\recurselevel} % outer value %D {\recurselevel} % inner value %D \endgraf} %D \stoptyping %D %D In this example the first, second and fourth %D \type{\recurselevel} concern the outer loop, while the third %D and fifth one concern the inner loop. The depth of the %D nesting is available for inspection in \type{\recursedepth}. %D %D Both \type{\recurselevel} and \type{\recursedepth} are %D macros. The real \COUNTERS\ are hidden from the user because %D we don't want any interference. \newcount\outerrecurse \newcount\innerrecurse \def\recursedepth{\the\outerrecurse} \def\recurselevel{0} \let\nextrecurse\relax %D Acceptable: %D %D \starttyping %D \long\def\dostepwiserecurse#1#2#3% %D {\let\nextrecurse\gobblefourarguments %D \ifnum#3>0\relax\ifnum#2<#1\relax\else %D \def\nextrecurse{\dosetstepwiserecurse>}% %D \fi\fi %D \ifnum#3<0\relax\ifnum#1<#2\relax\else %D \def\nextrecurse{\dosetstepwiserecurse<}% %D \fi\fi %D \nextrecurse{#1}{#2}{#3}} %D \stoptyping %D %D Better: %D %D \starttyping %D \long\def\dostepwiserecurse#1#2#3% %D {\let\nextrecurse\gobblefourarguments %D \ifnum#3>0\relax \ifnum#2<#1\relax \else %D \def\nextrecurse{\dosetstepwiserecurse>}% %D \fi \else \ifnum#3<0\relax \ifnum#1<#2\relax \else %D \def\nextrecurse{\dosetstepwiserecurse<}% %D \fi \fi \fi %D \nextrecurse{#1}{#2}{#3}} %D %D \def\@@irecurse{@@irecurse} % stepper %D \def\@@nrecurse{@@nrecurse} % number of steps %D \def\@@srecurse{@@srecurse} % step %D \def\@@drecurse{@@drecurse} % direction, < or > %D \def\@@arecurse{@@arecurse} % action %D %D \long\def\dosetstepwiserecurse#1#2#3#4#5% %D {\global\advance\outerrecurse 1 %D \setevalue{\@@drecurse\recursedepth}{#1}% %D \setevalue{\@@irecurse\recursedepth}{\number#2}% %D \setevalue{\@@nrecurse\recursedepth}{\number#3}% %D \setevalue{\@@srecurse\recursedepth}{\number#4}% %D \long\setvalue{\@@arecurse\recursedepth}{#5}% %D \dodorecurse} %D %D \def\donorecurse %D {} %D %D \def\dododorecurse %D {\edef\recurselevel{\csname\@@irecurse\recursedepth\endcsname}% %D \getvalue{\@@arecurse\recursedepth}% %D \edef\recurselevel{\csname\@@irecurse\recursedepth\endcsname}% %D \innerrecurse\recurselevel %D \advance\innerrecurse \csname\@@srecurse\recursedepth\endcsname %D \setevalue{\@@irecurse\recursedepth}{\the\innerrecurse}% %D \dodorecurse} %D %D \def\dodorecurse %D {\ifnum\csname\@@irecurse\recursedepth\endcsname %D \csname\@@drecurse\recursedepth\endcsname %D \csname\@@nrecurse\recursedepth\endcsname\relax %D \expandafter\nododorecurse %D \else %D \expandafter\dododorecurse %D \fi} %D %D \def\nododorecurse %D {\global\advance\outerrecurse -1 %D \edef\recurselevel{\csname\@@irecurse\recursedepth\endcsname}} %D \stoptyping %D %D Cleaner and much faster: \def\@@irecurse{@@ir@@} % ecurse} % stepper \def\@@arecurse{@@ar@@} % ecurse} % action % \mathchardef \long\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 {\global\advance\outerrecurse \plusone \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname{#4}% \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel \ifnum#3>0\relax \ifnum#2<#1\relax \let\nextrecurse\exitstepwiserecurse \else \let\nextrecurse\dodostepwiserecurse \fi \else \ifnum#3<0\relax \ifnum#1<#2\relax \let\nextrecurse\exitstepwiserecurse \else \let\nextrecurse\dodostepwisereverse \fi \else \let\nextrecurse\exitstepwiserecurse \fi \fi\expanded{\nextrecurse{\number#1}{\number#2}{\number#3}}} \beginETEX \numexpr \long\def\dodostepwiserecurse#1#2#3% from to step {\ifnum#1>#2\relax \@EA\nodostepwiserecurse \else \def\recurselevel{#1}% \@EAEAEA\redostepwiserecurse\@EA \fi\@EA{\the\numexpr\recurselevel+#3\relax}{#2}{#3}} \endETEX \beginTEX \long\def\dodostepwiserecurse#1#2#3% from to step {\ifnum#1>#2\relax \@EA\nodostepwiserecurse \else \def\recurselevel{#1}% \innerrecurse#1\advance\innerrecurse#3\relax \@EAEAEA\redostepwiserecurse\@EA \fi\@EA{\the\innerrecurse}{#2}{#3}} \endTEX \def\expandrecursecontent {\csname\@@arecurse\recursedepth\endcsname} \def\redostepwiserecurse {\expandrecursecontent\dodostepwiserecurse} \beginETEX \numexpr \long\def\dodostepwisereverse#1#2#3% from to step {\ifnum#1<#2\relax \@EA\nodostepwiserecurse \else \def\recurselevel{#1}% \@EAEAEA\redostepwisereverse\@EA \fi\@EA{\the\numexpr\recurselevel#3\relax}{#2}{#3}} \endETEX \beginTEX \long\def\dodostepwisereverse#1#2#3% from to step {\ifnum#1<#2\relax \@EA\nodostepwiserecurse \else \def\recurselevel{#1}% \innerrecurse#1\relax \advance\innerrecurse#3\relax \@EAEAEA\redostepwisereverse\@EA \fi\@EA{\the\innerrecurse}{#2}{#3}} \endTEX \def\redostepwisereverse {\expandrecursecontent\dodostepwisereverse} \def\exitstepwiserecurse {\nodostepwiserecurse\relax} \def\nodostepwiserecurse#1#2#3#4% {\@EA\let\@EA\recurselevel\csname\@@irecurse\recursedepth\endcsname \global\advance\outerrecurse \minusone} \def\nonostepwiserecurse#1#2#3% {\@EA\let\@EA\recurselevel\csname\@@irecurse\recursedepth\endcsname \global\advance\outerrecurse \minusone} \def\dorecurse#1% {\dostepwiserecurse1{#1}1} %D As we can see here, the simple command \type{\dorecurse} is %D a special case of the more general: %D %D \starttyping %D \dostepwiserecurse {from} {to} {step} {action} %D \stoptyping %D %D This commands accepts positive and negative steps. Illegal %D values are handles as good as possible and the macro accepts %D numbers and \COUNTERS. %D %D \starttyping %D \dostepwiserecurse {1} {10} {2} {...} %D \dostepwiserecurse {10} {1} {-2} {...} %D \stoptyping %D %D Because the simple case is used often, we implement it %D more efficiently: \long\def\dorecurse#1% {\ifcase#1\relax \expandafter\gobbletwoarguments \or \expandafter\ydorecurse \else \expandafter\xdorecurse \fi{#1}} \long\def\xdorecurse#1#2% {\global\advance\outerrecurse \plusone \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname{#2}% \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel \@EA\dodorecurse\@EA1\@EA{\number#1}} \long\def\ydorecurse#1#2% {\global\advance\outerrecurse \plusone \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel \let\recurselevel\!!plusone #2% \@EA\let\@EA\recurselevel\csname\@@irecurse\recursedepth\endcsname \global\advance\outerrecurse \minusone} \beginETEX \numexpr \long\def\dodorecurse#1#2% from to {\ifnum#1>#2\relax \@EA\nodorecurse \else \def\recurselevel{#1}% \@EAEAEA\redorecurse \fi\@EA{\the\numexpr\recurselevel+\plusone\relax}{#2}} \endETEX \beginTEX \long\def\dodorecurse#1#2% from to {\ifnum#1>#2\relax \@EA\nodorecurse \else \def\recurselevel{#1}% \innerrecurse#1\advance\innerrecurse\plusone \@EAEAEA\redorecurse \fi\@EA{\the\innerrecurse}{#2}} \endTEX \def\redorecurse {\expandrecursecontent\dodorecurse} \def\nodorecurse#1#2#3% {\@EA\let\@EA\recurselevel\csname\@@irecurse\recursedepth\endcsname \global\advance\outerrecurse \minusone } %D The third alternative looks a bit different and uses a %D pseudo counter. When this macro is nested, we have to use %D different counters. This time we use keywords. %D %D \starttyping %D \def\alfa{2} \def\beta{100} \def\gamma{3} %D %D \for \n=55 \to 100 \step 1 \do {... \n ...} %D \for \n=\alfa \to \beta \step \gamma \do {... \n ...} %D \for \n=\n \to 120 \step 1 \do {... \n ...} %D \for \n=120 \to 100 \step -3 \do {... \n ...} %D \for \n=55 \to 100 \step 2 \do {... \n ...} %D \stoptyping %D %D Only in the third example we need to predefine \type{\n}. %D The use of \type{\od} as a dilimiter would have made nested %D use more problematic. % maybe this one will become obsolete \def\for#1=#2\to#3\step#4\do#5% {\dostepwiserecurse{#2}{#3}{#4} {\let#1\recurselevel#5\let#1\recurselevel}} %D \macros %D {doloop,exitloop} %D %D Sometimes loops are not determined by counters, but by %D (a combinations of) conditions. We therefore implement a %D straightforward loop, which can only be left when we %D explictly exit it. Nesting is supported. First we present %D a more extensive alternative. %D %D \starttyping %D \doloop %D {Some kind of typesetting punishment \par %D \ifnum\pageno>100 \exitloop \fi} %D \stoptyping %D %D When needed, one can call for \type{\looplevel} and %D \type{\loopdepth}. %D %D If we write this macros from scratch, we end up with %D something like the ones described above: %D %D \starttyping %D \def\@@eloop{@@eloop} % exit %D \def\@@iloop{@@iloop} % stepper %D \def\@@aloop{@@aloop} % action %D %D \newcount\outerloop %D %D \def\loopdepth% %D {\the\outerloop} %D %D \def\exitloop% %D {\setevalue{\@@eloop\loopdepth}{0}} %D %D \long\def\doloop#1% %D {\global\advance\outerloop by 1 %D \setevalue{\@@iloop\loopdepth}{1}% %D \setevalue{\@@eloop\loopdepth}{1}% %D \long\setvalue{\@@aloop\loopdepth}{#1}% %D \dodoloop} %D %D \def\dodonoloop% %D {\global\advance\outerloop by -1\relax} %D %D \def\dododoloop% %D {\edef\looplevel{\getvalue{\@@iloop\loopdepth}}% %D \innerrecurse=\looplevel %D \advance\innerrecurse by 1 %D \setevalue{\@@iloop\loopdepth}{\the\innerrecurse}% %D \getvalue{\@@aloop\loopdepth}% %D \edef\looplevel{\getvalue{\@@iloop\loopdepth}}% %D \dodoloop} %D %D \def\dodoloop% %D {\ifnum\getvalue{\@@eloop\loopdepth}=0 %D \expandafter\dodonoloop %D \else %D \expandafter\dododoloop %D \fi} %D %D \def\doloop% %D {\dostepwiserecurse{1}{\maxdimen}{1}} %D %D \def\exitloop %D {\setvalue{\@@irecurse\recursedepth}{\maxdimen}} %D %D \def\looplevel{\recurselevel} %D \def\loopdepth{\recursedepth} %D \stoptyping %D %D We don't have to declare new counters for \type{\looplevel} %D and \type{\loopdepth} because we can use \type{\recurselevel} %D and \type{\recursedepth}. %D %D We prefer however a more byte saving implementation, that %D executes of course a bit slower. %D %D \starttyping %D \def\doloop% %D {\dostepwiserecurse1\maxdimen1} %D %D \def\exitloop% %D {\letvalue{\@@irecurse\recursedepth}\maxdimen} %D \stoptyping %D %D Although, the next version is faster because it used the %D simple loop. \let\endofloop\donothing \long\def\doloop#1% {\global\advance\outerrecurse \plusone \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname{#1}% \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel \let\endofloop\dodoloop \dodoloop1} % no \plusone else \recurselevel wrong \beginETEX \numexpr \long\def\dodoloop#1% {\def\recurselevel{#1}% \@EA\redoloop\@EA{\the\numexpr\recurselevel+\plusone\relax}} \endETEX \beginTEX \long\def\dodoloop#1% {\def\recurselevel{#1}% \innerrecurse#1\advance\innerrecurse\plusone \@EA\redoloop\@EA{\the\innerrecurse}} \endTEX \def\redoloop {\expandrecursecontent\endofloop} \def\nodoloop#1% {\let\endofloop\dodoloop % new, permits nested \doloop's \@EA\let\@EA\recurselevel\csname\@@irecurse\recursedepth\endcsname \global\advance\outerrecurse\minusone} \def\exitloop % \exitloop quits at end {\let\endofloop\nodoloop} \long\def\exitloopnow#1\endofloop % \exitloopnow quits directly {\nodoloop} %D The loop is executed at least once, so beware of situations %D like: %D %D \starttyping %D \doloop {\exitloop some commands} %D \stoptyping %D %D It's just a matter of putting the text into the \type{\if} %D statement that should be there anyway, like in: %D %D \starttyping %D \doloop {\ifwhatever \exitloop \else some commands\fi} %D \stoptyping %D %D You can also quit a loop immediately, by using \type %D {\exitloopnow} instead. Beware, this is more sensitive %D for conditional errors. %D \macros %D {newevery,everyline,EveryLine,EveryPar} %D %D Lets skip to something quite different. It's common use %D to use \type{\everypar} for special purposes. In \CONTEXT\ %D we use this primitive for locating sidefloats. This means %D that when user assignments to \type{\everypar} can interfere %D with those of the package. We therefore introduce %D \type{\EveryPar}. %D %D The same goes for \type{\EveryLine}. Because \TEX\ offers %D no \type{\everyline} primitive, we have to call for %D \type{\everyline} when we are working on a line by line %D basis. Just by calling \type{\EveryPar{}} and %D \type{\EveryLine{}} we restore the old situation. %D %D The definition command \type{\DoWithEvery} will be quite %D unreadable, so let's first show an implementation that %D shows how things are done: %D %D \starttyping %D \newtoks \everyline %D \newtoks \oldeveryline %D \newif \ifeveryline %D %D \def\DoWithEvery#1#2#3#4% %D {#3\else\edef\next{\noexpand#2={\the#1}}\next\fi %D \edef\next{\noexpand#1={\the#2\the\scratchtoks}}\next %D #4} %D %D \def\doEveryLine% %D {\DoWithEvery\everyline\oldeveryline\ifeveryline\everylinetrue} %D %D \def\EveryLine% %D {\afterassignment\doEveryLine\scratchtoks} %D %D The real implementation is a bit more complicated but we %D prefer something more versatile. % the old one % % \def\DoWithEvery#1% % {\csname if\strippedcsname#1\endcsname \else % \edef\next% % {\@EA\noexpand\csname old\strippedcsname#1\endcsname= % {\the#1}}% % \next % \fi % \edef\next% % {\noexpand#1= % {\@EA\the\csname old\strippedcsname#1\endcsname\the\scratchtoks}}% % \next % \csname\strippedcsname#1true\endcsname} % % \def\dowithevery#1% % {\@EA\afterassignment\csname do\strippedcsname#1\endcsname\scratchtoks} % % \def\newevery#1#2% % {\ifx#1\undefined\newtoks#1\fi % \ifx#2\relax\else\ifx#2\undefined % \@EA\newtoks\csname old\strippedcsname#1\endcsname % \@EA\newif \csname if\strippedcsname#1\endcsname % \@EA\def \csname do\strippedcsname#2\endcsname{\DoWithEvery#1}% % \def#2{\dowithevery#2}% % \fi\fi} % % cleaner and more efficient %\def\dowithevery#1% % {\def\dodowithevery% % {\ifcase\csname c\strippedcsname#1\endcsname \expandafter\chardef % \csname c\strippedcsname#1\endcsname=1 % \csname t\strippedcsname#1\endcsname=#1% % \fi % \edef\next% % {#1={\the\csname t\strippedcsname#1\endcsname\the\scratchtoks}}% % \next}% % \afterassignment\dodowithevery\scratchtoks} % % more efficient: \def\dodowithevery#1% {\ifcase\csname c\strippedcsname#1\endcsname \expandafter\chardef \csname c\strippedcsname#1\endcsname1 \csname t\strippedcsname#1\endcsname#1% \fi \edef\next% {#1{\the\csname t\strippedcsname#1\endcsname\the\scratchtoks}}% \next} \def\dowithevery#1% {\def\next{\dodowithevery#1}% \afterassignment\next\scratchtoks} \bgroup \let\newtoks\relax % plain safe (\outer) \gdef\newevery#1#2% {\ifx#1\undefined\csname newtoks\endcsname#1\fi % plain safe (\outer) \ifx#2\relax\else\ifx#2\undefined \expandafter\newtoks\csname t\strippedcsname#1\endcsname \expandafter\chardef\csname c\strippedcsname#1\endcsname\zerocount \def#2{\dowithevery#1}% \fi\fi} \egroup %D The first \type {\outer} hack is needed to trick \TEX\ %D into thinking that \type {\newtoks} is no outer macro, %D the second hack is needed due to some funny interaction %D between outer macros and \type {\if} at expansion time. %D This one permits definitions like: \newevery \everypar \EveryPar \newevery \everyline \EveryLine %D and how about: \newevery \neverypar \NeveryPar %D Which we're going to use indeed! When the second argument %D equals \type {\relax}, the first token list is created %D unless it is already defined. %D Technically spoken we could have used the method we are %D going to present in the visual debugger. First we save %D the primitive \type{\everypar}: %D %D \starttyping %D \let\normaleverypar=\everypar %D \stoptyping %D %D Next we allocate a \TOKENLIST\ named \type{\everypar}, %D which means that \type{\everypar} is no longer a primitive %D but something like \type{\toks44}. %D %D \starttyping %D \newtoks\everypar %D \stoptyping %D %D Because \TEX\ now executes \type{\normaleverypar} instead %D of \type{\everypar}, we are ready to assign some tokens to %D this internally known and used \TOKENLIST. %D %D \starttyping %D \normaleverypar={all the things the system wants to do \the\everypar} %D \stoptyping %D %D Where the user can provide his own tokens to be expanded %D every time he expects them to expand. %D %D \starttyping %D \everypar={something the user wants to do} %D \stoptyping %D %D We don't use this method because it undoubtly leads to %D confusing situations, especially when other packages are %D used, but it's this kind of tricks that make \TEX\ so %D powerful. %D \macros %D {convertargument,convertcommand,convertvalue} %D %D Some persistent experimenting led us to the next macro. This %D macro converts a parameter or an expanded macro to it's %D textual meaning. %D %D \starttyping %D \convertargument ... \to \command %D \stoptyping %D %D For example, %D %D \starttyping %D \convertargument{one \two \three{four}}\to\ascii %D \stoptyping %D %D The resulting macro \type{\ascii} can be written to a file %D or the terminal without problems. In \CONTEXT\ we use this %D macro for generating registers and tables of contents. %D %D The second conversion alternative accepts a command: %D %D \starttyping %D \convertcommand\command\to\ascii %D \stoptyping %D %D Both commands accept the prefix \type{\doglobal} for global %D assignments. \beginTEX \def\doconvertargument#1>{} \def\convertedcommand {\expandafter\doconvertargument\meaning} %\long\def\convertargument#1\to#2% % {\long\def\convertedargument{#1}% % \dodoglobal\edef#2{\convertedcommand\convertedargument}} \long\def\convertargument#1\to#2% {\long\def#2{#1}% saves a restore \dodoglobal\edef#2{\convertedcommand#2}} \long\def\convertcommand#1\to#2% {\dodoglobal\edef#2{\convertedcommand#1}} \long\def\defconvertedargument#1#2% less sensitive for \to {\long\def#1{#2}% saves a restore \dodoglobal\edef#1{\convertedcommand#1}} \long\def\defconvertedcommand#1#2% less sensitive for \to {\dodoglobal\edef#1{\convertedcommand#2}} \endTEX \def\convertvalue#1\to {\expandafter\convertcommand\csname#1\endcsname\to} \def\defconvertvalue#1#2% less sensitive for \to {\@EA\defconvertcommand\@EA#1\csname#2\endcsname} %D \macros %D {convertasciiafter} %D %D Sometimes we need to convert an argument to a string (letters %D only), for instance when we compare it with another string: %D %D \starttyping %D \convertasciiafter\doifinstringelse{em}{\ascii}{...} %D \stoptyping \def\convertasciiafter#1#2% {\convertargument#2\to\asciiafter \@EA#1\@EA{\asciiafter}} %D In \ETEX\ we can use \type {\detokenize} and gain some %D speed, but in general far less that 1\% for \type %D {\convertargument} and nil for \type {\convertcommand}. %D This macro is more robust than the pure \TEX\ one, %D something I found out when primitives like \type %D {\jobname} were fed (or something undefined). \beginETEX \detokenize \long\def\convertargument#1\to#2% {\dodoglobal\edef#2{\detokenize{#1}}} % \long\def\convertcommand#1\to#2% % {\@EA\dodoglobal\@EA\edef\@EA#2\@EA{\@EA\detokenize\@EA{#1}}} \long\def\convertcommand#1\to#2% {\dodoglobal\edef#2{\@EA\detokenize\@EA{#1}}} % hm, only second is also ok \long\def\defconvertedargument#1#2% less sensitive for \to {\dodoglobal\edef#1{\detokenize{#2}}} \long\def\defconvertedcommand#1#2% less sensitive for \to {\dodoglobal\edef#1{\detokenize\@EA{#2}}} \endETEX %D When you try to convert a primitive command, you'll find %D out that the \ETEX\ method fails on for instance \type %D {\jobname} in the sense that it returns the filename %D instead of just \type {\jobname}. So far this does not %D give real problems. %D This is typically a macro that one comes to after reading %D the \TEX book carefully. Even then, the definite solution %D was found after rereading the \TEX book. The first %D implementation was: %D %D \starttyping %D \def\doconvertargument#1->#2\\\\{#2} %D \stoptyping %D %D The \type{-}, the delimiter \type{\\\\} and the the second %D argument are completely redundant. % %D As said, the \TEX\ alternative fails on expanding primitives, % %D like in: % %D % %D \starttyping % %D \convertcommand\someprimitive\to\ascii % %D \stoptyping % %D % %D Because these primitives convert to themselves, we can use % %D the backslash as a signal to treat them different. At the % %D cost of slightly more overhead we can therefore define a % %D more robust alternative. The catcode trickery is needed to % %D get the backslash into the test as character (and not as % %D escape, letter or whatever code else). % % % % does not work: % % % % \beginTEX % % % % \let\dodoconvertargument\doconvertargument % % % % \bgroup % % \catcode`\*=\@@escape % % \catcode`\\=\@@other % % *gdef*doconvertargument#1% % % {*ifx#1\*else*expandafter*dodoconvertargument*fi#1} % % *egroup % % % % \endTEX %D \macros %D {showvalue,showargument} %D %D Two handy macros for testing purposes only: \def\showvalue#1% {\expandafter\show\csname#1\endcsname} \long\def\showargument#1% {\convertargument#1\to\ascii\show\ascii} %D \macros %D {doifmeaningelse} %D %D We can use both commands in testing, but alas, not all %D meanings expand to something \type {->}. This is no problem %D in the \ETEX\ implementation, but since we want %D compatibility, we need: %D %D \starttyping %D \doifmeaningelse {\next} {\something} {true} {false} %D \stoptyping %D %D Watch the one level expansion of the second argument. \def\doifmeaningelse#1#2% {\edef\!!stringa{\meaning#1}% \def\!!stringb{#2}\edef\!!stringb{\meaning\!!stringb}% \ifx\!!stringa\!!stringb \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} %D \macros %D {doifsamestringselse,doifsamestring,doifnotsamestring} %D %D The next comparison macro converts the arguments into %D expanded strings. This command can be used to compare for %D instance \type {\jobname} with a name stored in a macro. \def\@@doifsamestringelse#1#2% {\edef\!!stringa{#1}% \edef\!!stringb{#2}% \convertcommand\!!stringa\to\!!stringa \convertcommand\!!stringb\to\!!stringb \ifx\!!stringa\!!stringb} \def\doifsamestringelse#1#2% {\@@doifsamestringelse{#1}{#2}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \def\doifsamestring#1#2% {\@@doifsamestringelse{#1}{#2}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \def\doifnotsamestring#1#2% {\@@doifsamestringelse{#1}{#2}% \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} %D \macros %D {ExpandFirstAfter,ExpandSecondAfter,ExpandBothAfter} %D %D These three commands support expansion of arguments before %D executing the commands that uses them. We can best %D illustrate this with an example. %D %D \starttyping %D \def\first {alfa,beta,gamma} %D \def\second {alfa,epsilon,zeta} %D %D \ExpandFirstAfter \doifcommon {\first} {alfa} {\message{OK}} %D \ExpandSecondAfter \doifcommon {alfa} {\second} {\message{OK}} %D \ExpandBothAfter \doifcommon {\first} {\second} {\message{OK}} %D %D \ExpandFirstAfter\processcommalist[\first]\message %D %D \ExpandAfter \doifcommon {\first} {alfa} {\message{OK}} %D \stoptyping %D %D The first three calls result in the threefold message %D \type{OK}, the fourth one shows the three elements of %D \type{\first}. The command \type{\ExpandFirstAfter} takes %D care of (first) arguments that are delimited by \type{[ ]}, %D but the faster \type{\ExpandAfter} does not. \def\simpleExpandFirstAfter#1% {\long\xdef\@@expanded{\noexpand\ExpandCommand{#1}}\@@expanded} \def\complexExpandFirstAfter[#1]% {\long\xdef\@@expanded{\noexpand\ExpandCommand[#1]}\@@expanded} \def\ExpandFirstAfter#1% {\let\ExpandCommand#1% \doifnextcharelse[\complexExpandFirstAfter\simpleExpandFirstAfter} \def\ExpandSecondAfter#1#2#3% {\scratchtoks{#2}% \long\xdef\@@expanded{\noexpand#1{\the\scratchtoks}{#3}}\@@expanded} \def\ExpandBothAfter#1#2#3% {\long\xdef\@@expanded{\noexpand#1{#2}{#3}}\@@expanded} \def\ExpandAfter#1#2% {\long\xdef\@@expanded{\noexpand#1{#2}}\@@expanded} %D Now we can for instance define \type{\ifinstringelse} as: \def\ifinstringelse {\ExpandBothAfter\p!doifinstringelse} %D \macros %D {ConvertToConstant,ConvertConstantAfter} %D %D When comparing arguments with a constant, we can get into %D trouble when this argument consists of tricky expandable %D commands. One solution for this is converting the %D argument to a string of unexpandable characters. To make %D comparison possible, we have to convert the constant too %D %D \starttyping %D \ConvertToConstant\doifelse {...} {...} {then ...} {else ...} %D \stoptyping %D %D This construction is only needed when the first argument %D can give troubles. Misuse can slow down processing. %D %D \starttyping %D \ConvertToConstant\doifelse{\c!alfa} {\c!alfa}{...}{...} %D \ConvertToConstant\doifelse{alfa} {\c!alfa}{...}{...} %D \ConvertToConstant\doifelse{alfa} {alfa} {...}{...} %D \ConvertToConstant\doifelse{alfa \alfa test}{\c!alfa}{...}{...} %D \stoptyping %D %D In examples~2 and~3 both arguments equal, in~1 and~4 %D they differ. \beginTEX \def\ConvertToConstant#1#2#3% {\expandafter\convertargument\expandafter{#2}\to\!!stringa \expandafter\convertargument\expandafter{#3}\to\!!stringb #1{\!!stringa}{\!!stringb}} \endTEX \beginETEX \detokenize \def\ConvertToConstant#1#2#3% {\edef\!!stringa{\expandafter\detokenize\expandafter{#2}}% \edef\!!stringb{\expandafter\detokenize\expandafter{#3}}% #1{\!!stringa}{\!!stringb}} \endETEX %D When the argument \type{#1} consists of commands, we had %D better use %D %D \starttyping %D \ConvertConstantAfter\processaction[#1][...] %D \ConvertConstantAfter\doifelse{#1}{\v!something}{}{} %D \stoptyping %D %D This commands accepts things like: %D %D \starttyping %D \v!constant %D constant %D \hbox to \hsize{\rubish} %D \stoptyping %D %D As we will see in the core modules, this macro permits %D constructions like: %D %D \starttyping %D \setupfootertexts[...][...] %D \setupfootertexts[margin][...][...] %D \setupfootertexts[\v!margin][...][...] %D \stoptyping %D %D where \type{...} can be anything legally \TEX. \def\CheckConstantAfter#1#2% {\@EA\convertargument\v!prefix!\to\ascii \convertargument#1\to#2\relax \doifinstringelse\ascii{#2} {\expandafter\convertargument#1\to#2} {}} \def\ConvertConstantAfter#1#2#3% {\CheckConstantAfter{#2}\asciiA \CheckConstantAfter{#3}\asciiB #1{\asciiA}{\asciiB}} %D \macros %D {assignifempty} %D %D We can assign a default value to an empty macro using: %D %D \starttyping %D \assignifempty \macros {default value} %D \stoptyping %D %D We don't explicitly test if the macro is defined. \def\assignifempty#1#2% can be sped up {\doifsomething{#1}{\def#1{#2}}} % {\doifnot{#1}{}{\def#1{#2}}} %D \macros %D {gobbleuntil,grabuntil,gobbleuntilrelax, %D processbetween,processuntil} %D %D In \TEX\ gobbling usually stand for skipping arguments, so %D here are our gobbling macros. %D %D In \CONTEXT\ we use a lot of \type{\start}||\type{\stop} %D like constructions. Sometimes, the \type{\stop} is used as a %D hard coded delimiter like in: %D %D \starttyping %D \def\startcommand#1\stopcommand% %D {... #1 ...} %D \stoptyping %D %D In many cases the \type{\start}||\type{\stop} pair is %D defined at format generation time or during a job. This %D means that we cannot hardcode the \type{\stop} criterium. %D Only after completely understanding \type{\csname} and %D \type{\expandafter} I was able to to implement a solution, %D starting with: %D %D \starttyping %D \grabuntil{stop}\command %D \stoptyping %D %D This commands executes, after having encountered %D \type {\stop} the command \type {\command}. This command %D receives as argument the text preceding the \type {\stop}. %D This means that: %D %D \starttyping %D \def\starthello% %D {\grabuntil{stophello}\message} %D %D \starthello Hello world!\stophello %D \stoptyping %D %D results in: \type{\message{Hello world!}}. \def\dograbuntil#1#2% {\long\def\next##1#1{#2{##1}}\next} \def\grabuntil#1% {\expandafter\dograbuntil\expandafter{\csname#1\endcsname}} %D The next command build on this mechanism: %D %D \starttyping %D \processbetween{string}\command %D \stoptyping %D %D Here: %D %D \starttyping %D \processbetween{hello}\message %D \starthello Hello again!\stophello %D \stoptyping %D %D leads to: \type{\message{Hello again!}}. The command %D %D \starttyping %D \gobbleuntil{sequence} %D \stoptyping %D %D is related to these commands. This one simply throws away %D everything preceding \type{\command}. \long\def\processbetween#1#2% {\setvalue{\s!start#1}{\grabuntil{\s!stop#1}{#2}}} \def\gobbleuntil#1% {\long\def\next##1#1{}\next} \def\gobbleuntilrelax#1\relax {} %D The next one simply expands the pickup up tokens. %D %D \starttyping %D \processuntil{sequence} %D \stoptyping \def\processuntil#1% {\long\def\next##1#1{##1}\next} %D \macros %D {groupedcommand} %D %D Commands often manipulate argument as in: %D %D \starttyping %D \def\doezomaarwat#1{....#1....} %D \stoptyping %D %D A disadvantage of this approach is that the tokens that %D form \type{#1} are fixed the the moment the argument is read %D in. Normally this is no problem, but for instance verbatim %D environments adapt the \CATCODES\ of characters and therefore %D are not always happy with already fixed tokens. %D %D Another problem arises when the argument is grouped not by %D \type{{}} but by \type{\bgroup} and \type{\egroup}. Such an %D argument fails, because the \type{\bgroup} is een as the %D argument (which is quite normal). %D %D The next macro offers a solution for both unwanted %D situations: %D %D \starttyping %D \groupedcommand {before} {after} %D \stoptyping %D %D Which can be used like: %D %D \starttyping %D \def\cite% %D {\groupedcommand{\rightquote\rightquote}{\leftquote\leftquote}} %D \stoptyping %D %D This command is equivalent to, but more 'robust' than: %D %D \starttyping %D \def\cite#1% %D {\rightquote\rightquote#1\leftquote\leftquote} %D \stoptyping %D %D One should say that the next implementation would suffice: %D %D \starttyping %D \def\groupedcommand#1#2% %D {\def\BeforeGroup{#1\ignorespaces}% %D \def\AfterGroup{\unskip#2\egroup}% %D \bgroup\bgroup %D \aftergroup\AfterGroup %D \afterassignment\BeforeGroup %D \let\next=} %D \stoptyping %D %D It did indeed, but one day we decided to support the %D processing of boxes too: %D %D \starttyping %D \def\rightword% %D {\groupedcommand{\hfill\hbox}{\parfillskip\!!zeropoint}} %D %D .......... \rightword{the right way} %D \stoptyping %D %D Here \TEX\ typesets \type{\bf the right way} unbreakable %D at the end of the line. The solution mentioned before does %D not work here. %D %D \starttyping %D \long\unexpanded\def\groupedcommand#1#2% %D {\bgroup %D \long\def\BeforeGroup% %D {\bgroup#1\bgroup\aftergroup\AfterGroup}% %D \long\def\AfterGroup% %D {#2\egroup\egroup}% %D \afterassignment\BeforeGroup %D \let\next=} %D \stoptyping %D %D We used this method some time until the next alternative %D was needed. From now on we support both %D %D \starttyping %D to be \bold{bold} or not, that's the question %D \stoptyping %D %D and %D %D \starttyping %D to be {\bold bold} or not, that's the question %D \stoptyping %D %D This alternative checks for a \type{\bgroup} token first. %D The internal alternative does not accept the box handling %D mentioned before, but further nesting works all right. The %D extra \type{\bgroup}||\type{\egroup} is needed to keep %D \type{\AfterGroup} both into sight and local. \long\def\HandleGroup#1#2% {\bgroup \long\def\BeforeGroup{\bgroup#1\bgroup\aftergroup\AfterGroup}% \long\def\AfterGroup {#2\egroup\egroup}% \afterassignment\BeforeGroup \let\next=} \long\def\HandleSimpleGroup#1#2% no inner group (so no kerning interference) {\bgroup \long\def\BeforeGroup{\bgroup#1\aftergroup\AfterGroup}% interferes \long\def\BeforeGroup{\bgroup\aftergroup\AfterGroup#1}% \long\def\AfterGroup {#2\egroup}% \afterassignment\BeforeGroup \let\next=} \long\def\HandleNoGroup#1#2% {\long\def\AfterGroup{#2\egroup}% \bgroup\aftergroup\AfterGroup#1} %D These macros come together in: %D %D \starttyping %D \long\unexpanded\def\groupedcommand#1#2% %D {\def\dogroupedcommand% %D {\ifx\next\bgroup %D \let\next=\HandleGroup %D \else %D \let\next=\HandleNoGroup %D \fi %D \next{#1}{#2}}% %D \futurelet\next\dogroupedcommand} %D \stoptyping %D %D From the missing paragraph number one can deduce that the %D last macro is not the real one yet. I considered it a %D nuisance that %D %D \starttyping %D \color[green] %D {as grass} %D \stoptyping %D %D was not interpreted as one would expect. This is due to the %D fact that \type{\futurelet} obeys blank spaces, and a %D line||ending token is treated as a blank space. So the final %D implementation became: %\long\unexpanded\def\groupedcommand#1#2% % {\bgroup % \def\dogroupedcommand% % {\ifx\next\bgroup % \def\\{\egroup\HandleGroup{#1}{#2}}% % \else\ifx\next\blankspace % \def\\ {\egroup\groupedcommand{#1}{#2}}% % \else % \def\\{\egroup\HandleNoGroup{#1}{#2}}% % \fi\fi % \\}% % \futurelet\next\dogroupedcommand} % % compatible ? \long\unexpanded\def\groupedcommand#1#2% {\doifnextcharelse\bgroup {\HandleGroup{#1}{#2}}{\HandleNoGroup{#1}{#2}}} \long\unexpanded\def\simplegroupedcommand#1#2% {\doifnextcharelse\bgroup {\HandleSimpleGroup{#1}{#2}}{\HandleNoGroup{#1}{#2}}} %D Users should be aware of the fact that grouping can %D interfere with ones paragraph settings that are executed %D after the paragraph is closed. One should therefore %D explictly close the paragraph with \type{\par}, else the %D settings will be forgotten and not applied. So it's: %D %D \starttyping %D \def\BoldRaggedCenter% %D {\groupedcommand{\raggedcenter\bf}{\par}} %D \stoptyping %D \macros %D {checkdefined} %D %D The bigger the system, the greater the change that %D user defined commands collide with those that are part of %D the system. The next macro gives a warning when a command is %D already defined. We considered blocking the definition, but %D this is not always what we want. %D %D \starttyping %D \checkdefined {category} {class} {command} %D \stoptyping %D %D The user is warned with the suggestion to use %D \type{CAPITALS}. This suggestion is feasible, because %D \CONTEXT only defines lowcased macros. \def\showdefinederror#1#2% {\writestatus\m!systems{#1 #2 replaces a macro, use CAPITALS!}} \def\checkdefined#1#2#3% {\doifdefined{#3}{\showdefinederror{#2}{#3}}} %D \macros %D {GotoPar,GetPar} %D %D Typesetting a paragraph in a special way can be done by %D first grabbing the contents of the paragraph and processing %D this contents grouped. The next macro for instance typesets %D a paragraph in boldface. %D %D \starttyping %D \def\remark#1\par% %D {\bgroup\bf#1\egroup} %D \stoptyping %D %D This macro has to be called like %D %D \starttyping %D \remark some text ... ending with \par %D \stoptyping %D %D Instead of \type{\par} we can of course use an empty line. %D When we started typesetting with \TEX, we already had %D produced lots of text in plain \ASCII. In producing such %D simple formatted texts, we adopted an open layout, and when %D switching to \TEX, we continued this open habit. Although %D \TEX\ permits a cramped and badly formatted source, it adds %D to confusion and sometimes introduces errors. So we prefer: %D %D \starttyping %D \remark %D %D some text ... ending with an empty line %D \stoptyping %D %D We are going to implement a mechanism that allows such open %D specifications. The definition of the macro handling %D \type{\remark} becomes: %D %D \starttyping %D \def\remark% %D {\BeforePar{\bgroup\bf}% %D \AfterPar{\egroup}% %D \GetPar} %D \stoptyping %D %D A macro like \type{\GetPar} can be defined in several %D ways. The recent version, the fourth one in a row, %D originally was far more complicated, but some functionality %D has been moved to other macros. %D %D We start with the more simple but in some cases more %D appropriate alternative is \type{\GotoPar}. This one leaves %D \type{\par} unchanged and is therefore more robust. On the %D other hand, \type{\AfterPar} is not supported. \newtoks\BeforePar \newtoks\AfterPar \let\endoflinetoken=^^M %D The original definition was: %D %D \starttyping %D \def\doGotoPar %D {\ifx\nextchar\blankspace %D \@EA\GotoPar %D \else\ifx\nextchar\endoflinetoken %D \@EAEAEA\GotoPar %D \else %D \@EAEAEA\dodoGotoPar %D \fi\fi} %D %D \def\dodoGotoPar %D {\the\BeforePar %D \BeforePar\emptytoks %D \nextchar} %D %D \def\GotoPar %D {\afterassignment\doGotoPar\let\nextchar=} %D \stoptyping %D Its big brother \type{\GetPar} redefines the \type{\par} %D primitive, which can lead to unexpected results, depending %D in the context. %D %D \starttyping %D \def\GetPar %D {\expanded %D {\BeforePar %D {\the\BeforePar %D \BeforePar\emptytoks %D \bgroup %D \def\par %D {\egroup %D \par %D \the\AfterPar %D \BeforePar\emptytoks %D \AfterPar\emptytoks}}}% %D \GotoPar} %D \stoptyping %D However, we can implement a better alternative by using: %D %D \starttyping %D \def\dowithpar#1#2% %D {\def\handlepar##1\par{#1##1#2}% %D \def\gobblepar\par{\dowithpar{#1}{#2}}% %D \doifnextcharelse\par\gobblepar\handlepar} %D \stoptyping %D %D Or, nicer \def\redowithpar\par {\doifnextcharelse\par\redowithpar\dodowithpar}% \def\dowithpar#1#2% {\def\dodowithpar##1\par{#1##1#2}% \redowithpar\par} \def\redogotopar\par {\doifnextcharelse\par\redogotopar\dodogotopar}% \def\dogotopar#1% {\def\dodogotopar{#1}% \redogotopar\par} %D The previosuly defined macros now become: \def\GetPar {\expanded {\dowithpar {\the\BeforePar \BeforePar\emptytoks} {\the\AfterPar \BeforePar\emptytoks \AfterPar\emptytoks}}} \def\GotoPar {\expanded {\dogotopar {\the\BeforePar \BeforePar\emptytoks}}} %D \macros %D {dowithpargument,dowithwargument} %D %D The next macros are a variation on \type{\GetPar}. When %D macros expect an argument, it interprets a grouped sequence %D of characters a one token. While this adds to robustness and %D less ambiguous situations, we sometimes want to be a bit %D more flexible, or at least want to be a bit more tolerant %D to user input. %D %D We start with a commands that acts on paragraphs. This %D command is called as: %D %D \starttyping %D \dowithpargument\command %D \dowithpargument{\command ... } %D \stoptyping %D %D In \CONTEXT\ we use this one to read in the titles of %D chapters, sections etc. The commands responsible for these %D activities accept several alternative ways of argument %D passing. In these examples, the \type{\par} can be omitted %D when an empty line is present. %D %D \starttyping %D \command{...} %D \command ... \par %D \command %D {...} %D \command %D ... \par %D \stoptyping %D %D We show two implementations, of which for the moment the %D we prefier to use the second one: %D %D \starttyping %D \def\dowithpargument#1% %D {\def\dodowithpargument% %D {\ifx\next\bgroup %D \def\next{#1}% %D \else %D \def\next####1 \par{#1{####1}}% %D \fi %D \next}% %D \futurelet\next\dodowithpargument} %D \stoptyping %D %D A second and better implementation was: %D %D \starttyping %D \def\dowithpargument#1% %D {\def\nextpar##1 \par{#1{##1}}% %D \def\nextarg##1{#1{##1}}% %D \doifnextcharelse{\bgroup} %D {\nextarg} %D {\nextpar}} %D \stoptyping %D %D We ended up with an alternative that also accepts en empty %D argument. This command permits for instance chapters to %D have no title. %\def\dowithpargument#1% % {\def\nextpar##1 \par{#1{##1}}% % \def\nextarg##1{#1{##1}}% % \doifnextcharelse{\bgroup} % {\nextarg} % {\doifnextcharelse{\par} % {#1{}} % {\nextpar}}} \def\dowithpargument#1% {\def\nextpar##1 \par{#1{##1}}% \def\nextarg##1{#1{##1}}% \doifnextcharelse\bgroup\nextarg{\doifnextcharelse\par{#1{}}\nextpar}} %D The \type{p} in the previous command stands for paragraph. %D When we want to act upon words we can use the \type{w} %D alternative. %D %D \starttyping %D \dowithwargument\command %D \dowithwargument{... \command ...} %D \stoptyping %D %D The main difference bwteen two alternatives is in the %D handling of \type{\par}'s. This time the space token acts %D as a delimiter. %D %D \starttyping %D \command{...} %D \command ... %D \command %D {...} %D \command %D ... %D \stoptyping %D %D Again there are two implementations possible: %D %D \starttyping %D \def\dowithwargument#1% %D {\def\dodowithwargument% %D {\ifx\next\bgroup %D \def\next{#1}% %D \else %D \def\next####1 {#1{####1}}% %D \fi %D \next}% %D \futurelet\next\dodowithwargument} %D \stoptyping %D %D We've chosen: %\def\dowithwargument#1% % {\def\nextwar##1 {#1{##1}}% % \def\nextarg##1{#1{##1}}% % \doifnextcharelse{\bgroup} % {\nextarg} % {\nextwar}} \def\dowithwargument#1% {\def\nextwar##1 {#1{##1}}% \def\nextarg##1{#1{##1}}% \doifnextcharelse\bgroup\nextarg\nextwar} %D \macros %D {dorepeat,dorepeatwithcommand} %D %D When doing repetitive tasks, we stromgly advice to use %D \type{\dorecurse}. The next alternative however, suits %D better some of the \CONTEXT\ interface commands. %D %D \starttyping %D \dorepeat[n*\command] %D \stoptyping %D %D The value of the used \COUNTER\ can be called within %D \type{\command} by \type{\repeater}. %D %D A slightly different alternative is: %D %D \starttyping %D \dorepeatwithcommand[n*{...}]\command %D \stoptyping %D %D When we call for something like: %D %D \starttyping %D \dorepeatwithcommand[3*{Hello}]\message %D \stoptyping %D %D we get ourselves three \type{\message{Hello}} messages in %D a row. In both commands, the \type{n*} is optional. When this %D specification is missing, the command executes once. % this one is obsolete: \def\dorepeat[#1]% {\dodorepeat#1*\empty*\relax} \long\def\dodorepeat#1*#2#3*#4\relax {\ifx#2\empty#1\else\dorecurse{#1}{#2#3}\fi} \def\repeater {\recurselevel} % this one will be kept \def\dorepeatwithcommand[#1]% {\dodorepeatwithcommand#1*\empty*\relax} % \long\def\dodorepeatwithcommand#1*#2#3*#4\relax#5% % {\ifx#2\empty % #5{#1}% % \else % \dorecurse{#1}{#5{#2#3}}% % \fi} % % more complex but better: % \long\def\dodorepeatwithcommand#1*#2#3*#4\relax#5% % {\ifx#2\empty % #5{#1}% % \else\ifnum#1<\zerocount % % a la etex % % \dorecurse{-\numexpr(#1)}{#5{-#2#3}}% % % indirect % %\innerrecurse#1% % %\expanded{\dorecurse{\number-\innerrecurse}}{#5{-#2#3}}% % % safer: % \bgroup\scratchcounter#1% % \expanded{\egroup\noexpand\dorecurse{\number-\scratchcounter}}{#5{-#2#3}}% % \else\ifx#2+% % \dorecurse{#1}{#5{#3}}% % \else % \dorecurse{#1}{#5{#2#3}}% % \fi\fi\fi} \def\dorepeatwithcommand[#1]% {\dodorepeatwithcommand#1*\empty*\relax} \long\def\dodorepeatwithcommand#1*#2#3*#4\relax#5% {\ifx#2\empty\redorepeatwithcommand[#1]#5\else\dododorepeatwithcommand{#1}{#2}{#3}#5\fi} \long\def\dododorepeatwithcommand#1#2#3#4% {\ifx#2\empty % redundant but gives cleaner extensions #4{#1}% \else\ifnum#1<\zerocount \bgroup\scratchcounter#1% \expanded{\egroup\noexpand\dorecurse{\number-\scratchcounter}}{#4{-#2#3}}% \else\ifx#2+% \dorecurse{#1}{#4{#3}}% \else \dorecurse{#1}{#4{#2#3}}% \fi\fi\fi} \def\redorepeatwithcommand[#1]#2% {#2{#1}} %D The extension hook permits something like: %D %D \starttyping %D \bgroup %D %D \catcode`\*=\@@superscript %D %D \gdef\redorepeatwithcommand[#1]% %D {\redodorepeatwithcommand#1*\empty*\relax} %D %D \long\gdef\redodorepeatwithcommand#1*#2#3*#4\relax#5% %D {\dododorepeatwithcommand{#1}{#2}{#3}#5} %D %D \egroup %D \stoptyping %D %D although one may wonder if changing the catcode of \type {*} is wise. %D \macros %D {normalbgroup,normalgroup} %D %D No comment. \let\normalbgroup\bgroup \let\normalegroup\egroup %D \macros %D {doifstringinstringelse} %D %D The next macro is meant for situations where both strings %D are macros. This save some unneeded expansion. %D %D \starttyping %D \long\def\doifstringinstringelse#1#2% %D {\p!doifinstringelse#1#2% %D \@EA\firstoftwoarguments %D \else %D \@EA\secondoftwoarguments %D \fi} %D \stoptyping %D %D A bit faster is: \def\pp!doifstringinstringelse#1% {\if#1@% \@EA\secondoftwoarguments \else \@EA\firstoftwoarguments \fi} \long\def\doifstringinstringelse#1#2% {\long\@EA\def\@EA\p!doifstringinstringelse\@EA##\@EA1#1##2##3\war {\pp!doifstringinstringelse##2}% \@EA\@EA\@EA\p!doifstringinstringelse\@EA#2#1@@\war} %D \macros %D {appendtoks,prependtoks,appendtoksonce,prependtoksonce, %D doifintokselse,flushtoks,dotoks} %D %D We use \TOKENLISTS\ sparsely within \CONTEXT, because the %D comma separated lists are more suitable for the user %D interface. Nevertheless we have: %D %D \starttyping %D (\doglobal) \appendtoks ... \to\tokenlist %D (\doglobal) \prependtoks ... \to\tokenlist %D (\doglobal) \flushtoks\tokenlist %D \dotoks\tokenlist %D \stoptyping %D %D Er worden eerst enkele klad||registers gedefinieerd. These %D macros are clones of the ones implemented in page~378 of %D Knuth's \TeX book. %D %D A simple implementation, one that does not handle braces %D at the outer level, is: %D %D \starttyping %D \def\appendtoks#1\to#2% %D {\scratchtoks={#1}% %D \expanded{\dodoglobal\noexpand#2{\the#2\the\scratchtoks}}} %D %D \def\prependtoks#1\to#2% %D {\scratchtoks={#1}% %D \expanded{\dodoglobal\noexpand#2{\the\scratchtoks\the#2}}} %D \stoptyping %D %D But here we prefer: \newtoks\@@scratchtoks % before we had the once only alternatives, we had: % % \def\appendtoks {\doappendtoks \relax} % \def\prependtoks{\doprependtoks\relax} % % \long\def\doappendtoks#1\to#2% % {\@@scratchtoks\@EA{\gobbleoneargument#1}% % \expanded{\dodoglobal\noexpand#2{\the#2\the\@@scratchtoks}}} % % \long\def\doprependtoks#1\to#2% % {\@@scratchtoks\@EA{\gobbleoneargument#1}% % \expanded{\dodoglobal\noexpand#2{\the\@@scratchtoks\the#2}}} \def\appendtoks {\doappendtoks \relax} \def\prependtoks {\doprependtoks \relax} \def\appendtoksonce {\doappendtoksonce \relax} \def\prependtoksonce{\doprependtoksonce\relax} % \def\dodoappendtoks#1% % {\expanded{\dodoglobal\noexpand#1{\the#1\the\@@scratchtoks}}} % % \def\dodoprependtoks#1% % {\expanded{\dodoglobal\noexpand#1{\the\@@scratchtoks\the#1}}} % % \long\def\doappendtoks#1\to% % {\@@scratchtoks\@EA{\gobbleoneargument#1}\dodoappendtoks} % % \long\def\doprependtoks#1\to% % {\@@scratchtoks\@EA{\gobbleoneargument#1}\dodoprependtoks} % % \long\def\doappendtoksonce#1\to#2% % {\@@scratchtoks\@EA{\gobbleoneargument#1}% % \doifintokselse\@@scratchtoks{#2}{}{\dodoappendtoks{#2}}} % % \long\def\doprependtoksonce#1\to#2% % {\@@scratchtoks\@EA{\gobbleoneargument#1}% % \doifintokselse\@@scratchtoks{#2}{}{\dodoprependtoks{#2}}} % % A slightly (but in the case of large arguments % significantly) faster alternative is given below: \def\dodoappendtoks {\dodoglobal\@@toks\@EAEAEA{\@EA\the\@EA\@@toks\the\@@scratchtoks}} \def\dodoprependtoks {\dodoglobal\@@toks\@EAEAEA{\@EA\the\@EA\@@scratchtoks\the\@@toks}} \long\def\doappendtoks#1\to#2% {\def\@@toks{#2}% \@@scratchtoks\@EA{\gobbleoneargument#1}\dodoappendtoks} \long\def\doprependtoks#1\to#2% {\def\@@toks{#2}% \@@scratchtoks\@EA{\gobbleoneargument#1}\dodoprependtoks} \long\def\doappendtoksonce#1\to#2% {\def\@@toks{#2}% \@@scratchtoks\@EA{\gobbleoneargument#1}% \doifintokselse\@@scratchtoks\@@toks\donothing\dodoappendtoks} \long\def\doprependtoksonce#1\to#2% {\def\@@toks{#2}% \@@scratchtoks\@EA{\gobbleoneargument#1}% \doifintokselse\@@scratchtoks\@@toks\donothing\dodoprependtoks} %D The test macro: %D %D \starttyping %D \def\doifintokselse#1#2% #1 en #2 zijn toks %D {\edef\!!stringa{\the#1}\convertcommand\!!stringa\to\asciiA %D \edef\!!stringb{\the#2}\convertcommand\!!stringb\to\asciiB %D \doifinstringelse\asciiA\asciiB} %D \stoptyping %D %D Better: %D %D \starttyping %D \def\doifintokselse#1#2% #1 en #2 zijn toks %D {\edef\!!stringa{\the#1}\convertcommand\!!stringa\to\asciiA %D \edef\!!stringb{\the#2}\convertcommand\!!stringb\to\asciiB %D \doifstringinstringelse\asciiA\asciiB} %D \stoptyping %D %D Even better: \beginETEX \def\doifintokselse#1#2% #1 en #2 zijn toks {\@EA\convertargument\the#1\to\asciiA \@EA\convertargument\the#2\to\asciiB \doifstringinstringelse\asciiA\asciiB} \endETEX \beginTEX \def\doifintokselse#1#2% #1 en #2 zijn toks {\edef\asciiA{\the#1}\convertcommand\asciiA\to\asciiA \edef\asciiB{\the#2}\convertcommand\asciiB\to\asciiB \doifstringinstringelse\asciiA\asciiB} \endTEX %D Also: \def\appendetoks #1\to{\expanded{\appendtoks #1}\to} \def\prependetoks#1\to{\expanded{\prependtoks#1}\to} %D Hm. \def\flushtoks#1% {\@@scratchtoks#1\relax \dodoglobal#1\emptytoks \the\@@scratchtoks\relax} \let\dotoks=\the %D \macros %D {makecounter,pluscounter,minuscounter, %D resetcounter,setcounter,countervalue} %D %D Declaring, setting and resetting \COUNTERS\ can be doen %D with the next set of commands. %D %D \starttyping %D \makecounter {name} %D \pluscounter {name} %D \minuscounter {name} %D \resetcounter {name} %D \setcounter {name} {value} %D \countervalue {name} %D \stoptyping %D %D We prefer the use of global counters. This means that we %D have to load \PLAIN\ \TEX\ in a bit different way: %D %D \starttyping %D \let\oldouter=\outer %D \let\outer=\relax %D \input plain.tex %D \let\outer=\oldouter %D %D \def\newcount% %D {\alloc@0\count\countdef\insc@unt} %D \stoptyping %D %D First we show a solution in which we use real \COUNTERS. %D Apart from some expansion, nothing special is done. %D %D \starttyping %D \def\makecounter#1% %D {\expandafter\newcount\csname#1\endcsname} %D %D \def\pluscounter#1% %D {\global\advance\csname#1\endcsname by 1 } %D %D \def\minuscounter#1% %D {\global\advance\csname#1\endcsname by -1 } %D %D \def\resetcounter#1% %D {\expandafter\global\csname#1\endcsname=0 } %D %D \def\setcounter#1#2% %D {\expandafter\global\csname#1\endcsname=#2 } %D %D \def\countervalue#1% %D {\the\getvalue{#1}} %D \stoptyping %D %D Because these macros are already an indirect way of working %D with counters, there is no harm in using pseudo \COUNTERS\ %D here: \def\makecounter#1% {\letgvalue{#1}\zerocountervalue} % see earlier % \def\countervalue#1% % {\getvalue{#1}} \let\countervalue\getvalue \def\pluscounter#1% {\scratchcounter\getvalue{#1}% \advance\scratchcounter \plusone \setxvalue{#1}{\the\scratchcounter}} \def\minuscounter#1% {\scratchcounter\getvalue{#1}% \advance\scratchcounter \minusone \setxvalue{#1}{\the\scratchcounter}} \def\resetcounter#1% {\letgvalue{#1}\zerocountervalue} \def\setcounter#1#2% or: \setxvalue{#1}{\number#2} {\scratchcounter#2% \setxvalue{#1}{\the\scratchcounter}} %D \macros %D {savecounter,restorecounter} %D %D These two commands can be used to save and restore counter %D values. Only one level is saved. \def\savecounter#1% {{\scratchcounter\getvalue {#1}\setxvalue{!#1}{\the\scratchcounter}}} \def\restorecounter#1% {{\scratchcounter\getvalue{!#1}\setxvalue {#1}{\the\scratchcounter}}} % == {\setxvalue{#1}{\getvalue{!#1}}} %D The next \ETEX\ based solution is some 15\% faster, which %D goes unnoticed in any normal run, simply because these %D macros are not used milions of times. \beginETEX \numexpr \def\makecounter#1% {\global\@EA\let\csname#1\endcsname\zerocountervalue} % see earlier \def\countervalue#1% {\csname#1\endcsname} \def\pluscounter#1% {\@EA\xdef\csname#1\endcsname{\the\numexpr\csname#1\endcsname+\plusone\relax}} \def\minuscounter#1% {\@EA\xdef\csname#1\endcsname{\the\numexpr\csname#1\endcsname-\plusone\relax}} \def\resetcounter#1% {\global\@EA\let\csname#1\endcsname\zerocountervalue} \def\setcounter#1#2% {\@EA\xdef\csname#1\endcsname{\the\numexpr#2\relax}} \def\savecounter#1% {\@EA\xdef\csname !#1\endcsname{\the\numexpr\csname#1\endcsname\relax}} \def\restorecounter#1% {\@EA\xdef\csname#1\endcsname{\the\numexpr\csname !#1\endcsname\relax}} \endETEX %D \macros %D {beforesplitstring,aftersplitstring} %D %D These both commands split a string at a given point in two %D parts, so \type{x.y} becomes \type{x} or \type{y}. %D %D \starttyping %D \beforesplitstring test.tex\at.\to\filename %D \aftersplitstring test.tex\at.\to\extension %D \stoptyping %D %D The first routine looks (and is indeed) a bit simpler than %D the second one. The alternative looking more or less like %D the first one did not always give the results we needed. %D Both implementations show some insight in the manipulation %D of arguments. \def\beforesplitstring#1\at#2\to#3% {\def\dosplitstring##1#2##2#2##3\\% {\def#3{##1}}% \@EA\dosplitstring#1#2#2\\} \def\aftersplitstring#1\at#2\to#3% {\def\dosplitstring##1#2##2@@@##3\\% {\def#3{##2}}% \@EA\dosplitstring#1@@@#2@@@\\} %D \macros %D {splitstring,greedysplitstring} %D %D A bonus macro. \def\splitstring#1\at#2\to#3\and#4% {\def\dosplitstring##1#2##2\empty\empty\empty##3\\% {\def#3{##1}% \def\dosplitstring{##3}% \ifx\dosplitstring\empty \let#4\empty \else \def#4{##2}% \fi}% \@EA\dosplitstring#1\empty\empty\empty#2\empty\empty\empty\\} % Ok, but not for all cases: % % \def\greedysplitstring#1\at#2\to#3\and#4% % {\edef\asciib{#1}% % \let\asciic\asciib % \let#3\empty % \let#4\empty % \doloop % {\expandafter\splitstring\asciib\at#2\to\asciia\and\asciib % \ifx\asciib\empty % \exitloop % \else % \edef#3{\ifx#3\empty\else#3#2\fi\asciia}% % \let#4\asciib % \fi}% % \ifx#3\empty\let#3\asciic\fi} % % The next one is some 25\% faster, but it hardly matters because % we seldom use this macro. % % \def\greedysplitstring#1\at#2\to#3\and#4% % {\edef\asciib{#1}% % \let\asciic\asciib % \let#3\empty % \let#4\empty % \def\dogreedysplitstring % {\expandafter\splitstring\asciib\at#2\to\asciia\and\asciib % \ifx\asciib\empty % \expandafter\dogreedysplitstring % \else % \edef#3{\ifx#3\empty\else#3#2\fi\asciia}% % \let#4\asciib % \fi}% % \dogreedysplitstring % \ifx#3\empty\let#3\asciic\fi} % % The better alternative: \def\greedysplitstring#1\at#2\to#3\and#4% {\edef\asciib{#1}% \let\asciic\asciib \let#3\empty \let#4\empty \doloop {\expandafter\splitstring\asciib\at#2\to\asciia\and\asciib \ifx\asciib\empty \exitloop \else % not \edef#3{\ifx#3\empty\else#3#2\fi\asciia} else % /root/path fails because then #3==empty \edef#3{\ifcase\recurselevel\or\else#3#2\fi\asciia}% \let#4\asciib \fi}% \ifx#3\empty\let#3\asciic\fi} %D \macros %D {beforetestandsplitstring, %D aftertestandsplitstring, %D testandsplitstring} %D %D The next alternatives are for Simon Pepping. This time %D the result is empty when no split is done. % \def\beforetestandsplitstring#1\at#2\to#3% % {\def\dosplitstring##1#2##2#2##3\\{\doifelsenothing % {##3}{\let#3\empty}{\def#3{##1}}}% % \@EA\dosplitstring#1#2#2\\} % % \def\aftertestandsplitstring#1\at#2\to#3% % {\def\dosplitstring ##1#2##2@@@##3\\{\doifelsenothing % {##3}{\let#3\empty}{\def#3{##2}}}% % \@EA\dosplitstring #1@@@#2@@@\\} % % \def\testandsplitstring#1\at#2\to#3\and#4% % {\def\dosplitstring##1#2##2#2##3\\{\doifelsenothing % {##3}{\let#3\empty\let#4\empty}{\def#3{##1}\def#4{##2}}}% % \@EA\dosplitstring#1#2#2\\} % % faster: \def\beforetestandsplitstring#1\at#2\to#3% {\def\dosplitstring##1#2##2#2##3##4\\% {\ifx##3\empty\let#3\empty\else\def#3{##1}\fi}% \@EA\dosplitstring#1#2#2\empty\\} \def\aftertestandsplitstring#1\at#2\to#3% {\def\dosplitstring ##1#2##2@@@##3##4\\% {\ifx##3\empty\let#3\empty\else\def#3{##2}\fi}% \@EA\dosplitstring #1@@@#2@@@\empty\\} \def\testandsplitstring#1\at#2\to#3\and#4% {\def\dosplitstring##1#2##2#2##3##4\\% {\ifx##3\empty\let#3\empty\let#4\empty\else\def#3{##1}\def#4{##2}\fi}% \@EA\dosplitstring#1#2#2\empty\\} %D \macros %D {removesubstring} %D %D A first application of the two routines defined above is: %D %D \starttyping %D \removesubstring-\from first-last\to\nothyphenated %D \stoptyping %D %D Which in terms of \TEX\ looks like: %\def\removesubstring#1\from#2\to#3% % {\doifinstringelse{#1}{#2} % {\beforesplitstring#2\at#1\to\!!stringa % \aftersplitstring #2\at#1\to\!!stringb % \edef#3{\!!stringa\!!stringb}% % \removesubstring#1\from#3\to#3} % {}} \def\removesubstring#1\from#2\to#3% {\splitstring#2\to\!!stringa\and\!!stringb \dodoglobal#3{\!!stringa\!!stringb}} %D \macros %D {appendtocommalist,prependtocommalist, %D addtocommalist,removefromcommalist} %D %D When working with comma separated lists, one sooner or %D later want the tools to append or remove items from such a %D list. When we add an item, we first check if it's already %D there. This means that every item in the list is unique. %D %D \starttyping %D \addtocommalist {alfa} \name %D \addtocommalist {beta} \name %D \addtocommalist {gamma} \name %D \removefromcommalist {beta} \name %D \stoptyping %D %D These commands can be prefixed with \type{\doglobal}. The %D implementation of the second command is more complecated, %D because we have to take leading spaces into account. Keep in %D mind that users may provide lists with spaces after the %D commas. When one item is left, we also have to get rid of %D trailing spaces. %D %D \starttyping %D \def\words{alfa, beta, gamma, delta} %D \def\words{alfa,beta,gamma,delta} %D \stoptyping %D %D Removing an item takes more time than adding one. %D %D A fast appending alternative, without any testing, is %D also provided: %D %D \starttyping %D \appendtocommalist {something} \name %D \prependtocommalist {something} \name %D \stoptyping %D %D This can be implemented as follows: %D %D \starttyping %D \def\appendtocommalist#1#2% %D {\ifx#2\empty %D \dodoglobal\edef#2{#1}% %D \else % no test on empty %D \dodoglobal\edef#2{#2,#1}% %D \fi} %D %D \def\prependtocommalist#1#2% %D {\ifx#2\empty %D \dodoglobal\edef#2{#1}% %D \else % no test on empty %D \dodoglobal\edef#2{#1,#2}% %D \fi} %D \stoptyping %D %D The faster alternatives are: \def\appendtocommalist#1#2% {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}} \def\prependtocommalist#1#2% {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}} %D The old ones are: %D %D \starttyping %D \def\addtocommalist#1#2% %D {\ifx#2\empty %D \dodoglobal\edef#2{#1}% %D \else %D \edef\!!stringa{#2,,}% %D \beforesplitstring#2\at,,\to#2\relax %D \ExpandBothAfter\doifinsetelse{#1}{#2} %D {\resetglobal} %D {\dodoglobal\edef#2{#2,#1}}% %D \fi} %D %D \def\pretocommalist#1#2% %D {\ifx#2\empty %D \dodoglobal\edef#2{#1}% %D \else %D \edef\!!stringa{#2,,}% %D \beforesplitstring#2\at,,\to#2\relax %D \ExpandBothAfter\doifinsetelse{#1}{#2} %D {\resetglobal} %D {\dodoglobal\edef#2{#1,#2}}% %D \fi} %D %D \def\doremovefromcommalist#1#2#3% nog \doglobal %D {\edef\!!stringa{,,#3,,}% %D \beforesplitstring\!!stringa\at,#1#2,\to\!!stringb %D \aftersplitstring\!!stringa\at,#1#2,\to\!!stringc %D \edef#3{\!!stringb,\!!stringc}% %D \aftersplitstring#3\at,,\to#3\relax %D \beforesplitstring#3\at,,\to#3} %D %D \def\removefromcommalist#1#2% %D {\doremovefromcommalist{ }{#1}{#2}% %D \doremovefromcommalist{}{#1}{#2}% %D \dofrontstrip#2% %D \dodoglobal\edef#2{#2}} %D \stoptyping %D %D Significantly faster (especially for longer lists): \def\addtocommalist#1#2% {item} \cs {\rawdoifinsetelse{#1}#2\resetglobal {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}} \def\pretocommalist#1#2% {item} \cs {\rawdoifinsetelse{#1}#2\resetglobal {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}} \def\robustdoifinsetelse#1#2% {\expanded{\convertargument#1}\to\!!stringa \expanded{\convertargument#2}\to\!!stringb \rawdoifinsetelse\!!stringa\!!stringb} \def\robustaddtocommalist#1#2% {item} \cs {\robustdoifinsetelse{#1}#2\resetglobal {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}} \def\robustpretocommalist#1#2% {item} \cs {\robustdoifinsetelse{#1}#2\resetglobal {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}} \def\xsplitstring#1#2% \cs {str} {\def\dosplitstring##1,#2,##2,#2,##3\\% {\edef\!!stringa{\bcleanedupcommalist##1\empty\empty\relax}% \edef\!!stringb{\acleanedupcommalist##2,,\relax}}% \@EA\dosplitstring\@EA,#1,,#2,,#2,\\} \def\bcleanedupcommalist#1#2#3\relax{\if#1,\else#1\fi\if#2,\else#2\fi#3} \def\bcleanedupcommalist#1#2\relax{\if#1,\else#1\fi#2} \def\acleanedupcommalist#1,,#2\relax{#1} % \def\removefromcommalist#1#2% % {\expanded{\xsplitstring\noexpand#2{#1}}% % \dodoglobal\edef#2% % {\ifx\!!stringa\empty % \!!stringb % \else % \@EA\acleanedupcommalist\!!stringa,,\relax % \ifx\!!stringb\empty\else,\!!stringb\fi % \fi}} \def\removefromcommalist#1#2% {\rawdoifinsetelse{#1}#2% {\expanded{\xsplitstring\noexpand#2{#1}}% \dodoglobal\edef#2% {\ifx\!!stringa\empty \!!stringb \else \!!stringa\ifx\!!stringb\empty\else,\!!stringb\fi \fi}} \resetglobal} %D \macros %D {substituteincommalist} %D %D Slow but seldom used, so for the moment we stick to this %D implementation. %D %D \starttyping %D \substituteincommalist{old}{new}{list} %D \stoptyping \def\substituteincommalist#1#2#3% old, new, list (slooow) {\edef\!!stringb{#1}% \edef\!!stringd{#2}% \let\!!stringa#3% \let#3\empty \def\dosubstituteincommalist##1% {\edef\!!stringc{##1}% \ifx\!!stringb\!!stringc \ifx\!!stringd\empty\else \edef#3{#3\ifx#3\empty\else,\fi\!!stringd}% \fi \def\docommando####1{\edef#3{#3,####1}}% \else \edef#3{#3\ifx#3\empty\else,\fi##1}% \fi}% \@EA\rawprocesscommacommand\@EA[\!!stringa]\dosubstituteincommalist} %D A not so useful macro: \def\dodofrontstrip[#1#2]#3% {\ifx#1\space \def#3{#2}% \else \def#3{#1#2}% \fi} \def\dofrontstrip#1% {\edef\!!stringa{#1}% \ifx\!!stringa\empty \else \@EA\dodofrontstrip\@EA[#1]#1% \fi} %D \macros %D {replaceincommalist} %D %D The next macro can be used to replace an indexed element %D in a commalist: %D %D \starttyping %D \replaceincommalist\MyList{2} %D \stoptyping %D %D Element~2 will be replaced by the current meaning of the macro %D \type {\newcommalistelement}. The old meaning is saved in %D \type {\commalistelement}. The replacement honors grouped items, %D like in: %D %D \starttyping %D \def\MyList{a,b,c,d,e,f} \replaceincommalist\MyList{3} %D \def\MyList{a,b,c,d,e,f} \replaceincommalist\MyList{3} %D \def\MyList{a,{b,c},d,e,f} \replaceincommalist\MyList{3} %D \def\MyList{a,b,c,{d,e,f}} \replaceincommalist\MyList{3} %D \stoptyping \let\newcommalistelement\empty \def\replaceincommalist#1#2% #1 = commalistelement #2 = position starts at 1 {\def\doreplaceincommalist##1% {\ifnum\commalistcounter=#2\relax \ifx\newcommalistelement\empty\else \ifx\newcommalist\empty \let\newcommalist\newcommalistelement \else \@EA\@EA\@EA\def\@EA\@EA\@EA\newcommalist\@EA\@EA\@EA {\@EA\newcommalist\@EA,\newcommalistelement}% \fi \fi \def\commalistelement{##1}% \else \ifx\newcommalist\empty \ifx\nexttoken\bgroup % is known -) \def\newcommalist{{##1}}% \else \def\newcommalist{##1}% \fi \else \ifx\nexttoken\bgroup % is known -) \@EA\def\@EA\newcommalist\@EA{\newcommalist,{##1}}% \else \@EA\def\@EA\newcommalist\@EA{\newcommalist,##1}% \fi \fi \fi \advance\commalistcounter\plusone}% \let\commalistelement\empty \let\newcommalist\empty \commalistcounter\plusone \@EA\processcommalist\@EA[#1]\doreplaceincommalist \dodoglobal\let#1\newcommalist} %D \macros %D {globalprocesscommalist} %D %D The commalist processing commands are characterized by the %D fact that the way they handle expansion as well as the fact %D that they can be nested. This makes them kind of useless for %D handling comma lists in alignments. In these situations the %D next macro can be of use. \def\globalprocesscommaitem#1,% {\if]#1\else \globalcommacommand{#1}% \expandafter\globalprocesscommaitem \fi} \def\globalprocesscommalist[#1]#2% {\global\let\globalcommacommand#2% \expandafter\globalprocesscommaitem#1,],} %D \macros %D {withoutunit,withoutpt, %D PtToCm, %D numberofpoints,dimensiontocount} %D %D We can convert point into centimeters with: %D %D \starttyping %D \PtToCm{dimension} %D \stoptyping %D %D Splitting the value and the unit is done by: \def\withoutunit#1#2% {\begingroup \scratchdimen#1\relax \@EA\convertargument\the\scratchdimen\to\asciiA \@EA\convertargument#2\to\asciiB %\@EA\@EA\@EA\beforesplitstring\@EA\asciiA\@EA\at\asciiB\to\!!stringa \@EA\beforesplitstring\@EA\asciiA\@EA\at\asciiB\to\!!stringa \!!stringa \endgroup} \def\withoutpt#1{\withoutunit{#1}{pt}} \def\withoutcm#1{\withoutunit{#1}{cm}} %D A bit faster and more robust alternative is one that %D manipulates the \CATCODES. {\catcode`\.=\@@other \catcode`\p=\@@other \catcode`\t=\@@other \gdef\WITHOUTPT#1pt{#1}} \def\withoutpt#1% {\expandafter\WITHOUTPT#1} %D The capitals are needed because \type{p} and \type{t} have %D \CATCODE~12, while macronames only permit tokens with the %D \CATCODE~11. As a result we cannot use the \type{.group} %D primitives. Those who want to know more about this kind of %D manipulations, we advice to study the \TEX book in detail. %D Because this macro does not do any assignment, we can use it %D in the following way too. \def\PtToCm#1% {\begingroup \scratchdimen#1\relax \scratchdimen0.0351459804\scratchdimen % 2.54/72.27 \withoutpt\the\scratchdimen cm% \endgroup} %D We also support: %D %D \starttyping %D \numberofpoints {dimension} %D \dimensiontocount {dimension} {\count} %D \stoptyping %D %D Both macros return a rounded number. % todo: etex version \def\numberofpoints#1% {\scratchdimen#1% \advance\scratchdimen .5pt \withoutpt\the\scratchdimen} \def\dimensiontocount#1#2% {\scratchdimen#1% \advance\scratchdimen .5pt #2\scratchdimen \divide#2 \maxcard} %D \macros %D {swapdimens,swapmacros} %D %D Simple but effective are the next two macros. There name %D exactly states their purpose. The \type{\scratchdimen} and %D \type{\!!stringa} can only be swapped when being the first %D argument. \def\swapdimens#1#2% {\scratchdimen #1\redoglobal #1#2\dodoglobal #2\scratchdimen} \def\swapmacros#1#2% {\let\!!stringa#1\redoglobal\let#1#2\dodoglobal\let#2\!!stringa} %D \macros %D {pushmacro,popmacro} %D %D Premature and a bit of beta, we offer: %D %D \starttyping %D \pushmacro\macro %D \popmacro\macro %D \stoptyping %D %D Beware: global! % \def\@s@{@s@} % % \beginTEX % % \def\globalpushmacro#1% we can use a faster incement here % {\@EA\doglobal\@EA\increment\csname\@s@*\string#1\endcsname % \global\@EA\let\csname\csname\@s@*\string#1\endcsname*\string#1\endcsname#1} % % \def\globalpopmacro#1% \global\let % {\global\@EA\let\@EA#1\csname\csname\@s@*\string#1\endcsname*\string#1\endcsname % \@EA\doglobal\@EA\decrement\csname\@s@*\string#1\endcsname} % % \def\localpushmacro#1% this one can be used to push a value over an \egroup % {\@EA\doglobal\@EA\increment\csname\@s@**\string#1\endcsname % \global\@EA\let\csname\csname\@s@**\string#1\endcsname**\string#1\endcsname#1} % % \def\localpopmacro#1% \local\let % {\@EA\let\@EA#1\csname\csname\@s@**\string#1\endcsname**\string#1\endcsname % \global\@EA\decrement\csname\@s@**\string#1\endcsname} % % \endTEX % % \beginETEX \newcount % % \def\globalpushmacro#1% % {\ifcsname\@s@*\string#1\endcsname \else % \@EA\newcount\csname\@s@*\string#1\endcsname % \fi % \global\advance\csname\@s@*\string#1\endcsname \plusone % \global\@EA\let\csname\the\csname\@s@*\string#1\endcsname*\string#1\endcsname#1} % % \def\globalpopmacro#1% \global\let % {\global\@EA\let\@EA#1\csname\the\csname\@s@*\string#1\endcsname*\string#1\endcsname % \global\advance\csname\@s@*\string#1\endcsname \minusone} % % \def\localpushmacro#1% this one can be used to push a value over an \egroup % {\ifcsname\@s@**\string#1\endcsname \else % \@EA\newcount\csname\@s@**\string#1\endcsname % \fi % \global\advance\csname\@s@**\string#1\endcsname \plusone % \global\@EA\let\csname\the\csname\@s@**\string#1\endcsname**\string#1\endcsname#1} % % \def\localpopmacro#1% \local\let % {\@EA\let\@EA#1\csname\the\csname\@s@**\string#1\endcsname**\string#1\endcsname % \global\advance\csname\@s@**\string#1\endcsname \minusone } % % \endETEX % % some 5% faster (used a lot in typescripts, so ...) \def\@sl@{@sl@} \def\@sg@{@sg@} \let\@@pushedmacro\empty \beginTEX \def\globalpushmacro#1% we can use a faster incement here {\xdef\@@pushedmacro{\string#1}% \@EA\doglobal\@EA\increment\csname\@sg@\@@pushedmacro\endcsname \global\@EA\let\csname\csname\@sg@\@@pushedmacro\endcsname\@@pushedmacro\endcsname#1} \def\globalpopmacro#1% {\xdef\@@pushedmacro{\string#1}% \global\@EA\let\@EA#1\csname\csname\@sg@\@@pushedmacro\endcsname\@@pushedmacro\endcsname \@EA\doglobal\@EA\decrement\csname\@sg@\@@pushedmacro\endcsname} \def\localpushmacro#1% this one can be used to push a value over an \egroup {\xdef\@@pushedmacro{\string#1}% \@EA\doglobal\@EA\increment\csname\@sl@\@@pushedmacro\endcsname \global\@EA\let\csname\csname\@sl@\@@pushedmacro\endcsname\@@pushedmacro\endcsname#1} \def\localpopmacro#1% {\xdef\@@pushedmacro{\string#1}% \@EA\let\@EA#1\csname\csname\@sl@\@@pushedmacro\endcsname\@@pushedmacro\endcsname \global\@EA\decrement\csname\@sl@\@@pushedmacro\endcsname} \endTEX \beginETEX \def\globalpushmacro#1% {\xdef\@@pushedmacro{\string#1}% \ifcsname\@sg@\@@pushedmacro\endcsname \else \@EA\newcount\csname\@sg@\@@pushedmacro\endcsname \fi \global\advance\csname\@sg@\@@pushedmacro\endcsname \plusone \global\@EA\let\csname\the\csname\@sg@\@@pushedmacro\endcsname\@@pushedmacro\endcsname#1} \def\globalpopmacro#1% {\xdef\@@pushedmacro{\string#1}% \global\@EA\let\@EA#1\csname\the\csname\@sg@\@@pushedmacro\endcsname\@@pushedmacro\endcsname \global\advance\csname\@sg@\@@pushedmacro\endcsname \minusone} \def\localpushmacro#1% this one can be used to push a value over an \egroup {\xdef\@@pushedmacro{\string#1}% \ifcsname\@sl@\@@pushedmacro\endcsname \else \@EA\newcount\csname\@sl@\@@pushedmacro\endcsname \fi \global\advance\csname\@sl@\@@pushedmacro\endcsname \plusone \global\@EA\let\csname\the\csname\@sl@\@@pushedmacro\endcsname\@@pushedmacro\endcsname#1} \def\localpopmacro#1% {\xdef\@@pushedmacro{\string#1}% \@EA\let\@EA#1\csname\the\csname\@sl@\@@pushedmacro\endcsname\@@pushedmacro\endcsname \global\advance\csname\@sl@\@@pushedmacro\endcsname \minusone } \endETEX % \let\pushmacro\globalpushmacro % \let\popmacro \globalpopmacro \let\pushmacro\localpushmacro \let\popmacro \localpopmacro %D \macros %D {setlocalhsize} %D %D Sometimes we need to work with the \type{\hsize} that is %D corrected for indentation and left and right skips. The %D corrected value is available in \type{\localhsize}, which %D needs to be calculated with \type{\setlocalhsize} first. %D %D \starttyping %D \setlocalhsize \hbox to \localhsize{...} %D \setlocalhsize[-1em] \hbox to \localhsize{...} %D \setlocalhsize[.5ex] \hbox to \localhsize{...} %D \stoptyping %D %D These examples show us that an optional can be used. The %D value provided is added to \type{\localhsize}. \newdimen\localhsize \def\complexsetlocalhsize[#1]% don't change ! {\localhsize\hsize \ifnum\hangafter<\zerocount \advance\localhsize\ifdim\hangindent>\zeropoint-\fi\hangindent \fi \advance\localhsize -\leftskip \advance\localhsize -\rightskip \advance\localhsize #1\relax} \def\simplesetlocalhsize {\complexsetlocalhsize[\zeropoint]} \definecomplexorsimple\setlocalhsize %D \macros %D {processtokens} %D %D We fully agree with (most) typographers that inter||letter %D spacing is only permitted in fancy titles, we provide a %D macro that can be used to do so. Because this is %D (definitely and fortunately) no feature of \TEX, we have to %D step through the token list ourselves. %D %D \starttyping %D \processtokens {before} {between} {after} {space} {tokens} %D \stoptyping %D %D An example of a call is: %D %D \startbuffer %D \processtokens {[} {+} {]} {\space} {hello world} %D \stopbuffer %D %D \typebuffer %D %D This results in: %D %D \getbuffer %D %D The list of tokens may contain spaces, while \type{\\}, %D \type{{}} and \type{\ } are handled as space too. % \def\dodoprocesstokens% % {\ifx\next\lastcharacter % \after % \let\next=\relax % \else\ifx\next\bgroup % \def\next% % {\dowithnextbox % {\before\box\nextbox % \let\before=\between % \doprocesstokens} % \hbox\bgroup}% % \else % \expandafter\if\space\next % \before\white % \else % \before\next % \fi % \let\before=\between % \let\next=\doprocesstokens % \fi\fi % \next} % % \def\doprocesstokens% the space after = is essential % {\afterassignment\dodoprocesstokens\let\next= } % % \def\processtokens#1#2#3#4#5% % {\bgroup % \def\lastcharacter{\lastcharacter}% % \def\space{ }% % \let\\=\space % \def\before{#1}% % \def\between{#2}% % \def\after{#3}% % \def\white{#4}% % \doprocesstokens#5\lastcharacter % \egroup} \def\dodoprocesstokens {\ifx\nextprocessedtoken\lastcharacter \after \let\nextprocessedtoken\relax \else\ifx\nextprocessedtoken\bgroup \def\nextprocessedtoken {\dowithnextbox {\before{\copy\nextbox}% \before can use nextbox several times \let\before\between \doprocesstokens} \hbox\bgroup}% \else \expandafter\if\space\nextprocessedtoken \after\white \let\before\savedbefore \else \before\nextprocessedtoken \let\before\between \fi \let\nextprocessedtoken\doprocesstokens \fi\fi \nextprocessedtoken} \def\doprocesstokens% the space after = is essential {\afterassignment\dodoprocesstokens\let\nextprocessedtoken= } \def\processtokens#1#2#3#4#5% {\begingroup \def\lastcharacter{\lastcharacter}% \def\space{ }% \let\\=\space \def\before{#1}% \def\between{#2}% \def\after{#3}% \def\white{#4}% \let\savedbefore\before \doprocesstokens#5\lastcharacter \endgroup} %D \macros %D {doifvalue,doifnotvalue,doifelsevalue, %D doifnothing,doifsomething,doifelsenothing, %D doifvaluenothing,doifvaluesomething,doifelsevaluenothing} %D %D These long named \type{\if} commands can be used to access %D macros (or variables) that are normally accessed by using %D \type{\getvalue}. Using these alternatives safes us three %D tokens per call. Anyone familiar with the not||values %D ones, can derive their meaning from the definitions. \def\doifvalue#1{\doif {\csname#1\endcsname}} \def\doifnotvalue#1{\doifnot {\csname#1\endcsname}} \def\doifelsevalue#1{\doifelse{\csname#1\endcsname}} \def\doifnothing#1{\doif {#1}{}} \def\doifsomething#1{\doifnot {#1}{}} \def\doifelsenothing#1{\doifelse{#1}{}} \def\doifvaluenothing#1{\doif {\csname#1\endcsname}{}} \def\doifvaluesomething#1{\doifnot {\csname#1\endcsname}{}} \def\doifelsevaluenothing#1{\doifelse{\csname#1\endcsname}{}} %D Faster but spoiling inheritance (copying parameters): %D %D \starttyping %D \def\doifelsevaluesomething#1#2#3% %D {\expandafter\ifx\csname#1\endcsname\empty#3\else#2\fi} %D %D \def\doifvaluesomething#1#2% %D {\expandafter\ifx\csname#1\endcsname\empty\else#2\fi} %D %D \def\doifvaluenothing#1#2% %D {\expandafter\ifx\csname#1\endcsname\empty#2\fi} %D \stoptyping %D %D Slightly more efficient: \def\doifnothing{\doif \empty} \def\doifsomething{\doifnot \empty} \def\doifelsenothing{\doifelse\empty} %D The somewhat faster alternatives are: \long\def\doifvalue#1#2% {\edef\!!stringa{\csname#1\endcsname}\edef\!!stringb{#2}% \ifx\!!stringa\!!stringb \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \long\def\doifnotvalue#1#2% {\edef\!!stringa{\csname#1\endcsname}\edef\!!stringb{#2}% \ifx\!!stringa\!!stringb \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \long\def\doifelsevalue#1#2% {\edef\!!stringa{\csname#1\endcsname}\edef\!!stringb{#2}% \ifx\!!stringa\!!stringb \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \long\def\doifnothing#1% {\edef\!!stringa{#1}% \ifx\!!stringa\empty \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \long\def\doifsomething#1% {\edef\!!stringa{#1}% \ifx\!!stringa\empty \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \long\def\doifelsenothing#1% {\edef\!!stringa{#1}% \ifx\!!stringa\empty \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \long\def\doifvaluenothing#1% {\edef\!!stringa{\csname#1\endcsname}% \ifx\!!stringa\empty \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \long\def\doifvaluesomething#1% {\edef\!!stringa{\csname#1\endcsname}% \ifx\!!stringa\empty \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \long\def\doifelsevaluenothing#1% {\edef\!!stringa{\csname#1\endcsname}% \ifx\!!stringa\empty \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} %D \macros %D {doifemptyelsevalue, doifemptyvalue, doifnotemptyvalue} %D %D Also handy: \def\doifemptyelsevalue#1% {\@EA\ifx\csname#1\endcsname\empty \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \def\doifemptyvalue#1% {\@EA\ifx\csname#1\endcsname\empty \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \def\doifnotemptyvalue#1% {\@EA\ifx\csname#1\endcsname\empty \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} %D \macros %D {doifallcommonelse} %D %D A complete match of two sets can be tested with %D \type {\doifallcommonelse}, where the first two %D arguments are sets. %\def\doifallcommonelse#1#2#3#4% % {\def\p!docommoncheck##1% % {\doifnotinset{##1}{#2}{\donefalse}% % \ifdone\else\quitcommalist\fi}% % \donetrue % \processcommalist[#1]\p!docommoncheck % \ifdone#3\else#4\fi} \def\@@doifallcommonelse#1#2#3#4% slow {\def\p!docommoncheck##1% {\doifnotinset{##1}{#4}\donefalse \ifdone\else\expandafter\quitcommalist\fi}% \donetrue \processcommalist[#3]\p!docommoncheck \ifdone\expandafter#1\else\expandafter#2\fi} \def\doifallcommonelse {\@@doifallcommonelse\firstoftwoarguments\secondoftwoarguments} \def\doifallcommon {\@@doifallcommonelse\firstofonearguments\gobbleoneargument} \def\doifnotallcommon {\@@doifallcommonelse\gobbleoneargument\firstofonearguments} %D \macros %D {DOIF,DOIFELSE,DOIFNOT} %D %D \TEX\ is case sensitive. When comparing arguments, this %D feature sometimes is less desirable, for instance when we %D compare filenames. The next three alternatives upcase their %D arguments before comparing them. %D %D \starttyping %D \DOIF {string1} {string2} {...} %D \DOIFNOT {string1} {string2} {...} %D \DOIFELSE {string1} {string2} {then ...}{else ...} %D \stoptyping %D %D We have to use a two||step implementation, because the %D expansion has to take place outside \type{\uppercase}. \def\p!DOIF#1#2% {\uppercase{\ifinstringelse{$#1$}{$#2$}}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \def\p!DOIFNOT#1#2% {\uppercase{\ifinstringelse{$#1$}{$#2$}}% \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \def\p!DOIFELSE#1#2% {\uppercase{\ifinstringelse{$#1$}{$#2$}}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \def\p!DOIFINSTRINGELSE#1#2% {\uppercase{\ifinstringelse{#1}{#2}}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \def\DOIF {\ExpandBothAfter\p!DOIF} \def\DOIFNOT {\ExpandBothAfter\p!DOIFNOT} \def\DOIFELSE {\ExpandBothAfter\p!DOIFELSE} \def\DOIFINSTRINGELSE {\ExpandBothAfter\p!DOIFINSTRINGELSE} %D \macros %D {stripcharacters,stripspaces} %D %D The next command was needed first when we implemented %D the \CONTEXT\ interactivity macros. When we use labeled %D destinations, we often cannot use all the characters we %D want. We therefore strip some of the troublemakers, like %D spaces, from the labels before we write them to the %D \DVI||file, which passes them to for instance a PostScript %D file. %D %D \starttyping %D \stripspaces\from\one\to\two %D \stoptyping %D %D Both the old string \type{\one} and the new one \type{\two} %D are expanded. This command is a special case of: %D %D \starttyping %D \stripcharacter\char\from\one\to\two %D \stoptyping %D %D As we can see below, spaces following a control sequence are %D to enclosed in \type{{}}. % keep this one: % % \def\stripcharacter#1\from#2\to#3% % {\def\dostripcharacter##1#1##2\end % {\edef\!!strippedstring{\!!strippedstring##1}% % \doifnotempty{##2}{\dostripcharacter##2\end}}% % \let\!!strippedstring\empty % \edef\!!stringa{#2}% % \@EA\dostripcharacter\!!stringa#1\end % \dodoglobal\let#3\!!strippedstring} % % the following is better (comes from syst-loc): \def\stripcharacter#1\from#2\to#3% {\def\dostripcharacter##1#1##2\end {\edef\!!strippedstring{\!!strippedstring##1}% \doifnotempty{##2}{\dostripcharacter##2\end}}% \let\!!strippedstring\empty \edef\!!stringa{#2}% \@EA\dostripcharacter\!!stringa#1\end \dodoglobal\let#3\!!strippedstring} \def\stripspaces\from#1\to#2% will become \unspacestring#1\from#2 {\stripcharacter{ }\from#1\to#2} %D \macros %D {unspacestring} %D %D The next macro does the same but is more compatible with other macros, %D like \type {\convert...}. \def\unspacestring#1\to#2% {\stripcharacter{ }\from#1\to#2} %D \macros %D {executeifdefined} %D %D \CONTEXT\ uses one auxiliary file for all data concerning %D tables of contents, references, two||pass optimizations, %D sorted lists etc. This file is loaded as many times as %D needed. During such a pass we skip the commands thate are of %D no use at that moment. Because we don't want to come into %D trouble with undefined auxiliary commands, we call the %D macros in a way similar to \type{\getvalue}. The next macro %D take care of such executions and when not defined, gobbles %D the unwanted arguments. %D %D \starttyping %D \executeifdefined{name}\gobbleoneargument %D \stoptyping %D %D We can of course gobble more arguments using the %D appropriate gobbling command. \newif\ifexecuted % general purpose \def\executeifdefined#1#2% {\ifundefined{#1}% \def\next{#2}% \else \def\next{\getvalue{#1}}% \fi \next} %D Just for fun I times the next alternative: it was roughly %D timed about 15\% faster than the default (10+ sec to 9 sec)! \beginTEX \def\executeifdefined#1% #2 / never change this one again {\ifundefined{#1}% \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi {\csname#1\endcsname}} \endTEX \beginETEX \def\executeifdefined#1% #2 / never change this one again {\ifcsname#1\endcsname \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi {\csname#1\endcsname}} \endETEX %D This one also has the advantage that it is fully %D expandable and that it can be used after an assignment. %D \macros %D {executeifdefinedcs} %D %D An also fully expandable variant is the following: %D %D \starttyping %D \executeifdefinedcs{a}{b} %D \stoptyping %D %D In dit geval zijn beide argumenten csnames. \def\executeifdefinedcs#1#2% {\csname\ifundefined{#1}#2\else#1\fi\endcsname} %D We considered an alternative implementation accepting %D commands directly, like: %D %D \starttyping %D \executeifdefined\name\gobblefivearguments %D \stoptyping %D %D For the moment we don't need this one, so we stick to the %D faster one. %D \macros %D {executeandforget} %D %D The following macros were requested by Simon. Watch the %D global variant. %D %D \starttyping %D \executeandforget\SomeCommand %D \doglobal\executeandforget\AnotherCommand %D \stoptyping \def\executeandforget#1% {\global\let\@@expanded#1% \dodoglobal\let#1\relax \@@expanded} %D \macros %D {doifsomespaceelse} %D %D The next command checks a string on the presence of a space %D and executed a command accordingly. %D %D \starttyping %D \doifsomespaceelse {tekst} {then ...} {else ...} %D \stoptyping %D %D We use this command in \CONTEXT\ for determing if an %D argument must be broken into words when made interactive. %D Watch the use of \type{\noexpand}. %D Is this one still needed? % \long\def\doifsomespaceelse#1#2#3% % {\def\p!doifsomespaceelse##1 ##2##3\war% % {\if\noexpand##2@#3\else#2\fi}% % \p!doifsomespaceelse#1 @ @\war} \def\p!doifsomespaceelse#1 #2#3\war{\if\noexpand#2@} \long\def\doifsomespaceelse#1% % #2#3% {\p!doifsomespaceelse#1 @ @\war % #3\else#2\fi} \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} %D \macros %D {adaptdimension,balancedimensions} %D %D Again we introduce some macros that are closely related to %D an interface aspect of \CONTEXT. The first command can be %D used to adapt a \DIMENSION. %D %D \starttyping %D \adaptdimension {dimension} {value} %D \stoptyping %D %D When the value is preceed by a \type{+} or minus, the %D dimension is advanced accordingly, otherwise it gets the %D value. % \def\doadaptdimension#1#2\\#3\\% % {\if#1+% % \dodoglobal\advance#3 #1#2\relax % \else\if#1-% % \dodoglobal\advance#3 #1#2\relax % \else % \dodoglobal#3=#1#2\relax % \fi\fi} % % more fuzzy but also more efficient \def\doadaptdimension#1#2\\#3\\% {\if#1+% \dodoglobal\advance \else\if#1-% \dodoglobal\advance \else \dodoglobal \fi\fi #3 #1#2\relax} \def\adaptdimension#1#2% {\expandafter\doadaptdimension#2\\#1\\} %D A second command takes two \DIMENSIONS. Both are adapted, %D depending on the sign of the given value. %D maat. This time we take the value as it is, and don't look %D explicitly at the preceding sign. %D %D \starttyping %D \balancedimensions {dimension 1} {dimension 2} {value} %D \stoptyping %D %D When a positive value is given, the first dimension is %D incremented, the second ond is decremented. A negative value %D has the opposite result. \def\balancedimensions#1#2#3% {\scratchdimen#3\relax \redoglobal\advance#1 \scratchdimen \dodoglobal\advance#2 -\scratchdimen} %D Both commands can be preceded by \type{\doglobal}. Here we %D use \type{\redo} first, because \type{\dodo} resets the %D global character. %D \macros %D {processseparatedlist} %D %D Maybe a bit late, but here is a more general version of the %D \type{\processcommalist} command. This time we don't handle %D nesting but accept arbitrary seperators. %D %D \starttyping %D \processseparatedlist[list][separator]\command %D \stoptyping %D %D One can think of things like: %D %D \starttyping %D \processseparatedlist[alfa+beta+gamma][+]\message %D \stoptyping %D First we show the simple alternative: %D %D \starttyping %D \def\processseparatedlist[#1][#2]#3% %D {\def\doprocessseparatedlist##1##2#2% %D {\if]##1% %D \let\next=\relax %D \else\if]##2% %D \let\next=\relax %D \else\ifx\blankspace##2% %D #3{##1}% %D \let\next=\doprocessseparatedlist %D \else %D #3{##1##2}% %D \let\next=\doprocessseparatedlist %D \fi\fi\fi %D \next}% %D \doprocessseparatedlist#1#2]#2} %D \stoptyping %D %D However, we want to handle all situations, like: %D %D \startbuffer %D \processseparatedlist[{aap noot}] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} %D \processseparatedlist[{aap} {noot}][ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} %D \processseparatedlist[aap {noot}] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} %D \processseparatedlist[aap noot] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} %D \stopbuffer %D %D \typebuffer \getbuffer %D %D Therefore we smuggle a \type {\relax} in front of the %D argument, which we remove afterwards. \def\doprocessseparatedlist#1]#2[#3]#4% {\def\dodoprocessseparatedlist##1##2#3% {\if]##1% \let\dodoprocessseparatedlist\relax \else\if]##2% \let\dodoprocessseparatedlist\relax \else\ifx\blankspace##2% #4{##1}% \else #4{##1##2}% \fi\fi\fi \dodoprocessseparatedlist}% \@EA\dodoprocessseparatedlist\gobbleoneargument#1#3]#3} \def\processseparatedlist[% {\doprocessseparatedlist\relax} %D \macros %D {processlist} %D %D An even more general list processing macro is the %D following one: %D %D \starttyping %D \processlist{beginsym}{endsym}{separator}\docommando list %D \stoptyping %D %D This one supports arbitrary open and close symbols as well %D as user defined separators. %D %D \starttyping %D \processlist(){=>}\docommando(a=>b=>c=>d) %D \stoptyping \long\def\processlist#1#2#3#4% no blank skipping ! {\def\doprocesslist##1#2% {\def\dodoprocesslist####1####2#3% {\ifx#2####1% \let\dodoprocesslist\relax \else\ifx#2####2% \let\dodoprocesslist\relax \else #4{####1####2}% \fi\fi \dodoprocesslist}% \expandafter\dodoprocesslist\gobbleoneargument##1#3#2#3}% \def\dodoprocesslist#1% {\doprocesslist\relax}% \dodoprocesslist} % %D \macros % %D {dohonorgroupedargument} % %D % %D The previous macro uses yet another auxiliary macro to % %D handle the special case. % % \def\dohonorgroupedargument#1[% % {\doifnextcharelse\bgroup{\dodohonorgroupedargument#1}{#1[}} % % \def\dodohonorgroupedargument#1#2% % {#1[{{#2}}} %D \macros %D {processassignlist} %D %D Is possible to combine an assignment list with one %D containing keywords. Assignments are treated accordingly, %D keywords are treated by \type{\command}. %D %D \starttyping %D \processassignlist[...=...,...=...,...]\commando %D \stoptyping %D %D This command can be integrated in \type{\getparameters}, but %D we decided best not to do so. \def\processassignlist#1[#2]#3% {\def\p!dodogetparameter[##1=##2=##3]% {\doifnot{##3}\relax{#3{##1}}}% \def\p!dogetparameter##1% {\p!dodogetparameter[##1==\relax]}% \processcommalist[#2]\p!dogetparameter} % too ugly % % %D \macros % %D {DoAfterFi,DoAfterFiFi} % %D % %D Sometimes \type{\fi}'s can get into the way. We can reach % %D over such a troublemaker with: % %D % %D \starttyping % %D \DoAfterFi{some commands} % %D \DoAfterFiFi{some commands} % %D \stoptyping % %D % %D It saves us a \type{\next} construction. Skipping % %D \type{\else...\fi} is more tricky, so this one is not % %D provided. % % \def\DoAfterFi#1\fi{\fi#1} % \def\DoAfterFiFi#1\fi#2\fi{\fi\fi#1} %D \macros %D {untextargument %D untexcommand} %D %D When manipulating data(bases) and for instance generating %D index entries, the next three macros can be of help: %D %D \starttyping %D \untextargument{...}\to\name %D \untexcommand {...}\to\name %D \stoptyping %D %D They remove braces and backslashes and give us something to %D sort. \def\untexsomething {\begingroup \catcode`\{=\@@ignore \catcode`\}=\@@ignore \escapechar\minusone \dountexsomething} \long\def\dountexsomething#1#2\to#3% {\doglobal#1#2\to\untexedargument \endgroup \let#3\untexedargument} \def\untexargument{\untexsomething\convertargument} \def\untexcommand {\untexsomething\convertcommand} %D \macros %D {ScaledPointsToBigPoints,ScaledPointsToWholeBigPoints} %D %D One characteristic of \POSTSCRIPT\ and \PDF\ is that both %D used big points (\TEX's bp). The next macros convert points %D and scaled points into big points. %D %D \starttyping %D \ScaledPointsToBigPoints {number} \target %D \ScaledPointsToWholeBigPoints {number} \target %D \stoptyping %D %D The magic factor $72/72.27$ can be found in most \TEX\ %D related books. % \def\ScaledPointsToBigPoints #1{\PointsToBigPoints {#1sp}} % \def\ScaledPointsToWholeBigPoints#1{\PointsToWholeBigPoints{#1sp}} % % \def\PointsToBigPoints#1#2% % {\scratchdimen#1% % \scratchdimen.996264\scratchdimen % \edef#2{\withoutpt\the\scratchdimen}} % % \def\PointsToWholeBigPoints#1#2% % {\scratchdimen#1% % \scratchdimen.996264\scratchdimen % \scratchcounter\scratchdimen % \advance\scratchcounter \medcard % \divide\scratchcounter \maxcard % \edef#2{\the\scratchcounter}} % \PointsToBigPoints{10.53940pt}\test \test % \PointsToBigPoints{10.53941pt}\test \test % \PointsToBigPoints{10.53942pt}\test \test % \PointsToWholeBigPoints{10.53940pt}\test \test % \PointsToWholeBigPoints{10.53941pt}\test \test % \PointsToWholeBigPoints{10.53942pt}\test \test \beginTEX \def\PointsToBigPoints#1#2% {\scratchdimen#1% \scratchdimen.996264\scratchdimen \edef#2{\withoutpt\the\scratchdimen}} \def\PointsToWholeBigPoints#1#2% {\scratchdimen#1% \scratchdimen.996264\scratchdimen \scratchcounter\scratchdimen \advance\scratchcounter \medcard \divide\scratchcounter \maxcard \edef#2{\the\scratchcounter}} \endTEX \beginETEX \dimexpr \numexpr % \def\PointsToBigPoints#1#2% % {\edef#2{\withoutpt\the\dimexpr(.996264\dimexpr(#1))}} % \def\PointsToWholeBigPoints#1#2% % {\edef#2{\the\numexpr(\numexpr(\dimexpr(.996264\dimexpr(#1)))/\maxcard)}} \def\PointsToBigPoints#1#2% {\edef#2{\withoutpt\the\dimexpr.996264\dimexpr#1\relax\relax}} \def\PointsToWholeBigPoints#1#2% {\edef#2{\the\numexpr\dimexpr.996264\dimexpr#1\relax\relax/\maxcard\relax}} \endETEX \def\ScaledPointsToBigPoints #1{\PointsToBigPoints {\number#1\scaledpoint}} \def\ScaledPointsToWholeBigPoints#1{\PointsToWholeBigPoints{\number#1\scaledpoint}} %D \macros %D {PointsToReal} %D %D Points can be stripped from their suffix by using %D \type{\withoutpt}. The next macro enveloppes this macro. %D %D \starttyping %D \PointsToReal {dimension} \target %D \stoptyping \def\PointsToReal#1#2% {\scratchdimen#1% \edef#2{\withoutpt\the\scratchdimen}} %D \macros %D {dontleavehmode} %D %D Sometimes when we enter a paragraph with some command, the %D first token gets the whole first line. We can prevent this %D by saying: %D %D \starttyping %D \dontleavehmode %D \stoptyping %D %D This command is used in for instance the language module %D \type{lang-ini}. The first version was: %D %D \starttyping %D \def\dontleavehmode{\ifhmode\else\ifmmode\else$ $\fi\fi} %D \stoptyping %D %D Next, Taco came with a better alternative (using mathsurround): %D %D \starttyping %D \def\dontleavehmode %D {\ifhmode\else \ifmmode\else %D {\mathsurround\zeropoint\everymath\emptytoks$ $}% %D \fi \fi} %D \stoptyping %D %D And finaly we got the following alternative, one that avoids %D interfering grouping at the cost of a box. \newbox\@@dlhbox \unexpanded \def\dontleavehmode {\ifhmode\else \ifmmode\else \setbox\@@dlhbox\hbox{\mathsurround\zeropoint\everymath\emptytoks$ $}\unhbox\@@dlhbox \fi \fi} % Also ok, but more sensitive to lookahead expansion is: % % \def\dontleavehmode{\ifvmode \indent \fi} % % which assumes indent is kept unchanged. Protecting the macro is only % possible in etex (watch out: \unexpanded in context is eq to \protected). % % \unexpanded \def\dontleavehmode{\ifvmode \indent \fi} % functional spec TH %D But, if you run a recent version of \TEX, we can use the new %D primitive: \ifx\normalquitvmode\undefined \else \let\dontleavehmode\normalquitvmode \fi %D \macros %D {uppercasestring,lowercasestring} %D %D The names tell what they do: %D %D \starttyping %D \uppercasestring somestring\to\somestring %D \lowercasestring somestring\to\somestring %D \stoptyping %D %D the first argument may be a \type{\macro}. \def\uppercasestring#1\to#2% first @EA redundant {\edef#2{#1}\@EA\uppercase\@EA{\@EA\dodoglobal\@EA\edef\@EA#2\@EA{#2}}} \def\lowercasestring#1\to#2% first @EA redundant {\edef#2{#1}\@EA\lowercase\@EA{\@EA\dodoglobal\@EA\edef\@EA#2\@EA{#2}}} %D \macros %D {handletokens} %D %D With the next macro we enter a critical area of macro %D expansion. What we want is a macro that looks like: %D %D \starttyping %D \handletokens some tokens\with \somemacro %D \stoptyping %D %D At first sight the next implementation will suffice, but %D running this one shows that we loose the spaces. This is no %D surprise because we grab arguments and spaces preceding those %D are just ignored. %D %D \starttyping %D \def\nohandletokens#1\end% %D {} %D %D \def\dohandletokens#1#2\end% %D {\ifx#1\endoftoken %D \expandafter\nohandletokens %D \else %D \docommando{#1}% %D \expandafter\dohandletokens %D \fi %D #2\end} %D %D \long\def\handletokens#1\with#2% %D {\let\docommando=#2\relax %D \dohandletokens#1\endoftoken\end} %D \stoptyping %D %D A second approach therefore grabs the individual characters %D by using \type{\afterassignment}, in which case the space is %D read in as space. %D %D \starttyping %D \def\dodohandletokens% %D {\ifx\next\end \else %D \docommando{\next}% %D \expandafter\dohandletokens %D \fi} %D %D \def\dohandletokens %D {\afterassignment\dodohandletokens\let\next= } %D %D \long\def\handletokens#1\with#2% %D {\let\docommando=#2% %D \dohandletokens#1\end} %D \stoptyping %D A bonus example: %D %D \starttyping %D \hbox{\handletokens tekst en meer tekst\with\ruledhbox} %D %D \def\weetikveel#1{\if#1\blankspace\space\else\ruledhbox{#1}\fi} %D %D \hbox{\handletokens tekst en meer tekst\with\weetikveel} %D \stoptyping %D \macros %D {counttoken,counttokens} %D %D For the few occasions that we want to know the number of %D specific tokens in a string, we can use: %D %D \starttyping %D \counttoken token\in string\to \count %D \counttokens string\to \count %D \stoptyping %D %D This macro, that for instance is used in \type{cont-tab}, %D takes a real counter. The macro can be preceded by \type %D {\doglobal}. \def\counttoken#1\in#2\to#3% {\redoglobal#3\zerocount \def\!!stringa{#1}% \def\!!stringb{\end}% \def\docounttoken##1% obeys {} {\def\!!stringc{##1}% \ifx\!!stringb\!!stringc \else \ifx\!!stringa\!!stringc \dodoglobal\advance#3 \plusone \fi \expandafter\docounttoken \fi}% \docounttoken#2\end \resetglobal} % \def\counttoken#1\in#2\to#3% % {\redoglobal#3\zerocount % \def\!!stringa{#1}% % \def\docounttoken##1% obeys {} % {\def\!!stringb{##1}% % \ifx\!!stringa\!!stringb % \dodoglobal\advance#3\plusone % \fi}% % \handletokens#1\with\docounttoken % \resetglobal} \def\counttokens#1\to#2% {\redoglobal#2\zerocount \def\docounttoken##1{\dodoglobal\advance#2\plusone}% \handletokens#1\with\docounttoken \resetglobal} %D \macros %D {splitofftokens} %D %D Running this one not always gives the expected results. %D Consider for instance the macro for which I originally %D wrote this token handler. \long\def\splitofftokens#1\from#2\to#3% {\ifnum#1>\zerocount \scratchcounter#1\relax \def\dosplitofftokens##1% {\ifnum\scratchcounter>\zerocount \advance\scratchcounter \minusone \edef#3{#3##1}% \fi}% % \let#3\empty % #3 can be #2, so: \@EA\let\@EA#3\@EA\empty \@EA\handletokens#2\with\dosplitofftokens \else \edef#3{#2}% \fi} %D This macro can be called like: %D %D \startbuffer[example] %D \splitofftokens10\from01234567 890123456789\to\test [\test] %D \stopbuffer %D %D However, the characters that we expect to find in %D \type{\test} just don;t show up there. The reason for this %D is not that logical but follows from \TEX's sometimes %D mysterious way of expanding. Look at this: %D %D \startbuffer[next] %D \def\next{a} \edef\test{\next} [\test] %D \let\next=b \edef\test{\test\next} [\test] %D \let\next=c \edef\test{\next} [\test] %D \let\next=d \edef\test{\test\next} [\test] %D \let\next=e \@EA\edef\@EA\test\@EA{\test\next} [\test] %D \stopbuffer %D %D \typebuffer[next] %D %D Careful reading shows that inside an \type{\edef} macro's %D that are \type{\let} are not expanded! %D %D \unprotect\getbuffer[next]\protect %D %D That's why we finally end up with a macro that looks %D ahead by using an assignment, this time by using \type %D {\futurelet}, and grabbing an argument as well. That %D way we can handle the sentinal, a blank space and grouped %D tokens. \def\dohandletokens % \nexthandledtoken is part of interface {\futurelet\nexthandledtoken\dodohandletokens} \long\def\handletokens#1\with#2% {\gdef\dododohandletokens{#2}% permits more complex #2's \dohandletokens#1\end} %D A previous version said \type{\docommando=#2}, but to enable %D use in alignments, I decided to use another placeholder, one %D that is not sensitive to the global assignment. %D This alternatives does not handle grouped tokens well, so %D next we had (for a short moment): %D %D \starttyping %D \def\dodohandletokens#1% %D {\ifx\nexthandledtoken\blankspace %D \dododohandletokens{ }% %D \fi %D \ifx#1\end \else %D \dododohandletokens{#1}% %D \expandafter\dohandletokens %D \fi} %D \stoptyping %D %D This one failed on a trailing space, something we %D encounter in \JAVASCRIPT\ cleaning. %D %D \starttyping %D \def\dodohandletokens#1% %D {\ifx\nexthandledtoken\blankspace %D \dododohandletokens{ }% %D \fi %D \ifx\nexthandledtoken\end \else %D \dododohandletokens{#1}% %D \expandafter\dohandletokens %D \fi} %D \stoptyping %D %D So, now we have: \def\dodohandletokens % can be sped up {\ifx\nexthandledtoken\blankspace \def\next * {\dododohandletokens{ }\dohandletokens}% \else\ifx\nexthandledtoken\end \let\next\gobbletwoarguments \else \long\def\next *##1{\dododohandletokens{##1}\dohandletokens}% \fi\fi \next *} %D This macro is tested on: %D %D \def\xxx#1{[#1]} %D %D \startlines %D \handletokens abc\with\xxx %D \handletokens a b c\with\xxx %D \handletokens a b c\with\xxx %D \handletokens a{bc}d\with\xxx %D \handletokens a\space bc \with\xxx %D \stoplines %D %D And our previous example shows up as: %D %D \getbuffer[example] %D \macros %D {iftrialtypesetting} %D %D The next boolean is at first sight a strange one. Sometimes %D one does a trial typesetting run, for instance to determine %D dimensions. Some mechanisms, like object inclusion, can fail %D on such trials. Temporary setting the next boolean to true, %D helps a lot. \newif\iftrialtypesetting %D \macros %D {startlocal, startglobal} %D %D The next four macros are rather self explaining: %D %D \starttyping %D \startlocal %D whatever assignments %D \stoplocal %D %D \startglobal %D whatever assignments %D \stopglobal %D \stoptyping %D %D These macros are meant for those who know the difference %D between local and global assignments and are aware of the %D possible unwanted side effect \def\dostartglobaldefs#1#2% {\edef\!!stringa{\the\globaldefs}% \ifnum\globaldefs#10 \globaldefs-\globaldefs \fi \advance\globaldefs#21 \letvalue{@gd@\the\globaldefs}\!!stringa} \def\dostopglobaldefs% {\doifdefinedelse{@gd@\the\globaldefs} {\globaldefs\getvalue{@gd@\the\globaldefs}\relax} {\globaldefs\zerocount}} \def\startlocal {\dostartglobaldefs>-} \def\stoplocal {\dostopglobaldefs} \def\startglobal {\dostartglobaldefs<+} \def\stopglobal {\dostopglobaldefs} %D \macros %D {twodigitrounding} %D %D When using \type {\special}s or \type {\pdfliteral}s, it %D sometimes makes sense to limit the precission. The next %D macro rounds a real number to two digits. It takes one %D argument and only works in \ETEX. \beginTEX \def\twodigitrounding#1{#1} \endTEX \beginETEX \dimexpr \def\dotwodigitrounding#1.#2#3#4\relax% {\ifx#2*#1\else#1.#2#3\fi} % \def\twodigitrounding#1% % {\@EA\@EA\@EA\dotwodigitrounding\@EA\WITHOUTPT % \the\dimexpr#1pt+.005pt\relax000.*00\relax} \def\twodigitrounding#1% {\@EA\@EA\@EA\dotwodigitrounding\@EA\WITHOUTPT \the\dimexpr#1\points+.005\points\relax000.*00\relax} \endETEX %D \macros %D {processcontent} %D %D This is the first occasion where \TEX\ and \ETEX\ are no %D longer compatible, although in many cases things go ok. %D Beware of verbatim, i.e. catcode changes. %D %D \starttyping %D \def\starthans% %D {\processcontent{stophans}\test{\message{\test}\wait}} %D \stoptyping %D %D This macro is first used in the tabulation macros. \def\processcontent#1% {\begingroup\@EA\doprocesscontent\csname#1\endcsname} %\beginTEX \def\doprocesscontent#1#2#3% {\long\def\doprocesscontent##1#1% {\endgroup\long\def#2{##1}#3}% \doprocesscontent} %\endTEX % Hm. Side effect, spaces after \type{\test} in verbatim. % must set eof token %\beginETEX \scantokens % %\def\doprocesscontent#1#2#3% % {\long\def\doprocesscontent##1#1% % {\egroup\long\def#2{\scantokens{##1}}#3}% % \doprocesscontent} % %\endETEX %D \macros %D {dogobblesingleempty, dogobbledoubleempty} %D %D These two macros savely grab and dispose two arguments. \def\dogobblesingleempty{\dosingleempty\dodogobblesingleempty} \def\dogobbledoubleempty{\dodoubleempty\dodogobbledoubleempty} \def\dodogobblesingleempty [#1]{} \def\dodogobbledoubleempty[#1][#2]{} \let\gobblesingleempty\dogobblesingleempty % also used \let\gobbledoubleempty\dogobbledoubleempty % also used %D \macros %D {sortcommalist,sortcommacommand, %D donumericcompare,comparedresult} %D %D Sometimes we need to sort a commalist, so here is Taco's %D solution. This will in many cases be a list that is stored %D in a \type{\csname}, so both commalist and commacommands are %D supported. The sorting algorithm is very simple, so the list %D should not be too long or sorting will be very slow. %D %D \starttyping %D \sortcommalist[10,2,4,5,6,1,2,3,4,10,20]\donumericcompare %D %D \def\test{10,2,4,5,6,1,2,3,4,10,20} %D %D \sortcommacommand[\test]\donumericcompare %D \stoptyping %D %D In both cases, the result is available in the macro \type %D {\sortedcommalist}. %D %D Parameter \type{#2} is a macro that should accept two %D parameters, and it has to decide which one is larger, by %D setting the counter \type{\comparedresult} to~0 (for equal), %D 1~(if it's first argument is larger), or~2 (if it's second %D argument is larger). %D %D As said, these macro are largely written by Taco, and are %D (maybe therefore) also the first application of \type %D {\replaceincommalist}. \newcount\comparedresult \def\sortcommacommand[#1]% {\@EA\sortcommalist\@EA[#1]} \def\sortcommalist[#1]#2% {\getcommalistsize[#1]% \ifnum\commalistsize>1 \let\sortedcommalist\empty \let\comparecommand#2% \processcommalist[#1]\dosortcommacommand \else \def\sortedcommalist{#1}% \fi} \def\dosortcommacommand#1% {\ifx\sortedcommalist\empty \def\sortedcommalist{#1}% \else \def\!!tempa{#1}% \ifx\!!tempa\empty\else \scratchcounter\plusone \@EA\getcommalistsize\@EA[\sortedcommalist]% \@EA\processcommalist\@EA[\sortedcommalist]\docompareitems \fi \fi} %D All those \type{\expandafter}'s are there because I do not %D want to use \type{\edef}. \def\docompareitems#1% {\doifnotempty{#1} {\@EA\comparecommand\@EA{\!!tempa}{#1}\relax %\ifcase\compareresult % equal \ifnum\comparedresult<2 \ifnum\scratchcounter=\commalistsize \@EA\@EA\@EA\def\@EA\@EA\@EA\sortedcommalist \@EA\@EA\@EA{\@EA\sortedcommalist\@EA,\!!tempa}% \fi %\or % new element larger % \ifnum\scratchcounter=\commalistsize % \@EA\@EA\@EA\def\@EA\@EA\@EA\sortedcommalist % \@EA\@EA\@EA{\@EA\sortedcommalist\@EA,\!!tempa}% % \fi \else % old element larger \@EA\def\@EA\newcommalistelement\@EA{\!!tempa,#1}% \replaceincommalist\sortedcommalist\scratchcounter \expandafter\quitcommalist \fi}% \advance\scratchcounter \plusone} % bug, was \minusone %D The macro \type{\donumericcompare} considers everything %D that is not a number to be larger than any number. \def\donumericcompare#1#2% {\doifnumberelse{#1} {\doifnumberelse{#2} {\ifnum#1>#2\relax \comparedresult\plusone % #1 is larger \else\ifnum#1<#2\relax \comparedresult\plustwo % #2 is larger \else \comparedresult\zerocount % both are equal \fi\fi} {\comparedresult\plustwo}} {\comparedresult\plusone}} %D \macros %D {@saveprimitive} %D %D The next definition originates in the \type {amsgen} package. In %D case some preceding package redefined a primitive that we also %D want to redefine, we had better do some checking to make sure %D that we are able to save the primitive meaning for internal use. %D Primitive control sequences can be distinguished by the fact that %D \type {\string} and \type {\meaning} return the same information. \def\@saveprimitive#1#2% {\begingroup \edef\@tempa{\string#1}% \edef\@tempb{\meaning#1}% \ifx\@tempa\@tempb \global\let#2#1% %\debuggerinfo{prim}{Saving \string#1 as \string#2}% \else \edef\@tempb{\meaning#2}% %\ifx\@tempa\@tempb % \debuggerinfo{prim}{Saving \string#1 as \string#2}% %\else % \debuggerinfo{prim}{Can't define \string#2 properly; % primitive \noexpand#1 is no longer primitive}% %\fi \fi \endgroup} \def\saveprimitive#1% {\begingroup \@EA\edef\@EA\@tempa\@EA{\@EA\gobbleoneargument\string#1}% \@EA\let\csname normal\@tempa\endcsname\relax \@EA\@saveprimitive\@EA#1\csname normal\@tempa\endcsname \endgroup } %D In this macro, the message only shows up when the debugging %D is turned on. %D \macros %D {@True, @False, @Not, @And} %D %D Some predicate logic functions, used in for instance the %D math module. \def\@True {00} \def\@False {01} \def\@Not #1{0\ifcase#11 \or\@EA 1\else \@EA 0\fi} \def\@And #1#2{0\ifcase#1#2 \@EA 0\else \@EA 1\fi} %D \macros %D {setdimensionwithunit, freezedimensionwithunit} %D %D The next assignments are all valid: %D %D \starttyping %D \setdimensionwithunit\scratchdimen{10} {cm} %D \setdimensionwithunit\scratchdimen{10cm}{cm} %D \setdimensionwithunit\scratchdimen{10cm}{} %D \freezedimensionwithunit\SomeWidth{\textwidth} %D \freezedimensionwithunit\SomeDepth{\dp\strutbox} %D \stoptyping %D %D As an alternative for the next macro we can use a global %D assignment inside a box. The \type{\empty}'s permits %D gobbling while preventing spurious \type{\relax}'s. \def\setdimensionwithunit#1#2#3% number unit dimension / nice trick {\afterassignment\gobblefourarguments#1=#2#3pt\relax\empty\empty\empty\empty} \def\freezedimensionwithunit#1#2% {\setdimensionwithunit\scratchdimen#1{#2}\edef#1{\the\scratchdimen}} %D \macros %D {doifsometokselse} %D %D Not that fast I guess, but here's a way to test for token %D registers being empty. \def\doifsometokselse#1% % #2#3% {\edef\!!stringa{\the#1}% \ifx\!!stringa\empty % #3\else#2\fi} \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} %D \macros %D {startstrictinspectnextcharacter} %D %D This one if for Taco's bibliography module: \let\normalinspectnextcharacter\inspectnextcharacter \def\strictinspectnextcharacter% no user macro ! {\ifx\nexttoken\charactertoken \expandafter\!!stringa \else \expandafter\!!stringb \fi} % better: push/pop \def\startstrictinspectnextcharacter {\let\inspectnextcharacter\strictinspectnextcharacter} \def\stopstrictinspectnextcharacter {\let\inspectnextcharacter\normalinspectnextcharacter} %D \macros %D {gobblespacetokens} %D %D This macro needs a speed-up! %\def\gobblespacetokens % {\doifnextcharelse\empty\donothing\donothing} % no {}\do\do ! \def\gobblespacetokens {\afterassignment\nexttoken\let\nexttoken=} %D \macros %D {verbatimargument} %D %D As the name says, this macro converts its argument to a %D (rather safe) string. \def\verbatimstring#1% {\convertargument#1\to\ascii\ascii} %D These are needed in ordinal number conversions: \def\lastdigit#1% {\@EA\thelastdigit\number#1\relax} \def\thelastdigit#1#2% {\ifx#2\relax#1\else\@EA\thelastdigit\@EA#2\fi} \def\lasttwodigits#1% {\@EA\thelasttwodigits\@EA0\number#1\relax} \def\thelasttwodigits#1#2#3% 0 dig ... \relax {\ifx#3\relax#1#2\else\@EA\thelasttwodigits\@EA#2\@EA#3\fi} %D \macros %D {serializecommalist} %D %D Concatenate commalists: \def\serializecommalist[#1]% {\let\serializedcommalist\empty \def\docommando##1{\edef\serializedcommalist{\serializedcommalist##1}}% \processcommacommand[#1]\docommando} %D \macros %D {purenumber} %D %D Sometimes we need control over when \TEX\ stops reading a %D number, especially in full expandable macros where using %D \type {\relax} would lead to disasters. %D %D \starttyping %D \ifodd\purenumber{...}\space ... \else ... \fi %D \stoptyping %D %D Here we use a space as number delimiter in combination %D with a space- and relax-less \type {\purenumber}. This %D macro works ok with \type {\the}, \type {\number} as well %D as \ETEX's \type {\numexpr}. \def\purenumber#1{\@EA\firstofoneargument\@EA{\number#1}} %D \macros %D {filterfromvalue} %D %D \starttyping %D \setvalue{xx}{{A}{B}{C}} %D %D \filterfromvalue{xx}{3}{3} %D \filterfromvalue{xx}{3}{2} %D \filterfromvalue{xx}{3}{1} %D \stoptyping %D %D An alternative is to store 'max' in the list, say: %D %D \starttyping %D \setvalue{xx}{3{A}{B}{C}} %D %D \filterfromvalues{3}{xx}{3} %D \filterfromvalues{3}{xx}{2} %D \filterfromvalues{3}{xx}{1} %D \stoptyping %D %D I'll implement this when I'm in \quotation {writing dirty %D macros mood}. \def\dofilterfromstr#1#2% max n {\ifcase#1\or \ifcase#2\or \strippedcsname\firstofoneargument \else \strippedcsname\gobbleoneargument \fi \or \ifcase#2\or \strippedcsname\firstoftwoarguments \or \strippedcsname\secondoftwoarguments \else \strippedcsname\gobbletwoarguments \fi \or \ifcase#2\or \strippedcsname\firstofthreearguments \or \strippedcsname\secondofthreearguments \or \strippedcsname\thirdofthreearguments \else \strippedcsname\gobblethreearguments \fi \or \ifcase#2\or \strippedcsname\firstoffourarguments \or \strippedcsname\secondoffourarguments \or \strippedcsname\thirdoffourarguments \or \strippedcsname\fourthoffourarguments \else \strippedcsname\gobblefourarguments \fi \or \ifcase#2\or \strippedcsname\firstoffivearguments \or \strippedcsname\secondoffivearguments \or \strippedcsname\thirdoffivearguments \or \strippedcsname\fourthoffivearguments \or \strippedcsname\fifthoffivearguments \else \strippedcsname\gobblefivearguments \fi \fi} \def\filterfromvalue#1#2#3% value max n {\@EA\@EAEAEA\csname % we use the fact that an \@EA\ifx\csname#1\endcsname\relax % undefined cs has become \relax \strippedcsname\gobbleoneargument % which we then gobble here \else \dofilterfromstr{#2}{#3}% \fi \endcsname\csname#1\endcsname} \def\filterfromnext#1#2% max n {..}{..}{..}{..} {\csname\dofilterfromstr{#1}{#2}\endcsname} %D \macros %D {definemeasure} %D %D \starttyping %D \definemeasure[mywidth][\dimexpr(\textwidth-1cm)] %D %D ... \measure{mywidth} ... %D \stoptyping \def\??dm{@@dm} % brrr \def\definemeasure {\dodoubleargument\dodefinemeasure} \def\dodefinemeasure[#1][#2]% {\setvalue{\??dm#1}{#2}} % #2 could be omitted, but we want to support spaces % % \setmeasure {x} {1cm} % \setmeasure {xx} {1cm} % \setmeasure {xxx}{1cm} \def\setmeasure #1#2{\setvalue{\??dm#1}{#2}} % quick way \def\setemeasure#1#2{\setevalue{\??dm#1}{#2}} % quick way \def\setgmeasure#1#2{\setgvalue{\??dm#1}{#2}} % quick way \def\setxmeasure#1#2{\setxvalue{\??dm#1}{#2}} % quick way \def\measure#1% {\ifcsname\??dm#1\endcsname\csname\??dm#1\endcsname\else\zeropoint\fi} %D \macros %D {doifdimensionelse} %D %D This is a dirty one: we simply append a unit and discard it when needed. \def\doifdimensionelse#1% {\afterassignment\dodoifdimensionelse\scratchdimen#1pt\relax} \def\dodoifdimensionelse#1% {\ifx#1\relax \expandafter\secondoftwoarguments \else % #1=p ... t\relax \expandafter\thirdoffourarguments \fi} %D \macros %D {comparedimension,comparedimensioneps} %D %D This is a dirty one: we simply append a unit and discard it when needed. \newdimen\roundingeps \roundingeps=10sp \def\comparedimension#1#2% {\chardef\compresult \ifdim#1<#2% \zerocount \else\ifdim#1<#2% \plusone \else \plustwo \fi\fi} \beginETEX \def\comparedimensioneps#1#2% {\chardef\compresult \ifdim\dimexpr(#1-#2)<\roudingeps \zerocount \else\ifdim\dimexpr(#2-#1)<\roudingeps \zerocount \else\ifdim#1<#2% \plusone \else \plustwo \fi\fi\fi} \endETEX \beginTEX \newdimen\comparedimen \def\comparedimensioneps#1#2% {\comparedimen#1\advance\comparedimen-#2\relax \chardef\compresult \ifdim\comparedimen<\roudingeps \zerocount \else\ifdim-\comparedimen<\roudingeps \zerocount \else\ifdim#1<#2% \plusone \else \plustwo \fi\fi\fi} \endTEX \protect \endinput