diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/syst-gen.tex')
-rw-r--r-- | Master/texmf-dist/tex/context/base/syst-gen.tex | 4416 |
1 files changed, 4416 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/syst-gen.tex b/Master/texmf-dist/tex/context/base/syst-gen.tex new file mode 100644 index 00000000000..25e5390afd1 --- /dev/null +++ b/Master/texmf-dist/tex/context/base/syst-gen.tex @@ -0,0 +1,4416 @@ +%D \module +%D [ file=syst-gen, +%D version=1996.03.20, +%D title=\CONTEXT\ System Macros, +%D subtitle=General, +%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. + +% nagaan : \ifinstringelse in syst-ext.tex +% do => p! dodo pp! dododo ppp! + +%D The following macros are responsible for the interaction +%D with \CONTEXT. These macros have proven their use. These +%D macros are optimized as far as possible within of course, +%D the know how of the author. +%D +%D In this module we also show some of the optimizations, +%D mainly because we don't want to forget them and start doing +%D things over and over again. If showing them has a learing +%D effect for others too, we've surved another purpose too. + +%D \macros +%D {abortinputifdefined} +%D +%D Because this module can be used in a different context, we +%D want to prevent it being loaded more than once. This can be +%D done using: +%D +%D \starttyping +%D \abortinputifdefined\command +%D \stoptyping +%D +%D where \type{\command} is a command defined in the module +%D to be loaded only once. +%D +%D \starttyping +%D \def\abortinputifdefined#1% +%D {\ifx#1\undefined +%D \let\next=\relax +%D \else +%D \let\next=\endinput +%D \fi +%D \next} +%D \stoptyping +%D +%D This macro can be speed up in terms of speed as well as +%D memory. Because this is a nice example of a bit strange +%D command (\type{\endinput}), we spend some more lines on this. +%D +%D If we perform such actions directly, we can say: +%D +%D \starttyping +%D \ifx\somecommand\undefined +%D \let\next=\relax +%D \else +%D \let\next=\endinput +%D \fi +%D \next +%D \stoptyping +%D +%D We need the \type{\next} because we need to end the +%D \type{\fi}. The efficient one is: +%D +%D \starttyping +%D \ifx\somecommand\undefined +%D \else +%D \expandafter\endinput +%D \fi +%D \stoptyping +%D +%D Because \type{\endinput} comes into action after the current +%D line, we can also say: +%D +%D \starttyping +%D \ifx\somecommand\undefined \else \endinput \fi +%D \stoptyping +%D +%D When we define a macro, we tend to use a format which +%D shows as besat as can how things are done. \TEX\ however +%D stores the definitions as a sequence of tokens, so in fact +%D we can use a formatted definition: + +\def\abortinputifdefined#1% + {\ifx#1\undefined \else + \endinput + \fi} + +%D which also works. Keep in mind that this is entirely due to +%D the fact that \type{\endinput} after the line, i.e. at the +%D end of the macro. We therefore can burry this primitive quite +%D deep in code. + +%D And because this module implements \type{\writestatus}, we +%D just say: + +\abortinputifdefined\writestatus + +%D Normally we tell the users what module is being loaded. +%D However, the command that is needed for this is not yet +%D defined. +%D +%D \starttyping +%D \writestatus{laden}{Context Systeem Macro's (a)} +%D \stoptyping + +%D The next few macros are needed in case this module is +%D used outside \CONTEXT. + +\ifx\beginTEX\undefined + \let\beginTEX\relax\let\endTEX\relax + \long\def\beginETEX #1\endETEX {} + \long\def\beginOMEGA #1\endOMEGA{} +% \long\def\onlyTEX #1{#1} +% \long\def\onlyETEX #1{} +% \long\def\onlyOMEGA#1{} +\fi + +%D \macros +%D [protecting] +%D {protect,unprotect} +%D +%D We can shield macros from users by using some special +%D characters in their names. Some characters that are normally +%D no letters and therefore often used are: \type{@}, \type{!} +%D and \type{?}. Before and after the definition of protected +%D macros, we have to change the \CATCODE\ of these characters. +%D This is done by \type{\unprotect} and \type{\protect}, for +%D instance: +%D +%D \starttyping +%D \unprotect +%D \def\!test{test} +%D \protect +%D \stoptyping +%D +%D The defined command \type{\!test} can of course only be +%D called upon when we are in the \type{\unprotect}'ed state, +%D otherwise \TEX\ reads \type{\!} and probably complains +%D loudly about not being in math mode. +%D +%D Both commands can be used nested, but only the \CATCODE\ +%D of the outermost level is saved. We make use of +%D an auxilary macro \type{\doprotect} to prevent us from +%D conflicts with existing macro's \type{\protect}. When +%D nesting deeper than one level, the system shows the +%D protection level. + +\newcount\protectionlevel + +\ifx\protect\undefined + \def\protect{\message{<too much protection>}} +\else + % a simple version is already defined +\fi + +\let\normalprotect\protect % only for latex + +%D Although we don't need the \type{%} after commands that +%D don't take arguments, unless lines are obeyed, I decided +%D to put it there as a reminder. I only mention this once. + +\chardef\protectionthreshold=10 +%chardef\protectionthreshold=0 % for testing + +% \def\unprotect +% {\ifcase\protectionlevel +% \edef\doprotectcharacters% +% {\catcode`\noexpand @=\the\catcode`@ +% \catcode`\noexpand !=\the\catcode`! +% \catcode`\noexpand ?=\the\catcode`? }% +% \let\protect\doprotect +% \fi +% \catcode`@=11 +% \catcode`!=11 +% \catcode`?=11 +% \advance\protectionlevel 1 +% \ifnum\protectionlevel>\protectionthreshold +% \message{<unprotect \the\protectionlevel>}% +% \fi} +% +% \def\doprotect% +% {\ifnum\protectionlevel=1 +% \doprotectcharacters +% \let\doprotectcharacters\relax +% \let\protect\normalprotect +% \fi +% \ifnum\protectionlevel>\protectionthreshold +% \message{<protect \the\protectionlevel>}% +% \fi +% \advance\protectionlevel -1 } + +\def\saveprotectedcharacters + {\edef\doprotectcharacters + {\catcode`\noexpand @\the\catcode`@ + \catcode`\noexpand !\the\catcode`! + \catcode`\noexpand ?\the\catcode`? }} + +\def\setprotectedcharacters + {\catcode`@=11 + \catcode`!=11 + \catcode`?=11 } + +\def\unprotect + {\ifcase\protectionlevel + \saveprotectedcharacters + \let\protect\doprotect + \fi + \setprotectedcharacters + \advance\protectionlevel 1 + \ifnum\protectionlevel>\protectionthreshold + \reportunprotection + \fi} + +\def\doprotect + {\ifcase\protectionlevel\or + \doprotectcharacters + \let\doprotectcharacters\relax + \let\protect\normalprotect + \fi + \ifnum\protectionlevel>\protectionthreshold + \reportprotection + \fi + \advance\protectionlevel -1 } + +\def\reportunprotection {\message{<unprotect \protectionstate>}} +\def\reportprotection {\message{<protect \protectionstate>}} +\def\reportprotectionstate{\message{<protectionstate \protectionstate>}} + +\def\protectionstate + {\the\protectionlevel + \ifcase\protectionthreshold + :\space + @=\the\catcode`@\space\space + !=\the\catcode`!\space\space + ?=\the\catcode`?% + \fi} + +\ifx\everyeof\undefined + + \let\checkprotection\relax + +\else + + \def\checkprotection + {\everyeof{\writestatus{protection}{state: \protectionstate}}} + +\fi + +%D Now it is defined, we can make use of this very useful +%D macro. + +\unprotect + +%D So, now we can redefine a previously defined macro as +%D follows: + +\def\setprotectedcharacters + {\catcode`@\@@letter + \catcode`!\@@letter + \catcode`?\@@letter} + +%D \macros +%D {@@escape,@@begingroup,@@endgroup,@@mathshift,@@alignment, +%D @@endofline,@@parameter,@@superscript,@@subscript, +%D @@ignore,@@space,@@letter,@@other,@@active,@@comment} +%D +%D In \CONTEXT\ we sometimes manipulate the \CATCODES\ of +%D certain characters. Because we are not that good at numbers, +%D we introduce some symbolic names. + +\chardef\@@escape = 0 +\chardef\@@begingroup = 1 +\chardef\@@endgroup = 2 +\chardef\@@mathshift = 3 +\chardef\@@alignment = 4 +\chardef\@@endofline = 5 +\chardef\@@parameter = 6 +\chardef\@@superscript = 7 +\chardef\@@subscript = 8 +\chardef\@@ignore = 9 +\chardef\@@space = 10 +\chardef\@@letter = 11 +\chardef\@@other = 12 \chardef\other = 12 +\chardef\@@active = 13 \chardef\active = 13 +\chardef\@@comment = 14 + +%D \macros +%D {normalspace} +%D +%D We often need a space as defined in \PLAIN\ \TEX. Because +%D we cannot be sure of \type{\space} is redefined, we define: + +\def\normalspace{ } + +%D \macros +%D {scratchcounter, +%D scratchdimen,scratchskip,scratchmuskip, +%D scratchbox, +%D scratchtoks, +%D ifdone} +%D +%D Because we often need counters on a temporary basis, we +%D define the \COUNTER\ \type{\scratchcounter}. This is a +%D real \COUNTER, and not a pseudo one, as we will meet +%D further on. We also define some other scratch registers. + +\newcount \scratchcounter +\newdimen \scratchdimen +\newskip \scratchskip +\newmuskip \scratchmuskip +\newbox \scratchbox +\newtoks \scratchtoks +\newif \ifdone + +%D \macros +%D {ifCONTEXT} +%D +%D In the system and support modules we sometimes show examples +%D that make use of core commands. We can skip those parts of +%D the documentation when we use another macropackage. Of +%D course we default to false. + +\newif \ifCONTEXT + +%D \macros +%D {!!count, !!toks, !!dimen, !!box, +%D !!width, !!height, !!depth, !!string, !!done} +%D +%D We define some more \COUNTERS\ and \DIMENSIONS. We also +%D define some shortcuts to the local scatchregisters~0, 2, 4, +%D 6 and~8. + +\newcount\!!counta \toksdef\!!toksa=0 \dimendef\!!dimena= 0 \chardef\!!boxa =0 +\newcount\!!countb \toksdef\!!toksb=2 \dimendef\!!dimenb= 2 \chardef\!!boxb =2 +\newcount\!!countc \toksdef\!!toksc=4 \dimendef\!!dimenc= 4 \chardef\!!boxc =4 +\newcount\!!countd \toksdef\!!toksd=6 \dimendef\!!dimend= 6 \chardef\!!boxd =6 +\newcount\!!counte \toksdef\!!tokse=8 \dimendef\!!dimene= 8 \chardef\!!boxe =8 +\newcount\!!countf \dimendef\!!dimenf=10 %skipdef\!!skipa=0 + \dimendef\!!dimeng=12 %skipdef\!!skipb=2 + \dimendef\!!dimenh=14 %skipdef\!!skipc=4 + \dimendef\!!dimeni=16 %skipdef\!!skipd=6 + \dimendef\!!dimenj=18 %skipdef\!!skipe=8 + +\let\!!stringa=\empty \let\!!stringb=\empty \let\!!stringc=\empty +\let\!!stringd=\empty \let\!!stringe=\empty \let\!!stringf=\empty + +\newdimen\!!widtha \newdimen\!!heighta \newdimen\!!deptha +\newdimen\!!widthb \newdimen\!!heightb \newdimen\!!depthb +\newdimen\!!widthc \newdimen\!!heightc \newdimen\!!depthc + +\newif\if!!donea \newif\if!!doneb \newif\if!!donec +\newif\if!!doned \newif\if!!donee \newif\if!!donef + +\ifx \z@\undefined \else \let\zeropoint\z@ \fi +\ifx \@ne\undefined \else \let\plusone \@ne \fi +\ifx\m@ne\undefined \else \let\minusone \m@ne \fi + +\ifx\zeropoint\undefined \csname newdimen\endcsname\zeropoint \fi % plain +\ifx\plusone \undefined \chardef\plusone =1 \fi +\ifx\plustwo \undefined \chardef\plustwo =2 \fi +\ifx\plusthree\undefined \chardef\plusthree=3 \fi +\ifx\plusfour \undefined \chardef\plusfour =4 \fi +\ifx\plusfive \undefined \chardef\plusfive =5 \fi +\ifx\minusone \undefined \count\minusone \minusone=-1 \fi + +\ifx\@m\undefined \else \let\plusthousand \@m \fi +\ifx\@M\undefined \else \let\plusttenhousand\@M \fi + +\ifx\plusten \undefined \mathchardef\plusten = 10 \fi +\ifx\plushundred \undefined \mathchardef\plushundred = 100 \fi +\ifx\plusthousand \undefined \mathchardef\plusthousand = 1000 \fi +\ifx\plustenthousand\undefined \mathchardef\plustenthousand=10000 \fi + +\newcount\zerocount + +%D \macros +%D {s!,c!,e!,p!,v!,@@,??} +%D +%D To save memory, we use constants (sometimes called +%D variables). Redefining these constants can have desastrous +%D results. + +\def\v!prefix! {v!} \def\c!prefix! {c!} +\def\s!prefix! {s!} \def\p!prefix! {p!} + +\def\s!next {next} \def\s!default {default} +\def\s!dummy {dummy} \def\s!unknown {unknown} + +\def\s!do {do} \def\s!dodo {dodo} + +\def\s!complex {complex} \def\s!start {start} +\def\s!simple {simple} \def\s!stop {stop} + +%D \macros +%D {@EA,@EAEA,@EAEAEA,@EAEAEAEAEAEA,expanded,startexpanded} +%D +%D When in unprotected mode, to be entered with +%D \type{\unprotect}, one can use \type{\@EA} as equivalent +%D of \type{\expandafter}. + +\let\@EA =\expandafter +\def\@EAEA {\expandafter\expandafter} +\def\@EAEAEA{\expandafter\expandafter\expandafter} + +\def\@EAEAEAEAEAEA{\expandafter\@EAEAEA\expandafter} + +%D Sometimes we pass macros as arguments to commands that +%D don't expand them before interpretation. Such commands can +%D be enclosed with \type{\expanded}, like: +%D +%D \starttyping +%D \expanded{\setupsomething[\alfa]} +%D \stoptyping +%D +%D Such situations occur for instance when \type{\alfa} is a +%D commalist or when data stored in macros is fed to index of +%D list commands. If needed, one should use \type{\noexpand} +%D inside the argument. Later on we will meet some more clever +%D alternatives to this command. + +\long\def\@@expanded{} % always long; global (less restores) + +\long\def\expanded#1% + {\long\xdef\@@expanded{\noexpand#1}\@@expanded} + +%D Beware, the next one has no \type {\noexpand} before its +%D argument. + +\long\def\startexpanded#1\stopexpanded % see x-fo for example + {\long\xdef\@@expanded{#1}\@@expanded} + +%D \macros +%D {safeexpanded,everysafeexpanded} +%D +%D In addition we provide: + +\newtoks\everysafeexpanded + +\long\def\safeexpanded#1% + {\begingroup + \the\everysafeexpanded\long\xdef\@@expanded{\noexpand#1}% + \endgroup + \@@expanded} + +\def\safeedef#1#2% + {\begingroup + \the\everysafeexpanded\long\xdef\@@expanded{\noexpand#2}% + \endgroup + \let#1\@@expanded} + +\def\safexdef#1#2% + {\begingroup + \the\everysafeexpanded\long\xdef\@@expanded{\noexpand#2}% + \endgroup + \global\let#1\@@expanded} + +%D You can append protective measures to the token register if +%D needed, as we will do later. + +%D \macros +%D {expandoneargafter,expandtwoargsafter} +%D +%D These two commands make macros more readable by hiding a +%D lot of \type {\expandafter}'s. They expand the arguments +%D after the first command. +%D +%D \starttyping +%D \expandoneargafter \command{\abc} +%D \expandtwoargsafter\command{\abc}{\def} +%D \stoptyping +%D +%D These commands expect the arguments to be macros. + +\def\expandoneargafter #1{\@EA#1\@EA} +\def\expandtwoargsafter#1#2{\@EA\@EA\@EA#1\@EA\@EA\@EA{\@EA#2\@EA}\@EA} + +%D These two do a full expansion: + +\def\fullexpandoneargafter #1#2{\long\xdef\@@expanded{\noexpand#1{#2}}\@@expanded} +\def\fullexpandtwoargsafter#1#2#3{\long\xdef\@@expanded{\noexpand#1{#2}{#3}}\@@expanded} + +%D \macros +%D {gobbleoneargument,gobble...arguments} +%D +%D The next set of macros just do nothing, except that they +%D get rid of a number of arguments. + +\long\def\gobbleoneargument #1{} +\long\def\gobbletwoarguments #1#2{} +\long\def\gobblethreearguments #1#2#3{} +\long\def\gobblefourarguments #1#2#3#4{} +\long\def\gobblefivearguments #1#2#3#4#5{} +\long\def\gobblesixarguments #1#2#3#4#5#6{} +\long\def\gobblesevenarguments #1#2#3#4#5#6#7{} +\long\def\gobbleeightarguments #1#2#3#4#5#6#7#8{} +\long\def\gobbleninearguments #1#2#3#4#5#6#7#8#9{} +\long\def\gobbletenarguments #1{\gobbleninearguments} + +%D \macros +%D {doifnextcharelse} +%D +%D When we started using \TEX\ in the late eighties, our +%D first experiences with programming concerned a simple shell +%D around \LATEX. The commands probably use most at \PRAGMA, +%D are the itemizing ones. One of those few shell commands took +%D care of an optional argument, that enabled us to specify +%D what kind of item symbol we wanted. Without understanding +%D anything we were able to locate a \LATEX\ macro that could +%D be used to inspect the next character. +%D +%D It's this macro that the ancester of the next one presented +%D here. It executes one of two actions, dependant of the next +%D character. Disturbing spaces and line endings, which are +%D normally interpreted as spaces too, are skipped. +%D +%D \starttyping +%D \doifnextcharelse {karakter} {then ...} {else ...} +%D \stoptyping +%D +%D This macro differs from the original in the use of \type +%D {\localnext} because we don't want clashes with \type +%D {\next}. + +\long\def\doifnextcharelse#1#2#3% #1 should not be {} ! + {\let\charactertoken=#1% = needed here + \def\!!stringa{#2}% + \def\!!stringb{#3}% + \futurelet\nexttoken\inspectnextcharacter} + +\def\inspectnextcharacter + {\ifx\nexttoken\blankspace + \@EA\reinspectnextcharacter + \else\ifx\nexttoken\charactertoken + \@EAEAEA\!!stringa + \else + \@EAEAEA\!!stringb + \fi\fi} + +%\let\endoflinetoken=^^M +% +%\long\def\reinspectaftercharacter#1% +% {\futurelet\nexttoken\inspectnextcharacter} + +%D This macro uses some auxiliary macros. Although we were able +%D to program quite complicated things, I only understood these +%D after rereading the \TEX book. The trick is in using a +%D command with a one character name. Such commands differ from +%D the longer ones in the fact that trailing spaces are {\em +%D not} skipped. This enables us to indirectly define a long +%D named macro that gobbles a space. +%D +%D In the first line we define \type{\blankspace}. Next we +%D make \type{\:} equivalent to \type{\reinspect...}. This +%D one||character command is expanded before the next +%D \type{\def} comes into action. This way the space after +%D \type{\:} becomes a delimiter of the longer named +%D \type{\reinspectnextcharacter}. The chain reaction is +%D visually compatible with the next sequence: +%D +%D \starttyping +%D \expandafter\def\reinspectnextcharacter % +%D {\futurelet\nexttoken\inspectnextcharacter} +%D \stoptyping +%D +%D However complicated it may look, I'm still glad I stumbled +%D into this construction. Saving and restoring \type {\:} is +%D needed when we use \PPCHTEX\ in \LATEX. + +\let\next\: + +\def\:{\let\blankspace= } \: + +\def\:{\reinspectnextcharacter} + +\expandafter\def\: {\futurelet\nexttoken\inspectnextcharacter} + +\let\:\next + +%D \macros +%D {setvalue,setgvalue,setevalue,setxvalue, +%D letvalue, +%D getvalue, +%D resetvalue} +%D +%D \TEX's primitive \type{\csname} can be used to construct +%D all kind of commands that cannot be defined with +%D \type{\def} and \type{\let}. Every macro programmer sooner +%D or later wants macros like these. +%D +%D \starttyping +%D \setvalue {name}{...} = \def\name{...} +%D \setgvalue {name}{...} = \gdef\name{...} +%D \setevalue {name}{...} = \edef\name{...} +%D \setxvalue {name}{...} = \xdef\name{...} +%D \letvalue {name}=\... = \let\name=\... +%D \letgvalue {name}=\... = \global\let\name=\... +%D \getvalue {name} = \name +%D \resetvalue {name} = \def\name{} +%D \stoptyping +%D +%D As we will see, \CONTEXT\ uses these commands many times, +%D which is mainly due to its object oriented and parameter +%D driven character. + +\def\setvalue #1{\expandafter \def\csname#1\endcsname} +\def\setgvalue #1{\expandafter\gdef\csname#1\endcsname} +\def\setevalue #1{\expandafter\edef\csname#1\endcsname} +\def\setxvalue #1{\expandafter\xdef\csname#1\endcsname} +\def\getvalue #1{\csname#1\endcsname} +\def\letvalue #1{\expandafter\let\csname#1\endcsname} +\def\letgvalue #1{\global\expandafter\let\csname#1\endcsname} +\def\resetvalue #1{\expandafter\let\csname#1\endcsname\empty} +\def\ignorevalue#1#2{\expandafter\let\csname#1\endcsname\empty} + +%D \macros +%D {globallet,glet} +%D +%D In \CONTEXT\ of May 2000 using \type {\globallet} +%D instead of the two tokens will save us some +%D $300\times4=1200$ bytes of format file on a 32~bit +%D system. So: + +\def\globallet{\global\let} \let\glet\globallet + +%D \macros +%D {donottest,unexpanded} +%D +%D When expansion of a macro gives problems, we can precede it +%D by \type{\donottest}. It seems that protection is one of the +%D burdens of developers of packages, so maybe that's why in +%D \ETEX\ protection is solved in a more robust way. +%D +%D Sometimes prefixing the macro with \type{\donottest} leads +%D to defining an auxiliary macro, like +%D +%D \starttyping +%D \def\dosomecommand {... ... ...} +%D \def\somecommand {\donottest\dosomecommand} +%D \stoptyping +%D +%D This double definition can be made transparant by using +%D \type{\unexpanded}, as in: +%D +%D \starttyping +%D \unexpanded\def\somecommand{... ... ...} +%D \stoptyping +%D +%D The protection mechanism uses: + +\beginTEX + +\def\dontprocesstest#1{==} +\def\doprocesstest #1{#1} + +\let\donottest=\doprocesstest + +\endTEX + +\beginETEX \detokenize + +\def\donottest#1{#1} % {\detokenize{#1}} + +\endETEX + +%D By the way, we use a placeholder because we don't want +%D interference when testing on empty strings. Using a +%D placeholder of 8~characters increases the processing time +%D of simple \type{\doifelse} tests by about 10 \%. When we +%D process the test, we have to remove the braces and +%D therefore explictly gobble \type{#1}. + +%D \macros +%D {honorunexpanded,forceunexpanded} +%D +%D The fact that many macros have the same prefix, could have +%D a negative impact on searching in the hash table. Because +%D some simple testing does not show differences, we just use: +%D +%D \starttyping +%D \def\unexpanded#1#2% +%D {\@EA#1\@EA#2\@EA{\@EA\donottest\csname\s!do\string#2\endcsname}% +%D \@EA#1\csname\s!do\string#2\endcsname} +%D \stoptyping +%D +%D Well, in fact we use the bit more versatile alternative. The +%D \type {\honorunexpanded} can be used to \type {\string} +%D the protected command, which by the way is seldom needed +%D in \CONTEXT. + +\beginTEX + +\def\dosetunexpanded#1#2% + {\@EA#1\@EA{\@EA#2\@EA}% + \@EA{\@EA\donottest\csname\s!do\@EA\string\csname#2\endcsname\endcsname}% + \@EA#1\@EA{\@EA\s!do\@EA\string\csname#2\endcsname}} + +\def\docomunexpanded#1#2% + {\@EA#1\@EA#2\@EA{\@EA\donottest\csname\s!do\string#2\endcsname}% + \@EA#1\csname\s!do\string#2\endcsname} + +\def\unexpanded#1% + {\def\dounexpanded + {\ifx\next\bgroup + \@EA\dosetunexpanded + \else + \@EA\docomunexpanded + \fi#1}% + \futurelet\next\dounexpanded} + +\def\honorunexpanded% for writing to a file or message + {\def\donottest##1{\expandafter\gobblethreearguments\string##1}} + +\def\forceunexpanded% for preventing expansion in \xdef + {\def\donottest##1% + {\expandafter\noexpand\csname\expandafter\gobblefourarguments\string##1\endcsname}} + +\def\resetunexpanded% + {\let\donottest\doprocesstest} + +\endTEX + +\beginETEX \protected + +\let \unexpanded \normalprotected +\let \honorunexpanded \empty % \relax +\let \forceunexpanded \empty % \relax +\let \resetunexpanded \empty % \relax + +\endETEX + +%D This one accepts the more direct \type{\def} and cousins +%D as well as the \CONTEXT\ specific \type{\setvalue} ones. +%D +%D And so the definition in our example turns out to be: +%D +%D \starttyping +%D \def\csname do\somecommand\endcsname{... ... ...} +%D \def\somecommand{\donottest\csname do\somecommand\endcsname} +%D \stoptyping +%D +%D In which \type{do\somecommand} is hidden from the user and +%D cannot lead to confusion. It's still permitted to define +%D auxiliary macros like \type{\dosomecommand}. +%D +%D When we are going to use e-\TEX, we'll probably end up +%D redefining some commands, but we can probably keep the +%D \type{\unexpanded} ones unchanged. + +%D \macros +%D {doifundefined,doifdefined, +%D doifundefinedelse,doifdefinedelse, +%D doifalldefinedelse} +%D +%D The standard way of testing if a macro is defined is +%D comparing its meaning with another undefined one, usually +%D \type{\undefined}. To garantee correct working of the next +%D set of macros, \type{\undefined} may never be defined! +%D +%D \starttyping +%D \doifundefined {string} {...} +%D \doifdefined {string} {...} +%D \doifundefinedelse {string} {then ...} {else ...} +%D \doifdefinedelse {string} {then ...} {else ...} +%D \doifalldefinedelse {commalist} {then ...} {else ...} +%D \stoptyping +%D +%D Every macroname that \TEX\ builds gets an entry in the hash +%D table, which is of limited size. It is expected that e-\TeX\ +%D will offer a less memory||consuming alternative. + +%D Although it will probably never be a big problem, it is good +%D to be aware of the difference between testing on a macro +%D name to be build by using \type{\csname} and +%D \type{\endcsname} and testing the \type{\name} directly. +%D +%D \starttyping +%D \expandafter\ifx\csname NameA\endcsname\relax ... \else ... \fi +%D +%D \ifx\NameB\undefined ... \else ... \fi +%D \stoptyping +%D +%D I became aware of this when I mistakenly testen the first +%D one against \type{\undefined}. When \TEX\ build a name using +%D \type{\csname} it automatically sets it to \type{\relax}, +%D which is definitely not the same as \type{\undefined}. The +%D quickest way to check these things is asking \TEX\ to show +%D the meaning of the names: +%D +%D \starttyping +%D \expandafter\show\csname NameA\endcsname +%D +%D \show\NameB +%D \stoptyping +%D +%D The main reason why this never will be a big problem is that +%D when one uses the \type{\csname} way, one probably has to do +%D with some macroname that always is dealt with that way. +%D Confusion can however arise when one applies both testing +%D methods to the same macroname. By the way, the assignment +%D of \type{\relax} obeys grouping. + +%D The first one gets rid of \type{#1}, but still expands to +%D something and the second one expands to \type{#1}. Because +%D we accept arguments between \type{{}}, we have to get rid +%D of one level of braces. +%D +%D Our first implementation of \type{\ifundefined} was +%D straightforward and readable: +%D +%D \starttyping +%D \def\ifundefined#1% +%D {\expandafter\ifx\csname#1\endcsname\relax}% +%D +%D \def\doifundefinedelse#1#2#3% +%D {\let\donottest=\dontprocesstest +%D \ifundefined{#1}% +%D \let\donottest=\doprocesstest#2% +%D \else +%D \let\donottest=\doprocesstest#3% +%D \fi} +%D +%D \def\doifdefinedelse#1#2#3% +%D {\doifundefinedelse{#1}{#3}{#2}} +%D +%D \def\doifundefined#1#2% +%D {\doifundefinedelse{#1}{#2}{}} +%D +%D \def\doifdefined#1#2% +%D {\doifundefinedelse{#1}{}{#2}} +%D +%D \def\doifalldefinedelse#1#2#3% +%D {\begingroup +%D \donetrue +%D \def\checkcommand##1% +%D {\doifundefined{##1}{\donefalse}}% +%D \processcommalist[#1]\checkcommand +%D \ifdone +%D \endgroup#2% +%D \else +%D \endgroup#3% +%D \fi} +%D \stoptyping +%D +%D When this module was optimized, timing showed that the +%D next alternative can be upto twice as fast, especially when +%D longer arguments are used. Watch how we reach over the +%D \type {\else} and \type {\fi}: this way they cannot get +%D into the way (and we can avoid those \type {\next} +%D hacks); I started using this method after I did some +%D speed optimization tests in the xtag modules; the +%D efficiency of such hacks depends on the length of the +%D argument etc. etc.) + +\beginTEX + +\def\ifundefined#1% + {\expandafter\ifx\csname#1\endcsname\relax} + +\def\p!doifundefined#1% + {\let\donottest\dontprocesstest + \expandafter\ifx\csname#1\endcsname\relax} + +\def\doifundefinedelse#1% + {\p!doifundefined{#1}% + \let\donottest\doprocesstest\@EA\firstoftwoarguments + \else + \let\donottest\doprocesstest\@EA\secondoftwoarguments + \fi} + +\def\doifdefinedelse#1% + {\p!doifundefined{#1}% + \let\donottest\doprocesstest\@EA\secondoftwoarguments + \else + \let\donottest\doprocesstest\@EA\firstoftwoarguments + \fi} + +\def\doifundefined#1% + {\p!doifundefined{#1}% + \let\donottest\doprocesstest\@EA\firstofoneargument + \else + \let\donottest\doprocesstest\@EA\gobbleoneargument + \fi} + +\def\doifdefined#1% + {\p!doifundefined{#1}% + \let\donottest\doprocesstest\@EA\gobbleoneargument + \else + \let\donottest\doprocesstest\@EA\firstofoneargument + \fi} + +\endTEX + +\beginETEX \ifcsname + +\def\ifundefined#1% ongelukkige naam + {\unless\ifcsname#1\endcsname} + +\def\p!doifundefined#1% + {\edef\p!defined{#1}% + \unless\ifcsname\detokenize\@EA{\p!defined}\endcsname} + +\def\doifundefinedelse#1% + {\edef\p!defined{#1}% + \ifcsname\detokenize\@EA{\p!defined}\endcsname + \expandafter\secondoftwoarguments + \else + \expandafter\firstoftwoarguments + \fi} + +\def\doifdefinedelse#1% + {\edef\p!defined{#1}% + \ifcsname\detokenize\@EA{\p!defined}\endcsname + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\def\doifundefined#1% + {\edef\p!defined{#1}% + \ifcsname\detokenize\@EA{\p!defined}\endcsname + \expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +\def\doifdefined#1% + {\edef\p!defined{#1}% + \ifcsname\detokenize\@EA{\p!defined}\endcsname + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\endETEX + +%D \macros +%D {letbeundefined} +%D +%D Testing for being undefined comes down to testing on \type +%D {\relax} when we use \type {\csname}, but when using \type +%D {\ifx}, we test on being \type {\undefined}! In \ETEX\ we +%D have \type {\ifcsname} and that way of testing on existance +%D is not the same as the one described here. Therefore we +%D introduce: + +\beginTEX + +\def\letbeundefined#1% + {\expandafter\let\csname#1\endcsname\relax} + +\endTEX + +\beginETEX \undefined + +\def\letbeundefined#1% potential stack buildup when used \global + {\expandafter\let\csname#1\endcsname\undefined} + +\endETEX + +%D Beware, being \type {\undefined} in \ETEX\ means that the macro +%D {\em is} defined! + +%D Before we start using this variant, we used another one, +%D which is even a bit faster. This one looked like: +%D +%D \starttyping +%D \def\p!doifundefined% +%D {\begingroup +%D \let\donottest=\dontprocesstest +%D \ifundefined} +%D +%D \def\doifundefinedelse#1#2#3% +%D {\p!doifundefined{#1}% +%D \endgroup#2% +%D \else +%D \endgroup#3% +%D \fi} +%D \stoptyping +%D +%D A even more previous version used \type{\bgroup} and +%D \type {\egroup}. In math mode however, \type{$1{x}2$} differs +%D from \type{$1x2$}. This can been seen when one compares the +%D output of: +%D +%D \starttyping +%D $\kern10pt\showthe\lastkern$ +%D $\kern10pt{\showthe\lastkern}$ +%D $\kern10pt\begingroup\showthe\lastkern\endgroup$ +%D \stoptyping +%D +%D Also in math mode, one can better use \type {\begingroup} +%D and companion instead of \type {\bgroup}. +%D +%D When we were developing the scientific units module, we +%D encountered different behavior in text and math mode, which +%D was due to this grouping subtilities. We therefore decided +%D to use \type{\begingroup} instead of \type{\bgroup}. +%D Later, when we had optimized some macro's the grouped +%D solution turned out to be unsafe when typesetting this +%D documentation, especially when using \type{\globaldefs}. +%D +%D We still have to define \type{\doifalldefinedelse}. Watch +%D the use of grouping, which garantees local use of the +%D boolean \type{\ifdone}. + +\beginTEX + +\def\docheckonedefined#1% + {\ifundefined{#1}% + \donefalse + \fi} + +\def\doifalldefinedelse#1% + {\begingroup + \let\donottest\dontprocesstest + \donetrue + \processcommalist[#1]\docheckonedefined + \ifdone + \endgroup\let\donottest\doprocesstest + \expandafter\firstoftwoarguments + \else + \endgroup\let\donottest\doprocesstest + \expandafter\secondoftwoarguments + \fi} + +\endTEX + +\beginETEX \ifcsname + +\def\docheckonedefined#1% + {\unless\ifcsname#1\endcsname + \donefalse + \fi} + +\def\doifalldefinedelse#1% + {\begingroup + \donetrue \processcommalist[#1]\docheckonedefined + \ifdone + \endgroup\expandafter\firstoftwoarguments + \else + \endgroup\expandafter\secondoftwoarguments + \fi} + +\endETEX + +%D \macros +%D {doif,doifelse,doifnot, +%D donottest} +%D +%D Programming in \TEX\ differs from programming in procedural +%D languages like \MODULA. This means that one --- well, let me +%D speek for myself --- tries to do the things in the well +%D known way. Therefore the next set of \type{\ifthenelse} +%D commands were between the first ones we needed. A few years +%D later, the opposite became true: when programming in +%D \MODULA, I sometimes miss handy things like grouping, +%D runtime redefinition, expansion etc. While \MODULA\ taught +%D me to structure, \TEX\ taught me to think recursive. +%D +%D \starttyping +%D \doif {string1} {string2} {...} +%D \doifnot {string1} {string2} {...} +%D \doifelse {string1} {string2} {then ...}{else ...} +%D \stoptyping +%D +%D When expansion gives problems, we can precede the +%D troublemaker with \type{\donottest}. +%D +%D This implementatie does not use the construction which is +%D more robust for nested conditionals. +%D +%D \starttyping +%D \ifx\!!stringa\!!stringb +%D \def\next{#3}% +%D \else +%D \def\next{#4}% +%D \fi +%D \next +%D \stoptyping +%D +%D In practice, this alternative is at least 20\% slower than +%D the alternative used here. The few cases in which we +%D really need the \type{\next} construction, often need some +%D other precautions and or adaptions too. + +\beginTEX + +% \long\def\doif#1#2#3% +% {\let\donottest\dontprocesstest +% \edef\!!stringa{#1}% +% \edef\!!stringb{#2}% +% \let\donottest\doprocesstest +% \ifx\!!stringa\!!stringb +% #3% +% \fi} +% +% \long\def\doifnot#1#2#3% +% {\let\donottest\dontprocesstest +% \edef\!!stringa{#1}% +% \edef\!!stringb{#2}% +% \let\donottest\doprocesstest +% \ifx\!!stringa\!!stringb +% \else +% #3% +% \fi} +% +% \long\def\doifelse#1#2#3#4% +% {\let\donottest\dontprocesstest +% \edef\!!stringa{#1}% +% \edef\!!stringb{#2}% +% \let\donottest\doprocesstest +% \ifx\!!stringa\!!stringb +% #3% +% \else +% #4% +% \fi} + +%D Slightly faster on big arguments, as well as \type +%D {\next} avoiding: + +\long\def\doif#1#2% + {\let\donottest\dontprocesstest + \edef\!!stringa{#1}% + \edef\!!stringb{#2}% + \let\donottest\doprocesstest + \ifx\!!stringa\!!stringb + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\long\def\doifnot#1#2% + {\let\donottest\dontprocesstest + \edef\!!stringa{#1}% + \edef\!!stringb{#2}% + \let\donottest\doprocesstest + \ifx\!!stringa\!!stringb + \expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + + +\long\def\doifelse#1#2% + {\let\donottest\dontprocesstest + \edef\!!stringa{#1}% + \edef\!!stringb{#2}% + \let\donottest\doprocesstest + \ifx\!!stringa\!!stringb + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\endTEX + +\beginETEX \protected + +% \long\def\doif#1#2#3% +% {\edef\!!stringa{#1}\edef\!!stringb{#2}% +% \ifx\!!stringa\!!stringb#3\fi} +% +% \long\def\doifnot#1#2#3% +% {\edef\!!stringa{#1}\edef\!!stringb{#2}% +% \unless\ifx\!!stringa\!!stringb#3\fi} +% +% \long\def\doifelse#1#2#3#4% +% {\edef\!!stringa{#1}\edef\!!stringb{#2}% +% \ifx\!!stringa\!!stringb#3\else#4\fi} + +%D Slightly faster on big arguments, as well as \type +%D {\next} avoiding: + +\long\def\doif#1#2% + {\edef\!!stringa{#1}\edef\!!stringb{#2}% + \ifx\!!stringa\!!stringb + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\long\def\doifnot#1#2% + {\edef\!!stringa{#1}\edef\!!stringb{#2}% + \ifx\!!stringa\!!stringb + \expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +\long\def\doifelse#1#2% + {\edef\!!stringa{#1}\edef\!!stringb{#2}% + \ifx\!!stringa\!!stringb + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\endETEX + +%D One could wonder why we don't follow the the same approach +%D as in \type{\doifdefined} c.s.\ and use \type{\begingroup} +%D and \type{\endgroup}. In this case, this alternative is +%D slower, which is probably due to the fact that more meanings +%D need to be restored. +%D +%D The in terms of memory more efficient alternative using a +%D auxiliary macro also proved to be slower, so we definitely +%D did not choose for: +%D +%D \starttyping +%D \def\p!doifelse#1#2% +%D {\let\donottest=\dontprocesstest +%D \edef\!!stringa{#1}% +%D \edef\!!stringb{#2}% +%D \let\donottest=\doprocesstest +%D \ifx\!!stringa\!!stringb} +%D +%D \long\def\doif#1#2#3% +%D {\p!doifelse{#1}{#2}#3\fi} +%D +%D \long\def\doifnot#1#2#3% +%D {\p!doifelse{#1}{#2}\else#3\fi} +%D +%D \long\def\doifelse#1#2#3#4% +%D {\p!doifelse{#1}{#2}#3\else#4\fi} +%D \stoptyping +%D +%D Optimizations like this are related of course to the +%D bottlenecks in \TEX. It seems that restoring saved meanings +%D and passing arguments takes some time. + +%D \macros +%D {doifempty,doifemptyelse,doifnotempty} +%D +%D We complete our set of conditionals with: +%D +%D \starttyping +%D \doifempty {string} {...} +%D \doifnotempty {string} {...} +%D \doifemptyelse {string} {then ...} {else ...} +%D \stoptyping +%D +%D This time, the string is not expanded. + +\long\def\doifemptyelse#1% + {\def\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\long\def\doifempty#1% + {\def\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\long\def\doifnotempty#1% + {\def\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\gobbleoneargument + \else + \expandafter\firstofoneargument + \fi} + +%D \macros +%D {doifinset,doifnotinset,doifinsetelse} +%D +%D We can check if a string is present in a comma separated +%D set of strings. Depending on the result, some action is +%D taken. +%D +%D \starttyping +%D \doifinset {string} {string,...} {...} +%D \doifnotinset {string} {string,...} {...} +%D \doifinsetelse {string} {string,...} {then ...} {else ...} +%D \stoptyping +%D +%D The second argument is the comma separated set of strings. +%D +%D \starttyping +%D \long\def\doifinsetelse#1#2#3#4% +%D {\doifelse{#1}{} +%D {#4} +%D {\donefalse +%D \def\p!checkiteminset##1% +%D {\doif{#1}{##1} +%D {\donetrue +%D \let\p!checkiteminset=\gobbleoneargument}}% +%D \processcommalist[#2]\p!checkiteminset +%D \ifdone +%D #3% +%D \else +%D #4% +%D \fi}} +%D +%D \long\def\doifinset#1#2#3% +%D {\doifinsetelse{#1}{#2}{#3}{}} +%D +%D \long\def\doifnotinset#1#2#3% +%D {\doifinsetelse{#1}{#2}{}{#3}} +%D \stoptyping +%D +%D Because this macro is called quite often we've spent some +%D time optimizing it. This time, the gain in speed is due to +%D (1)~defining an external auxiliary macro, (2)~not calling +%D any other macros and (3)~minimizing the passing of +%D arguments. The gain in speed is impressive. + +% \def\p!dodocheckiteminset#1% +% {\edef\!!stringb{#1}% +% \ifx\!!stringa\!!stringb +% \donetrue +% \let\p!docheckiteminset\gobbleoneargument +% \fi} +% +% \beginTEX +% +% \def\p!doifinsetelse#1#2% +% {\let\donottest\dontprocesstest +% \donefalse +% \edef\!!stringa{#1}% +% \ifx\!!stringa\empty +% \else +% \let\p!docheckiteminset\p!dodocheckiteminset +% \processcommalist[#2]\p!docheckiteminset +% \fi +% \let\donottest\doprocesstest +% \ifdone} +% +% \endTEX +% +% \beginETEX \protected +% +% \def\p!doifinsetelse#1#2% +% {\donefalse +% \edef\!!stringa{#1}% +% \ifx\!!stringa\empty +% \else +% \let\p!docheckiteminset\p!dodocheckiteminset +% \processcommalist[#2]\p!docheckiteminset +% \fi +% \ifdone} +% +% \endETEX + +% then we had: +% +% \def\p!docheckiteminset#1% +% {\edef\!!stringb{#1}% +% \ifx\!!stringa\!!stringb +% \donetrue +% \expandafter\quitcommalist +% \fi} +% +% \beginTEX +% +% \def\p!doifinsetelse#1#2% +% {\let\donottest\dontprocesstest +% \donefalse +% \edef\!!stringa{#1}% +% \ifx\!!stringa\empty +% \else +% \processcommalist[#2]\p!docheckiteminset +% \fi +% \let\donottest\doprocesstest +% \ifdone} +% +% \endTEX +% +% % can be sped up with processnext... +% +% \beginETEX \protected +% +% \def\p!doifinsetelse#1#2% +% {\donefalse +% \edef\!!stringa{#1}% +% \ifx\!!stringa\empty +% \else +% \processcommalist[#2]\p!docheckiteminset +% \fi +% \ifdone} +% +% \endETEX +% +% \long\def\doifinsetelse#1#2% +% {\p!doifinsetelse{#1}{#2}% +% \expandafter\firstoftwoarguments +% \else +% \expandafter\secondoftwoarguments +% \fi} +% +% \long\def\doifinset#1#2% +% {\p!doifinsetelse{#1}{#2}% +% \expandafter\firstofoneargument +% \else +% \expandafter\gobbleoneargument +% \fi} +% +% \long\def\doifnotinset#1#2% +% {\p!doifinsetelse{#1}{#2}% +% \expandafter\gobbleoneargument +% \else +% \expandafter\firstofoneargument +% \fi} +% +% now we have + +\def\p!docheckiteminset#1% + {\edef\!!stringb{#1}% + \ifx\!!stringa\!!stringb + \donetrue + \expandafter\quitcommalist + \fi} + +\beginTEX + +\def\p!doifinsetelse#1#2#3#4% + {\let\donottest\dontprocesstest + \donefalse + \edef\!!stringa{#3}% + \ifx\!!stringa\empty + \else + \processcommalist[#4]\p!docheckiteminset + \fi + \let\donottest\doprocesstest + \ifdone\expandafter#1\else\expandafter#2\fi} + +\endTEX + +\beginETEX \protected + +\def\p!doifinsetelse#1#2#3#4% + {\donefalse + \edef\!!stringa{#3}% + \ifx\!!stringa\empty + \else + \processcommalist[#4]\p!docheckiteminset + \fi + \ifdone\expandafter#1\else\expandafter#2\fi} + +\endETEX + +\long\def\doifinsetelse + {\p!doifinsetelse\firstoftwoarguments\secondoftwoarguments} + +\long\def\doifinset + {\p!doifinsetelse\firstofoneargument\gobbleoneargument} + +\long\def\doifnotinset + {\p!doifinsetelse\gobbleoneargument\firstofoneargument} + +%D \macros +%D {doifcommon,doifnotcommon,doifcommonelse} +%D +%D Probably the most time consuming tests are those that test +%D for overlap in sets of strings. +%D +%D \starttyping +%D \doifcommon {string,...} {string,...} {...} +%D \doifnotcommon {string,...} {string,...} {...} +%D \doifcommonelse {string,...} {string,...} {then ...} {else ...} +%D \stoptyping +%D +%D We show the slower alternative first, because it shows us +%D how things are done. +%D +%D \starttyping +%D \long\def\doifcommonelse#1#2#3#4% +%D {\donefalse +%D \def\p!docommoncheck##1% +%D {\def\p!dodocommoncheck####1% +%D {\doif{####1}{##1} +%D {\donetrue +%D \def\commalistelement{##1}% +%D \let\p!docommoncheck=\gobbleoneargument +%D \let\p!dodocommoncheck=\gobbleoneargument}}% +%D \processcommalist[#2]\p!dodocommoncheck}% +%D \processcommalist[#1]\p!docommoncheck +%D \ifdone +%D #3% +%D \else +%D #4% +%D \fi} +%D +%D \long\def\doifcommon#1#2#3% +%D {\doifcommonelse{#1}{#2}{#3}{}} +%D +%D \long\def\doifnotcommon#1#2#3% +%D {\doifcommonelse{#1}{#2}{}{#3}} +%D \stoptyping +%D +%D The processing time is shortened by getting the auxiliary +%D macro to the outermost level and using less \type{\edef}'s. +%D Sometimes it makes more sence to define local macro's not +%D only because this way we can be sure that they are not +%D redefined, but also because it shows the dependance. In +%D compiled languages, this is no problem at all. It can even +%D save us bytes and processing time. In interpreted languages +%D like \TEX\ it nearly always slows down processing. + +% \def\p!dododocommoncheck#1% +% {\edef\!!stringb{#1}% +% \ifx\!!stringa\!!stringb +% \donetrue +% \let\p!docommoncheck\gobbleoneargument +% \let\p!dodocommoncheck\gobbleoneargument +% \fi} +% +% \beginTEX +% +% \def\p!doifcommonelse#1#2% +% {\donefalse +% \let\donottest\dontprocesstest +% \let\p!dodocommoncheck\p!dododocommoncheck +% \def\p!docommoncheck##1% +% {\edef\!!stringa{##1}% +% \def\commalistelement{##1}% +% \processcommalist[#2]\p!dodocommoncheck}% +% \processcommalist[#1]\p!docommoncheck +% \let\donottest\doprocesstest +% \ifdone} +% +% \endTEX +% +% \beginETEX \protected +% +% \def\p!doifcommonelse#1#2% +% {\donefalse +% \let\p!dodocommoncheck\p!dododocommoncheck +% \def\p!docommoncheck##1% +% {\edef\!!stringa{##1}% +% \def\commalistelement{##1}% +% \processcommalist[#2]\p!dodocommoncheck}% +% \processcommalist[#1]\p!docommoncheck +% \ifdone} +% +% \endETEX + +% \def\p!dodocommoncheck#1% +% {\edef\!!stringb{#1}% +% \ifx\!!stringa\!!stringb +% \donetrue +% \expandafter\quitprevcommalist +% \fi} +% +% \beginTEX +% +% \def\p!doifcommonelse#1#2% +% {\donefalse +% \let\donottest\dontprocesstest +% \def\p!docommoncheck##1% +% {\edef\!!stringa{##1}% +% \def\commalistelement{##1}% no let to stringa +% \processcommalist[#2]\p!dodocommoncheck}% +% \processcommalist[#1]\p!docommoncheck +% \let\donottest\doprocesstest +% \ifdone} +% +% \endTEX +% +% \beginETEX \protected +% +% \def\p!doifcommonelse#1#2% +% {\donefalse +% \def\p!docommoncheck##1% +% {\edef\!!stringa{##1}% +% \def\commalistelement{##1}% +% \processcommalist[#2]\p!dodocommoncheck}% +% \processcommalist[#1]\p!docommoncheck +% \ifdone} +% +% \endETEX +% +% \long\def\doifcommonelse#1#2% % #3#4% +% {\p!doifcommonelse{#1}{#2}% % #3\else#4\fi} +% \expandafter\firstoftwoarguments +% \else +% \expandafter\secondoftwoarguments +% \fi} +% +% \long\def\doifcommon#1#2% +% {\p!doifcommonelse{#1}{#2}% +% \expandafter\firstofoneargument +% \else +% \expandafter\gobbleoneargument +% \fi} +% +% \long\def\doifnotcommon#1#2% +% {\p!doifcommonelse{#1}{#2}% +% \expandafter\gobbleoneargument +% \else +% \expandafter\firstofoneargument +% \fi} + +\def\p!dodocommoncheck#1% + {\edef\!!stringb{#1}% + \ifx\!!stringa\!!stringb + \donetrue + \expandafter\quitprevcommalist + \fi} + +\beginTEX + +\def\p!doifcommonelse#1#2#3#4% + {\donefalse + \let\donottest\dontprocesstest + \def\p!docommoncheck##1% + {\edef\!!stringa{##1}% + \def\commalistelement{##1}% no let to stringa + \processcommalist[#4]\p!dodocommoncheck}% + \processcommalist[#3]\p!docommoncheck + \let\donottest\doprocesstest + \ifdone\expandafter#1\else\expandafter#2\fi} + +\endTEX + +\beginETEX \protected + +\def\p!doifcommonelse#1#2#3#4% + {\donefalse + \def\p!docommoncheck##1% + {\edef\!!stringa{##1}% + \def\commalistelement{##1}% + \processcommalist[#4]\p!dodocommoncheck}% + \processcommalist[#3]\p!docommoncheck + \ifdone\expandafter#1\else\expandafter#2\fi} + +\endETEX + +\def\doifcommonelse + {\p!doifcommonelse\firstoftwoarguments\secondoftwoarguments} + +\def\doifcommon + {\p!doifcommonelse\firstofoneargument \gobbleoneargument} + +\def\doifnotcommon + {\p!doifcommonelse\gobbleoneargument \firstofoneargument} + +%D \macros +%D {processcommalist,processcommacommand,quitcommalist, +%D processcommalistwithparameters} +%D +%D We've already seen some macros that take care of comma +%D separated lists. Such list can be processed with +%D +%D \starttyping +%D \processcommalist[string,string,...]\commando +%D \stoptyping +%D +%D The user supplied command \type{\commando} receives one +%D argument: the string. This command permits nesting and +%D spaces after commas are skipped. Empty sets are no problem. +%D +%D \startbuffer +%D \def\dosomething#1{(#1)} +%D +%D 1: \processcommalist [\hbox{$a,b,c,d,e,f$}] \dosomething \par +%D 2: \processcommalist [{a,b,c,d,e,f}] \dosomething \par +%D 3: \processcommalist [{a,b,c},d,e,f] \dosomething \par +%D 4: \processcommalist [a,b,{c,d,e},f] \dosomething \par +%D 5: \processcommalist [a{b,c},d,e,f] \dosomething \par +%D 6: \processcommalist [{a,b}c,d,e,f] \dosomething \par +%D 7: \processcommalist [] \dosomething \par +%D 8: \processcommalist [{[}] \dosomething \par +%D \stopbuffer +%D +%D \typebuffer +%D +%D Before we show the result, we present the macro's: + +\newcount\commalevel + +\def\dododoprocesscommaitem + {\csname\s!next\the\commalevel\endcsname} + +%\def\dodoprocesscommaitem% +% {\ifx\nexttoken\blankspace +% \let\nextcommaitem\redoprocesscommaitem +% \else\ifx\nexttoken]% +% \let\nextcommaitem\gobbleoneargument +% \else +% \let\nextcommaitem\dododoprocesscommaitem +% \fi\fi +% \nextcommaitem} +% +% faster ? + +\def\dodoprocesscommaitem + {\ifx\nexttoken\blankspace + \@EA\redoprocesscommaitem + \else\ifx\nexttoken]% + \@EAEAEA\gobbleoneargument + \else + \@EAEAEA\dododoprocesscommaitem + \fi\fi} + +\def\doprocesscommaitem + {\futurelet\nexttoken\dodoprocesscommaitem} + +%D Empty arguments are not processed. Empty items (\type{,,}) +%D however are treated. We have to check for the special case +%D \type{[{a,b,c}]}. +%D +%D \starttyping +%D \def\processcommalist[% +%D {\futurelet\nexttoken\docheckcommaitem} +%D +%D \def\docheckcommaitem% +%D {\ifx\nexttoken]% +%D \let\nextcommaitem\gobbletwoarguments +%D \else\ifx\nexttoken\bgroup +%D \let\nextcommaitem\doprocesscommalistA +%D \else +%D \let\nextcommaitem\doprocesscommalistB +%D \fi\fi +%D \nextcommaitem} +%D +%D \def\doprocesscommalistA#1#2]#3% +%D {\global\advance\commalevel 1 +%D \long\expandafter\def\csname\s!next\the\commalevel\endcsname##1,% +%D {#3{##1}\doprocesscommaitem}% +%D \doprocesscommaitem{#1}#2,]\relax +%D \global\advance\commalevel -1 } +%D +%D \def\doprocesscommalistB#1]#2% +%D {\global\advance\commalevel 1 +%D \long\expandafter\def\csname\s!next\the\commalevel\endcsname##1,% +%D {#2{##1}\doprocesscommaitem}% +%D \doprocesscommaitem#1,]\relax +%D \global\advance\commalevel -1 } +%D \stoptyping +%D +%D However, this is not a the most straightforward solution! +%D We can misuse one of \TEX's hidden features, and prepend +%D and remove a \type {\relax}. By the way, although it +%D involves less testing, this cleaner alternative is not +%D faster. + +\def\processcommalist[% + {\futurelet\nexttoken\docheckcommaitem} + +\def\docheckcommaitem + {\ifx\nexttoken]% + \expandafter\gobblethreearguments + \else + \expandafter\doprocesscommalist + \fi + \relax} % this one preserved the next {} + +\def\doprocesscommalist#1]#2% + {\global\advance\commalevel \plusone + \long\expandafter\def\csname\s!next\the\commalevel\endcsname##1,% + {#2{##1}\doprocesscommaitem}% + \@EA\dodoprocesscommaitem\gobbleoneargument#1,]\relax + \global\advance\commalevel \minusone } + +%D One way of quitting a commalist halfway is: + +\def\quitcommalist + {\begingroup\let\doprocesscommaitem\doquitcommalist} + +\def\doquitcommalist#1]% + {\endgroup} + +\def\quitprevcommalist + {\begingroup\let\doprocesscommaitem\doquitprevcommalist} + +\def\doquitprevcommalist#1]% + {\let\doprocesscommaitem\doquitcommalist} + +%D The hack we used for checking the next character +%D \type {\doifnextcharelse} is also used here. + +\def\:{\redoprocesscommaitem} + +\expandafter\def\: {\futurelet\nexttoken\dodoprocesscommaitem} + +%D The previous examples lead to: +%D +%D \getbuffer + +%D When a list is saved in a macro, we can use a construction +%D like: +%D +%D \starttyping +%D \expandafter\processcommalist\expandafter[\list]\command +%D \stoptyping +%D +%D Such solutions suit most situations, but we wanted a bit +%D more. +%D +%D \starttyping +%D \processcommacommand[string,\stringset,string]\commando +%D \stoptyping +%D +%D where \type{\stringset} is a predefined set, like: +%D +%D \starttyping +%D \def\first{aap,noot,mies} +%D \def\second{laatste} +%D +%D \processcommacommand[\first]\message +%D \processcommacommand[\first,second,third]\message +%D \processcommacommand[\first,between,\second]\message +%D \stoptyping +%D +%D Commands that are part of the list are expanded, so the +%D use of this macro has its limits. + +\def\processcommacommand[#1]% + {\expanded{\processcommalist[#1]}} + +%D The argument to \type{\command} is not delimited. Because +%D we often use \type{[]} as delimiters, we also have: +%D +%D \starttyping +%D \processcommalistwithparameters[string,string,...]\command +%D \stoptyping +%D +%D where \type{\command} looks like: +%D +%D \starttyping +%D \def\command[#1]{... #1 ...} +%D \stoptyping + +\def\processcommalistwithparameters[#1]#2% + {\def\docommand##1{#2[##1]}% + \processcommalist[#1]\docommand} + +%D \macros +%D {processaction, +%D processfirstactioninset, +%D processallactionsinset} +%D +%D \CONTEXT\ makes extensive use of a sort of case or switch +%D command. Depending of the presence of one or more provided +%D items, some actions is taken. These macros can be nested +%D without problems. +%D +%D \starttyping +%D \processaction [x] [a=>\a,b=>\b,c=>\c] +%D \processfirstactioninset [x,y,z] [a=>\a,b=>\b,c=>\c] +%D \processallactionsinset [x,y,z] [a=>\a,b=>\b,c=>\c] +%D \stoptyping +%D +%D We can supply both a \type{default} action and an action +%D to be undertaken when an \type{unknown} value is met: +%D +%D \starttyping +%D \processallactionsinset +%D [x,y,z] +%D [ a=>\a, +%D b=>\b, +%D c=>\c, +%D default=>\default, +%D unknown=>\unknown{... \commalistelement ...}] +%D \stoptyping +%D +%D When \type{#1} is empty, this macro scans list \type{#2} for +%D the keyword \type{default} and executed the related action +%D if present. When \type{#1} is non empty and not in the list, +%D the action related to \type{unknown} is executed. Both +%D keywords must be at the end of list \type{#2}. Afterwards, +%D the actually found keyword is available in +%D \type{\commalistelement}. An advanced example of the use of +%D this macro can be found in \PPCHTEX, where we completely +%D rely on \TEX\ for interpreting user supplied keywords like +%D \type{SB}, \type{SB1..6}, \type{SB125} etc. +%D +%D Even a quick glance at the macros below show some overlap, +%D which means that more efficient alternatives are possible. +%D Because these macro's are very sensitive to subtle changes, +%D we've decided to present the readable originals first +%D Maybe these these macros look complicated, but this is a +%D direct result of the support of nesting. Protection is only +%D applied in \type{\processaction}. +%D +%D \starttyping +%D \newcount\processlevel +%D +%D \def\processaction[#1]#2[#3]% +%D {\doifelse{#1}{} +%D {\def\p!compareprocessaction[##1=>##2]% +%D {\edef\!!stringa{##1}% +%D \ifx\!!stringa\s!default +%D \def\commalistelement{#1}% +%D ##2% +%D \fi}} +%D {\let\donottest=\dontprocesstest +%D \edef\!!stringb{#1}% +%D \let\donottest=\doprocesstest +%D \def\p!compareprocessaction[##1=>##2]% +%D {\edef\!!stringa{##1}% +%D \ifx\!!stringa\!!stringb +%D \def\commalistelement{#1}% +%D ##2% +%D \let\p!doprocessaction=\gobbleoneargument +%D \else\ifx\!!stringa\s!unknown +%D \def\commalistelement{#1}% +%D ##2% +%D \fi\fi}}% +%D \def\p!doprocessaction##1% +%D {\p!compareprocessaction[##1]}% +%D \processcommalist[#3]\p!doprocessaction} +%D +%D \def\processfirstactioninset[#1]#2[#3]% +%D {\doifelse{#1}{} +%D {\processaction[][#3]} +%D {\def\p!compareprocessaction[##1=>##2][##3]% +%D {\edef\!!stringa{##1}% +%D \edef\!!stringb{##3}% +%D \ifx\!!stringa\!!stringb +%D \def\commalistelement{##3}% +%D ##2% +%D \let\p!doprocessaction=\gobbleoneargument +%D \let\p!dodoprocessaction=\gobbleoneargument +%D \else\ifx\!!stringa\s!unknown +%D \def\commalistelement{##3}% +%D ##2% +%D \fi\fi}% +%D \def\p!doprocessaction##1% +%D {\def\p!dodoprocessaction####1% +%D {\p!compareprocessaction[####1][##1]}% +%D \processcommalist[#3]\p!dodoprocessaction}% +%D \processcommalist[#1]\p!doprocessaction}} +%D +%D \def\processallactionsinset[#1]#2[#3]% +%D {\doifelse{#1}{} +%D {\processaction[][#3]} +%D {\advance\processlevel by 1 +%D \def\p!compareprocessaction[##1=>##2][##3]% +%D {\edef\!!stringa{##1}% +%D \edef\!!stringb{##3}% +%D \ifx\!!stringa\!!stringb +%D \def\commalistelement{##3}% +%D ##2% +%D \let\p!dodoprocessaction=\gobbleoneargument +%D \else\ifx\!!stringa\s!unknown +%D \def\commalistelement{##3}% +%D ##2% +%D \fi\fi}% +%D \setvalue{\s!do\the\processlevel}##1% +%D {\def\p!dodoprocessaction####1% +%D {\p!compareprocessaction[####1][##1]}% +%D \processcommalist[#3]\p!dodoprocessaction}% +%D \processcommalist[#1]{\getvalue{\s!do\the\processlevel}}% +%D \advance\processlevel by -1 }} +%D \stoptyping +%D +%D The gain of speed in the (again) next implementation is +%D around 20\%, depending on the application. + +\newcount\processlevel + +\def\p!compareprocessactionA[#1=>#2][#3]% + {\edef\!!stringb{#1}% + \ifx\!!stringb\s!default + \let\commalistelement\empty + #2% + \fi} + +% \def\p!compareprocessactionB[#1=>#2][#3]% +% {\expandedaction\!!stringb{#1}% +% \ifx\!!stringa\!!stringb +% \def\commalistelement{#3}% +% #2% +% \let\p!doprocessaction\gobbleoneargument +% \else +% \edef\!!stringb{#1}% +% \ifx\!!stringb\s!unknown +% \def\commalistelement{#3}% beware of loops +% #2% +% \fi +% \fi} + +% met \quitcommalist tot meer dan 25\% sneller + +\def\p!compareprocessactionB[#1=>#2][#3]% + {\expandedaction\!!stringb{#1}% + \ifx\!!stringa\!!stringb + \def\commalistelement{#3}% + #2% + \expandafter\quitcommalist + \else + \edef\!!stringb{#1}% + \ifx\!!stringb\s!unknown + \def\commalistelement{#3}% beware of loops + #2% + \fi + \fi} + +\beginTEX + +\def\processaction[#1]#2[#3]% + {\let\donottest\dontprocesstest + \expandedaction\!!stringa{#1}% + \let\donottest\doprocesstest + \ifx\!!stringa\empty + \let\p!compareprocessaction\p!compareprocessactionA + \else + \let\p!compareprocessaction\p!compareprocessactionB + \fi + \def\p!doprocessaction##1% + {\p!compareprocessaction[##1][#1]}% + \processcommalist[#3]\p!doprocessaction + \expandactions} + +\endTEX + +\beginETEX \protected + +\def\processaction[#1]#2[#3]% faster version follows + {\expandedaction\!!stringa{#1}% + \ifx\!!stringa\empty + \let\p!compareprocessaction\p!compareprocessactionA + \else + \let\p!compareprocessaction\p!compareprocessactionB + \fi + \def\p!doprocessaction##1% + {\p!compareprocessaction[##1][#1]}% + \processcommalist[#3]\p!doprocessaction + \expandactions} + +\endETEX + +% \def\p!compareprocessactionC[#1=>#2][#3]% +% {\expandedaction\!!stringa{#1}% +% \expandedaction\!!stringb{#3}% +% \ifx\!!stringa\!!stringb +% \def\commalistelement{#3}% +% #2% +% \let\p!doprocessaction\gobbleoneargument +% \let\p!dodoprocessaction\gobbleoneargument +% \else +% \edef\!!stringa{#1}% +% \ifx\!!stringa\s!unknown +% \def\commalistelement{#3}% +% #2% +% \fi +% \fi} + +\def\p!compareprocessactionC[#1=>#2][#3]% + {\expandedaction\!!stringa{#1}% + \expandedaction\!!stringb{#3}% + \ifx\!!stringa\!!stringb + \def\commalistelement{#3}% + #2% + \expandafter\quitprevcommalist + \else + \edef\!!stringa{#1}% + \ifx\!!stringa\s!unknown + \def\commalistelement{#3}% + #2% + \fi + \fi} + +\def\processfirstactioninset[#1]#2[#3]% faster version follows + {\expandedaction\!!stringa{#1}% + \ifx\!!stringa\empty + \processaction[][#3]% + \else + \def\p!doprocessaction##1% + {\def\p!dodoprocessaction####1% + {\p!compareprocessactionC[####1][##1]}% + \processcommalist[#3]\p!dodoprocessaction}% + \processcommalist[#1]\p!doprocessaction + \fi + \expandactions} + +% \def\p!compareprocessactionD[#1=>#2][#3]% +% {\expandedaction\!!stringa{#1}% +% \expandedaction\!!stringb{#3}% +% \ifx\!!stringa\!!stringb +% \def\commalistelement{#3}% +% #2% +% \let\p!dodoprocessaction\gobbleoneargument +% \else +% \edef\!!stringa{#1}% +% \ifx\!!stringa\s!unknown +% \def\commalistelement{#3}% +% #2% +% \fi +% \fi} + +\def\p!compareprocessactionD[#1=>#2][#3]% + {\expandedaction\!!stringa{#1}% + \expandedaction\!!stringb{#3}% + \ifx\!!stringa\!!stringb + \def\commalistelement{#3}% + #2% + \expandafter\quitcommalist + \else + \edef\!!stringa{#1}% + \ifx\!!stringa\s!unknown + \def\commalistelement{#3}% + #2% + \fi + \fi} + +\def\doprocessallactionsinset + {\csname\s!do\the\processlevel\endcsname} + +\def\processallactionsinset[#1]#2[#3]% faster version follows + {\expandedaction\!!stringa{#1}% + \ifx\!!stringa\empty + \processaction[][#3]% + \else + \advance\processlevel \plusone + \expandafter\def\csname\s!do\the\processlevel\endcsname##1% + {\def\p!dodoprocessaction####1% + {\p!compareprocessactionD[####1][##1]}% + \processcommalist[#3]\p!dodoprocessaction}% + \processcommalist[#1]\doprocessallactionsinset + \advance\processlevel \minusone + \fi + \expandactions} + +%D We can speed up these macros a bit when we use a dedicated +%D commalist processor, one that avoids passing the (often) +%D big action list. + +\beginTEX + +\def\processaction[#1]#2[% + {\let\donottest\dontprocesstest + \expandedaction\!!stringa{#1}% + \let\donottest\doprocesstest + \ifx\!!stringa\empty + \let\p!compareprocessaction\p!compareprocessactionA + \else + \let\p!compareprocessaction\p!compareprocessactionB + \fi + \def\p!doprocessaction##1% + {\p!compareprocessaction[##1][#1]}% + \processnextcommalist\relax\expandactions\p!doprocessaction[} + +\endTEX + +\beginETEX + +\def\processaction[#1]#2[% + {\expandedaction\!!stringa{#1}% + \ifx\!!stringa\empty + \let\p!compareprocessaction\p!compareprocessactionA + \else + \let\p!compareprocessaction\p!compareprocessactionB + \fi + \def\p!doprocessaction##1% + {\p!compareprocessaction[##1][#1]}% + \processnextcommalist\relax\expandactions\p!doprocessaction[} + +\endETEX + +\def\processfirstactionsinset[#1]% + {\expandedaction\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\processaction + \else + \expandafter\processfirstactionsinsetindeed + \fi + [#1]} + +\def\processfirstactioninsetindeed[#1]#2[#3]% + {\def\p!doprocessaction##1% + {\def\p!dodoprocessaction####1% + {\p!compareprocessactionC[####1][##1]}% + \processcommalist[#3]\p!dodoprocessaction}% + \processcommalist[#1]\p!doprocessaction + \expandactions} + +\def\processallactionsinset[#1]% + {\expandedaction\!!stringa{#1}% + \ifx\!!stringa\empty + \expandafter\processaction + \else + \expandafter\processallactionsinsetindeed + \fi + [#1]} + +\def\processallactionsinsetindeed[#1]#2[#3]% + {\advance\processlevel \plusone + \expandafter\def\csname\s!do\the\processlevel\endcsname##1% + {\def\p!dodoprocessaction####1% + {\p!compareprocessactionD[####1][##1]}% + \processcommalist[#3]\p!dodoprocessaction}% + \processcommalist[#1]\doprocessallactionsinset + \advance\processlevel \minusone + \expandactions} + +\def\processnextcommalist#1#2#3[#4#5]% + {#1% + \let\nexttoken#4% + \global\advance\commalevel \plusone + \long\expandafter\def\csname\s!next\the\commalevel\endcsname##1,% + {#3{##1}\doprocesscommaitem}% + \dodoprocesscommaitem#4#5,]\relax + \global\advance\commalevel \minusone + #2} + +%D I do have an even faster version (saving 3 sec on a 13 +%D sec run for 50K invocations, but normally we don't have +%D that many calls and that alternative uses more macros and is +%D even less readable. What we did add, was \type {\@EA}, so +%D that we can pass a command. + +%D \macros +%D {unexpandedprocessaction, +%D unexpandedprocessfirstactioninset, +%D unexpandedprocessallactionsinset} +%D +%D Now what are those expansion commands doing there. Well, +%D sometimes we want to compare actions that may consist off +%D commands (i.e. are no constants). In such occasions we can +%D use the a bit slower alternatives: + +\def\unexpandedprocessfirstactioninset{\dontexpandactions\processfirstactioninset} +\def\unexpandedprocessaction {\dontexpandactions\processaction} +\def\unexpandedprocessallactionsinset {\dontexpandactions\processallactionsinset} + +%D By default we expand actions: + +\def\expandactions{\let\expandedaction\edef} \expandactions + +%D But when needed we convert the strings to meaningful +%D sequences of characters. + +\def\unexpandedaction#1>{} + +\def\noexpandedaction#1#2% + {\def\convertedargument{#2}% + \@EA\edef\@EA#1\@EA{\@EA\unexpandedaction\meaning\convertedargument}} + +\def\dontexpandactions% + {\let\expandedaction\noexpandedaction} + +%D \macros +%D {getfirstcharacter,firstcharacter, doiffirstcharacter} +%D +%D Sometimes the action to be undertaken depends on the +%D next character. This macro get this character and puts it in +%D \type{\firstcharacter}. +%D +%D \starttyping +%D \getfirstcharacter {string} +%D \stoptyping +%D +%D A two step expansion is used to prevent problems with +%D complicated arguments, for instance arguments that +%D consist of two or more expandable tokens. + +\def\dogetfirstcharacter#1#2\\% + {\def\firstcharacter{#1}} + +\def\getfirstcharacter#1% + {\edef\!!stringa{#1}% + \expandafter\dogetfirstcharacter\!!stringa\\} + +\def\doiffirstcharelse#1#2% char string +% kort (maar onleesbaar) +% {\expanded{\dogetfirstcharacter#2}\\\doifelse{#1}\firstcharacter} +% korter (en begrijpelijk)) + {\getfirstcharacter{#2}\doifelse{#1}\firstcharacter} +% snel (maar zelden gebruikt, dus niet zo belangrijk) +% {\getfirstcharacter{#2}% +% \edef\!!stringa{#1}% +% \ifx\!!stringa\firstcharacter +% \expandafter\firstoftwoarguments +% \else +% \expandafter\secondoftwoarguments +% \fi} + +%D \macros +%D {doifinstringelse, doifincsnameelse} +%D +%D We can check for the presence of a substring in a given +%D sequence of characters. +%D +%D \starttyping +%D \doifinsetelse {substring} {string} {then ...} {else ...} +%D \stoptyping +%D +%D An application of this command can be found further on. +%D Like before, we first show some alternatives, like the one +%D we started with: +%D +%D \starttyping +%D \long\def\p!doifinstringelse#1#2#3#4% +%D {\def\pp!doifinstringelse##1#1##2##3\war% +%D {\if##2@% +%D #4% +%D \else +%D #3% +%D \fi}% +%D \pp!doifinstringelse#2#1@@\war} +%D +%D \def\doifinstringelse% +%D {\ExpandBothAfter\p!doifinstringelse} +%D \stoptyping +%D +%D After this we came to: +%D +%D \starttyping +%D \def\p!doifinstringelse#1#2% +%D {\def\pp!doifinstringelse##1#1##2##3\war% +%D {\if##2@}% +%D \pp!doifinstringelse#2#1@@\war} +%D +%D \def\doifinstringelse#1#2#3#4% +%D {\ExpandBothAfter\p!doifinstringelse{#1}{#2}% +%D #4% +%D \else +%D #3% +%D \fi} +%D \stoptyping +%D +%D Sometimes the second argument is passed as a macro. By +%D postponing the expansion of this macro, we gain quite some +%D run time, simply because the less tokens we pass, the faster +%D \TEX\ runs. So finally the definition became: + +\long\def\doifinstringelse#1% + {\edef\@@@instring{#1}% expand #1 here + \ifx\@@@instring\empty + \@EA\thirdofthreearguments + \else + \@EA\dodoifinstringelse + \fi} + +\long\def\dodoifinstringelse#1% + {\p!doifinstringelse\@@@instring{#1}% + \@EA\firstoftwoarguments + \else + \@EA\secondoftwoarguments + \fi} + +\long\def\doifinstring#1%% + {\edef\@@@instring{#1}% expand #1 here + \ifx\@@@instring\empty + \@EA\gobbletwoarguments + \else + \@EA\dodoifinstring + \fi} + +\long\def\dodoifinstring#1% + {\p!doifinstringelse\@@@instring{#1}% + \@EA\firstofoneargument + \else + \@EA\gobbleoneargument + \fi} + +%D \starttyping +%D \beginTEX +%D +%D \long\def\p!doifinstringelse#1#2% +%D {\long\def\pp!doifinstringelse##1#1##2##3\war% +%D {\csname if\if##2@fals\else tru\fi e\endcsname}% +%D \expanded{\pp!doifinstringelse#2#1@@\noexpand\war}} % expand #2 here +%D +%D \endTEX +%D +%D \beginETEX \unless +%D +%D \long\def\p!doifinstringelse#1#2% +%D {\long\def\pp!doifinstringelse##1#1##2##3\war% +%D {\unless\if##2@}% +%D \expanded{\pp!doifinstringelse#2#1@@\noexpand\war}} % expand #2 here +%D +%D \endETEX +%D \stoptyping +%D +%D And then \unknown\ after a couple of years, we ran into a +%D situation where \type {##2} was something \type {{bla}}. So +%D finally we need to use an auxiliary macro, otherwise we get +%D funny strings in the output. + +% \long\def\p!doifinstringelse#1#2% ##2 can be {abc} +% {\long\@EA\def\@EA\pp!doifinstringelse\@EA##\@EA1#1##2##3\war % expand #1 here +% {\ppp!doifinstringelse##2\war}% +% \expanded{\pp!doifinstringelse#2#1@@\noexpand\war}} % expand #2 here +% +% \beginTEX +% +% \def\ppp!doifinstringelse#1#2\war% +% {\csname if\ifx#1@fals\else tru\fi e\endcsname}% +% +% \endTEX +% +% \beginETEX \unless +% +% \def\ppp!doifinstringelse#1#2\war% +% {\unless\ifx#1@} +% +% \endETEX + +\beginETEX + +\long\def\p!doifinstringelse#1#2% ##2 can be {abc} + {\long\@EA\def\@EA\pp!doifinstringelse\@EA##\@EA1#1##2##3\war % expand #1 here + {\unless\if##2@}% +% \expanded{\pp!doifinstringelse#2#1@@\noexpand\war}} % expand #2 here + \expanded{\pp!doifinstringelse#2#1}@@\war} % expand #2 here + +\endETEX + +\beginTEX + +\long\def\p!doifinstringelse#1#2% ##2 can be {abc} + {\long\@EA\def\@EA\pp!doifinstringelse\@EA##\@EA1#1##2##3\war % expand #1 here + {\csname if\if##2@fals\else tru\fi e\endcsname}% + %\expanded{\pp!doifinstringelse#2#1@@\noexpand\war}} % expand #2 here + \expanded{\pp!doifinstringelse#2#1}@@\war} % expand #2 here + +\endTEX + +%D The next alternative proved to be upto twice as fast on +%D tasks like checking reserved words in pretty verbatim +%D typesetting! This is mainly due to the fact that passing +%D (expanded) strings is much slower that passing a macro. +%D +%D \starttyping +%D \doifincsnameelse {substring} {\string} {then ...} {else ...} +%D \stoptyping +%D +%D Where \type{\doifinstringelse} does as much expansion as +%D possible, the latter alternative does minimal (one level) +%D expansion. + +\beginTEX + +\long\def\p!doifincsnameelse#1#2% + {\long\def\pp!doifincsnameelse##1#1##2##3\war + {\csname if\if##2@fals\else tru\fi e\endcsname}% + \@EA\pp!doifincsnameelse#2#1@@\war} + +\endTEX + +\beginETEX \unless + +\long\def\p!doifincsnameelse#1#2% + {\long\def\pp!doifincsnameelse##1#1##2##3\war + {\unless\if##2@}% + \@EA\pp!doifincsnameelse#2#1@@\war} + +\endETEX + +\long\def\doifincsnameelse#1#2% % #3#4% + {\edef\@@@instring{#1}% + \@EA\p!doifincsnameelse\@EA{\@@@instring}{#2}% % #3\else#4\fi} + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +%D \macros +%D {doifnumberelse} +%D +%D The next macro executes a command depending of the outcome +%D of a test on numerals. This is probably one of the fastest +%D test possible, exept from a less robust 10||step +%D \type{\if}||ladder or some tricky \type{\lcode} checking. +%D +%D \starttyping +%D \doifnumberelse {string} {then ...} {else ...} +%D \stoptyping +%D +%D The macro accepts \type{123}, \type{abc}, \type{{}}, +%D \type{\getal} and \type{\the\count...}. This macro is a +%D rather dirty one. +%D +%D \starttyping +%D \long\def\doifnumberelse#1#2#3% +%D {\begingroup\donefalse +%D \ifcase1#1\or\or\or\or\or\or\or\or\or\else\donetrue\fi +%D \ifdone\endgroup#2\else\endgroup#3\fi} +%D \stoptyping +%D +%D Or better: +%D +%D \starttyping +%D \long\def\doifnumberelse#1% +%D {\begingroup\donefalse +%D \ifcase1#1\or\or\or\or\or\or\or\or\or\else\donetrue\fi +%D \ifdone +%D \endgroup\expandafter\firstoftwoarguments +%D \else +%D \endgroup\expandafter\secondoftwoarguments +%D \fi} +%D \stoptyping +%D +%D A previous implementation was: +%D +%D \starttyping +%D \long\def\doifnumberelse#1#2#3% +%D {\getfirstcharacter{#1}% +%D \@EA\p!doifinstringelse\@EA{\firstcharacter}{1234567890}% +%D #2% +%D \else +%D #3% +%D \fi} +%D \starttyping +%D +%D And before we had \type{\p!doifinstringelse} available, we +%D used: +%D +%D \starttyping +%D \def\doifnumberelse#1% +%D {\getfirstcharacter{#1}% +%D \rawdoifinsetelse{\firstcharacter}{1,2,3,4,5,6,7,8,9,0}} +%D \stoptyping +%D +%D The implementation using \type {\ifcase} is much faster, but +%D the next one is not, not even when testing milion calls. +%D +%D \starttyping +%D \newif\ifitsanumber +%D +%D \long\def\isitanumber#1% +%D {\itsanumberfalse +%D \ifcase1#1\or\or\or\or\or\or\or\or\or\else\itsanumbertrue\fi} +%D +%D \long\def\doifnumberelse#1#2#3% +%D {\isitanumber{#1}\ifitsanumber#2\else#3\fi} +%D \stoptyping +%D +%D After a while the next evolved and this one is the one we +%D will use. This one is some 5\% faster than the group/done +%D one (partly because it does not have to pass arguments). +%D Even more important is that this alternative is fully +%D expandable! + +\long\def\doifnumberelse#1% + {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space + \expandafter\secondoftwoarguments + \else + \expandafter\firstoftwoarguments + \fi} + +%D \macros +%D {makerawcommalist, +%D rawdoinsetelse, +%D rawprocesscommalist, +%D rawprocessaction} +%D +%D Some of the commands mentioned earlier are effective but +%D slow. When one is desperately in need of faster alternatives +%D and when the conditions are predictable safe, the \type{\raw} +%D alternatives come into focus. A major drawback is that +%D they do not take \type{\c!constants} into account, simply +%D because no expansion is done. This is no problem with +%D \type{\rawprocesscommalist}, because this macro does not +%D compare anything. Expandable macros are permitted as search +%D string. +%D +%D \starttyping +%D \makerawcommalist[string,string,...]\stringlist +%D \rawdoifinsetelse{string}{string,...}{...}{...} +%D \rawprocesscommalist[string,string,...]\commando +%D \rawprocessaction[x][a=>\a,b=>\b,c=>\c] +%D \stoptyping +%D +%D Spaces embedded in the list, for instance after commas, +%D spoil the search process. The gain in speed depends on the +%D length of the argument (the longer the argument, the less +%D we gain). +%D +%D The slow alternative looks like: +%D +%D \starttyping +%D \def\makerawcommalist[#1]#2% +%D {\def\appendtocommalist##1% +%D {\doifelse{#2}{} +%D {\edef#2{##1}} +%D {\edef#2{#2,##1}}}% +%D \def#2{}% +%D \processcommalist[#1]\appendtocommalist} +%D \stoptyping +%D +%D But we prefer: + +% \appendtocommalist is defined in syst-ext + +\def\makerawcommalist[#1]#2% use \processnext ... here + {\def\domakerawcommalist##1% we don't expand ##1 + {\ifx#2\empty + \def#2{##1}% + \else + \@EA\def\@EA#2\@EA{#2,##1}% + \fi}% + \let#2\empty + \processcommalist[#1]\domakerawcommalist} + +\def\rawprocesscommaitem#1,#2% #2 eats up preceding space + {\if]#1\else + \csname\s!next\the\commalevel\endcsname{#1}% + \expandafter\rawprocesscommaitem + \fi#2} + +\def\rawprocesscommalist[#1]#2% accepteert ook [\cs] + {\global\advance\commalevel \plusone + \expandafter\let\csname\s!next\the\commalevel\endcsname#2% + \expandafter\rawprocesscommaitem#1,],% \relax + \global\advance\commalevel \minusone } + +\def\rawprocesscommacommand[#1]% not really needed + {\expanded{\rawprocesscommalist[#1]}} + +% \def\rawdoifinsetelse#1#2{\doifinstringelse{,#1,}{,#2,}} +% \def\rawdoifinset #1#2{\doifinstring {,#1,}{,#2,}} + +\def\@@rawempty{,,} + +\long\def\rawdoifinsetelse#1% + {\edef\@@@instring{,#1,}% expand #1 here + \ifx\@@@instring\@@rawempty + \@EA\thirdofthreearguments + \else + \@EA\rawdodoifinsetelse + \fi} + +\long\def\rawdodoifinsetelse#1% + {\p!doifinstringelse\@@@instring{,#1,}% + \@EA\firstoftwoarguments + \else + \@EA\secondoftwoarguments + \fi} + +\long\def\rawdoifinset#1% + {\edef\@@@instring{,#1,}% expand #1 here + \ifx\@@@instring\@@rawempty + \@EA\gobbletwoarguments + \else + \@EA\rawdodoifinset + \fi} + +\long\def\rawdodoifinset#1%% + {\p!doifinstringelse\@@@instring{,#1,}% + \@EA\firstofoneargument + \else + \@EA\gobbleoneargument + \fi} + +%D Some more raw material: + +\def\p!rawprocessaction[#1][#2]% + {\def\pp!rawprocessaction##1,#1=>##2,##3\war% + {\if##3@\else + \def\!!processaction{##2}% + \fi}% + \pp!rawprocessaction,#2,#1=>,@\war} + +\def\rawprocessaction[#1]#2[#3]% + {\edef\!!stringa{#1}% + \edef\!!stringb{undefined}% better \!!undefined + \let\!!processaction\!!stringb + \ifx\!!stringa\empty + \@EA\p!rawprocessaction\@EA[\s!default][#3]% + \else + \expandafter\p!rawprocessaction\expandafter[\!!stringa][#3]% + \ifx\!!processaction\!!stringb + \@EA\p!rawprocessaction\@EA[\s!unknown][#3]% + \fi + \fi + \ifx\!!processaction\!!stringb + \else + \!!processaction + \fi} + +% not needed +% +% \def\rawprocessallactionsinset[#1]#2[#3]% +% {\def\docommando##1% +% {\rawprocessaction[##1][#3]}% +% \processcommalist[#1]\docommando} + +%D When we process the list \type{a,b,c,d,e}, the raw routine +%D takes over 30\% less time, when we feed $20+$ character +%D strings we gain about 20\%. Alternatives which use +%D \type{\futurelet} perform worse. Part of the speedup is +%D due to the \type{\let} and \type{\expandafter} in the test. + +% %D \macros +% %D {processunexpandedcommalist} +% %D +% %D When processing commalists, the arguments are expanded. The +% %D main reason for doing so lays in the fact that these +% %D macros are used for interfacing. The next alternative can be used +% %D for +% %D +% %D \starttyping +% %D \processunexpandedcommalist +% %D [\alfa\beta,\gamma,\delta\epsilon] +% %D \handleitem +% %D \stoptyping +% %D +% %D This time nesting is not supported. +% +% %\def\processunexpandedcommaitem#1,% +% % {\if]\noexpand#1% +% % \let\nextcommaitem\relax +% % \else +% % \handleunexpandedcommaitem{#1}% +% % \let\nextcommaitem\processunexpandedcommaitem +% \fi +% \nextcommaitem} +% +% faster: +% +% \def\processunexpandedcommaitem#1,% +% {\if]\noexpand#1\else +% \handleunexpandedcommaitem{#1}% +% \expandafter\processunexpandedcommaitem +% \fi} +% +% \def\processunexpandedcommalist[#1]#2% +% {\def\handleunexpandedcommaitem{#2}% +% \processunexpandedcommaitem#1,],}% \relax} +% +% %D Or faster: +% +% \def\processunexpandedcommaitem#1,% +% {\if]\noexpand#1\else +% \handleunexpandedcommaitem{#1}% +% \expandafter\processunexpandedcommaitem +% \fi} + +%D \macros +%D {dosetvalue,dosetevalue,dosetgvalue,docopyvalue,doresetvalue, +%D dogetvalue} +%D +%D When we are going to do assignments, we have to take +%D multi||linguality into account. For the moment we keep +%D things simple and single||lingual. +%D +%D \starttyping +%D \dosetvalue {label} {variable} {value} +%D \dosetevalue {label} {variable} {value} +%D \dosetgvalue {label} {variable} {value} +%D \docopyvalue {to label} {from label} {variable} +%D \doresetvalue {label} {variable} +%D \stoptyping +%D +%D These macros are in fact auxiliary ones and are not meant +%D for use outside the assignment macros. + +\def\dosetvalue#1#2% #3 + {\@EA\def\csname#1#2\endcsname} % {#3}} + +\def\dosetevalue#1#2% #3 + {\@EA\edef\csname#1#2\endcsname} % {#3}} + +\def\dosetgvalue#1#2% #3 + {\@EA\gdef\csname#1#2\endcsname} % {#3}} + +\def\doresetvalue#1#2% + {\@EA\let\csname#1#2\endcsname\empty} + +\def\doignorevalue#1#2#3% + {\@EA\let\csname#1#2\endcsname\empty} + +\def\docopyvalue#1#2#3% + {\@EA\def\csname#1#3\endcsname{\csname#2#3\endcsname}} + +%D \macros +%D {doassign,undoassign,doassignempty} +%D +%D Assignments are the backbone of \CONTEXT. Abhorred by the +%D concept of style file hacking, we took a considerable effort +%D in building a parameterized system. Unfortunately there is a +%D price to pay in terms of speed. Compared to other packages +%D and taking the functionality of \CONTEXT\ into account, the +%D total size of the format file is still very acceptable. Now +%D how are these assignments done. +%D +%D Assignments can be realized with: +%D +%D \starttyping +%D \doassign[label][variable=value] +%D \undoassign[label][variable=value] +%D \stoptyping +%D +%D and: +%D +%D \starttyping +%D \doassignempty[label][variable=value] +%D \stoptyping +%D +%D Assignments like \type{\doassign} are compatible with: +%D +%D \starttyping +%D \def\labelvariable{value} +%D \stoptyping +%D +%D We do check for the presence of an \type{=} and loudly +%D complain of it's missed. We will redefine this macro later +%D on, when a more advanced message mechanism is implemented. + +\newif\iferrorisfatal + +\def\waitonfatalerror + {\iferrorisfatal\wait\fi} + +\def\showassignerror#1#2% + {\writestatus{setup}{missing or ungrouped '=' after '#1' in line #2}% + \waitonfatalerror} + +%\def\p!doassign#1[#2][#3=#4=#5]% +% {\let\donottest\dontprocesstest +% \edef\!!stringa{#5}% +% \let\!!stringb\relax +% \let\donottest\doprocesstest +% \ifx\!!stringa\!!stringb +% \showassignerror{#3}% +% \else +% #1{#2}{#3}{#4}% +% \fi} + +\def\p!doassign#1[#2][#3=#4=#5]% + {\ifx\empty#3\else % and definitely not \ifx#3\empty + \ifx\relax#5% + \showassignerror{#3}{\the\inputlineno\space(#2)}% + \else + #1{#2}{#3}{#4}% + \fi + \fi} + +\def\doassign [#1][#2]{\p!doassign\dosetvalue [#1][#2==\relax]} +\def\doeassign [#1][#2]{\p!doassign\dosetevalue [#1][#2==\relax]} +\def\undoassign[#1][#2]{\p!doassign\doresetvalue[#1][#2==\relax]} + +\def\doassignempty[#1][#2=#3]% + {\ifundefined{#1#2}\dosetvalue{#1}{#2}{#3}\fi} + +%D \macros +%D {getparameters,geteparameters,getgparameters, +%D forgetparameters} +%D +%D Using the assignment commands directly is not our +%D ideal of user friendly interfacing, so we take some further +%D steps. +%D +%D \starttyping +%D \getparameters [label] [...=...,...=...] +% %D \forgetparameters [label] [...=...,...=...] +%D \stoptyping +%D +%D Again, the label identifies the category a variable +%D belongs to. The second argument can be a comma separated +%D list of assignments. +%D +%D \starttyping +%D \getparameters +%D [demo] +%D [alfa=1, +%D beta=2] +%D \stoptyping +%D +%D is equivalent to +%D +%D \starttyping +%D \def\demoalfa{1} +%D \def\demobeta{2} +%D \stoptyping +%D +%D +%D In the pre||multi||lingual stadium \CONTEXT\ took the next +%D approach. With +%D +%D \starttyping +%D \def\??demo {@@demo} +%D \def\!!alfa {alfa} +%D \def\!!beta {beta} +%D \stoptyping +%D +%D calling +%D +%D \starttyping +%D \getparameters +%D [\??demo] +%D [\!!alfa=1, +%D \!!beta=2] +%D \stoptyping +%D +%D lead to: +%D +%D \starttyping +%D \def\@@demoalfa{1} +%D \def\@@demobeta{2} +%D \stoptyping +%D +%D Because we want to be able to distinguish the \type{!!} +%D pre||tagged user supplied variables from internal +%D counterparts, we will introduce a slightly different tag in +%D the multi||lingual modules. There we will use \type{c!} or +%D \type{v!}, depending on the context. +%D +%D By calling \type{\p!doassign} directly, we save ourselves +%D some argument passing and gain some speed. Whatever +%D optimizations we do, this command will always be one of the +%D bigger bottlenecks. +%D +%D The alternative \type{\geteparameters} --- it's funny to +%D see that this alternative saw the light so lately --- can be +%D used to do expanded assigments. + +\def\dogetparameters#1[#2]#3[#4]% + {\def\p!dogetparameter##1% + {\p!doassign#1[#2][##1==\relax]}% + \processcommalist[#4]\p!dogetparameter} + +\def\getparameters {\dogetparameters\dosetvalue} +\def\geteparameters {\dogetparameters\dosetevalue} +\def\getgparameters {\dogetparameters\dosetgvalue} +\def\forgetparameters{\dogetparameters\doignorevalue} + +\let\getexpandedparameters=\geteparameters + +%D This one is slightly faster: + +\def\dogetparameters#1[#2]#3[#4% + {\if\noexpand#4]% + \expandafter\gobbleoneargument + \else + \def\p!dogetparameter##1{\p!doassign#1[#2][##1==\relax]}% + \expandafter\xdogetparameters + \fi#4} + +\def\xdogetparameters#1]% + {\processcommalist[#1]\p!dogetparameter} + +%D The next alternative is much faster but also uglier. Because +%D in \XML\ processing we will probably set much more parameters +%D than normally we need this faster one. + +\def\dogetparameters#1[#2]#3[#4% + {\if\noexpand#4]% + \expandafter\gobbleoneargument + \else + \def\p!dogetparameter{\p!doassign#1#2}% + \expandafter\xdogetparameters + \fi#4} + +\def\xdogetparameters#1]% + {\xprocesscommaitem#1,],\@relax@} + +% \long\def\xprocesscommaitem#1,#2% #2 takes space before , +% {\if]#1% +% \expandafter\gobbleoneargument +% \else +% \p!dogetparameter\@relax@#1==\@relax@ +% \expandafter\xprocesscommaitem +% \fi#2} + +\long\def\xprocesscommaitem#1,#2% #2 takes space before , + {\if,#1,% dirty trick for testing #1=empty + \@EA\xprocesscommaitem + \else\if]#1% + \@EAEAEA\gobbleoneargument + \else + \p!dogetparameter\@relax@#1==\empty\@relax@ + \@EAEAEA\xprocesscommaitem + \fi\fi#2} + +%D Here we use a slightly different assignment macro: + +% \def\p!doassign#1#2\@relax@#3=#4=#5\@relax@ +% {\ifx\@relax@#5\@EA\xshowassignerror\else\@EA#1\fi{#2}{#3}{#4}} + +% \def\p!doassign#1#2\@relax@#3=#4=#5\@relax@ +% {\ifx#5\empty\@EA\xshowassignerror\else\@EA#1\fi{#2}{#3}{#4}} + +\def\p!doassign#1#2\@relax@#3=#4=#5#6\@relax@ + {\ifx#5\empty + \@EA\xshowassignerror + \else\ifx#5=% + \@EAEAEA#1% + \else + \@EAEAEA\xshowassignerror + \fi\fi + {#2}{#3}{#4}} + +\def\xshowassignerror#1#2#3% + {\showassignerror{#2}{\the\inputlineno\space(#1)}} + +%D Now we also have to change the other macros that depend +%D on this low level one. + +% \def\doassign [#1][#2]{\p!doassign\dosetvalue #1\@relax@#2==\@relax@} +% \def\doeassign [#1][#2]{\p!doassign\dosetevalue #1\@relax@#2==\@relax@} +% \def\undoassign[#1][#2]{\p!doassign\doresetvalue#1\@relax@#2==\@relax@} + +\def\doassign [#1][#2]{\p!doassign\dosetvalue #1\@relax@#2==\empty\@relax@} +\def\doeassign [#1][#2]{\p!doassign\dosetevalue #1\@relax@#2==\empty\@relax@} +\def\undoassign[#1][#2]{\p!doassign\doresetvalue#1\@relax@#2==\empty\@relax@} + +%D \macros +%D {getemptyparameters} +%D +%D Sometimes we explicitly want variables to default to an +%D empty string, so we welcome: +%D +%D \starttyping +%D \getemptyparameters [label] [...=...,...=...] +%D \stoptyping + +\def\getemptyparameters[#1]#2[#3]% + {\def\p!dogetemptyparameter##1% + {\doassignempty[#1][##1]}% + \processcommalist[#3]\p!dogetemptyparameter} + +%D \macros +%D {copyparameters} +%D +%D Some \CONTEXT\ commands take their default setups from +%D others. All commands that are able to provide backgounds +%D or rules around some content, for instance default to the +%D standard command for ruled boxes. Is situations like this +%D we can use: +%D +%D \starttyping +%D \copyparameters [to-label] [from-label] [name1,name2,...] +%D \stoptyping +%D +%D For instance +%D +%D \starttyping +%D \copyparameters +%D [internal][external] +%D [alfa,beta] +%D \stoptyping +%D +%D Leads to: +%D +%D \starttyping +%D \def\internalalfa {\externalalfa} +%D \def\internalbeta {\externalbeta} +%D \stoptyping +%D +%D By using \type{\docopyvalue} we've prepared this command +%D for use in a multi||lingual environment. + +\def\copyparameters[#1]#2[#3]#4[#5]% + {\doifnot{#1}{#3} + {\def\docopyparameter{\docopyvalue{#1}{#3}}% + %\def\docopyparameter##1{\docopyvalue{#1}{#3}{##1}}% + \processcommalist[#5]\docopyparameter}} + +%D \macros +%D {doifassignmentelse} +%D +%D A lot of \CONTEXT\ commands take optional arguments, for +%D instance: +%D +%D \starttyping +%D \dothisorthat[alfa,beta] +%D \dothisorthat[first=foo,second=bar] +%D \dothisorthat[alfa,beta][first=foo,second=bar] +%D \stoptyping +%D +%D Although a combined solution is possible, we prefer a +%D seperation. The next command takes care of propper +%D handling of such multi||faced commands. +%D +%D \starttyping +%D \doifassignmentelse {...} {then ...} {else ...} +%D \stoptyping + +% not robust +% +% \def\doifassignmentelse% +% {\doifinstringelse{=}} +% +% readable +% +% \def\doifassignmentelse#1% +% {\convertargument#1\to\ascii +% \doifinstringelse{=}{\ascii}} + +\def\doifassignmentelse#1% + {\convertargument#1\to\ascii + \doifinstringelse=\ascii} + +%D \macros +%D {ifparameters,checkparameters} +%D +%D A slightly different one is \type{\checkparameters}, which +%D also checks on the presence of a~\type{=}. +%D +%D The boolean \type{\ifparameters} can be used afterwards. +%D Combining both in one \type{\if}||macro would lead to +%D problems with nested \type{\if}'s. +%D +%D \starttyping +%D \checkparameters[argument] +%D \stoptyping + +\newif\ifparameters + +\def\p!checkparameters#1=#2#3\war% + {\if#2@\parametersfalse\else\parameterstrue\fi} + +\def\checkparameters[#1]% + {\p!checkparameters#1=@@\war} + +%D \macros +%D {getfromcommalist,getfromcommacommand, +%D commalistelement, +%D getcommalistsize,getcommacommandsize} +%D +%D It's possible to get an element from a commalist or a +%D command representing a commalist. +%D +%D \starttyping +%D \getfromcommalist [string] [n] +%D \getfromcommacommand [string,\strings,string,...] [n] +%D \stoptyping +%D +%D The difference betwee the two of them is the same as the +%D difference between \type{\processcomma...}. The found string +%D is stored in \type{\commalistelement}. +%D +%D We can calculate the size of a comma separated list by +%D using: +%D +%D \starttyping +%D \getcommalistsize [string,string,...] +%D \getcommacommandsize [string,\strings,string,...] +%D \stoptyping +%D +%D Afterwards, the length is available in the macro +%D \type{\commalistsize} (not a \COUNTER). + +\newcount\commalistcounter + +\def\commalistsize{0} + +\def\p!dogetcommalistsize#1% + {\advance\commalistcounter\plusone} + +\def\getcommalistsize[#1]% + {\commalistcounter\zerocount + \processcommalist[#1]\p!dogetcommalistsize % was [{#1}] + \edef\commalistsize{\the\commalistcounter}} + +\def\getcommacommandsize[#1]% + {\edef\commacommand{#1}% + \scratchtoks\expandafter{\expandafter[\commacommand]}% + \expandafter\getcommalistsize\the\scratchtoks } + +% to be tested first +% +% \def\getcommacommandsize[#1]% +% {\expanded{\getcommalistsize[#1]}} + +% \def\p!dogetfromcommalist#1% +% {\advance\commalistcounter \minusone +% \ifcase\commalistcounter +% \def\commalistelement{#1}% +% \begingroup\def\doprocesscommaitem##1]{\endgroup}% +% \fi} + +\def\p!dogetfromcommalist#1% + {\advance\commalistcounter \minusone + \ifcase\commalistcounter + \def\commalistelement{#1}% + \expandafter\quitcommalist + \fi} + +\def\getfromcommalist[#1]#2[#3]% + {\let\commalistelement\empty + \commalistcounter#3\relax + \processcommalist[#1]\p!dogetfromcommalist} + +% \def\getfromcommacommand[#1]% why so complicated, still needed? +% {\edef\commacommand{#1}% +% \toks0=\expandafter{\expandafter[\commacommand]}% +% \expandafter\getfromcommalist\the\toks0 } + +\def\getfromcommacommand[#1]% + {\expanded{\getfromcommalist[#1]}} + +%D Because 0, 1 and~2 are often asked for, we optimize this +%D macro for those cases. The indirect call however slows +%D down the other cases. +%D +%D \starttyping +%D \def\p!dogetfirstfromcommalist [#1,#2]{\def\commalistelement{#1}} +%D \def\p!dogetsecondfromcommalist[#1,#2,#3]{\def\commalistelement{#2}} +%D \let\p!dogetotherfromcommalist=\getfromcommalist +%D +%D \def\getfromcommalist[#1]#2[#3]% optimized for 0,1,2 +%D {\ifcase#3\relax +%D \let\commalistelement\empty +%D \or +%D \p!dogetfirstfromcommalist[#1,]% +%D \or +%D \p!dogetsecondfromcommalist[#1,,]% +%D \else +%D \p!dogetotherfromcommalist[#1][#3]% +%D \fi} +%D \stoptyping +%D +%D Even worse, this alternative does not strip preceding +%D spaces, which is what we want. So, we stick to the slow +%D alternative. + +%D Watertight (and efficient) solutions are hard to find, due +%D to the handling of braces during parameters passing and +%D scanning. Nevertheless: +%D +%D \startbuffer +%D \def\dosomething#1{(#1=\commalistsize) } +%D +%D \getcommalistsize [\hbox{$a,b,c,d,e,f$}] \dosomething 1 +%D \getcommalistsize [{a,b,c,d,e,f}] \dosomething 1 +%D \getcommalistsize [{a,b,c},d,e,f] \dosomething 4 +%D \getcommalistsize [a,b,{c,d,e},f] \dosomething 4 +%D \getcommalistsize [a{b,c},d,e,f] \dosomething 4 +%D \getcommalistsize [{a,b}c,d,e,f] \dosomething 4 +%D \getcommalistsize [] \dosomething 0 +%D \getcommalistsize [{[}] \dosomething 1 +%D \stopbuffer +%D +%D \typebuffer +%D +%D reports: +%D +%D \getbuffer + +%D \macros +%D {dogetcommalistelement,dogetcommacommandelement} +%D +%D For low level (fast) purposes, we can also use the next +%D alternative, which can handle 8~elements at most. +%D +%D \starttyping +%D \dogetcommalistelement1\from a,b,c\to\commalistelement +%D \stoptyping + +\def\dodogetcommalistelement#1\from#2,#3,#4,#5,#6,#7,#8\to#9% + {\edef#9{\ifcase#1\relax\or#2\or#3\or#4\or#5\or#6\or#7\or#8\fi}} + +% maybe better: +% +% {\@EA\edef\@EA#9\@EA{\ifcase#1\relax\or#2\or#3\or#4\or#5\or#6\or#7\or#8\fi}} + +\def\dogetcommalistelement#1\from#2\to% + {\dodogetcommalistelement#1\from#2,,,,,,\to} + +% check sources + +\def\dogetcommacommandelement#1\from#2\to% + {\@EA\dodogetcommalistelement\@EA#1\@EA\from#2,,,,,,\to} + +%D \macros +%D {dosingleargument,dodoubleargument,dotripleargument, +%D doquadrupleargument,doquintupleargument,dosixtupleargument, +%D doseventupleargument} +%D +%D When working with delimited arguments, spaces and +%D lineendings can interfere. The next set of macros uses +%D \TEX' internal scanner for grabbing everything between +%D arguments. Forgive me the funny names. +%D +%D \starttyping +%D \dosingleargument\commando = \commando[#1] +%D \dodoubleargument\commando = \commando[#1][#2] +%D \dotripleargument\commando = \commando[#1][#2][#3] +%D \doquadrupleargument\commando = \commando[#1][#2][#3][#4] +%D \doquintupleargument\commando = \commando[#1][#2][#3][#4][#5] +%D \dosixtupleargument\commando = \commando[#1][#2][#3][#4][#5][#6] +%D \doseventupleargument\command= \commando[#1][#2][#3][#4][#5][#6][#7] +%D \stoptyping +%D +%D These macros are used in the following way: +%D +%D \starttyping +%D \def\dosetupsomething[#1][#2]% +%D {... #1 ... #2 ...} +%D +%D \def\setupsomething% +%D {\dodoubleargument\dosetupsomething} +%D \stoptyping +%D +%D The implementation can be surprisingly simple and needs no +%D further explanation, like: +%D +%D \starttyping +%D \def\dosingleargument#1[#2]% +%D {#1[#2]} +%D \def\dotripleargument#1[#2]#3[#4]#5[#6]% +%D {#1[#2][#4][#6]} +%D \def\doquintupleargument#1% +%D {\def\dodoquintupleargument[##1]##2[##3]##4[##5]##6[##7]##8[##9]% +%D {#1[##1][##3][##5][##7][##9]}% +%D \dodoquintupleargument} +%D \stoptyping +%D +%D Because \TEX\ accepts 9~arguments at most, we have to use +%D two||step solution when getting five or more arguments. +%D +%D When developing more and more of the real \CONTEXT, we +%D started using some alternatives that provided empty +%D arguments (in fact optional ones) whenever the user failed +%D to supply them. Because this more complicated macros enable +%D us to do some checking, we reimplemented the non||empty +%D ones. + +\def\dosingleargument {\chardef\expectedarguments 1 \dosingleempty } +\def\dodoubleargument {\chardef\expectedarguments 2 \dodoubleempty } +\def\dotripleargument {\chardef\expectedarguments 3 \dotripleempty } +\def\doquadrupleargument {\chardef\expectedarguments 4 \doquadrupleempty } +\def\doquintupleargument {\chardef\expectedarguments 5 \doquintupleempty } +\def\dosixtupleargument {\chardef\expectedarguments 6 \dosixtupleempty } +\def\doseventupleargument{\chardef\expectedarguments 7 \doseventupleempty} + +%D \macros +%D {iffirstagument,ifsecondargument,ifthirdargument, +%D iffourthargument,iffifthargument,ifsixthargument, +%D ifseventhargument} +%D +%D We use some signals for telling the calling macros if all +%D wanted arguments are indeed supplied by the user. + +\newif\iffirstargument +\newif\ifsecondargument +\newif\ifthirdargument +\newif\iffourthargument +\newif\iffifthargument +\newif\ifsixthargument +\newif\ifseventhargument + +%D \macros +%D {dosingleempty,dodoubleempty,dotripleempty, +%D doquadrupleempty,doquintupleempty,dosixtupeempty, +%D doseventupleempty} +%D +%D The empty argument supplying macros mentioned before, look +%D like: +%D +%D \starttyping +%D \dosingleempty \command +%D \dodoubleempty \command +%D \dotripleempty \command +%D \doquadrupleempty \command +%D \doquintupleempty \command +%D \dosixtupleempty \command +%D \doseventupleempty\command +%D \stoptyping +%D +%D So \type{\dodoubleempty} leades to: +%D +%D \starttyping +%D \command[#1][#2] +%D \command[#1][] +%D \command[][] +%D \stoptyping +%D +%D Depending of the generousity of the user. Afterwards one can +%D use the \type{\if...argument} boolean. For novice: watch +%D the stepwise doubling of \type{#}'s + +% idea: \ignorespaces afterwards + +\chardef\noexpectedarguments=0 +\chardef\expectedarguments =0 + +\def\showargumenterror#1#2% + {\writestatus{system}{#1 argument(s) expected in line #2}} + +% \long\def\dogetargument#1#2#3#4% redefined in mult-ini +% {\doifnextcharelse{#1} +% {\let\expectedarguments\noexpectedarguments +% #3\dodogetargument} +% {\ifnum\expectedarguments>\noexpectedarguments +% \showargumenterror{\expectedarguments} +% \fi +% \let\expectedarguments\noexpectedarguments +% #4\dodogetargument#1#2}} +% +% less to pass + +\def\doshowargumenterror + {\ifnum\expectedarguments>\noexpectedarguments + \showargumenterror{\number\expectedarguments}{\number\inputlineno}% + \fi + \noshowargumenterror} + +\def\noshowargumenterror + {\let\expectedarguments\noexpectedarguments} + +% \long\def\dogetargument#1#2#3#4% +% {\doifnextcharelse#1% +% {\noshowargumenterror#3\dodogetargument} +% {\doshowargumenterror#4\dodogetargument#1#2}} +% +% faster ? + +\long\def\dogetargument#1#2#3#4% + {\let\charactertoken=#1% + \def\!!stringa{\noshowargumenterror#3\dodogetargument}% + \def\!!stringb{\doshowargumenterror#4\dodogetargument#1#2}% + \futurelet\nexttoken\inspectnextcharacter} + +%\def\getsingleempty#1#2#3% +% {\def\dodogetargument% +% {#3}% +% \dogetargument#1#2\firstargumenttrue\firstargumentfalse} +% +%\def\getdoubleempty#1#2#3% +% {\def\dodogetargument#1##1#2% +% {\def\dodogetargument% +% {#3#1##1#2}% +% \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% +% \dogetargument#1#2\firstargumenttrue\firstargumentfalse} +% +%\def\gettripleempty#1#2#3% +% {\def\dodogetargument#1##1#2% +% {\def\dodogetargument#1####1#2% +% {\def\dodogetargument% +% {#3#1##1#2% +% #1####1#2}% +% \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% +% \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% +% \dogetargument#1#2\firstargumenttrue\firstargumentfalse} +% +%\def\getquadrupleempty#1#2#3% +% {\def\dodogetargument#1##1#2% +% {\def\dodogetargument#1####1#2% +% {\def\dodogetargument#1########1#2% +% {\def\dodogetargument% +% {#3#1##1#2% +% #1####1#2% +% #1########1#2}% +% \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}% +% \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% +% \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% +% \dogetargument#1#2\firstargumenttrue\firstargumentfalse} +% +%\def\getquintupleempty#1#2#3% +% {\def\dodogetargument#1##1#2% +% {\def\dodogetargument#1####1#2% +% {\def\dodogetargument#1########1#2% +% {\def\dodogetargument#1################1#2% +% {\def\dodogetargument% +% {#3#1##1#2% +% #1####1#2% +% #1########1#2% +% #1################1#2}% +% \dogetargument#1#2\fifthargumenttrue\fifthargumentfalse}% +% \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}% +% \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% +% \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% +% \dogetargument#1#2\firstargumenttrue\firstargumentfalse} +% +%\def\getsixtupleempty#1#2#3% +% {\def\dodogetargument#1##1#2% +% {\def\dodogetargument#1####1#2% +% {\def\dodogetargument#1########1#2% +% {\def\dodogetargument#1################1#2% +% {\def\dodogetargument#1################################1#2% +% {\def\dodogetargument% +% {#3#1##1#2% +% #1####1#2% +% #1########1#2% +% #1################1#2% +% #1################################1#2}% +% \dogetargument#1#2\sixthargumenttrue\sixthargumentfalse}% +% \dogetargument#1#2\fifthargumenttrue\fifthargumentfalse}% +% \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}% +% \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% +% \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% +% \dogetargument#1#2\firstargumenttrue\firstargumentfalse} +% +%\def\getseventupleempty#1#2#3% +% {\def\dodogetargument#1##1#2% +% {\def\dodogetargument#1####1#2% +% {\def\dodogetargument#1########1#2% +% {\def\dodogetargument#1################1#2% +% {\def\dodogetargument#1################################1#2% +% {\def\dodogetargument#1###############################% +% ################################1#2% +% {\def\dodogetargument% +% {#3#1##1#2% +% #1####1#2% +% #1########1#2% +% #1################1#2% +% #1################################1#2% +% #1###############################% +% ################################1#2}% +% \dogetargument#1#2\seventhargumenttrue\seventhargumentfalse}% +% \dogetargument#1#2\sixthargumenttrue\sixthargumentfalse}% +% \dogetargument#1#2\fifthargumenttrue\fifthargumentfalse}% +% \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}% +% \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% +% \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% +% \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\getsingleempty#1#2#3% + {\def\dodogetargument% + {#3}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\getdoubleempty#1#2#3% + {\def\dodogetargument#1##1#2% + {\def\dodogetargument% + {#3#1{##1}#2}% + \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\gettripleempty#1#2#3% + {\def\dodogetargument#1##1#2% + {\def\dodogetargument#1####1#2% + {\def\dodogetargument% + {#3#1{##1}#2% + #1{####1}#2}% + \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% + \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\getquadrupleempty#1#2#3% + {\def\dodogetargument#1##1#2% + {\def\dodogetargument#1####1#2% + {\def\dodogetargument#1########1#2% + {\def\dodogetargument% + {#3#1{##1}#2% + #1{####1}#2% + #1{########1}#2}% + \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}% + \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% + \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\getquintupleempty#1#2#3% + {\def\dodogetargument#1##1#2% + {\def\dodogetargument#1####1#2% + {\def\dodogetargument#1########1#2% + {\def\dodogetargument#1################1#2% + {\def\dodogetargument% + {#3#1{##1}#2% + #1{####1}#2% + #1{########1}#2% + #1{################1}#2}% + \dogetargument#1#2\fifthargumenttrue\fifthargumentfalse}% + \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}% + \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% + \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\getsixtupleempty#1#2#3% + {\def\dodogetargument#1##1#2% + {\def\dodogetargument#1####1#2% + {\def\dodogetargument#1########1#2% + {\def\dodogetargument#1################1#2% + {\def\dodogetargument#1################################1#2% + {\def\dodogetargument% + {#3#1{##1}#2% + #1{####1}#2% + #1{########1}#2% + #1{################1}#2% + #1{################################1}#2}% + \dogetargument#1#2\sixthargumenttrue\sixthargumentfalse}% + \dogetargument#1#2\fifthargumenttrue\fifthargumentfalse}% + \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}% + \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% + \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\getseventupleempty#1#2#3% + {\def\dodogetargument#1##1#2% + {\def\dodogetargument#1####1#2% + {\def\dodogetargument#1########1#2% + {\def\dodogetargument#1################1#2% + {\def\dodogetargument#1################################1#2% + {\def\dodogetargument#1################################% + ################################1#2% + {\def\dodogetargument% + {#3#1{##1}#2% + #1{####1}#2% + #1{########1}#2% + #1{################1}#2% + #1{################################1}#2% + #1{################################% + ################################1}#2}% + \dogetargument#1#2\seventhargumenttrue\seventhargumentfalse}% + \dogetargument#1#2\sixthargumenttrue\sixthargumentfalse}% + \dogetargument#1#2\fifthargumenttrue\fifthargumentfalse}% + \dogetargument#1#2\fourthargumenttrue\fourthargumentfalse}% + \dogetargument#1#2\thirdargumenttrue\thirdargumentfalse}% + \dogetargument#1#2\secondargumenttrue\secondargumentfalse}% + \dogetargument#1#2\firstargumenttrue\firstargumentfalse} + +\def\dosingleempty {\getsingleempty []} +\def\dodoubleempty {\getdoubleempty []} +\def\dotripleempty {\gettripleempty []} +\def\doquadrupleempty {\getquadrupleempty []} +\def\doquintupleempty {\getquintupleempty []} +\def\dosixtupleempty {\getsixtupleempty []} +\def\doseventupleempty{\getseventupleempty[]} + +%D Because some of these are called quite often, we will now +%D replace the more general version by alternatives tuned for +%D speed. + +\def\dosingleempty#1% + {\noshowargumenterror % \relax % prevents lookahead, brr + \doifnextcharelse[% + {\firstargumenttrue#1} + {\dosinglefakeempty#1}} + +\def\dodoubleempty#1% + {\noshowargumenterror % \relax % prevents lookahead, brr + \doifnextcharelse[% + {\dodoubletestempty#1} + {\dodoublefakeempty#1}} + +\def\dotripleempty#1% + {\noshowargumenterror % \relax % prevents lookahead, brr + \doifnextcharelse[% + {\dotripletestempty#1} + {\dotriplefakeempty#1}} + +\def\dosinglefakeempty#1% + {\firstargumentfalse#1[]} + +\def\dodoublefakeempty#1% + {\firstargumentfalse\secondargumentfalse#1[][]} + +\def\dotriplefakeempty#1% + {\firstargumentfalse\secondargumentfalse\thirdargumentfalse#1[][][]} + +\long\def\dodoubletestempty#1[#2]% + {\firstargumenttrue + \doifnextcharelse[% + {\secondargumenttrue #1[{#2}]} + {\secondargumentfalse#1[{#2}][]}} + +\long\def\dotripletestempty#1[#2]% + {\firstargumenttrue + \doifnextcharelse[% + {\dotripletestemptyx #1[{#2}]} + {\secondargumentfalse + \thirdargumentfalse #1[{#2}][][]}} + +\long\def\dotripletestemptyx#1[#2][#3]% + {\secondargumenttrue + \doifnextcharelse[% + {\thirdargumenttrue #1[{#2}][{#3}]} + {\thirdargumentfalse#1[{#2}][{#3}][]}} + +%D \macros +%D {dosingleargumentwithset, +%D dodoubleargumentwithset,dodoubleemptywithset, +%D dotripleargumentwithset,dotripleemptywithset} +%D +%D These maybe too mysterious macros enable us to handle more +%D than one setup at once. +%D +%D \starttyping +%D \dosingleargumentwithset \command[#1] +%D \dodoubleargumentwithset \command[#1][#2] +%D \dotripleargumentwithset \command[#1][#2][#3] +%D \dodoubleemptywithset \command[#1][#2] +%D \dotripleemptywithset \command[#1][#2][#3] +%D \stoptyping +%D +%D The first macro calls \type{\command[##1]} for each string +%D in the set~\type{#1}. The second one calls for +%D \type{\commando[##1][#2]} and the third, well one may guess. +%D These commands support constructions like: +%D +%D \starttyping +%D \def\dodefinesomething[#1][#2]% +%D {\getparameters[\??xx#1][#2]} +%D +%D \def\definesomething% +%D {\dodoubleargumentwithset\dodefinesomething} +%D \stoptyping +%D +%D Which accepts calls like: +%D +%D \starttyping +%D \definesomething[alfa,beta,...][variable=...,...] +%D \stoptyping +%D +%D Now a whole bunch of variables like \type{\@@xxalfavariable} +%D and \type{\@@xxbetavariable} is defined. + +\def\dosingleargumentwithset#1% + {\def\dodosinglewithset[##1]% + {\def\dododosinglewithset####1% + {#1[####1]}% + \processcommalist[##1]\dododosinglewithset}% + \dosingleargument\dodosinglewithset}% + +\def\dodoublewithset#1#2% + {\def\dododoublewithset[##1][##2]% + {\doifnot{##1}{} + {\def\dodododoublewithset####1% + {#2[####1][##2]}% + \processcommalist[##1]\dodododoublewithset}}% + #1\dododoublewithset}% + +\def\dodoubleemptywithset% + {\dodoublewithset\dodoubleempty} + +\def\dodoubleargumentwithset% + {\dodoublewithset\dodoubleargument} + +\def\dotriplewithset#1#2% + {\def\dodotriplewithset[##1][##2][##3]% + {\doifnot{##1}{} + {\def\dododotriplewithset####1% + {#2[####1][##2][##3]}% + \processcommalist[##1]\dododotriplewithset}}% + #1\dodotriplewithset}% + +\def\dotripleemptywithset% + {\dotriplewithset\dotripleempty} + +\def\dotripleargumentwithset% + {\dotriplewithset\dotripleargument} + +%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}. + +\def\strippedcsname% + {\expandafter\gobbleoneargument\string} + +%D \macros +%D {complexorsimple,complexorsimpleempty} +%D +%D Setups can be optional. A command expecting a setup is +%D prefixed by \type{\complex}, a command without one gets the +%D prefix \type{\simple}. Commands like this can be defined by: +%D +%D \starttyping +%D \complexorsimple\command +%D \stoptyping +%D +%D When \type{\command} is followed by a \type{[setup]}, then +%D +%D \starttyping +%D \complexcommand [setup] +%D \stoptyping +%D +%D executes, else we get +%D +%D \starttyping +%D \simplecommand +%D \stoptyping +%D +%D An alternative for \type{\complexorsimple} is: +%D +%D \starttyping +%D \complexorsimpleempty {command} +%D \stoptyping +%D +%D Depending on the presence of \type{[setup]}, this one +%D leads to one of: +%D +%D \starttyping +%D \complexcommando [setup] +%D \complexcommando [] +%D \stoptyping +%D +%D Many \CONTEXT\ commands started as complex or simple ones, +%D but changed into more versatile (more object oriented) ones +%D using the \type{\get..argument} commands. + +% This method is needed when traditional tex is used with +% the efficient definition (marked **) below. + +% an old one: +% +% \def\setnameofcommand#1% handles {abc} as well as \abc +% {\begingroup +% \escapechar=-1 +% \globaldefs=0 % pretty important! +% \xdef\nameofcommand{\string#1}% +% \endgroup} +% +% \def\complexorsimple#1% +% {\setnameofcommand{#1}% +% \doifnextcharelse{[} +% {\firstargumenttrue \getvalue{\s!complex\nameofcommand}} +% {\firstargumentfalse\getvalue{\s!simple \nameofcommand}}} +% +% \def\complexorsimpleempty#1% +% {\setnameofcommand{#1}% +% \doifnextcharelse{[} +% {\firstargumenttrue \getvalue{\s!complex\nameofcommand}} +% {\firstargumentfalse\getvalue{\s!complex\nameofcommand}[]}} +% +% a newer one: + +\def\complexorsimple#1% + {% \relax % prevents lookahead, brrr + \doifnextcharelse[% + {\firstargumenttrue \csname\s!complex\strippedcsname#1\endcsname} + {\firstargumentfalse\csname\s!simple \strippedcsname#1\endcsname}} + +\def\complexorsimpleempty#1% + {% \relax % prevents lookahead, brrr + \doifnextcharelse[% + {\firstargumenttrue \csname\s!complex\strippedcsname#1\endcsname} + {\firstargumentfalse\csname\s!complex\strippedcsname#1\endcsname[]}} + +%D \macros +%D {definecomplexorsimple,definecomplexorsimpleempty} +%D +%D The previous commands are used that often that we found it +%D worthwile to offer two more alternatives. Watch the build +%D in protection. + +% See earlier. Because we don't want \type {\simple..} and +% \type {\complex..} commands to show up in expansions, we need +% to pass them as \type {simple..} and \type {complex..}. + +% \beginTEX +% +% \def\definewithnameofcommand#1#2% +% {\setnameofcommand{#2}% +% \@EA\def\@EA#2\@EA{\@EA\donottest\@EA#1\@EA{\nameofcommand}}} +% +% \def\definecomplexorsimple% +% {\definewithnameofcommand\complexorsimple} +% +% \def\definecomplexorsimpleempty% +% {\definewithnameofcommand\complexorsimpleempty} +% +% \endTEX +% +% \beginETEX \protected +% +% \def\definecomplexorsimple#1% +% {\normalprotected\def#1{\complexorsimple#1}} +% +% \def\definecomplexorsimpleempty#1% +% {\normalprotected\def#1{\complexorsimpleempty#1}} +% +% \endETEX + +% However, since this one uses an idirect method, things go +% okay (at the cost of extra macros). + +% \def\definecomplexorsimple#1% +% {\unexpanded\def#1{\complexorsimple#1}} +% +% \def\definecomplexorsimpleempty#1% +% {\unexpanded\def#1{\complexorsimpleempty#1}} +% +% faster, since no \strippedcsname needed in call, but more spacy + +\def\docomplexorsimple#1#2% + {\doifnextcharelse[{\firstargumenttrue#1}{\firstargumentfalse#2}} + +\def\docomplexorsimpleempty#1% + {\doifnextcharelse[{\firstargumenttrue#1}{\firstargumentfalse#1[]}} + +\def\definecomplexorsimple#1% + {\unexpanded\edef#1% + {\noexpand\docomplexorsimple + \@EA\noexpand\csname\s!complex\strippedcsname#1\endcsname + \@EA\noexpand\csname\s!simple \strippedcsname#1\endcsname}} + +\def\definecomplexorsimpleempty#1% + {\unexpanded\edef#1% + {\noexpand\docomplexorsimpleempty + \@EA\noexpand\csname\s!complex\strippedcsname#1\endcsname}} + +%D These commands are called as: +%D +%D \starttyping +%D \definecomplexorsimple\command +%D \stoptyping +%D +%D Of course, we must have available +%D +%D \starttyping +%D \def\complexcommand[#1]{...} +%D \def\simplecommand {...} +%D \stoptyping +%D +%D Using this construction saves a few string now and then. + +%D \macros +%D {definestartstopcommand} +%D +%D Those who get the creeps of expansion may skip the next +%D one. It's one of the most recent additions and concerns +%D \type{\start}||\type{\stop} pairs with complicated +%D arguments. +%D +%D We won't go into details here, but the general form of +%D this using this command is: +%D +%D \starttyping +%D \definestartstopcommand\somecommand\e!specifier{arg}{arg}% +%D {do something with arg} +%D \stoptyping +%D +%D This expands to something like: +%D +%D \starttyping +%D \def\somecommand arg \startspecifier arg \stopspecifier% +%D {do something with arg} +%D \stoptyping +%D +%D The arguments can be anything reasonable, but double +%D \type{#}'s are needed in the specification part, like: +%D +%D \starttyping +%D \definestartstopcommand\somecommand\e!specifier{[##1][##2]}{##3}% +%D {do #1 something #2 with #3 arg} +%D \stoptyping +%D +%D which becomes: +%D +%D \starttyping +%D \def\somecommand[#1][#2]\startspecifier#3\stopspecifier% +%D {do #1 something #2 with #3 arg} +%D \stoptyping +%D +%D We will see some real applications of this command in the +%D core modules. + +\def\definestartstopcommand#1#2#3#4% can be done with \expanded ot better, toks + {\def\!stringa{#3}% % but let's keep this unused one crappy + \def\!stringb{\e!start#2}% + \def\!stringc{#4}% + \def\!stringd{\e!stop#2}% + \@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA + \def\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA\@EA + #1\@EA\@EA\@EA\@EA\@EA\@EA\@EA + \!stringa\@EA\@EA\@EA + \csname\@EA\@EA\@EA\!stringb\@EA\@EA\@EA\endcsname\@EA + \!stringc + \csname\!stringd\endcsname} + +%D \macros +%D {dosinglegroupempty,dodoublegroupempty,dotriplegroupempty, +%D doquadruplegroupempty, doquintuplegroupempty} +%D +%D We've already seen some commands that take care of +%D optional arguments between \type{[]}. The next two commands +%D handle the ones with \type{{}}. They are called as: +%D +%D \starttyping +%D \dosinglegroupempty \ineedONEargument +%D \dodoublegroupempty \ineedTWOarguments +%D \dotriplegroupempty \ineedTHREEarguments +%D \doquadruplegroupempty \ineedFOURarguments +%D \doquintuplegroupempty \ineedFIVEarguments +%D \stoptyping +%D +%D where \type{\ineedONEargument} takes one and the others +%D two and three arguments. These macro's were first needed in +%D \PPCHTEX. +%D +%D \starttyping +%D \def\dogetgroupargument#1#2% redefined in mult-ini +%D {\def\nextnextargument% +%D {\ifx\nextargument\bgroup +%D \let\expectedarguments\noexpectedarguments +%D \def\nextargument{#1\dodogetargument}% +%D %\else\ifx\nextargument\lineending % this can be an option +%D % \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument#1#2}\\}% +%D %\else\ifx\nextargument\blankspace % but it may never be default +%D % \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument#1#2}\\}% +%D \else +%D \ifnum\expectedarguments>\noexpectedarguments +%D \writestatus +%D {setup} +%D {\the\expectedarguments\space argument(s) expected +%D in line \the\inputlineno\space}% +%D \fi +%D \let\expectedarguments\noexpectedarguments +%D \def\nextargument{#2\dodogetargument{}}% +%D \fi%\fi\fi % so let's get rid of it +%D \nextargument}% +%D \futurelet\nextargument\nextnextargument} +%D \stoptyping +%D +%D In order to catch \type {\nextargument}'s that expand to +%D \type {\if} and friends, in practice we will use a +%D slightly more complicated macro. + +\let\normalif \if +\let\normalifx \ifx +\let\normalifnum \ifnum +\let\normalifdim \ifdim +\let\normalifcat \ifcat +\let\normalifcase \ifcase +\let\normalifcsname \ifcsname +\let\normalifhmode \ifhmode +\let\normalifvmode \ifvmode +\let\normalor \or +\let\normalelse \else +\let\normalfi \fi + +\def\beginrobusttest + {\begingroup + \let\if \relax + \let\ifx \relax + \let\ifnum \relax + \let\ifdim \relax + \let\ifcat \relax + \let\ifcase \relax + \let\ifcsname\relax + \let\or \relax + \let\else \relax + \let\fi \relax} + +\let\endrobusttest\endgroup + +% \def\dogetgroupargument#1#2% +% {\def\nextnextargument% +% {\normalifx\nextargument\bgroup +% \endrobusttest +% \let\expectedarguments\noexpectedarguments +% \def\nextargument{#1\dodogetargument}% +% %\normalelse\normalifx\nextargument\lineending % this can be an option +% % \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument#1#2}\\}% +% %\normalelse\normalifx\nextargument\blankspace % but may never be default +% % \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument#1#2}\\}% +% \normalelse +% \endrobusttest +% \ifnum\expectedarguments>\noexpectedarguments +% \showargumenterror{\number\expectedarguments}{\number\inputlineno}% +% \fi +% \let\expectedarguments\noexpectedarguments +% \def\nextargument{#2\dodogetargument{}}% +% \normalfi%\normalfi\normalfi % so let's get rid of it +% \nextargument}% +% \beginrobusttest +% \futurelet\nextargument\nextnextargument} + +% \def\dogetgroupargument#1#2% +% {\def\nextnextargument% +% {\normalifx\nextargument\bgroup +% \endrobusttest +% \noshowargumenterror +% \def\nextargument{#1\dodogetargument}% +% %\normalelse\normalifx\nextargument\lineending % this can be an option +% % \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument#1#2}\\}% +% %\normalelse\normalifx\nextargument\blankspace % but may never be default +% % \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument#1#2}\\}% +% \normalelse +% \endrobusttest +% \doshowargumenterror +% \def\nextargument{#2\dodogetargument{}}% +% \normalfi%\normalfi\normalfi % so let's get rid of it +% \nextargument}% +% \beginrobusttest +% \futurelet\nextargument\nextnextargument} + +% we need to use an \ifcase in order to honor the \normal... + +\def \permitspacesbetweengroups{\chardef\@@permitspacesbetweengroups=0 } +\def\dontpermitspacesbetweengroups{\chardef\@@permitspacesbetweengroups=1 } + +\dontpermitspacesbetweengroups + +\def\dogetgroupargument#1#2% + {\def\nextnextargument% + {\normalifx\nextargument\bgroup + \endrobusttest + \noshowargumenterror + \def\nextargument{#1\dodogetargument}% + \normalelse + \normalifcase\@@permitspacesbetweengroups + \normalifx\nextargument\lineending + \endrobusttest + \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument#1#2}\\}% + \normalelse\normalifx\nextargument\blankspace + \endrobusttest + \def\nextargument{\begingroup\def\\ {\endgroup\dogetgroupargument#1#2}\\}% + \normalelse + \endrobusttest + \doshowargumenterror + \def\nextargument{#2\dodogetargument{}}% + \normalfi\normalfi + \normalelse + \endrobusttest + \doshowargumenterror + \def\nextargument{#2\dodogetargument{}}% + \normalfi + \normalfi + \nextargument}% + \beginrobusttest + \futurelet\nextargument\nextnextargument} + +\def\dosinglegroupempty#1% + {\def\dodogetargument% + {\dontpermitspacesbetweengroups + #1}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + +\def\dodoublegroupempty#1% + {\def\dodogetargument##1% + {\def\dodogetargument% + {\dontpermitspacesbetweengroups + #1{##1}}% + \dogetgroupargument\secondargumenttrue\secondargumentfalse}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + +\def\dotriplegroupempty#1% + {\def\dodogetargument##1% + {\def\dodogetargument####1% + {\def\dodogetargument% + {\dontpermitspacesbetweengroups + #1{##1}{####1}}% + \dogetgroupargument\thirdargumenttrue\thirdargumentfalse}% + \dogetgroupargument\secondargumenttrue\secondargumentfalse}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + +\def\doquadruplegroupempty#1% + {\def\dodogetargument##1% + {\def\dodogetargument####1% + {\def\dodogetargument########1% + {\def\dodogetargument% + {\dontpermitspacesbetweengroups + #1{##1}{####1}{########1}}% + \dogetgroupargument\fourthargumenttrue\fourthargumentfalse}% + \dogetgroupargument\thirdargumenttrue\thirdargumentfalse}% + \dogetgroupargument\secondargumenttrue\secondargumentfalse}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + +\def\doquintuplegroupempty#1% + {\def\dodogetargument##1% + {\def\dodogetargument####1% + {\def\dodogetargument########1% + {\def\dodogetargument################1% + {\def\dodogetargument% + {\dontpermitspacesbetweengroups + #1{##1}{####1}{########1}{################1}}% + \dogetgroupargument\fifthargumenttrue\fifthargumentfalse}% + \dogetgroupargument\fourthargumenttrue\fourthargumentfalse}% + \dogetgroupargument\thirdargumenttrue\thirdargumentfalse}% + \dogetgroupargument\secondargumenttrue\secondargumentfalse}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + +%D These macros can explictly take care of spaces, which means +%D that the next definition and calls are valid: +%D +%D \starttyping +%D \def\test#1#2#3{[#1#2#3]} +%D +%D \dotriplegroupempty\test {a}{b}{c} +%D \dotriplegroupempty\test {a}{b} +%D \dotriplegroupempty\test {a} +%D \dotriplegroupempty\test +%D \dotriplegroupempty\test {a} {b} {c} +%D \dotriplegroupempty\test {a} {b} +%D \dotriplegroupempty\test +%D {a} +%D {b} +%D \stoptyping +%D +%D And alike. + +%D \macros +%D {firstofoneargument, firstoftwoarguments, firstofthreearguments +%D secondoftwoarguments, secondofthreearguments, +%D thirdofthreearguments} +%D +%D The next six macros (dedicated to Taco) can conveniently +%D used to select arguments. Their names explain their +%D functionality. + +\long\def\firstofoneargument #1{#1} +\long\def\firstoftwoarguments #1#2{#1} +\long\def\firstofthreearguments #1#2#3{#1} +\long\def\firstoffourarguments #1#2#3#4{#1} +\long\def\firstoffivearguments #1#2#3#4#5{#1} +\long\def\secondoftwoarguments #1#2{#2} +\long\def\secondofthreearguments #1#2#3{#2} +\long\def\secondoffourarguments #1#2#3#4{#2} +\long\def\secondoffivearguments #1#2#3#4#5{#2} +\long\def\thirdofthreearguments #1#2#3{#3} +\long\def\thirdoffourarguments #1#2#3#4{#3} +\long\def\thirdoffivearguments #1#2#3#4#5{#3} +\long\def\fourthoffourarguments #1#2#3#4{#4} +\long\def\fourthoffivearguments #1#2#3#4#5{#4} +\long\def\fifthoffivearguments #1#2#3#4#5{#5} + +%D \macros +%D {globalletempty,letempty,letvalueempty,letgvalueempty} +%D +%D Trivial: + +\def\letempty #1{\let#1\empty} +\def\globalletempty#1{\global\let#1\empty} + +\def\letvalueempty #1{\expandafter\let\csname#1\endcsname\empty} +\def\letgvalueempty#1{\global\expandafter\let\csname#1\endcsname\empty} + +%D \macros +%D {wait} +%D +%D The next macro hardly needs explanation. Because no +%D nesting is to be expected, we can reuse \type{\wait} within +%D \type{\wait} itself. + +\def\wait + {\begingroup + \read16 to \wait + \endgroup} + +%D \macros +%D {writestring,writeline, +%D writestatus,statuswidth} +%D +%D Maybe one didn't notice, but we've already introduced a +%D macro for showing messages. In the multi||lingual modules, +%D we will also introduce a mechanism for message passing. For +%D the moment we stick to the core macros: +%D +%D \starttyping +%D \writestring {string} +%D \writeline +%D \writestatus {category} {message} +%D \stoptyping +%D +%D Messages are formatted. One can provide the maximum with +%D of the identification string with the macro \type +%D {\statuswidth}. + +% \chardef\statuswidth=15 +% +% \def\writestring% +% {\immediate\write16} +% +% \def\writeline% +% {\writestring{}} +% +% \def\dosplitstatus#1#2\end% +% {\ifx#1?% +% \loop +% \advance\scratchcounter by 1 +% \ifnum\scratchcounter<\statuswidth +% \edef\messagecontentA{\messagecontentA\space}% +% \repeat +% \else +% \advance\scratchcounter by 1 +% \ifnum\scratchcounter<\statuswidth +% \edef\messagecontentA{\messagecontentA#1}% +% \fi +% \dosplitstatus#2\end +% \fi} +% +% \def\writestatus#1#2% +% {\begingroup +% \let\messagecontentA=\empty +% \edef\messagecontentB{#2}% maybe it's \the\scratchcounter +% \scratchcounter=0 +% \expandafter\dosplitstatus#1?\end +% \writestring{\messagecontentA\space:\space\messagecontentB}% +% \endgroup} + +\chardef\statuswidth=15 +\chardef\statuswrite=16 + +\def\writestring + {\immediate\write\statuswrite} + +\def\writeline + {\writestring{}} + +%D First we present the normal \TEX\ variant, later we will +%D show the \ETEX-way. + +\beginTEX + +\newcount\statuscounter + +\def\dosplitstatus#1% + {\advance\statuscounter \minusone + \ifcase\statuscounter + \expandafter\nosplitstatus + \else + \scratchtoks\@EA{\the\scratchtoks#1}% + \expandafter\dosplitstatus + \fi} + +\def\nosplitstatus#1\end + {} + +\def\writestatus#1#2% + {\begingroup + \scratchtoks\emptytoks + \statuscounter\statuswidth + \expandafter\dosplitstatus#1% + \space\space\space\space\space\space\space + \space\space\space\space\space\space\space + \space\space\space\space\space\space\end + %\expanded{\writestring{\the\scratchtoks\space:\space#2}}% + \@EA\writestring\@EA{\the\scratchtoks\space:\space#2}% + \endgroup} + +\endTEX + +%D Because we're grouped, we could have initialized at forehand: +%D +%D \starttyping +%D \statuscounter\statuswidth +%D \stoptyping +%D +%D The next implementation saves only some 10 words of format +%D memory, but we hardly gain any speed. +%D +%D \starttyping +%D \def\dosplitstatus#1#2#3#4#5#6#7#8#9% +%D {#1#2#3#4#5#6#7#8#9\dodosplitstatus} +%D +%D \def\dodosplitstatus#1#2#3#4#5#6\end +%D {#1#2#3#4#5} +%D +%D \def\writestatus#1#2% +%D {\writestring +%D {\expandafter\dosplitstatus#1% +%D \space\space\space\space\space +%D \space\space\space\space\space +%D \space\space\space\space\space\end +%D \space:\space#2}} +%D \stoptyping + +%D Okay then, more obscure but slightly faster: no split grabs +%D the do split part and skipping the else branch has to happen +%D anyway, so: + +\beginTEX + +\def\dosplitstatus#1% + {\advance\statuscounter \minusone + \ifcase\statuscounter + \expandafter\nosplitstatus + \fi + \scratchtoks\@EA{\the\scratchtoks#1}% + \dosplitstatus} + +\endTEX + +%D The next (\ETEX\ specific) variant is twice as fast in 5/40 +%D situations, only gains some speed when multiple runs of large docs +%D are done; fully expandable, no statuscounter needed, no restore (due +%D to grouping) needed etc. + +\beginETEX \numexpr + +\def\writestatus#1#2% + {\writestring{\expandafter\dosplitstatus\expandafter\statuswidth#1% + \space\space\space\space\space\space\space + \space\space\space\space\space\space\space + \space\space\space\space\space\space\end + \space:\space#2}} + +\def\dosplitstatus#1#2% + {\ifcase#1 \expandafter\nosplitstatus\fi#2% + \expandafter\dosplitstatus\expandafter{\the\numexpr#1+\minusone\relax}} + +\def\nosplitstatus#1\end + {} + +\endETEX + +%D \macros +%D {emptytoks} +%D +%D For this we need an empty token register, analogous +%D to \type {\empty}. + +\newtoks\emptytoks + +%D \macros +%D {debuggerinfo} +%D +%D For debugging purposes we can enhance macros with the +%D next alternative. Here \type{debuggerinfo} stands for both +%D a macro accepting two arguments and a boolean (in fact a +%D few macro's too). + +\newif\ifdebuggerinfo + +\def\debuggerinfo#1#2% + {\ifdebuggerinfo + \writestatus{debugger}{#1:: #2}% + \fi} + +%D Finally we do what from now on will be done at the top of +%D the files: we tell the user what we are loading. + +\writestatus{loading}{Context System Macros / General} + +%D Well, the real final command is the one that resets the +%D unprotected characters \type{@}, \type{?} and \type{!}. + +\protect \endinput |