diff options
author | Hans Hagen <pragma@wxs.nl> | 1998-03-27 00:00:00 +0100 |
---|---|---|
committer | Hans Hagen <pragma@wxs.nl> | 1998-03-27 00:00:00 +0100 |
commit | 5f54d546a687e0615f87a117c5950b78ef346af7 (patch) | |
tree | ca1c0ce7e09685b5a3a55e57edca776e7dd66c59 /tex/context/base/syst-gen.tex | |
parent | 4da38599c2b3c2397582838a9ac715897af7b1a8 (diff) | |
download | context-5f54d546a687e0615f87a117c5950b78ef346af7.tar.gz |
stable 1998.03.27
Diffstat (limited to 'tex/context/base/syst-gen.tex')
-rw-r--r-- | tex/context/base/syst-gen.tex | 5401 |
1 files changed, 2719 insertions, 2682 deletions
diff --git a/tex/context/base/syst-gen.tex b/tex/context/base/syst-gen.tex index b9b3da9d9..9cdc8dd62 100644 --- a/tex/context/base/syst-gen.tex +++ b/tex/context/base/syst-gen.tex @@ -1,2682 +1,2719 @@ -%D \module
-%D [ file=syst-gen,
-%D version=1996.3.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. Non||commercial use is
-%C granted.
-
-%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
-%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 \starttypen
-%D \abortinputifdefined\command
-%D \stoptypen
-%D
-%D where \type{\command} is a command defined in the module
-%D to be loaded only once.
-%D
-%D \starttypen
-%D \def\abortinputifdefined#1%
-%D {\ifx#1\undefined
-%D \let\next=\relax
-%D \else
-%D \let\next=\endinput
-%D \fi
-%D \next}
-%D \stoptypen
-%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 \starttypen
-%D \ifx\somecommand\undefined
-%D \let\next=\relax
-%D \else
-%D \let\next=\endinput
-%D \fi
-%D \next
-%D \stoptypen
-%D
-%D We need the \type{\next} because we need to end the
-%D \type{\fi}. The efficient one is:
-%D
-%D \starttypen
-%D \ifx\somecommand\undefined
-%D \else
-%D \expandafter\endinput
-%D \fi
-%D \stoptypen
-%D
-%D Because \type{\endinput} comes into action after the current
-%D line, we can also say:
-%D
-%D \starttypen
-%D \ifx\somecommand\undefined \else \endinput \fi
-%D \stoptypen
-%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 \starttypen
-%D \writestatus{laden}{Context Systeem Macro's (a)}
-%D \stoptypen
-
-%D \macros
-%D [beschermen]
-%D {protect,unprotect}
-%D {}
-%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 \starttypen
-%D \unprotect
-%D \def\!test{test}
-%D \protect
-%D \stoptypen
-%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>}}
-\fi
-
-\let\normalprotect=\protect
-
-%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.
-
-\def\unprotect%
- {\ifnum\protectionlevel=0
- \edef\doprotectcharacters%
- {\catcode`@=\the\catcode`@\relax
- \catcode`!=\the\catcode`!\relax
- \catcode`?=\the\catcode`?\relax}%
- \catcode`@=11
- \catcode`!=11
- \catcode`?=11
- \let\protect=\doprotect
- \fi
- \advance\protectionlevel by 1
- \ifnum\protectionlevel>1
- \message{<unprotect \the\protectionlevel>}%
- \fi}
-
-\def\doprotect%
- {\ifnum\protectionlevel=1
- \doprotectcharacters
- \let\protect=\normalprotect
- \fi
- \ifnum\protectionlevel>1
- \message{<protect \the\protectionlevel>}%
- \fi
- \advance\protectionlevel by -1\relax}
-
-%D Now it is defined, we can make use of this very useful
-%D macro.
-
-\unprotect
-
-%D \macros
-%D {@@escape,@@begingroup,@@endgroup,@@mathshift,@@alignment,
-%D @@endofline,@@parameter,@@superscript,@@subscript,
-%D @@ignore,@@space,@@letter,@@other,@@active,@@comment}
-%D {}
-%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
-%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
-%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
-%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
-%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
-
-\def\!!stringa{} \def\!!stringb{} \def\!!stringc{}
-\def\!!stringd{} \def\!!stringe{} \def\!!stringf{}
-
-\newdimen\!!widtha \newdimen\!!heighta \newdimen\!!deptha \newif\if!!donea
-\newdimen\!!widthb \newdimen\!!heightb \newdimen\!!depthb \newif\if!!doneb
-
-%D \macros
-%D {s!,c!,e!,p!,v!,@@,??}
-%D {}
-%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,expanded}
-%D {}
-%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
-
-%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 \starttypen
-%D \expanded{\setupsomething[\alfa]}
-%D \stoptypen
-%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.
-
-\def\expanded#1%
- {\edef\@@expanded{\noexpand#1}\@@expanded}
-
-%D \macros
-%D {gobbleoneargument,gobble...arguments}
-%D {}
-%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{}
-
-%D \macros
-%D {doifnextcharelse}
-%D {}
-%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 \starttypen
-%D \doifnextcharelse {karakter} {then ...} {else ...}
-%D \stoptypen
-%D
-%D This macro differs from the original in testing on
-%D \type{\endoflinetoken}, which of course we have to define
-%D first. We also use \type{\localnext} because we don't want
-%D clashes with \type{\next}.
-
-\let\endoflinetoken=^^M
-
-\long\def\doifnextcharelse#1#2#3%
- {\let\charactertoken=#1%
- \def\!!stringa{#2}%
- \def\!!stringb{#3}%
- \futurelet\nexttoken\inspectnextcharacter}
-
-\def\inspectnextcharacter%
- {\ifx\nexttoken\blankspace
- \let\localnext\reinspectnextcharacter
- \else\ifx\!!stringc\endoflinetoken
- \let\localnext\reinspectnextcharacter
- \else\ifx\nexttoken\charactertoken
- \let\localnext\!!stringa
- \else
- \let\localnext\!!stringb
- \fi\fi\fi
- \localnext}
-
-%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 \starttypen
-%D \expandafter\def\reinspectnextcharacter %
-%D {\futurelet\nexttoken\inspectnextcharacter}
-%D \stoptypen
-%D
-%D However complicated it may look, I'm still glad I stumbled
-%D into this construction.
-
-\def\:{\let\blankspace= } \:
-
-\def\:{\reinspectnextcharacter}
-
-\expandafter\def\: {\futurelet\nexttoken\inspectnextcharacter}
-
-%D \macros
-%D {setvalue,setgvalue,setevalue,setxvalue,
-%D letvalue,
-%D getvalue,
-%D resetvalue}
-%D {}
-%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 \starttypen
-%D \setvalue {naam}{...} = \def\naam{...}
-%D \setgvalue {naam}{...} = \gdef\naam{...}
-%D \setevalue {naam}{...} = \edef\naam{...}
-%D \setxvalue {naam}{...} = \xdef\naam{...}
-%D \letvalue {naam}=\... = \let\naam=\...
-%D \getvalue {naam} = \naam
-%D \resetvalue {naam} = \def\naam{}
-%D \stoptypen
-%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\resetvalue#1%
- {\setvalue{#1}{}}
-
-%D \macros
-%D {donottest,unexpanded}
-%D {}
-%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 e-\TeX\ protection will be 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 \starttypen
-%D \def\dosomecommand {... ... ...}
-%D \def\somecommand {\donottest\dosomecommand}
-%D \stoptypen
-%D
-%D This double definition can be made transparant by using
-%D \type{\protecte}, as in:
-%D
-%D \starttypen
-%D \unexpanded\def\somecommand{... ... ...}
-%D \stoptypen
-%D
-%D The protection mechanism uses:
-
-\def\dontprocesstest#1%
- {==}
-
-\def\doprocesstest#1%
- {#1}
-
-\let\donottest=\doprocesstest
-
-%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 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 \starttypen
-%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 \stoptypen
-%D
-%D Well, in fact we use the bit more versaatile alternative:
-
-\def\dosetunexpanded#1#2%
- {\@EA#1\@EA{\@EA#2\@EA}%
- \@EA{\@EA\donottest\csname\s!do\@EA\string\csname#2\endcsname\endcsname}%
- \@EA#1{\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}
-
-%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 \starttypen
-%D \def\csname do\somecommand\endcsname{... ... ...}
-%D \def\somecommand{\donottest\csname do\somecommand\endcsname}
-%D \stoptypen
-%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
-%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 \starttypen
-%D \doifundefined {string} {...}
-%D \doifdefined {string} {...}
-%D \doifundefinedelse {string} {then ...} {else ...}
-%D \doifdefinedelse {string} {then ...} {else ...}
-%D \doifalldefinedelse {commalist} {then ...} {else ...}
-%D \stoptypen
-%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 \starttypen
-%D \expandafter\ifx\csname NameA\endcsname\relax ... \else ... \fi
-%D
-%D \ifx\NameB\undefined ... \else ... \fi
-%D \stoptypen
-%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 \starttypen
-%D \expandafter\show\csname NameA\endcsname
-%D
-%D \show\NameB
-%D \stoptypen
-%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 \starttypen
-%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 {\bgroup
-%D \donetrue
-%D \def\checkcommand##1%
-%D {\doifundefined{##1}{\donefalse}}%
-%D \processcommalist[#1]\checkcommand
-%D \ifdone
-%D \egroup#2%
-%D \else
-%D \egroup#3%
-%D \fi}
-%D \stoptypen
-%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.
-
-\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#2#3%
- {\p!doifundefined{#1}%
- \let\donottest=\doprocesstest#2%
- \else
- \let\donottest=\doprocesstest#3%
- \fi}
-
-\def\doifdefinedelse#1#2#3%
- {\p!doifundefined{#1}%
- \let\donottest=\doprocesstest#3%
- \else
- \let\donottest=\doprocesstest#2%
- \fi}
-
-\def\doifundefined#1#2%
- {\p!doifundefined{#1}%
- \let\donottest=\doprocesstest#2%
- \else
- \let\donottest=\doprocesstest
- \fi}
-
-\def\doifdefined#1#2%
- {\p!doifundefined{#1}%
- \let\donottest=\doprocesstest
- \else
- \let\donottest=\doprocesstest#2%
- \fi}
-
-%D Before we start using this variant, we used another one,
-%D which is even a bit faster. This one looked like:
-%D
-%D \starttypen
-%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 \stoptypen
-%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 \starttypen
-%D $\kern10pt\showthe\lastkern$
-%D $\kern10pt{\showthe\lastkern}$
-%D $\kern10pt\begingroup\showthe\lastkern\endgroup$
-%D \stoptypen
-%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}.
-
-\def\docheckonedefined#1%
- {\ifundefined{#1}%
- \donefalse
- \fi}
-
-\def\doifalldefinedelse#1#2#3%
- {\begingroup
- \let\donottest=\dontprocesstest
- \donetrue
- \processcommalist[#1]\docheckonedefined
- \ifdone
- \endgroup\let\donottest=\doprocesstest#2%
- \else
- \endgroup\let\donottest=\doprocesstest#3%
- \fi}
-
-%D \macros
-%D {doif,doifelse,doifnot,
-%D donottest}
-%D {}
-%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 \starttypen
-%D \doif {string1} {string2} {...}
-%D \doifnot {string1} {string2} {...}
-%D \doifelse {string1} {string2} {then ...}{else ...}
-%D \stoptypen
-%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 \starttypen
-%D \ifx\!!stringa\!!stringb
-%D \def\next{#3}%
-%D \else
-%D \def\next{#4}%
-%D \fi
-%D \next
-%D \stoptypen
-%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.
-
-\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 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 \starttypen
-%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 \stoptypen
-%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
-%D We complete our set of conditionals with:
-%D
-%D \starttypen
-%D \doifempty {string} {...}
-%D \doifnot {string} {...}
-%D \doifemptyelse {string} {then ...} {else ...}
-%D \stoptypen
-%D
-%D This time, the string is not expanded.
-
-\long\def\doifemptyelse#1#2#3%
- {\def\!!stringa{#1}%
- \ifx\!!stringa\empty
- #2%
- \else
- #3%
- \fi}
-
-\long\def\doifempty#1#2%
- {\def\!!stringa{#1}%
- \ifx\!!stringa\empty
- #2%
- \fi}
-
-\long\def\doifnotempty#1#2%
- {\def\!!stringa{#1}%
- \ifx\!!stringa\empty
- \else
- #2%
- \fi}
-
-%D \macros
-%D {doifinset,doifnotinset,doifinsetelse}
-%D {}
-%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 \starttypen
-%D \doifinset {string} {string,...} {...}
-%D \doifnotinset {string} {string,...} {...}
-%D \doifinsetelse {string} {string,...} {then ...} {else ...}
-%D \stoptypen
-%D
-%D The second argument is the comma separated set of strings.
-%D
-%D \starttypen
-%D \long\def\doifinsetelse#1#2#3#4%
-%D {\doifelse{#1}{}
-%D {#4}
-%D {\donefalse
-%D \def\v!checkiteminset##1%
-%D {\doif{#1}{##1}
-%D {\donetrue
-%D \let\v!checkiteminset=\gobbleoneargument}}%
-%D \processcommalist[#2]\v!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 \stoptypen
-%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}
-
-\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}
-
-\long\def\doifinsetelse#1#2#3#4%
- {\p!doifinsetelse{#1}{#2}%
- #3%
- \else
- #4%
- \fi}
-
-\long\def\doifinset#1#2#3%
- {\p!doifinsetelse{#1}{#2}%
- #3%
- \fi}
-
-\long\def\doifnotinset#1#2#3%
- {\p!doifinsetelse{#1}{#2}%
- \else
- #3%
- \fi}
-
-%D \macros
-%D {doifcommon,doifnotcommon,doifcommonelse}
-%D {}
-%D
-%D Probably the most time consuming tests are those that test
-%D for overlap in sets of strings.
-%D
-%D \starttypen
-%D \doifcommon {string,...} {string,...} {...}
-%D \doifnotcommon {string,...} {string,...} {...}
-%D \doifcommonelse {string,...} {string,...} {then ...} {else ...}
-%D \stoptypen
-%D
-%D We show the slower alternative first, because it shows us
-%D how things are done.
-%D
-%D \starttypen
-%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 \stoptypen
-%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}
-
-\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}
-
-\long\def\doifcommonelse#1#2#3#4%
- {\p!doifcommonelse{#1}{#2}%
- #3%
- \else
- #4%
- \fi}
-
-\long\def\doifcommon#1#2#3%
- {\p!doifcommonelse{#1}{#2}%
- #3%
- \fi}
-
-\long\def\doifnotcommon#1#2#3%
- {\p!doifcommonelse{#1}{#2}%
- \else
- #3%
- \fi}
-
-%D \macros
-%D {processcommalist,processcommacommand,
-%D processcommalistwithparameters}
-%D {}
-%D
-%D We've already seen some macros that take care of comma
-%D separated lists. Such list can be processed with
-%D
-%D \starttypen
-%D \processcommalist[string,string,...]\commando
-%D \stoptypen
-%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 \processcommalist [\hbox{$a,b,c,d,e,f$}] \dosomething \par
-%D \processcommalist [{a,b,c,d,e,f}] \dosomething \par
-%D \processcommalist [{a,b,c},d,e,f] \dosomething \par
-%D \processcommalist [a,b,{c,d,e},f] \dosomething \par
-%D \processcommalist [a{b,c},d,e,f] \dosomething \par
-%D \processcommalist [{a,b}c,d,e,f] \dosomething \par
-%D \processcommalist [] \dosomething \par
-%D \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\endoflinetoken
- %\let\nextcommaitem\redoprocesscommaitem
- \else\ifx\nexttoken]%
- \let\nextcommaitem=\gobbleoneargument
- \else
- \let\nextcommaitem=\dododoprocesscommaitem
- \fi\fi%\fi
- \nextcommaitem}
-
-\def\doprocesscommaitem%
- {\futurelet\nexttoken\dodoprocesscommaitem}
-
-\def\doprocesscommalist#1]#2%
- {\advance\commalevel by 1\relax
- \long\expandafter\def\csname\s!next\the\commalevel\endcsname##1,%
- {#2{##1}\doprocesscommaitem}%
- \doprocesscommaitem#1,]\relax
- \advance\commalevel by -1\relax}
-
-%D Empty arguments are not processed. Empty items (\type{,,})
-%D however are treated.
-
-\def\docheckcommaitem%
- {\ifx\nexttoken]%
- \let\nextcommaitem=\gobbletwoarguments
- \else
- \let\nextcommaitem=\doprocesscommalist
- \fi
- \nextcommaitem}
-
-\def\processcommalist[%
- {\futurelet\nexttoken\docheckcommaitem}
-
-%D We use the same hack for checking the next character, that
-%D we use in \type{\doifnextcharelse}.
-
-\def\:{\redoprocesscommaitem}
-
-\expandafter\def\: {\futurelet\nexttoken\dodoprocesscommaitem}
-
-%D The previous examples lead to:
-%D
-%D \haalbuffer
-
-%D When a list is saved in a macro, we can use a construction
-%D like:
-%D
-%D \starttypen
-%D \expandafter\processcommalist\expandafter[\list]\command
-%D \stoptypen
-%D
-%D Such solutions suit most situations, but we wanted a bit
-%D more.
-%D
-%D \starttypen
-%D \processcommacommand[string,\stringset,string]\commando
-%D \stoptypen
-%D
-%D where \type{\stringset} is a predefined set, like:
-%D
-%D \starttypen
-%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 \stoptypen
-%D
-%D Commands that are part of the list are expanded, so the
-%D use of this macro has it slimits.
-
-\def\processcommacommand[#1]%
- {\edef\commacommand{#1}%
- \toks0=\expandafter{\expandafter[\commacommand]}%
- \expandafter\processcommalist\the\toks0 }
-
-%D The argument to \type{\command} is not delimited. Because
-%D we often use \type{[]} as delimiters, we also have:
-%D
-%D \starttypen
-%D \processcommalistwithparameters[string,string,...]\command
-%D \stoptypen
-%D
-%D where \type{\command} looks like:
-%D
-%D \starttypen
-%D \def\command[#1]{... #1 ...}
-%D \stoptypen
-
-\def\processcommalistwithparameters[#1]#2%
- {\def\docommand##1{#2[##1]}%
- \processcommalist[#1]\docommand}
-
-%D \macros
-%D {processaction,
-%D processfirstactioninset,
-%D processallactionsinset}
-%D {}
-%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 \starttypen
-%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 \stoptypen
-%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 \starttypen
-%D \processallactionsinset
-%D [x,y,z]
-%D [ a=>\a,
-%D b=>\b,
-%D c=>\c,
-%D default=>\default,
-%D unknown=>\unknown{... \commalistelement ...}]
-%D \stoptypen
-%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 \starttypen
-%D \newcount\processlevel
-%D
-%D \def\processaction[#1]#2[#3]%
-%D {\doifelse{#1}{}
-%D {\def\c!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\c!compareprocessaction[##1=>##2]%
-%D {\edef\!!stringa{##1}%
-%D \ifx\!!stringa\!!stringb
-%D \def\commalistelement{#1}%
-%D ##2%
-%D \let\c!doprocessaction=\gobbleoneargument
-%D \else\ifx\!!stringa\s!unknown
-%D \def\commalistelement{#1}%
-%D ##2%
-%D \fi\fi}}%
-%D \def\c!doprocessaction##1%
-%D {\c!compareprocessaction[##1]}%
-%D \processcommalist[#3]\c!doprocessaction}
-%D
-%D \def\processfirstactioninset[#1]#2[#3]%
-%D {\doifelse{#1}{}
-%D {\processaction[][#3]}
-%D {\def\c!compareprocessaction[##1=>##2][##3]%
-%D {\edef\!!stringa{##1}%
-%D \edef\!!stringb{##3}%
-%D \ifx\!!stringa\!!stringb
-%D \def\commalistelement{##3}%
-%D ##2%
-%D \let\c!doprocessaction=\gobbleoneargument
-%D \let\c!dodoprocessaction=\gobbleoneargument
-%D \else\ifx\!!stringa\s!unknown
-%D \def\commalistelement{##3}%
-%D ##2%
-%D \fi\fi}%
-%D \def\c!doprocessaction##1%
-%D {\def\c!dodoprocessaction####1%
-%D {\c!compareprocessaction[####1][##1]}%
-%D \processcommalist[#3]\c!dodoprocessaction}%
-%D \processcommalist[#1]\c!doprocessaction}}
-%D
-%D \def\processallactionsinset[#1]#2[#3]%
-%D {\doifelse{#1}{}
-%D {\processaction[][#3]}
-%D {\advance\processlevel by 1\relax
-%D \def\c!compareprocessaction[##1=>##2][##3]%
-%D {\edef\!!stringa{##1}%
-%D \edef\!!stringb{##3}%
-%D \ifx\!!stringa\!!stringb
-%D \def\commalistelement{##3}%
-%D ##2%
-%D \let\c!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\c!dodoprocessaction####1%
-%D {\c!compareprocessaction[####1][##1]}%
-%D \processcommalist[#3]\c!dodoprocessaction}%
-%D \processcommalist[#1]{\getvalue{\s!do\the\processlevel}}%
-%D \advance\processlevel by -1\relax}}
-%D \stoptypen
-%D
-%D The gain of speed in the final implementation is around
-%D 20\%, depending on the application.
-
-\newcount\processlevel
-
-\def\v!compareprocessactionA[#1=>#2]%
- {\edef\!!stringb{#1}%
- \ifx\!!stringb\s!default
- #2%
- \fi}
-
-\def\v!compareprocessactionB[#1=>#2]%
- {\expandedaction\!!stringb{#1}%
- \ifx\!!stringa\!!stringb
- \def\commalistelement{#1}%
- #2%
- \let\p!doprocessaction=\gobbleoneargument
- \else
- \edef\!!stringb{#1}%
- \ifx\!!stringb\s!unknown
- \def\commalistelement{#1}%
- #2%
- \fi
- \fi}
-
-\def\processaction[#1]#2[#3]%
- {\let\donottest=\dontprocesstest
- \expandedaction\!!stringa{#1}%
- \let\donottest=\doprocesstest
- \ifx\!!stringa\empty
- \let\v!compareprocessaction=\v!compareprocessactionA
- \else
- \let\v!compareprocessaction=\v!compareprocessactionB
- \fi
- \def\p!doprocessaction##1%
- {\v!compareprocessaction[##1]}%
- \processcommalist[#3]\p!doprocessaction
- \expandactions}
-
-\def\v!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\processfirstactioninset[#1]#2[#3]%
- {\expandedaction\!!stringa{#1}%
- \ifx\!!stringa\empty
- \processaction[][#3]%
- \else
- \def\p!doprocessaction##1%
- {\def\p!dodoprocessaction####1%
- {\v!compareprocessactionC[####1][##1]}%
- \processcommalist[#3]\p!dodoprocessaction}%
- \processcommalist[#1]\p!doprocessaction
- \fi
- \expandactions}
-
-\def\v!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\processallactionsinset[#1]#2[#3]%
- {\expandedaction\!!stringa{#1}%
- \ifx\!!stringa\empty
- \processaction[][#3]%
- \else
- \advance\processlevel by 1\relax
- \setvalue{\s!do\the\processlevel}##1%
- {\def\p!dodoprocessaction####1%
- {\v!compareprocessactionD[####1][##1]}%
- \processcommalist[#3]\p!dodoprocessaction}%
- \processcommalist[#1]{\getvalue{\s!do\the\processlevel}}%
- \advance\processlevel by -1\relax
- \fi
- \expandactions}
-
-%D \macros
-%D {unexpandedprocessaction,
-%D unexpandedprocessfirstactioninset,
-%D unexpandedprocessallactionsinset}
-%D {}
-%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}
-%D {}
-%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 \starttypen
-%D \getfirstcharacter {string}
-%D \stoptypen
-%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\\}
-
-%D \macros
-%D {doifinstringelse}
-%D {}
-%D
-%D We can check for the presence of a substring in a given
-%D sequence of characters.
-%D
-%D \starttypen
-%D \doifinsetelse {substring} {string} {then ...} {else ...}
-%D \stoptypen
-%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 \starttypen
-%D \long\def\p!doifinstringelse#1#2#3#4%
-%D {\def\c!doifinstringelse##1#1##2##3\war%
-%D {\if##2@%
-%D #4%
-%D \else
-%D #3%
-%D \fi}%
-%D \c!doifinstringelse#2#1@@\war}
-%D
-%D \def\doifinstringelse%
-%D {\ExpandBothAfter\p!doifinstringelse}
-%D \stoptypen
-%D
-%D After this we came to:
-%D
-%D \starttypen
-%D \def\p!doifinstringelse#1#2%
-%D {\def\c!doifinstringelse##1#1##2##3\war%
-%D {\if##2@}%
-%D \c!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 \stoptypen
-%D
-%D And finaly it became:
-
-\def\v!ifinstringelse#1#2%
- {\def\c!ifinstringelse##1#1##2##3\war%
- {\csname\if##2@iffalse\else iftrue\fi\endcsname}%
- \c!ifinstringelse#2#1@@\war}
-
-\def\ifinstringelse#1#2%
- {\expanded{\v!ifinstringelse{#1}{#2}}}
-
-\long\def\doifinstringelse#1#2#3#4%
- {\ifinstringelse{#1}{#2}%
- #3%
- \else
- #4%
- \fi}
-
-%D \macros
-%D {doifnumberelse}
-%D {}
-%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 \starttypen
-%D \doifnumberelse {string} {then ...} {else ...}
-%D \stoptypen
-%D
-%D The macro accepts \type{123}, \type{abc}, \type{{}},
-%D \type{\getal} and \type{\the\count...}.
-
-\long\def\doifnumberelse#1#2#3%
- {\getfirstcharacter{#1}%
- \@EA\ifinstringelse\firstcharacter{1234567890}%
- #2%
- \else
- #3%
- \fi}
-
-%D Before we had \type{\ifinstringelse} available, we used:
-%D
-%D \starttypen
-%D \def\doifnumberelse#1%
-%D {\getfirstcharacter{#1}%
-%D \rawdoifinsetelse{\firstcharacter}{1,2,3,4,5,6,7,8,9,0}}
-%D \stoptypen
-
-%D A faster but less fail safe alternative is:
-%D
-%D \starttypen
-%D \dostepwiserecurse{0}{9}{1}
-%D {\@EA\uccode\@EA`\recurselevel=1}
-%D
-%D \long\def\doifnumberelse#1#2#3%
-%D {\getfirstcharacter{#1}%
-%D \@EA\ifnum\@EA\uccode\@EA`\firstcharacter=1
-%D #2%
-%D \else
-%D #3%
-%D \fi}
-%D \stoptypen
-%D
-%D This one only works when the \type{\firstcharacter} is
-%D indeed a character. Numbers and strings of characters go
-%D all right, but arguments like \type{\relax} let things
-%D go wrong.
-
-%D \macros
-%D {makerawcommalist,
-%D rawdoinsetelse,
-%D rawprocesscommalist,
-%D rawprocessaction}
-%D {}
-%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 \starttypen
-%D \makerawcommalist[string,string,...]\stringlist
-%D \rawdoifinsetelse{string}{string,...}{...}{...}
-%D \rawprocesscommalist[string,string,...]\commando
-%D \rawprocessaction[x][a=>\a,b=>\b,c=>\c]
-%D \stoptypen
-%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).
-
-\def\makerawcommalist[#1]#2%
- {\def\appendtocommalist##1%
- {\doifelse{#2}{}
- {\edef#2{##1}}
- {\edef#2{#2,##1}}}%
- \def#2{}%
- \processcommalist[#1]\appendtocommalist}
-
-\def\rawprocesscommaitem#1,%
- {\if]#1\else
- \csname\s!next\the\commalevel\endcsname{#1}%
- \expandafter\rawprocesscommaitem
- \fi}
-
-\def\rawprocesscommalist[#1]#2%
- {\advance\commalevel by 1\relax
- \expandafter\let\csname\s!next\the\commalevel\endcsname=#2%
- \expandafter\rawprocesscommaitem#1,],\relax
- \advance\commalevel by -1\relax}
-
-\def\rawdoifinsetelse#1#2%
- {\doifinstringelse{,#1,}{,#2,}}
-
-\def\v!rawprocessaction[#1][#2]%
- {\def\c!rawprocessaction##1,#1=>##2,##3\war%
- {\if##3@\else
- \def\v!processaction{##2}%
- \fi}%
- \c!rawprocessaction,#2,#1=>,@\war}
-
-\def\rawprocessaction[#1]#2[#3]%
- {\edef\!!stringa{#1}%
- \edef\!!stringb{undefined}%
- \let\v!processaction=\!!stringb
- \ifx\!!stringa\empty
- \@EA\v!rawprocessaction\@EA[\s!default][#3]%
- \else
- \expandafter\v!rawprocessaction\expandafter[\!!stringa][#3]%
- \ifx\v!processaction\!!stringb
- \@EA\v!rawprocessaction\@EA[\s!unknown][#3]%
- \fi
- \fi
- \ifx\v!processaction\!!stringb
- \else
- \v!processaction
- \fi}
-
-%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
-%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 \starttypen
-%D \processunexpandedcommalist
-%D [\alfa\beta,\gamma,\delta\epsilon]
-%D \handleitem
-%D \stoptypen
-%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}
-
-\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,docopyvalue,doresetvalue,
-%D dogetvalue}
-%D {}
-%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 \starttypen
-%D \dosetvalue {label} {variable} {value}
-%D \dosetevalue {label} {variable} {value}
-%D \docopyvalue {to label} {from label} {variable}
-%D \doresetvalue {label} {variable}
-%D \stoptypen
-%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\doresetvalue#1#2%
- {\@EA\def\csname#1#2\endcsname{}}
-
-\def\docopyvalue#1#2#3%
- {\@EA\def\csname#1#3\endcsname{\csname#2#3\endcsname}}
-
-%D \macros
-%D {doassign,undoassign,doassignempty}
-%D {}
-%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 \starttypen
-%D \doassign[label][variable=value]
-%D \undoassign[label][variable=value]
-%D \stoptypen
-%D
-%D and:
-%D
-%D \starttypen
-%D \doassignempty[label][variable=value]
-%D \stoptypen
-%D
-%D Assignments like \type{\doassign} are compatible with:
-%D
-%D \starttypen
-%D \def\labelvariable{value}
-%D \stoptypen
-%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.
-
-%\def\p!doassign#1[#2][#3=#4=#5]%
-% {\let\donottest=\dontprocesstest
-% \edef\!!stringa{#5}%
-% \let\!!stringb=\relax
-% \let\donottest=\doprocesstest
-% \ifx\!!stringa\!!stringb
-% \writestatus
-% {setup}
-% {missing '=' after '#3' in line \the\inputlineno}%
-% \else
-% #1{#2}{#3}{#4}%
-% \fi}
-
-\def\p!doassign#1[#2][#3=#4=#5]% redefined in mult-ini
- {\ifx\empty#3\else % and definitely not \ifx#3\empty
- \ifx\relax#5%
- \writestatus
- {setup}
- {missing '=' after '#3' in line \the\inputlineno}%
- \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]%
- {\doifundefined{#1#2}
- {\dosetvalue{#1}{#2}{#3}}}
-
-%D \macros
-%D {getparameters,geteparameters,forgetparameters}
-%D {}
-%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 \starttypen
-%D \getparameters [label] [...=...,...=...]
-%D \forgetparameters [label] [...=...,...=...]
-%D \stoptypen
-%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 \starttypen
-%D \getparameters
-%D [demo]
-%D [alfa=1,
-%D beta=2]
-%D \stoptypen
-%D
-%D is equivalent to
-%D
-%D \starttypen
-%D \def\demoalfa{1}
-%D \def\demobeta{2}
-%D \stoptypen
-%D
-%D
-%D In the pre||multi||lingual stadium \CONTEXT\ took the next
-%D approach. With
-%D
-%D \starttypen
-%D \def\??demo {@@demo}
-%D \def\!!alfa {alfa}
-%D \def\!!beta {beta}
-%D \stoptypen
-%D
-%D calling
-%D
-%D \starttypen
-%D \getparameters
-%D [\??demo]
-%D [\!!alfa=1,
-%D \!!beta=2]
-%D \stoptypen
-%D
-%D lead to:
-%D
-%D \starttypen
-%D \def\@@demoalfa{1}
-%D \def\@@demobeta{2}
-%D \stoptypen
-%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\forgetparameters%
- {\dogetparameters\doresetvalue}
-
-\let\getexpandedparameters=\geteparameters
-
-%D \macros
-%D {getemptyparameters}
-%D {}
-%D
-%D Sometimes we explicitly want variables to default to an
-%D empty string, so we welcome:
-%D
-%D \starttypen
-%D \getemptyparameters [label] [...=...,...=...]
-%D \stoptypen
-
-\def\getemptyparameters[#1]#2[#3]%
- {\def\p!dogetemptyparameter##1%
- {\doassignempty[#1][##1]}%
- \processcommalist[#3]\p!dogetemptyparameter}
-
-%D \macros
-%D {copyparameters}
-%D {}
-%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 \starttypen
-%D \copyparameters [to-label] [from-label] [name1,name2,...]
-%D \stoptypen
-%D
-%D For instance
-%D
-%D \starttypen
-%D \copyparameters
-%D [internal][external]
-%D [alfa,beta]
-%D \stoptypen
-%D
-%D Leads to:
-%D
-%D \starttypen
-%D \def\internalalfa {\externalalfa}
-%D \def\internalbeta {\externalbeta}
-%D \stoptypen
-%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##1%
- {\docopyvalue{#1}{#3}{##1}}%
- \processcommalist[#5]\docopyparameter}}
-
-%D \macros
-%D {doifassignmentelse}
-%D {}
-%D
-%D A lot of \CONTEXT\ commands take optional arguments, for
-%D instance:
-%D
-%D \starttypen
-%D \dothisorthat[alfa,beta]
-%D \dothisorthat[first=foo,second=bar]
-%D \dothisorthat[alfa,beta][first=foo,second=bar]
-%D \stoptypen
-%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 \starttypen
-%D \doifassignmentelse {...} {then ...} {else ...}
-%D \stoptypen
-
-\def\doifassignmentelse%
- {\doifinstringelse{=}}
-
-%D \macros
-%D {ifparameters,checkparameters}
-%D {}
-%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 \starttypen
-%D \checkparameters[argument]
-%D \stoptypen
-
-\newif\ifparameters
-
-\def\c!checkparameters#1=#2#3\war%
- {\if#2@\parametersfalse\else\parameterstrue\fi}
-
-\def\checkparameters[#1]%
- {\c!checkparameters#1=@@\war}
-
-%D \macros
-%D {getfromcommalist,getfromcommacommand,
-%D commalistelement,
-%D getcommalistsize,getcommacommandsize}
-%D {}
-%D
-%D It's possible to get an element from a commalist or a
-%D command representing a commalist.
-%D
-%D \starttypen
-%D \getfromcommalist [string] [n]
-%D \getfromcommacommand [string,\strings,string,...] [n]
-%D \stoptypen
-%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 \starttypen
-%D \getcommalistsize [string,string,...]
-%D \getcommacommandsize [string,\strings,string,...]
-%D \stoptypen
-%D
-%D Afterwards, the length is available in the macro
-%D \type{\commalistsize} (not a \COUNTER).
-
-\def\commalistsize{0}
-
-\def\p!dogetcommalistsize#1[#2]%
- {\scratchcounter=0\relax
- \def\p!dodogetcommalistsize##1%
- {\advance\scratchcounter by 1\relax}%
- #1[#2]\p!dodogetcommalistsize % was [{#2}]
- \edef\commalistsize{\the\scratchcounter}}
-
-\def\getcommalistsize%
- {\p!dogetcommalistsize\processcommalist}
-
-\def\getcommacommandsize%
- {\p!dogetcommalistsize\processcommacommand}
-
-\def\p!dodogetfromcommalist#1%
- {\advance\scratchcounter by -1\relax
- \ifnum\scratchcounter=0\relax
- \gdef\globalcommalistelement{#1}%
- \def\doprocesscommaitem##1]{}%
- \fi}
-
-\def\p!dogetfromcommalist#1[#2]#3[#4]%
- {\global\let\globalcommalistelement=\empty
- \bgroup
- \scratchcounter=#4\relax
- #1[#2]\p!dodogetfromcommalist
- \egroup
- \let\commalistelement=\globalcommalistelement}
-
-\def\getfromcommalist%
- {\p!dogetfromcommalist\processcommalist}
-
-\def\getfromcommacommand%
- {\p!dogetfromcommalist\processcommacommand}
-
-%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 \haalbuffer
-
-%D \macros
-%D {dosingleargument,dodoubleargument,dotripleargument,
-%D doquadrupleargument,doquintupleargument,dosixtupleargument}
-%D {}
-%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.
-%D
-%D \starttypen
-%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 \stoptypen
-%D
-%D These macros are used in the following way:
-%D
-%D \starttypen
-%D \def\dosetupsomething[#1][#2]%
-%D {... #1 ... #2 ...}
-%D
-%D \def\setupsomething%
-%D {\dodoubleargument\dosetupsomething}
-%D \stoptypen
-%D
-%D The implementation can be surprisingly simple and needs no
-%D further explanation, like:
-%D
-%D \starttypen
-%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 \stoptypen
-%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\doquintupleargument%
- {\chardef\expectedarguments=6
- \dosixtupleempty}
-
-%D \macros
-%D {iffirstagument,ifsecondargument,ifthirdargument,
-%D iffourthargument,iffifthargument,ifsixthargument}
-%D {}
-%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
-
-%D \macros
-%D {dosingleempty,dodoubleempty,dotripleempty,
-%D doquadrupleempty,doquintupleempty}
-%D {}
-%D
-%D The empty argument supplying macros mentioned before, look
-%D like:
-%D
-%D \starttypen
-%D \dosingleempty \command
-%D \dodoubleempty \command
-%D \dotripleempty \command
-%D \doquadrupleempty \command
-%D \doquintupleempty \command
-%D \dosixtupleempty \command
-%D \stoptypen
-%D
-%D So \type{\dodoubleempty} leades to:
-%D
-%D \starttypen
-%D \command[#1][#2]
-%D \command[#1][]
-%D \command[][]
-%D \stoptypen
-%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
-
-\chardef\noexpectedarguments=0
-\chardef\expectedarguments =0
-
-\def\dogetargument#1#2#3#4% redefined in mult-ini
- {\doifnextcharelse{#1}
- {\let\expectedarguments=\noexpectedarguments
- #3\dodogetargument}
- {\ifnum\expectedarguments>\noexpectedarguments
- \writestatus
- {setup}
- {\expectedarguments\space argument(s) expected
- in line \the\inputlineno\space}%
- \fi
- \let\expectedarguments=\noexpectedarguments
- #4\dodogetargument#1#2}}
-
-\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\dosingleempty {\getsingleempty []}
-\def\dodoubleempty {\getdoubleempty []}
-\def\dotripleempty {\gettripleempty []}
-\def\doquadrupleempty {\getquadrupleempty []}
-\def\doquintupleempty {\getquintupleempty []}
-\def\dosixtupleempty {\getsixtupleempty []}
-
-%D \macros
-%D {dosingleargumentwithset,
-%D dodoubleargumentwithset,dodoubleemptywithset,
-%D dotripleargumentwithset,dotripleemptywithset}
-%D {}
-%D
-%D These maybe too mysterious macros enable us to handle more
-%D than one setup at once.
-%D
-%D \starttypen
-%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 \stoptypen
-%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 \starttypen
-%D \def\dodefinesomething[#1][#2]%
-%D {\getparameters[\??xx#1][#2]}
-%D
-%D \def\definesomething%
-%D {\dodoubleargumentwithset\dodefinesomething}
-%D \stoptypen
-%D
-%D Which accepts calls like:
-%D
-%D \starttypen
-%D \definesomething[alfa,beta,...][variable=...,...]
-%D \stoptypen
-%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 {complexorsimple,complexorsimpleempty}
-%D {}
-%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 \starttypen
-%D \complexorsimple {command}
-%D \stoptypen
-%D
-%D When \type{\command} is followed by a \type{[setup]}, then
-%D
-%D \starttypen
-%D \complexcommand [setup]
-%D \stoptypen
-%D
-%D executes, else we get
-%D
-%D \starttypen
-%D \simplecommand
-%D \stoptypen
-%D
-%D An alternative for \type{\complexorsimple} is:
-%D
-%D \starttypen
-%D \complexorsimpleempty {command}
-%D \stoptypen
-%D
-%D Depending on the presence of \type{[setup]}, this one
-%D leads to one of:
-%D
-%D \starttypen
-%D \complexcommando [setup]
-%D \complexcommando []
-%D \stoptypen
-%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.
-
-\def\complexorsimple#1%
- {\doifnextcharelse{[}
- {\firstargumenttrue\getvalue{\s!complex#1}}
- {\firstargumentfalse\getvalue{\s!simple#1}}}
-
-\def\complexorsimpleempty#1%
- {\doifnextcharelse{[}
- {\firstargumenttrue\getvalue{\s!complex#1}}
- {\firstargumentfalse\getvalue{\s!complex#1}[]}}
-
-%D \macros
-%D {definecomplexorsimple,definecomplexorsimpleempty}
-%D {}
-%D
-%D The previous commands are used that often that we found it
-%D worthwile to offer two more alternatives.
-
-\def\setnameofcommand#1%
- {\bgroup
- \escapechar=-1\relax
- \xdef\nameofcommand{\string#1}%
- \egroup}
-
-\def\definewithnameofcommand#1#2% watch the \donottest
- {\setnameofcommand{#2}%
- \@EA\def\@EA#2\@EA{\@EA\donottest\@EA#1\@EA{\nameofcommand}}}
-
-\def\definecomplexorsimple%
- {\definewithnameofcommand\complexorsimple}
-
-\def\definecomplexorsimpleempty%
- {\definewithnameofcommand\complexorsimpleempty}
-
-%D These commands are called as:
-%D
-%D \starttypen
-%D \definecomplexorsimple\command
-%D \stoptypen
-%D
-%D Of course, we must have available
-%D
-%D \starttypen
-%D \def\complexcommand[#1]{...}
-%D \def\simplecommand {...}
-%D \stoptypen
-%D
-%D Using this construction saves a few string now and then.
-
-%D \macros
-%D {definestartstopcommand}
-%D {}
-%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 \starttypen
-%D \definestartstopcommand\somecommand\v!specifier{arg}{arg}%
-%D {do something with arg}
-%D \stoptypen
-%D
-%D This expands to something like:
-%D
-%D \starttypen
-%D \def\somecommand arg \startspecifier arg \stopspecifier%
-%D {do something with arg}
-%D \stoptypen
-%D
-%D The argumentss can be anything reasonable, but double
-%D \type{#}'s are needed in the specification part, like:
-%D
-%D \starttypen
-%D \definestartstopcommand\somecommand\v!specifier{[##1][##2]}{##3}%
-%D {do #1 something #2 with #3 arg}
-%D \stoptypen
-%D
-%D which becomes:
-%D
-%D \starttypen
-%D \def\somecommand[#1][#2]\startspecifier#3\stopspecifier%
-%D {do #1 something #2 with #3 arg}
-%D \stoptypen
-%D
-%D We will see some real applications of this command in the
-%D core modules.
-
-\def\definestartstopcommand#1#2#3#4%
- {\def\!stringa{#3}%
- \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}
-%D {}
-%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 \starttypen
-%D \dosinglegroupempty \IneedONEargument
-%D \dodoublegroupempty \IneedTWOarguments
-%D \dotriplegroupempty \IneedTHREEarguments
-%D \dotriplegroupempty \IneedFOURarguments
-%D \stoptypen
-%D
-%D where \type{\IneedONEargument} takes one and the others
-%D two and three arguments. These macro's were first needed in
-%D \PPCHTEX.
-
-\def\dogetgroupargument#1#2% redefined in mult-ini
- {\def\nextnextargument%
- {\ifx\nextargument\bgroup
- \let\expectedarguments=\noexpectedarguments
- \def\nextargument{#1\dodogetargument}%
- %\else\ifx\nextargument\lineending % this can be an option
- % \def\nextargument{\bgroup\def\\ {\egroup\dogetgroupargument#1#2}\\}%
- %\else\ifx\nextargument\blankspace % but it may never be default
- % \def\nextargument{\bgroup\def\\ {\egroup\dogetgroupargument#1#2}\\}%
- \else
- \ifnum\expectedarguments>\noexpectedarguments
- \writestatus
- {setup}
- {\the\expectedarguments\space argument(s) expected
- in line \the\inputlineno\space}%
- \fi
- \let\expectedarguments=\noexpectedarguments
- \def\nextargument{#2\dodogetargument{}}%
- \fi%\fi\fi % so let's get rid of it
- \nextargument}%
- \futurelet\nextargument\nextnextargument}
-
-\def\dosinglegroupempty#1%
- {\def\dodogetargument%
- {#1}%
- \dogetgroupargument\firstargumenttrue\firstargumentfalse}
-
-\def\dodoublegroupempty#1%
- {\def\dodogetargument##1%
- {\def\dodogetargument%
- {#1{##1}}%
- \dogetgroupargument\secondargumenttrue\secondargumentfalse}%
- \dogetgroupargument\firstargumenttrue\firstargumentfalse}
-
-\def\dotriplegroupempty#1%
- {\def\dodogetargument##1%
- {\def\dodogetargument####1%
- {\def\dodogetargument%
- {#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%
- {#1{##1}{####1}{########1}}%
- \dogetgroupargument\fourthargumenttrue\fourthargumentfalse}%
- \dogetgroupargument\thirdargumenttrue\thirdargumentfalse}%
- \dogetgroupargument\secondargumenttrue\secondargumentfalse}%
- \dogetgroupargument\firstargumenttrue\firstargumentfalse}
-
-
-%D These macros explictly take care of spaces, which means
-%D that the next definition and calls are valid:
-%D
-%D \starttypen
-%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 \stoptypen
-%D
-%D And alike.
-
-%D \macros
-%D {wait}
-%D {}
-%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%
- {\bgroup
- \read16 to \wait
- \egroup}
-
-%D \macros
-%D {writestring,writeline,
-%D writestatus,statuswidth}
-%D {}
-%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 \starttypen
-%D \writestring {string}
-%D \writeline
-%D \writestatus {category} {message}
-%D \stoptypen
-%D
-%D Messages are formatted. One can provide the maximum with
-%D of the identification string with the macro
-%D \type{\statuswidth}.
-
-\def\statuswidth {15}
-
-\def\writestring%
- {\immediate\write16}
-
-\def\writeline%
- {\writestring{}}
-
-\def\dosplitstatus#1#2\end%
- {\ifx#1?%
- \loop
- \advance\scratchcounter by 1
- \ifnum\scratchcounter<\statuswidth\relax
- \edef\messagecontentA{\messagecontentA\space}%
- \repeat
- \else
- \advance\scratchcounter by 1
- \ifnum\scratchcounter<\statuswidth\relax
- \edef\messagecontentA{\messagecontentA#1}%
- \fi
- \dosplitstatus#2\end
- \fi}
-
-\def\writestatus#1#2%
- {\bgroup
- \edef\messagecontentA{}%
- \edef\messagecontentB{#2}% maybe it's \the\scratchcounter
- \scratchcounter=0
- \expandafter\dosplitstatus#1?\end
- \writestring{\messagecontentA\space:\space\messagecontentB}%
- \egroup}
-
-%D \macros
-%D {debuggerinfo}
-%D {}
-%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
-
+%D \module +%D [ file=syst-gen, +%D version=1996.3.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. Non||commercial use is +%C granted. + +%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 +%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 \starttypen +%D \abortinputifdefined\command +%D \stoptypen +%D +%D where \type{\command} is a command defined in the module +%D to be loaded only once. +%D +%D \starttypen +%D \def\abortinputifdefined#1% +%D {\ifx#1\undefined +%D \let\next=\relax +%D \else +%D \let\next=\endinput +%D \fi +%D \next} +%D \stoptypen +%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 \starttypen +%D \ifx\somecommand\undefined +%D \let\next=\relax +%D \else +%D \let\next=\endinput +%D \fi +%D \next +%D \stoptypen +%D +%D We need the \type{\next} because we need to end the +%D \type{\fi}. The efficient one is: +%D +%D \starttypen +%D \ifx\somecommand\undefined +%D \else +%D \expandafter\endinput +%D \fi +%D \stoptypen +%D +%D Because \type{\endinput} comes into action after the current +%D line, we can also say: +%D +%D \starttypen +%D \ifx\somecommand\undefined \else \endinput \fi +%D \stoptypen +%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 \starttypen +%D \writestatus{laden}{Context Systeem Macro's (a)} +%D \stoptypen + +%D \macros +%D [beschermen] +%D {protect,unprotect} +%D {} +%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 \starttypen +%D \unprotect +%D \def\!test{test} +%D \protect +%D \stoptypen +%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>}} +\fi + +\let\normalprotect=\protect + +%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. + +\def\unprotect% + {\ifnum\protectionlevel=0 + \edef\doprotectcharacters% + {\catcode`@=\the\catcode`@\relax + \catcode`!=\the\catcode`!\relax + \catcode`?=\the\catcode`?\relax}% + \catcode`@=11 + \catcode`!=11 + \catcode`?=11 + \let\protect=\doprotect + \fi + \advance\protectionlevel by 1 + \ifnum\protectionlevel>1 + \message{<unprotect \the\protectionlevel>}% + \fi} + +\def\doprotect% + {\ifnum\protectionlevel=1 + \doprotectcharacters + \let\protect=\normalprotect + \fi + \ifnum\protectionlevel>1 + \message{<protect \the\protectionlevel>}% + \fi + \advance\protectionlevel by -1\relax} + +%D Now it is defined, we can make use of this very useful +%D macro. + +\unprotect + +%D \macros +%D {@@escape,@@begingroup,@@endgroup,@@mathshift,@@alignment, +%D @@endofline,@@parameter,@@superscript,@@subscript, +%D @@ignore,@@space,@@letter,@@other,@@active,@@comment} +%D {} +%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 +%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 +%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 +%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 +%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 + +\def\!!stringa{} \def\!!stringb{} \def\!!stringc{} +\def\!!stringd{} \def\!!stringe{} \def\!!stringf{} + +\newdimen\!!widtha \newdimen\!!heighta \newdimen\!!deptha \newif\if!!donea +\newdimen\!!widthb \newdimen\!!heightb \newdimen\!!depthb \newif\if!!doneb + +%D \macros +%D {s!,c!,e!,p!,v!,@@,??} +%D {} +%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,expanded} +%D {} +%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 + +%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 \starttypen +%D \expanded{\setupsomething[\alfa]} +%D \stoptypen +%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. + +\def\expanded#1% + {\edef\@@expanded{\noexpand#1}\@@expanded} + +%D \macros +%D {gobbleoneargument,gobble...arguments} +%D {} +%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{} + +%D \macros +%D {doifnextcharelse} +%D {} +%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 \starttypen +%D \doifnextcharelse {karakter} {then ...} {else ...} +%D \stoptypen +%D +%D This macro differs from the original in testing on +%D \type{\endoflinetoken}, which of course we have to define +%D first. We also use \type{\localnext} because we don't want +%D clashes with \type{\next}. + +\let\endoflinetoken=^^M + +\long\def\doifnextcharelse#1#2#3% + {\let\charactertoken=#1% + \def\!!stringa{#2}% + \def\!!stringb{#3}% + \futurelet\nexttoken\inspectnextcharacter} + +\def\inspectnextcharacter% + {\ifx\nexttoken\blankspace + \let\localnext\reinspectnextcharacter + \else\ifx\!!stringc\endoflinetoken + \let\localnext\reinspectnextcharacter + \else\ifx\nexttoken\charactertoken + \let\localnext\!!stringa + \else + \let\localnext\!!stringb + \fi\fi\fi + \localnext} + +%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 \starttypen +%D \expandafter\def\reinspectnextcharacter % +%D {\futurelet\nexttoken\inspectnextcharacter} +%D \stoptypen +%D +%D However complicated it may look, I'm still glad I stumbled +%D into this construction. + +\def\:{\let\blankspace= } \: + +\def\:{\reinspectnextcharacter} + +\expandafter\def\: {\futurelet\nexttoken\inspectnextcharacter} + +%D \macros +%D {setvalue,setgvalue,setevalue,setxvalue, +%D letvalue, +%D getvalue, +%D resetvalue} +%D {} +%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 \starttypen +%D \setvalue {naam}{...} = \def\naam{...} +%D \setgvalue {naam}{...} = \gdef\naam{...} +%D \setevalue {naam}{...} = \edef\naam{...} +%D \setxvalue {naam}{...} = \xdef\naam{...} +%D \letvalue {naam}=\... = \let\naam=\... +%D \getvalue {naam} = \naam +%D \resetvalue {naam} = \def\naam{} +%D \stoptypen +%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\resetvalue#1% + {\setvalue{#1}{}} + +%D \macros +%D {donottest,unexpanded} +%D {} +%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 \starttypen +%D \def\dosomecommand {... ... ...} +%D \def\somecommand {\donottest\dosomecommand} +%D \stoptypen +%D +%D This double definition can be made transparant by using +%D \type{\protecte}, as in: +%D +%D \starttypen +%D \unexpanded\def\somecommand{... ... ...} +%D \stoptypen +%D +%D The protection mechanism uses: + +\def\dontprocesstest#1% + {==} + +\def\doprocesstest#1% + {#1} + +\let\donottest=\doprocesstest + +%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 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 \starttypen +%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 \stoptypen +%D +%D Well, in fact we use the bit more versaatile alternative: + +\ifnoETEX \def\dosetunexpanded#1#2% + {\@EA#1\@EA{\@EA#2\@EA}% + \@EA{\@EA\donottest\csname\s!do\@EA\string\csname#2\endcsname\endcsname}% + \@EA#1{\s!do\@EA\string\csname#2\endcsname}} + +\ifnoETEX \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} + +\ifnoETEX \def\unexpanded#1% + {\def\dounexpanded% + {\ifx\next\bgroup + \@EA\dosetunexpanded + \else + \@EA\docomunexpanded + \fi#1}% + \futurelet\next\dounexpanded} + +\doifETEX \let\unexpanded=\normalprotected + +%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 \starttypen +%D \def\csname do\somecommand\endcsname{... ... ...} +%D \def\somecommand{\donottest\csname do\somecommand\endcsname} +%D \stoptypen +%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 +%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 \starttypen +%D \doifundefined {string} {...} +%D \doifdefined {string} {...} +%D \doifundefinedelse {string} {then ...} {else ...} +%D \doifdefinedelse {string} {then ...} {else ...} +%D \doifalldefinedelse {commalist} {then ...} {else ...} +%D \stoptypen +%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 \starttypen +%D \expandafter\ifx\csname NameA\endcsname\relax ... \else ... \fi +%D +%D \ifx\NameB\undefined ... \else ... \fi +%D \stoptypen +%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 \starttypen +%D \expandafter\show\csname NameA\endcsname +%D +%D \show\NameB +%D \stoptypen +%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 \starttypen +%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 {\bgroup +%D \donetrue +%D \def\checkcommand##1% +%D {\doifundefined{##1}{\donefalse}}% +%D \processcommalist[#1]\checkcommand +%D \ifdone +%D \egroup#2% +%D \else +%D \egroup#3% +%D \fi} +%D \stoptypen +%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. + +\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#2#3% + {\p!doifundefined{#1}% + \let\donottest=\doprocesstest#2% + \else + \let\donottest=\doprocesstest#3% + \fi} + +\def\doifdefinedelse#1#2#3% + {\p!doifundefined{#1}% + \let\donottest=\doprocesstest#3% + \else + \let\donottest=\doprocesstest#2% + \fi} + +\def\doifundefined#1#2% + {\p!doifundefined{#1}% + \let\donottest=\doprocesstest#2% + \else + \let\donottest=\doprocesstest + \fi} + +\def\doifdefined#1#2% + {\p!doifundefined{#1}% + \let\donottest=\doprocesstest + \else + \let\donottest=\doprocesstest#2% + \fi} + +\doifETEX \def\ifundefined#1% + {\unless\ifcsname#1\endcsname} + +\doifETEX \def\p!doifundefined#1% + {\let\donottest=\dontprocesstest + \unless\ifcsname#1\endcsname} + +%D Before we start using this variant, we used another one, +%D which is even a bit faster. This one looked like: +%D +%D \starttypen +%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 \stoptypen +%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 \starttypen +%D $\kern10pt\showthe\lastkern$ +%D $\kern10pt{\showthe\lastkern}$ +%D $\kern10pt\begingroup\showthe\lastkern\endgroup$ +%D \stoptypen +%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}. + +\def\docheckonedefined#1% + {\ifundefined{#1}% + \donefalse + \fi} + +\def\doifalldefinedelse#1#2#3% + {\begingroup + \let\donottest=\dontprocesstest + \donetrue + \processcommalist[#1]\docheckonedefined + \ifdone + \endgroup\let\donottest=\doprocesstest#2% + \else + \endgroup\let\donottest=\doprocesstest#3% + \fi} + +%D \macros +%D {doif,doifelse,doifnot, +%D donottest} +%D {} +%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 \starttypen +%D \doif {string1} {string2} {...} +%D \doifnot {string1} {string2} {...} +%D \doifelse {string1} {string2} {then ...}{else ...} +%D \stoptypen +%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 \starttypen +%D \ifx\!!stringa\!!stringb +%D \def\next{#3}% +%D \else +%D \def\next{#4}% +%D \fi +%D \next +%D \stoptypen +%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. + +\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 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 \starttypen +%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 \stoptypen +%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 +%D We complete our set of conditionals with: +%D +%D \starttypen +%D \doifempty {string} {...} +%D \doifnot {string} {...} +%D \doifemptyelse {string} {then ...} {else ...} +%D \stoptypen +%D +%D This time, the string is not expanded. + +\long\def\doifemptyelse#1#2#3% + {\def\!!stringa{#1}% + \ifx\!!stringa\empty + #2% + \else + #3% + \fi} + +\long\def\doifempty#1#2% + {\def\!!stringa{#1}% + \ifx\!!stringa\empty + #2% + \fi} + +\long\def\doifnotempty#1#2% + {\def\!!stringa{#1}% + \ifx\!!stringa\empty + \else + #2% + \fi} + +%D \macros +%D {doifinset,doifnotinset,doifinsetelse} +%D {} +%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 \starttypen +%D \doifinset {string} {string,...} {...} +%D \doifnotinset {string} {string,...} {...} +%D \doifinsetelse {string} {string,...} {then ...} {else ...} +%D \stoptypen +%D +%D The second argument is the comma separated set of strings. +%D +%D \starttypen +%D \long\def\doifinsetelse#1#2#3#4% +%D {\doifelse{#1}{} +%D {#4} +%D {\donefalse +%D \def\v!checkiteminset##1% +%D {\doif{#1}{##1} +%D {\donetrue +%D \let\v!checkiteminset=\gobbleoneargument}}% +%D \processcommalist[#2]\v!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 \stoptypen +%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} + +\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} + +\long\def\doifinsetelse#1#2#3#4% + {\p!doifinsetelse{#1}{#2}% + #3% + \else + #4% + \fi} + +\long\def\doifinset#1#2#3% + {\p!doifinsetelse{#1}{#2}% + #3% + \fi} + +\long\def\doifnotinset#1#2#3% + {\p!doifinsetelse{#1}{#2}% + \else + #3% + \fi} + +%D \macros +%D {doifcommon,doifnotcommon,doifcommonelse} +%D {} +%D +%D Probably the most time consuming tests are those that test +%D for overlap in sets of strings. +%D +%D \starttypen +%D \doifcommon {string,...} {string,...} {...} +%D \doifnotcommon {string,...} {string,...} {...} +%D \doifcommonelse {string,...} {string,...} {then ...} {else ...} +%D \stoptypen +%D +%D We show the slower alternative first, because it shows us +%D how things are done. +%D +%D \starttypen +%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 \stoptypen +%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} + +\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} + +\long\def\doifcommonelse#1#2#3#4% + {\p!doifcommonelse{#1}{#2}% + #3% + \else + #4% + \fi} + +\long\def\doifcommon#1#2#3% + {\p!doifcommonelse{#1}{#2}% + #3% + \fi} + +\long\def\doifnotcommon#1#2#3% + {\p!doifcommonelse{#1}{#2}% + \else + #3% + \fi} + +%D \macros +%D {processcommalist,processcommacommand, +%D processcommalistwithparameters} +%D {} +%D +%D We've already seen some macros that take care of comma +%D separated lists. Such list can be processed with +%D +%D \starttypen +%D \processcommalist[string,string,...]\commando +%D \stoptypen +%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 \processcommalist [\hbox{$a,b,c,d,e,f$}] \dosomething \par +%D \processcommalist [{a,b,c,d,e,f}] \dosomething \par +%D \processcommalist [{a,b,c},d,e,f] \dosomething \par +%D \processcommalist [a,b,{c,d,e},f] \dosomething \par +%D \processcommalist [a{b,c},d,e,f] \dosomething \par +%D \processcommalist [{a,b}c,d,e,f] \dosomething \par +%D \processcommalist [] \dosomething \par +%D \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\endoflinetoken + %\let\nextcommaitem\redoprocesscommaitem + \else\ifx\nexttoken]% + \let\nextcommaitem=\gobbleoneargument + \else + \let\nextcommaitem=\dododoprocesscommaitem + \fi\fi%\fi + \nextcommaitem} + +\def\doprocesscommaitem% + {\futurelet\nexttoken\dodoprocesscommaitem} + +\def\doprocesscommalist#1]#2% + {\advance\commalevel by 1\relax + \long\expandafter\def\csname\s!next\the\commalevel\endcsname##1,% + {#2{##1}\doprocesscommaitem}% + \doprocesscommaitem#1,]\relax + \advance\commalevel by -1\relax} + +%D Empty arguments are not processed. Empty items (\type{,,}) +%D however are treated. + +\def\docheckcommaitem% + {\ifx\nexttoken]% + \let\nextcommaitem=\gobbletwoarguments + \else + \let\nextcommaitem=\doprocesscommalist + \fi + \nextcommaitem} + +\def\processcommalist[% + {\futurelet\nexttoken\docheckcommaitem} + +%D We use the same hack for checking the next character, that +%D we use in \type{\doifnextcharelse}. + +\def\:{\redoprocesscommaitem} + +\expandafter\def\: {\futurelet\nexttoken\dodoprocesscommaitem} + +%D The previous examples lead to: +%D +%D \haalbuffer + +%D When a list is saved in a macro, we can use a construction +%D like: +%D +%D \starttypen +%D \expandafter\processcommalist\expandafter[\list]\command +%D \stoptypen +%D +%D Such solutions suit most situations, but we wanted a bit +%D more. +%D +%D \starttypen +%D \processcommacommand[string,\stringset,string]\commando +%D \stoptypen +%D +%D where \type{\stringset} is a predefined set, like: +%D +%D \starttypen +%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 \stoptypen +%D +%D Commands that are part of the list are expanded, so the +%D use of this macro has it slimits. + +\def\processcommacommand[#1]% + {\edef\commacommand{#1}% + \toks0=\expandafter{\expandafter[\commacommand]}% + \expandafter\processcommalist\the\toks0 } + +%D The argument to \type{\command} is not delimited. Because +%D we often use \type{[]} as delimiters, we also have: +%D +%D \starttypen +%D \processcommalistwithparameters[string,string,...]\command +%D \stoptypen +%D +%D where \type{\command} looks like: +%D +%D \starttypen +%D \def\command[#1]{... #1 ...} +%D \stoptypen + +\def\processcommalistwithparameters[#1]#2% + {\def\docommand##1{#2[##1]}% + \processcommalist[#1]\docommand} + +%D \macros +%D {processaction, +%D processfirstactioninset, +%D processallactionsinset} +%D {} +%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 \starttypen +%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 \stoptypen +%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 \starttypen +%D \processallactionsinset +%D [x,y,z] +%D [ a=>\a, +%D b=>\b, +%D c=>\c, +%D default=>\default, +%D unknown=>\unknown{... \commalistelement ...}] +%D \stoptypen +%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 \starttypen +%D \newcount\processlevel +%D +%D \def\processaction[#1]#2[#3]% +%D {\doifelse{#1}{} +%D {\def\c!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\c!compareprocessaction[##1=>##2]% +%D {\edef\!!stringa{##1}% +%D \ifx\!!stringa\!!stringb +%D \def\commalistelement{#1}% +%D ##2% +%D \let\c!doprocessaction=\gobbleoneargument +%D \else\ifx\!!stringa\s!unknown +%D \def\commalistelement{#1}% +%D ##2% +%D \fi\fi}}% +%D \def\c!doprocessaction##1% +%D {\c!compareprocessaction[##1]}% +%D \processcommalist[#3]\c!doprocessaction} +%D +%D \def\processfirstactioninset[#1]#2[#3]% +%D {\doifelse{#1}{} +%D {\processaction[][#3]} +%D {\def\c!compareprocessaction[##1=>##2][##3]% +%D {\edef\!!stringa{##1}% +%D \edef\!!stringb{##3}% +%D \ifx\!!stringa\!!stringb +%D \def\commalistelement{##3}% +%D ##2% +%D \let\c!doprocessaction=\gobbleoneargument +%D \let\c!dodoprocessaction=\gobbleoneargument +%D \else\ifx\!!stringa\s!unknown +%D \def\commalistelement{##3}% +%D ##2% +%D \fi\fi}% +%D \def\c!doprocessaction##1% +%D {\def\c!dodoprocessaction####1% +%D {\c!compareprocessaction[####1][##1]}% +%D \processcommalist[#3]\c!dodoprocessaction}% +%D \processcommalist[#1]\c!doprocessaction}} +%D +%D \def\processallactionsinset[#1]#2[#3]% +%D {\doifelse{#1}{} +%D {\processaction[][#3]} +%D {\advance\processlevel by 1\relax +%D \def\c!compareprocessaction[##1=>##2][##3]% +%D {\edef\!!stringa{##1}% +%D \edef\!!stringb{##3}% +%D \ifx\!!stringa\!!stringb +%D \def\commalistelement{##3}% +%D ##2% +%D \let\c!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\c!dodoprocessaction####1% +%D {\c!compareprocessaction[####1][##1]}% +%D \processcommalist[#3]\c!dodoprocessaction}% +%D \processcommalist[#1]{\getvalue{\s!do\the\processlevel}}% +%D \advance\processlevel by -1\relax}} +%D \stoptypen +%D +%D The gain of speed in the final implementation is around +%D 20\%, depending on the application. + +\newcount\processlevel + +\def\v!compareprocessactionA[#1=>#2][#3]% + {\edef\!!stringb{#1}% + \ifx\!!stringb\s!default + \let\commalistelement=\empty + #2% + \fi} + +\def\v!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} + +\def\processaction[#1]#2[#3]% + {\let\donottest=\dontprocesstest + \expandedaction\!!stringa{#1}% + \let\donottest=\doprocesstest + \ifx\!!stringa\empty + \let\v!compareprocessaction=\v!compareprocessactionA + \else + \let\v!compareprocessaction=\v!compareprocessactionB + \fi + \def\p!doprocessaction##1% + {\v!compareprocessaction[##1][#1]}% + \processcommalist[#3]\p!doprocessaction + \expandactions} + +\def\v!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\processfirstactioninset[#1]#2[#3]% + {\expandedaction\!!stringa{#1}% + \ifx\!!stringa\empty + \processaction[][#3]% + \else + \def\p!doprocessaction##1% + {\def\p!dodoprocessaction####1% + {\v!compareprocessactionC[####1][##1]}% + \processcommalist[#3]\p!dodoprocessaction}% + \processcommalist[#1]\p!doprocessaction + \fi + \expandactions} + +\def\v!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\processallactionsinset[#1]#2[#3]% + {\expandedaction\!!stringa{#1}% + \ifx\!!stringa\empty + \processaction[][#3]% + \else + \advance\processlevel by 1\relax + \setvalue{\s!do\the\processlevel}##1% + {\def\p!dodoprocessaction####1% + {\v!compareprocessactionD[####1][##1]}% + \processcommalist[#3]\p!dodoprocessaction}% + \processcommalist[#1]{\getvalue{\s!do\the\processlevel}}% + \advance\processlevel by -1\relax + \fi + \expandactions} + +%D \macros +%D {unexpandedprocessaction, +%D unexpandedprocessfirstactioninset, +%D unexpandedprocessallactionsinset} +%D {} +%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} +%D {} +%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 \starttypen +%D \getfirstcharacter {string} +%D \stoptypen +%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\\} + +%D \macros +%D {doifinstringelse, doifincsnameelse} +%D {} +%D +%D We can check for the presence of a substring in a given +%D sequence of characters. +%D +%D \starttypen +%D \doifinsetelse {substring} {string} {then ...} {else ...} +%D \stoptypen +%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 \starttypen +%D \long\def\p!doifinstringelse#1#2#3#4% +%D {\def\c!doifinstringelse##1#1##2##3\war% +%D {\if##2@% +%D #4% +%D \else +%D #3% +%D \fi}% +%D \c!doifinstringelse#2#1@@\war} +%D +%D \def\doifinstringelse% +%D {\ExpandBothAfter\p!doifinstringelse} +%D \stoptypen +%D +%D After this we came to: +%D +%D \starttypen +%D \def\p!doifinstringelse#1#2% +%D {\def\c!doifinstringelse##1#1##2##3\war% +%D {\if##2@}% +%D \c!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 \stoptypen +%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: + +\def\v!ifinstringelse#1#2% + {\def\c!ifinstringelse##1#1##2##3\war% + {\csname\if##2@iffalse\else iftrue\fi\endcsname}% + \expanded{\c!ifinstringelse#2#1@@\noexpand\war}} % expand #2 here + +\long\def\doifinstringelse#1#2#3#4% + {\edef\@@instring{#1}% expand #1 here + \@EA\v!ifinstringelse\@EA{\@@instring}{#2}% + #3% + \else + #4% + \fi} + +%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 \starttypen +%D \doifincsnameelse {substring} {\string} {then ...} {else ...} +%D \stoptypen +%D +%D Where \type{\doifinstringelse} does as much expansion as +%D possible, the latter alternative does minimal (one level) +%D expansion. + +\def\v!ifincsnameelse#1#2% + {\def\c!ifincsnameelse##1#1##2##3\war% + {\csname\if##2@iffalse\else iftrue\fi\endcsname}% + \@EA\c!ifincsnameelse#2#1@@\war} + +\long\def\doifincsnameelse#1#2#3#4% + {\edef\@@instring{#1}% + \@EA\v!ifincsnameelse\@EA{\@@instring}{#2}% + #3% + \else + #4% + \fi} + +%D \macros +%D {doifnumberelse} +%D {} +%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 \starttypen +%D \doifnumberelse {string} {then ...} {else ...} +%D \stoptypen +%D +%D The macro accepts \type{123}, \type{abc}, \type{{}}, +%D \type{\getal} and \type{\the\count...}. + +\long\def\doifnumberelse#1#2#3% + {\getfirstcharacter{#1}% + \@EA\ifinstringelse\firstcharacter{1234567890}% + #2% + \else + #3% + \fi} + +%D Before we had \type{\ifinstringelse} available, we used: +%D +%D \starttypen +%D \def\doifnumberelse#1% +%D {\getfirstcharacter{#1}% +%D \rawdoifinsetelse{\firstcharacter}{1,2,3,4,5,6,7,8,9,0}} +%D \stoptypen + +%D A faster but less fail safe alternative is: +%D +%D \starttypen +%D \dostepwiserecurse{0}{9}{1} +%D {\@EA\uccode\@EA`\recurselevel=1} +%D +%D \long\def\doifnumberelse#1#2#3% +%D {\getfirstcharacter{#1}% +%D \@EA\ifnum\@EA\uccode\@EA`\firstcharacter=1 +%D #2% +%D \else +%D #3% +%D \fi} +%D \stoptypen +%D +%D This one only works when the \type{\firstcharacter} is +%D indeed a character. Numbers and strings of characters go +%D all right, but arguments like \type{\relax} let things +%D go wrong. + +%D \macros +%D {makerawcommalist, +%D rawdoinsetelse, +%D rawprocesscommalist, +%D rawprocessaction} +%D {} +%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 \starttypen +%D \makerawcommalist[string,string,...]\stringlist +%D \rawdoifinsetelse{string}{string,...}{...}{...} +%D \rawprocesscommalist[string,string,...]\commando +%D \rawprocessaction[x][a=>\a,b=>\b,c=>\c] +%D \stoptypen +%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). + +\def\makerawcommalist[#1]#2% + {\def\appendtocommalist##1% + {\doifelse{#2}{} + {\edef#2{##1}} + {\edef#2{#2,##1}}}% + \def#2{}% + \processcommalist[#1]\appendtocommalist} + +\def\rawprocesscommaitem#1,% + {\if]#1\else + \csname\s!next\the\commalevel\endcsname{#1}% + \expandafter\rawprocesscommaitem + \fi} + +\def\rawprocesscommalist[#1]#2% + {\advance\commalevel by 1\relax + \expandafter\let\csname\s!next\the\commalevel\endcsname=#2% + \expandafter\rawprocesscommaitem#1,],% \relax + \advance\commalevel by -1\relax} + +\def\rawdoifinsetelse#1#2% + {\doifinstringelse{,#1,}{,#2,}} + +\def\v!rawprocessaction[#1][#2]% + {\def\c!rawprocessaction##1,#1=>##2,##3\war% + {\if##3@\else + \def\v!processaction{##2}% + \fi}% + \c!rawprocessaction,#2,#1=>,@\war} + +\def\rawprocessaction[#1]#2[#3]% + {\edef\!!stringa{#1}% + \edef\!!stringb{undefined}% + \let\v!processaction=\!!stringb + \ifx\!!stringa\empty + \@EA\v!rawprocessaction\@EA[\s!default][#3]% + \else + \expandafter\v!rawprocessaction\expandafter[\!!stringa][#3]% + \ifx\v!processaction\!!stringb + \@EA\v!rawprocessaction\@EA[\s!unknown][#3]% + \fi + \fi + \ifx\v!processaction\!!stringb + \else + \v!processaction + \fi} + +%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 +%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 \starttypen +%D \processunexpandedcommalist +%D [\alfa\beta,\gamma,\delta\epsilon] +%D \handleitem +%D \stoptypen +%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} + +\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,docopyvalue,doresetvalue, +%D dogetvalue} +%D {} +%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 \starttypen +%D \dosetvalue {label} {variable} {value} +%D \dosetevalue {label} {variable} {value} +%D \docopyvalue {to label} {from label} {variable} +%D \doresetvalue {label} {variable} +%D \stoptypen +%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\doresetvalue#1#2% + {\@EA\def\csname#1#2\endcsname{}} + +\def\docopyvalue#1#2#3% + {\@EA\def\csname#1#3\endcsname{\csname#2#3\endcsname}} + +%D \macros +%D {doassign,undoassign,doassignempty} +%D {} +%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 \starttypen +%D \doassign[label][variable=value] +%D \undoassign[label][variable=value] +%D \stoptypen +%D +%D and: +%D +%D \starttypen +%D \doassignempty[label][variable=value] +%D \stoptypen +%D +%D Assignments like \type{\doassign} are compatible with: +%D +%D \starttypen +%D \def\labelvariable{value} +%D \stoptypen +%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. + +%\def\p!doassign#1[#2][#3=#4=#5]% +% {\let\donottest=\dontprocesstest +% \edef\!!stringa{#5}% +% \let\!!stringb=\relax +% \let\donottest=\doprocesstest +% \ifx\!!stringa\!!stringb +% \writestatus +% {setup} +% {missing '=' after '#3' in line \the\inputlineno}% +% \else +% #1{#2}{#3}{#4}% +% \fi} + +\def\p!doassign#1[#2][#3=#4=#5]% redefined in mult-ini + {\ifx\empty#3\else % and definitely not \ifx#3\empty + \ifx\relax#5% + \writestatus + {setup} + {missing '=' after '#3' in line \the\inputlineno}% + \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]% + {\doifundefined{#1#2} + {\dosetvalue{#1}{#2}{#3}}} + +%D \macros +%D {getparameters,geteparameters,forgetparameters} +%D {} +%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 \starttypen +%D \getparameters [label] [...=...,...=...] +%D \forgetparameters [label] [...=...,...=...] +%D \stoptypen +%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 \starttypen +%D \getparameters +%D [demo] +%D [alfa=1, +%D beta=2] +%D \stoptypen +%D +%D is equivalent to +%D +%D \starttypen +%D \def\demoalfa{1} +%D \def\demobeta{2} +%D \stoptypen +%D +%D +%D In the pre||multi||lingual stadium \CONTEXT\ took the next +%D approach. With +%D +%D \starttypen +%D \def\??demo {@@demo} +%D \def\!!alfa {alfa} +%D \def\!!beta {beta} +%D \stoptypen +%D +%D calling +%D +%D \starttypen +%D \getparameters +%D [\??demo] +%D [\!!alfa=1, +%D \!!beta=2] +%D \stoptypen +%D +%D lead to: +%D +%D \starttypen +%D \def\@@demoalfa{1} +%D \def\@@demobeta{2} +%D \stoptypen +%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\forgetparameters% + {\dogetparameters\doresetvalue} + +\let\getexpandedparameters=\geteparameters + +%D \macros +%D {getemptyparameters} +%D {} +%D +%D Sometimes we explicitly want variables to default to an +%D empty string, so we welcome: +%D +%D \starttypen +%D \getemptyparameters [label] [...=...,...=...] +%D \stoptypen + +\def\getemptyparameters[#1]#2[#3]% + {\def\p!dogetemptyparameter##1% + {\doassignempty[#1][##1]}% + \processcommalist[#3]\p!dogetemptyparameter} + +%D \macros +%D {copyparameters} +%D {} +%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 \starttypen +%D \copyparameters [to-label] [from-label] [name1,name2,...] +%D \stoptypen +%D +%D For instance +%D +%D \starttypen +%D \copyparameters +%D [internal][external] +%D [alfa,beta] +%D \stoptypen +%D +%D Leads to: +%D +%D \starttypen +%D \def\internalalfa {\externalalfa} +%D \def\internalbeta {\externalbeta} +%D \stoptypen +%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##1% + {\docopyvalue{#1}{#3}{##1}}% + \processcommalist[#5]\docopyparameter}} + +%D \macros +%D {doifassignmentelse} +%D {} +%D +%D A lot of \CONTEXT\ commands take optional arguments, for +%D instance: +%D +%D \starttypen +%D \dothisorthat[alfa,beta] +%D \dothisorthat[first=foo,second=bar] +%D \dothisorthat[alfa,beta][first=foo,second=bar] +%D \stoptypen +%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 \starttypen +%D \doifassignmentelse {...} {then ...} {else ...} +%D \stoptypen + +\def\doifassignmentelse% + {\doifinstringelse{=}} + +%D \macros +%D {ifparameters,checkparameters} +%D {} +%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 \starttypen +%D \checkparameters[argument] +%D \stoptypen + +\newif\ifparameters + +\def\c!checkparameters#1=#2#3\war% + {\if#2@\parametersfalse\else\parameterstrue\fi} + +\def\checkparameters[#1]% + {\c!checkparameters#1=@@\war} + +%D \macros +%D {getfromcommalist,getfromcommacommand, +%D commalistelement, +%D getcommalistsize,getcommacommandsize} +%D {} +%D +%D It's possible to get an element from a commalist or a +%D command representing a commalist. +%D +%D \starttypen +%D \getfromcommalist [string] [n] +%D \getfromcommacommand [string,\strings,string,...] [n] +%D \stoptypen +%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 \starttypen +%D \getcommalistsize [string,string,...] +%D \getcommacommandsize [string,\strings,string,...] +%D \stoptypen +%D +%D Afterwards, the length is available in the macro +%D \type{\commalistsize} (not a \COUNTER). + +\def\commalistsize{0} + +\def\p!dogetcommalistsize#1[#2]% + {\scratchcounter=0\relax + \def\p!dodogetcommalistsize##1% + {\advance\scratchcounter by 1\relax}% + #1[#2]\p!dodogetcommalistsize % was [{#2}] + \edef\commalistsize{\the\scratchcounter}} + +\def\getcommalistsize% + {\p!dogetcommalistsize\processcommalist} + +\def\getcommacommandsize% + {\p!dogetcommalistsize\processcommacommand} + +\def\p!dodogetfromcommalist#1% + {\advance\scratchcounter by -1\relax + \ifnum\scratchcounter=0\relax + \gdef\globalcommalistelement{#1}% + \def\doprocesscommaitem##1]{}% + \fi} + +\def\p!dogetfromcommalist#1[#2]#3[#4]% + {\global\let\globalcommalistelement=\empty + \bgroup + \scratchcounter=#4\relax + #1[#2]\p!dodogetfromcommalist + \egroup + \let\commalistelement=\globalcommalistelement} + +\def\getfromcommalist% + {\p!dogetfromcommalist\processcommalist} + +\def\getfromcommacommand% + {\p!dogetfromcommalist\processcommacommand} + +%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 \haalbuffer + +%D \macros +%D {dosingleargument,dodoubleargument,dotripleargument, +%D doquadrupleargument,doquintupleargument,dosixtupleargument} +%D {} +%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. +%D +%D \starttypen +%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 \stoptypen +%D +%D These macros are used in the following way: +%D +%D \starttypen +%D \def\dosetupsomething[#1][#2]% +%D {... #1 ... #2 ...} +%D +%D \def\setupsomething% +%D {\dodoubleargument\dosetupsomething} +%D \stoptypen +%D +%D The implementation can be surprisingly simple and needs no +%D further explanation, like: +%D +%D \starttypen +%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 \stoptypen +%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\doquintupleargument% + {\chardef\expectedarguments=6 + \dosixtupleempty} + +%D \macros +%D {iffirstagument,ifsecondargument,ifthirdargument, +%D iffourthargument,iffifthargument,ifsixthargument} +%D {} +%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 + +%D \macros +%D {dosingleempty,dodoubleempty,dotripleempty, +%D doquadrupleempty,doquintupleempty} +%D {} +%D +%D The empty argument supplying macros mentioned before, look +%D like: +%D +%D \starttypen +%D \dosingleempty \command +%D \dodoubleempty \command +%D \dotripleempty \command +%D \doquadrupleempty \command +%D \doquintupleempty \command +%D \dosixtupleempty \command +%D \stoptypen +%D +%D So \type{\dodoubleempty} leades to: +%D +%D \starttypen +%D \command[#1][#2] +%D \command[#1][] +%D \command[][] +%D \stoptypen +%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 + +\chardef\noexpectedarguments=0 +\chardef\expectedarguments =0 + +\def\dogetargument#1#2#3#4% redefined in mult-ini + {\doifnextcharelse{#1} + {\let\expectedarguments=\noexpectedarguments + #3\dodogetargument} + {\ifnum\expectedarguments>\noexpectedarguments + \writestatus + {setup} + {\expectedarguments\space argument(s) expected + in line \the\inputlineno\space}% + \fi + \let\expectedarguments=\noexpectedarguments + #4\dodogetargument#1#2}} + +\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\dosingleempty {\getsingleempty []} +\def\dodoubleempty {\getdoubleempty []} +\def\dotripleempty {\gettripleempty []} +\def\doquadrupleempty {\getquadrupleempty []} +\def\doquintupleempty {\getquintupleempty []} +\def\dosixtupleempty {\getsixtupleempty []} + +%D \macros +%D {dosingleargumentwithset, +%D dodoubleargumentwithset,dodoubleemptywithset, +%D dotripleargumentwithset,dotripleemptywithset} +%D {} +%D +%D These maybe too mysterious macros enable us to handle more +%D than one setup at once. +%D +%D \starttypen +%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 \stoptypen +%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 \starttypen +%D \def\dodefinesomething[#1][#2]% +%D {\getparameters[\??xx#1][#2]} +%D +%D \def\definesomething% +%D {\dodoubleargumentwithset\dodefinesomething} +%D \stoptypen +%D +%D Which accepts calls like: +%D +%D \starttypen +%D \definesomething[alfa,beta,...][variable=...,...] +%D \stoptypen +%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 {complexorsimple,complexorsimpleempty} +%D {} +%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 \starttypen +%D \complexorsimple {command} +%D \stoptypen +%D +%D When \type{\command} is followed by a \type{[setup]}, then +%D +%D \starttypen +%D \complexcommand [setup] +%D \stoptypen +%D +%D executes, else we get +%D +%D \starttypen +%D \simplecommand +%D \stoptypen +%D +%D An alternative for \type{\complexorsimple} is: +%D +%D \starttypen +%D \complexorsimpleempty {command} +%D \stoptypen +%D +%D Depending on the presence of \type{[setup]}, this one +%D leads to one of: +%D +%D \starttypen +%D \complexcommando [setup] +%D \complexcommando [] +%D \stoptypen +%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. + +\def\complexorsimple#1% + {\doifnextcharelse{[} + {\firstargumenttrue\getvalue{\s!complex#1}} + {\firstargumentfalse\getvalue{\s!simple#1}}} + +\def\complexorsimpleempty#1% + {\doifnextcharelse{[} + {\firstargumenttrue\getvalue{\s!complex#1}} + {\firstargumentfalse\getvalue{\s!complex#1}[]}} + +%D \macros +%D {definecomplexorsimple,definecomplexorsimpleempty} +%D {} +%D +%D The previous commands are used that often that we found it +%D worthwile to offer two more alternatives. + +\def\setnameofcommand#1% + {\bgroup + \escapechar=-1\relax + \xdef\nameofcommand{\string#1}% + \egroup} + +\def\definewithnameofcommand#1#2% watch the \donottest + {\setnameofcommand{#2}% + \@EA\def\@EA#2\@EA{\@EA\donottest\@EA#1\@EA{\nameofcommand}}} + +\def\definecomplexorsimple% + {\definewithnameofcommand\complexorsimple} + +\def\definecomplexorsimpleempty% + {\definewithnameofcommand\complexorsimpleempty} + +%D These commands are called as: +%D +%D \starttypen +%D \definecomplexorsimple\command +%D \stoptypen +%D +%D Of course, we must have available +%D +%D \starttypen +%D \def\complexcommand[#1]{...} +%D \def\simplecommand {...} +%D \stoptypen +%D +%D Using this construction saves a few string now and then. + +%D \macros +%D {definestartstopcommand} +%D {} +%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 \starttypen +%D \definestartstopcommand\somecommand\v!specifier{arg}{arg}% +%D {do something with arg} +%D \stoptypen +%D +%D This expands to something like: +%D +%D \starttypen +%D \def\somecommand arg \startspecifier arg \stopspecifier% +%D {do something with arg} +%D \stoptypen +%D +%D The argumentss can be anything reasonable, but double +%D \type{#}'s are needed in the specification part, like: +%D +%D \starttypen +%D \definestartstopcommand\somecommand\v!specifier{[##1][##2]}{##3}% +%D {do #1 something #2 with #3 arg} +%D \stoptypen +%D +%D which becomes: +%D +%D \starttypen +%D \def\somecommand[#1][#2]\startspecifier#3\stopspecifier% +%D {do #1 something #2 with #3 arg} +%D \stoptypen +%D +%D We will see some real applications of this command in the +%D core modules. + +\def\definestartstopcommand#1#2#3#4% + {\def\!stringa{#3}% + \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} +%D {} +%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 \starttypen +%D \dosinglegroupempty \IneedONEargument +%D \dodoublegroupempty \IneedTWOarguments +%D \dotriplegroupempty \IneedTHREEarguments +%D \dotriplegroupempty \IneedFOURarguments +%D \stoptypen +%D +%D where \type{\IneedONEargument} takes one and the others +%D two and three arguments. These macro's were first needed in +%D \PPCHTEX. + +\def\dogetgroupargument#1#2% redefined in mult-ini + {\def\nextnextargument% + {\ifx\nextargument\bgroup + \let\expectedarguments=\noexpectedarguments + \def\nextargument{#1\dodogetargument}% + %\else\ifx\nextargument\lineending % this can be an option + % \def\nextargument{\bgroup\def\\ {\egroup\dogetgroupargument#1#2}\\}% + %\else\ifx\nextargument\blankspace % but it may never be default + % \def\nextargument{\bgroup\def\\ {\egroup\dogetgroupargument#1#2}\\}% + \else + \ifnum\expectedarguments>\noexpectedarguments + \writestatus + {setup} + {\the\expectedarguments\space argument(s) expected + in line \the\inputlineno\space}% + \fi + \let\expectedarguments=\noexpectedarguments + \def\nextargument{#2\dodogetargument{}}% + \fi%\fi\fi % so let's get rid of it + \nextargument}% + \futurelet\nextargument\nextnextargument} + +\def\dosinglegroupempty#1% + {\def\dodogetargument% + {#1}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + +\def\dodoublegroupempty#1% + {\def\dodogetargument##1% + {\def\dodogetargument% + {#1{##1}}% + \dogetgroupargument\secondargumenttrue\secondargumentfalse}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + +\def\dotriplegroupempty#1% + {\def\dodogetargument##1% + {\def\dodogetargument####1% + {\def\dodogetargument% + {#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% + {#1{##1}{####1}{########1}}% + \dogetgroupargument\fourthargumenttrue\fourthargumentfalse}% + \dogetgroupargument\thirdargumenttrue\thirdargumentfalse}% + \dogetgroupargument\secondargumenttrue\secondargumentfalse}% + \dogetgroupargument\firstargumenttrue\firstargumentfalse} + + +%D These macros explictly take care of spaces, which means +%D that the next definition and calls are valid: +%D +%D \starttypen +%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 \stoptypen +%D +%D And alike. + +%D \macros +%D {wait} +%D {} +%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% + {\bgroup + \read16 to \wait + \egroup} + +%D \macros +%D {writestring,writeline, +%D writestatus,statuswidth} +%D {} +%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 \starttypen +%D \writestring {string} +%D \writeline +%D \writestatus {category} {message} +%D \stoptypen +%D +%D Messages are formatted. One can provide the maximum with +%D of the identification string with the macro +%D \type{\statuswidth}. + +\def\statuswidth {15} + +\def\writestring% + {\immediate\write16} + +\def\writeline% + {\writestring{}} + +\def\dosplitstatus#1#2\end% + {\ifx#1?% + \loop + \advance\scratchcounter by 1 + \ifnum\scratchcounter<\statuswidth\relax + \edef\messagecontentA{\messagecontentA\space}% + \repeat + \else + \advance\scratchcounter by 1 + \ifnum\scratchcounter<\statuswidth\relax + \edef\messagecontentA{\messagecontentA#1}% + \fi + \dosplitstatus#2\end + \fi} + +\def\writestatus#1#2% + {\bgroup + \edef\messagecontentA{}% + \edef\messagecontentB{#2}% maybe it's \the\scratchcounter + \scratchcounter=0 + \expandafter\dosplitstatus#1?\end + \writestring{\messagecontentA\space:\space\messagecontentB}% + \egroup} + +%D \macros +%D {debuggerinfo} +%D {} +%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 + |