%D \module %D [ file=syst-aux, % merge of syst-gen cum suis %D version=1996.03.20, %D title=\CONTEXT\ System Macros, %D subtitle=General, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. %D There are some references to \LUA\ variants here but these concern %D (often old) experiments, moved from local test modules to here, %D cleaned up, but not really used. After all it's not that urgent %D and replacing helpers is a delicate process. Don't depend on it. \registerctxluafile{syst-aux}{1.001} %D This is a stripped down combination of: %D %D \startitemize %D \item \type {syst-gen.tex} %D \item \type {syst-ext.tex} %D \item \type {syst-new.tex} %D \stopitemize %D %D We keep them around (for \MKII) so you can find comments, %D experiences, intermediate versions and cleaner variants %D there (and also non-\ETEX\ variants). %D %D Contrary to the older files, we now assume that this one %D is used in \CONTEXT\ and therefore we might also assume that %D some basic functionality is available. %D %D The original files contain previous implementations and notes about %D performance. This file will be stripped down in due time. %D Some of the macros here were only used in the bibliography module. They %D have been be moved to a separate syst module since the bib module is no %D longer using them. Some more will go away. \unprotect %D \macros %D {unexpanded} %D %D Because we use this module only in \MKIV, we have removed the %D old protection code. %D %D \starttyping %D \unexpanded\def\somecommand{... ... ...} %D \stoptyping %D %D This overloads the \ETEX\ primitive but as we already had an \MKII\ %D solution we keep the same name for a similar mechanism. \let\unexpanded\normalprotected % %D \macros % %D {expunded} % %D % %D \unexpanded\edef\TestA{zzz} % %D \edef\TestB{zzz} % %D % %D \doifelse {\TestA} {\TestB} {WRONG} {OKAY} \par % %D \doifelse {\TestA} {\expunded\TestB} {WRONG} {OKAY} \par % %D \doifelse {\expunded\TestA} {\TestB} {OKAY} {WRONG} \par % %D \doifelse {\expunded\TestA} {\expunded\TestB} {OKAY} {WRONG} \par % %def\expunded#1{\normalexpanded\expandafter{#1}} % \def\expunded#1{\expandafter\empty#1} % used within an edef anyway %D As we don't have namespace definers yet, we use a special one: \newcount\c_syst_helpers_n_of_namespaces \c_syst_helpers_n_of_namespaces\pluseight % 1-8 reserved for catcodes \def\v_interfaces_prefix_template_system{\number \c_syst_helpers_n_of_namespaces>>} %def\v_interfaces_prefix_template_system{\characters\c_syst_helpers_n_of_namespaces>>} % no \characters yet \unexpanded\def\installsystemnamespace#1% maybe move this to syst-ini {\ifcsname ??#1\endcsname \writestatus\m!system{duplicate system namespace '#1'}\wait \else \global\advance\c_syst_helpers_n_of_namespaces\plusone \expandafter\edef\csname ??#1\endcsname{\v_interfaces_prefix_template_system}% \fi} %D \macros %D {normalspace} %D %D There is already \type{\space} but just to be sure we also %D provide: \def\normalspace{ } %D \macros %D {!!count, !!toks, !!dimen, !!box, %D !!width, !!height, !!depth, !!string, !!done} %D %D We define some more \COUNTERS\ and \DIMENSIONS. We also %D define some shortcuts to the local scatchregisters~0, 2, 4, %D 6 and~8. \newcount\!!counta \newtoks\!!toksa \newdimen\!!dimena \newbox\!!boxa \newcount\!!countb \newtoks\!!toksb \newdimen\!!dimenb \newbox\!!boxb \newcount\!!countc \newtoks\!!toksc \newdimen\!!dimenc \newbox\!!boxc \newcount\!!countd \newtoks\!!toksd \newdimen\!!dimend \newbox\!!boxd \newcount\!!counte \newtoks\!!tokse \newdimen\!!dimene \newbox\!!boxe \newcount\!!countf \newtoks\!!toksf \newdimen\!!dimenf \newbox\!!boxf \newdimen\!!dimeng \newdimen\!!dimenh \newdimen\!!dimeni \newdimen\!!dimenj \newdimen\!!dimenk \let\!!stringa\empty \let\!!stringb\empty \let\!!stringc\empty \let\!!stringd\empty \let\!!stringe\empty \let\!!stringf\empty \newdimen\!!widtha \newdimen\!!heighta \newdimen\!!deptha \newdimen\!!widthb \newdimen\!!heightb \newdimen\!!depthb \newdimen\!!widthc \newdimen\!!heightc \newdimen\!!depthc \newdimen\!!widthd \newdimen\!!heightd \newdimen\!!depthd \newif\if!!donea \newif\if!!doneb \newif\if!!donec \newif\if!!doned \newif\if!!donee \newif\if!!donef \def\!!zerocount {0} % alongside \zerocount \def\!!minusone {-1} % alongside \minusone \def\!!plusone {1} % alongside \plusone \def\!!plustwo {2} % alongside \plustwo \def\!!plusthree {3} % alongside \plusthree \setnewconstant \uprotationangle 0 \setnewconstant\rightrotationangle 90 \setnewconstant \downrotationangle 180 \setnewconstant \leftrotationangle 270 \ifdefined\data \else \let\data \relax \fi % dep checker %D \macros %D {s!,c!,e!,p!,v!,@@,??} %D %D To save memory, we use constants (sometimes called %D variables). Redefining these constants can have disastrous %D results. \def\v!prefix! {v!} \def\c!prefix! {c!} \def\s!prefix! {s!} \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} \def\s!empty {empty} %D These are not needed any more now that we have wide screens (and %D bytes come cheap). \let\@EA \singleexpandafter \let\@EAEAEA \doubleexpandafter \let\@EAEAEAEAEAEA\tripleexpandafter \let\@NX \noexpand \def\@EAEA {\expandafter\expandafter} % can often be avoided %D Sometimes we pass macros as arguments to commands that don't expand them %D before interpretation. Such commands can be enclosed with \type {\expanded}, %D like: %D %D \starttyping %D \expanded{\setupsomething[\alfa]} %D \stoptyping %D %D Such situations occur for instance when \type{\alfa} is a commalist or when data %D stored in macros is fed to index of list commands. If needed, one should use %D \type{\noexpand} inside the argument. Later on we will meet some more clever %D alternatives to this command. Beware, only the simple one has \type {\noexpand} %D before its argument. \let\m_syst_helpers_expanded\empty \unexpanded\def\expanded#1% {\xdef\m_syst_helpers_expanded{\noexpand#1}\m_syst_helpers_expanded} \unexpanded\def\startexpanded#1\stopexpanded {\xdef\m_syst_helpers_expanded{#1}\m_syst_helpers_expanded} \let\stopexpanded\relax %D Recent \TEX\ engines have a primitive \type {\expanded} and we will use that when %D possible. After all, we can make not expandable macros now. % We cannot use the next variant as first we need to adapt \type {##}'s % in callers: % % \def\expanded#1% % {\normalexpanded{\noexpand#1}} % % \def\startexpanded#1\stopexpanded % {\normalexpanded{#1}} %D \macros %D {gobbleoneargument,gobble...arguments} %D %D The next set of macros just do nothing, except that they get rid of a number of %D arguments. \def\gobbleoneargument #1{} \def\gobbletwoarguments #1#2{} \def\gobblethreearguments#1#2#3{} \def\gobblefourarguments #1#2#3#4{} \def\gobblefivearguments #1#2#3#4#5{} \def\gobblesixarguments #1#2#3#4#5#6{} \def\gobblesevenarguments#1#2#3#4#5#6#7{} \def\gobbleeightarguments#1#2#3#4#5#6#7#8{} \def\gobbleninearguments #1#2#3#4#5#6#7#8#9{} \def\gobbletenarguments #1{\gobbleninearguments} \def\gobbleoneoptional [#1]{} \def\gobbletwooptionals [#1][#2]{} \def\gobblethreeoptionals[#1][#2][#3]{} \def\gobblefouroptionals [#1][#2][#3][#4]{} \def\gobblefiveoptionals [#1][#2][#3][#4][#5]{} %D Reserved macros for tests: \let\donothing\empty \let\m_syst_string_one \empty \let\m_syst_string_two \empty \let\m_syst_string_three\empty \let\m_syst_string_four \empty \let\m_syst_action_yes \empty \let\m_syst_action_nop \empty %D \macros %D {doifnextcharelse} %D %D When we started using \TEX\ in the late eighties, our %D first experiences with programming concerned a simple shell %D around \LATEX. The commands probably use most at \PRAGMA, %D are the itemizing ones. One of those few shell commands took %D care of an optional argument, that enabled us to specify %D what kind of item symbol we wanted. Without understanding %D anything we were able to locate a \LATEX\ macro that could %D be used to inspect the next character. %D %D It's this macro that the ancester of the next one presented %D here. It executes one of two actions, dependant of the next %D character. Disturbing spaces and line endings, which are %D normally interpreted as spaces too, are skipped. %D %D \starttyping %D \doifnextcharelse {karakter} {then ...} {else ...} %D \stoptyping %D %D This macro differs from the original in the use of \type %D {\localnext} because we don't want clashes with \type %D {\next}. \unexpanded\def\doifnextcharelse#1#2#3% #1 should not be {} ! {\let\charactertoken=#1% = needed here \def\m_syst_action_yes{#2}% \def\m_syst_action_nop{#3}% \futurelet\nexttoken\syst_helpers_inspect_next_character} \def\syst_helpers_inspect_next_character {\ifx\nexttoken\blankspace \expandafter\syst_helpers_reinspect_next_character \else \expandafter\syst_helpers_inspect_next_character_indeed \fi} \def\syst_helpers_inspect_next_character_indeed {\ifx\nexttoken\charactertoken \expandafter\m_syst_action_yes \else \expandafter\m_syst_action_nop \fi} %D Because we will mostly use this macro for testing if the next %D character is \type {[}, we also make a slightly faster variant %D as it is not uncommon to have tens of thousands of calls to this %D test in a run. Of course it also is more convenient to read a %D trace then. % We could make variants without the \if_next_blank_space_token but % the overhead is only .1 sec on 3.5 for 10^6 tests and often that % branch is not entered anyway. The fast variants with less checking % do make a difference however: % \testfeature{1000000}{\doifnextoptionalelse \gobbleoneargument\gobbleoneargument[} % 2.902s % \testfeature{1000000}{\doifnextoptionalcselse \gobbleoneargument\gobbleoneargument[} % 2.590s % \testfeature{1000000}{\doiffastoptionalcheckelse \gobbleoneargument\gobbleoneargument[} % 2.387s % \testfeature{1000000}{\doiffastoptionalcheckcselse\gobbleoneargument\gobbleoneargument[} % 2.168s \newif\if_next_blank_space_token \let\syst_helpers_next_optional_character_token=[ \unexpanded\def\doifnextoptionalelse#1#2% {\def\m_syst_action_yes{#1}% \def\m_syst_action_nop{#2}% \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \unexpanded\def\doifnextoptionalcselse#1#2% \cs \cs (upto 10% faster) {\let\m_syst_action_yes#1% \let\m_syst_action_nop#2% \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_inspect_next_optional_character {\ifx\nexttoken\blankspace \expandafter\syst_helpers_reinspect_next_optional_character \else \expandafter\syst_helpers_inspect_next_optional_character_indeed \fi} \def\syst_helpers_inspect_next_optional_character_indeed {\ifx\nexttoken\syst_helpers_next_optional_character_token \expandafter\m_syst_action_yes \else \expandafter\m_syst_action_nop \fi} \let\syst_helpers_next_bgroup_character_token\bgroup \unexpanded\def\doifnextbgroupelse#1#2% {\def\m_syst_action_yes{#1}% \def\m_syst_action_nop{#2}% \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_bgroup_character} \def\syst_helpers_inspect_next_bgroup_character {\ifx\nexttoken\blankspace \expandafter\syst_helpers_reinspect_next_bgroup_character \else \expandafter\syst_helpers_inspect_next_bgroup_character_indeed \fi} \def\syst_helpers_inspect_next_bgroup_character_indeed {\ifx\nexttoken\syst_helpers_next_bgroup_character_token \expandafter\m_syst_action_yes \else \expandafter\m_syst_action_nop \fi} \let\syst_helpers_next_parenthesis_character_token( \unexpanded\def\doifnextparenthesiselse#1#2% {\def\m_syst_action_yes{#1}% \def\m_syst_action_nop{#2}% \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_parenthesis_character} \def\syst_helpers_inspect_next_parenthesis_character {\ifx\nexttoken\blankspace \expandafter\syst_helpers_reinspect_next_parenthesis_character \else \expandafter\syst_helpers_inspect_next_parenthesis_character_indeed \fi} \def\syst_helpers_inspect_next_parenthesis_character_indeed {\ifx\nexttoken\syst_helpers_next_parenthesis_character_token \expandafter\m_syst_action_yes \else \expandafter\m_syst_action_nop \fi} %D The next one is handy in predictable situations: \unexpanded\def\doiffastoptionalcheckelse#1#2% {\def\m_syst_action_yes{#1}% \def\m_syst_action_nop{#2}% \futurelet\nexttoken\syst_helpers_do_if_fast_optional_check_else} \unexpanded\def\doiffastoptionalcheckcselse#1#2% \cs \cs {\let\m_syst_action_yes#1% \let\m_syst_action_nop#2% \futurelet\nexttoken\syst_helpers_do_if_fast_optional_check_else} \def\syst_helpers_do_if_fast_optional_check_else {\ifx\nexttoken\syst_helpers_next_optional_character_token \expandafter\m_syst_action_yes \else \expandafter\m_syst_action_nop \fi} %D This macro uses some auxiliary macros. Although we were able to program quite %D complicated things, I only understood these after rereading the \TEX book. The %D trick is in using a command with a one character name. Such commands differ from %D the longer ones in the fact that trailing spaces are {\em not} skipped. This %D enables us to indirectly define a long named macro that gobbles a space. In the %D first line we define \type {\blankspace}. Next we make \type {\:} equivalent to %D \type {\reinspect...}. This one||character command is expanded before the next %D \type {\def} comes into action. This way the space after \type {\:} becomes a %D delimiter of the longer named \type {\reinspectnextcharacter}. % try: \expandafter\def\firstofoneargument{\syst_helpers_reinspect_next_character} {...} \let\next\: \def\:{\let\blankspace= } \: \def\:{\syst_helpers_reinspect_next_character} \expandafter\def\: {\let\if_next_blank_space_token\iftrue\futurelet\nexttoken\syst_helpers_inspect_next_character} \def\:{\syst_helpers_reinspect_next_optional_character} \expandafter\def\: {\let\if_next_blank_space_token\iftrue\futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\:{\syst_helpers_reinspect_next_bgroup_character} \expandafter\def\: {\let\if_next_blank_space_token\iftrue\futurelet\nexttoken\syst_helpers_inspect_next_bgroup_character} \def\:{\syst_helpers_reinspect_next_parenthesis_character} \expandafter\def\: {\let\if_next_blank_space_token\iftrue\futurelet\nexttoken\syst_helpers_inspect_next_parenthesis_character} \let\:\next %D \macros %D {setvalue,setgvalue,setevalue,setxvalue, %D letvalue,letgvalue,getvalue,resetvalue, %D undefinevalue,ignorevalue} %D %D \TEX's primitive \type {\csname} can be used to construct all kind of commands %D that cannot be defined with \type {\def} and \type {\let}. Every macro programmer %D sooner or later wants macros like these. %D %D \starttyping %D \setvalue {name}{...} = \def\name{...} %D \setgvalue {name}{...} = \gdef\name{...} %D \setevalue {name}{...} = \edef\name{...} %D \setxvalue {name}{...} = \xdef\name{...} %D \letvalue {name}=\... = \let\name=\... %D \letgvalue {name}=\... = \global\let\name=\... %D \getvalue {name} = \name %D \resetvalue {name} = \def\name{} %D \stoptyping %D %D As we will see, \CONTEXT\ uses these commands many times, which is mainly due to %D its object oriented and parameter driven character. \def\setvalue #1{\expandafter \def\csname#1\endcsname} \def\setgvalue #1{\expandafter\gdef\csname#1\endcsname} \def\setevalue #1{\expandafter\edef\csname#1\endcsname} \def\setxvalue #1{\expandafter\xdef\csname#1\endcsname} \def\getvalue #1{\csname#1\endcsname} \def\letvalue #1{\expandafter\let\csname#1\endcsname} \def\letgvalue #1{\global\expandafter\let\csname#1\endcsname} \def\resetvalue #1{\expandafter\let\csname#1\endcsname\empty} \def\undefinevalue#1{\expandafter\let\csname#1\endcsname\undefined} \def\ignorevalue#1#2{\expandafter\let\csname#1\endcsname\empty} \def\setuvalue #1{\normalprotected\expandafter \def\csname#1\endcsname} \def\setuevalue #1{\normalprotected\expandafter\edef\csname#1\endcsname} \def\setugvalue #1{\normalprotected\expandafter\gdef\csname#1\endcsname} \def\setuxvalue #1{\normalprotected\expandafter\xdef\csname#1\endcsname} %D \macros %D {globallet,glet} %D %D In \CONTEXT\ of May 2000 using \type {\globallet} instead of the two %D tokens will save us some $300\times4=1200$ bytes of format file on a 32~bit %D system. Not that it matters much today. This shortcut is already defined: \unexpanded\def\glet{\global\let} \let\globallet\glet %D \macros %D {doifundefined,doifdefined, %D doifundefinedelse,doifdefinedelse, %D doifalldefinedelse} %D %D The standard way of testing if a macro is defined is comparing its meaning with %D another undefined one, usually \type{\undefined}. To garantee correct working of %D the next set of macros, \type{\undefined} may never be defined! %D %D \starttyping %D \doifundefined {string} {...} %D \doifdefined {string} {...} %D \doifundefinedelse {string} {then ...} {else ...} %D \doifdefinedelse {string} {then ...} {else ...} %D \doifalldefinedelse {commalist} {then ...} {else ...} %D \stoptyping %D %D Every macroname that \TEX\ builds gets an entry in the hash table, which is of %D limited size. It is expected that \ETEX\ will offer a less memory||consuming %D alternative. %D Although it will probably never be a big problem, it is good to be aware of the %D difference between testing on a macro name to be build by using \type{\csname} and %D \type{\endcsname} and testing the \type{\name} directly. %D %D \starttyping %D \expandafter\ifx\csname NameA\endcsname\relax ... \else ... \fi %D %D \ifundefined\NameB ... \else ... \fi %D \stoptyping \suppressifcsnameerror\plusone \def\doifundefinedelse#1% {\ifcsname#1\endcsname \expandafter\secondoftwoarguments\else\expandafter\firstoftwoarguments \fi} \def\doifdefinedelse#1% {\ifcsname#1\endcsname \expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments \fi} \def\doifundefined#1% {\ifcsname#1\endcsname \expandafter\gobbleoneargument\else\expandafter\firstofoneargument \fi} \def\doifdefined#1% {\ifcsname#1\endcsname \expandafter\firstofoneargument\else\expandafter\gobbleoneargument \fi} %D \macros %D {letbeundefined} %D %D Testing for being undefined comes down to testing on \type {\relax} when we use %D \type {\csname}, but when using \type {\ifx}, we test on being \type %D {\undefined}! In \ETEX\ we have \type {\ifcsname} and that way of testing on %D existance is not the same as the one described here. Therefore we introduce: \def\letbeundefined#1% potential stack buildup when used \global {\expandafter\let\csname#1\endcsname\undefined} \def\localundefine#1% conditional {\ifcsname#1\endcsname\expandafter\let\csname#1\endcsname\undefined\fi} \def\globalundefine#1% conditional {\ifcsname#1\endcsname\expandafter\global\let\csname#1\endcsname\undefined\fi} %D Beware, being \type {\undefined} in \ETEX\ means that the macro {\em is} defined! %D %D When we were developing the scientific units module, we encountered different %D behavior in text and math mode, which was due to this grouping subtilities. We %D therefore decided to use \type{\begingroup} instead of \type{\bgroup}. \unexpanded\def\doifalldefinedelse#1% {\begingroup \donetrue % we could use a reserved one and avoid the group \processcommalist[#1]\syst_helpers_do_if_all_defined_else \ifdone \endgroup\expandafter\firstoftwoarguments \else \endgroup\expandafter\secondoftwoarguments \fi} \def\syst_helpers_do_if_all_defined_else#1% {\ifcsname#1\endcsname\else \donefalse \expandafter\quitcommalist % added \fi} %D \macros %D {doif,doifelse,doifnot} %D %D Programming in \TEX\ differs from programming in procedural languages like %D \MODULA. This means that one --- well, let me speek for myself --- tries to do %D the things in the well known way. Therefore the next set of \type{\ifthenelse} %D commands were between the first ones we needed. A few years later, the opposite %D became true: when programming in \MODULA, I sometimes miss handy things like %D grouping, runtime redefinition, expansion etc. While \MODULA\ taught me to %D structure, \TEX\ taught me to think recursive. %D %D \starttyping %D \doif {string1} {string2} {...} %D \doifnot {string1} {string2} {...} %D \doifelse {string1} {string2} {then ...}{else ...} %D \stoptyping \unexpanded\def\doif#1#2% {\edef\m_syst_string_one{#1}% \edef\m_syst_string_two{#2}% \ifx\m_syst_string_one\m_syst_string_two \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\doifnot#1#2% {\edef\m_syst_string_one{#1}% \edef\m_syst_string_two{#2}% \ifx\m_syst_string_one\m_syst_string_two \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \unexpanded\def\doifelse#1#2% {\edef\m_syst_string_one{#1}% \edef\m_syst_string_two{#2}% \ifx\m_syst_string_one\m_syst_string_two \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} %D \macros %D {doifempty,doifemptyelse,doifnotempty} %D %D We complete our set of conditionals with: %D %D \starttyping %D \doifempty {string} {...} %D \doifnotempty {string} {...} %D \doifemptyelse {string} {then ...} {else ...} %D \stoptyping %D %D This time, the string is not expanded. \unexpanded\def\doifemptyelse#1% {\def\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\doifempty#1% {\def\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\doifnotempty#1% {\def\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} %D \macros %D {doifinset,doifnotinset,doifinsetelse} %D %D We can check if a string is present in a comma separated set of strings. %D Depending on the result, some action is taken. %D %D \starttyping %D \doifinset {string} {string,...} {...} %D \doifnotinset {string} {string,...} {...} %D \doifinsetelse {string} {string,...} {then ...} {else ...} %D \stoptyping % !0nop=\doifinsetelse{ccc}{,}{yes}{nop} % !0nop=\doifinsetelse{ccc}{,,}{yes}{nop} % !0nop=\doifinsetelse{ccc}{,,,}{yes}{nop} % !1nop=\doifinsetelse{}{}{yes}{nop} % !2yes=\doifinsetelse{aaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop} % !3nop=\doifinsetelse{aaa}{bbb}{yes}{nop} % !4yes=\doifinsetelse{aaa}{aaa}{yes}{nop} % !5nop=\doifinsetelse{aaaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop} % !6nop=\doifinsetelse{}{}{yes}{nop} % !7nop=\doifinsetelse{}{aaa}{yes}{nop} % !8nop=\doifinsetelse{aaa}{}{yes}{nop} % !1=\doifinset{}{}{yes} % !2yes=\doifinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes} % !3=\doifinset{aaa}{bbb}{yes} % !4yes=\doifinset{aaa}{aaa}{yes} % !5=\doifinset{}{}{yes} % !6=\doifinset{aaa}{}{yes} % !1yes=\doifnotinset{}{}{yes} % !2=\doifnotinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes} % !3yes=\doifnotinset{aaa}{bbb}{yes} % !4=\doifnotinset{aaa}{aaa}{yes} % !5yes=\doifnotinset{}{}{yes} % !6yes=\doifnotinset{aaa}{}{yes} \def\v_syst_helpers_right_optional_bracket{]} \def\syst_helpers_do_quit_if_item_in_set_else#1],\relax{\firstoftwoarguments} \def\syst_helpers_do_quit_if_item_in_set #1],\relax{\firstofoneargument} \def\syst_helpers_do_quit_if_item_not_in_set #1],\relax{\gobbleoneargument} \def\syst_helpers_re_do_if_in_set_else{\expandafter\syst_helpers_do_check_if_item_in_set_else\m_syst_string_two,],\relax} \def\syst_helpers_re_do_if_in_set {\expandafter\syst_helpers_do_check_if_item_in_set \m_syst_string_two,],\relax} \def\syst_helpers_re_do_if_not_in_set {\expandafter\syst_helpers_do_check_if_item_not_in_set \m_syst_string_two,],\relax} \unexpanded\def\doifinsetelse#1% make this two step too {\edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\thirdofthreearguments \else \expandafter\syst_helpers_do_if_in_set_else \fi} \def\syst_helpers_do_if_in_set_else#1% {\edef\m_syst_string_two{#1}% \ifx\m_syst_string_two\empty \expandafter\secondoftwoarguments \else \expandafter\syst_helpers_re_do_if_in_set_else \fi} \unexpanded\def\doifinset#1% {\edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\gobbletwoarguments \else \expandafter\syst_helpers_do_if_in_set \fi} \def\syst_helpers_do_if_in_set#1% {\edef\m_syst_string_two{#1}% \ifx\m_syst_string_two\empty \expandafter\gobbleoneargument \else \expandafter\syst_helpers_re_do_if_in_set \fi} \unexpanded\def\doifnotinset#1% {\edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\secondoftwoarguments \else \expandafter\syst_helpers_do_if_not_in_set \fi} \def\syst_helpers_do_if_not_in_set#1% {\edef\m_syst_string_two{#1}% \ifx\m_syst_string_two\empty \expandafter\firstofoneargument \else \expandafter\syst_helpers_re_do_if_not_in_set % ...]{true} \fi} \def\syst_helpers_do_check_if_item_in_set_else#1,#2% #2 eats up preceding space {\edef\m_syst_string_two{#1}% \ifx\m_syst_string_two\empty \expandafter\syst_helpers_do_check_if_item_in_set_else \else \expandafter\syst_helpers_do_do_check_if_item_in_set_else \fi#2} \def\syst_helpers_do_do_check_if_item_in_set_else {\ifx\m_syst_string_two\v_syst_helpers_right_optional_bracket \expandafter\thirdofthreearguments \else \expandafter\syst_helpers_do_do_do_check_if_item_in_set_else \fi} \def\syst_helpers_do_do_do_check_if_item_in_set_else {\ifx\m_syst_string_one\m_syst_string_two \expandafter\syst_helpers_do_quit_if_item_in_set_else \else \expandafter\syst_helpers_do_check_if_item_in_set_else \fi} \def\syst_helpers_do_check_if_item_in_set#1,#2% #2 eats up preceding space {\edef\m_syst_string_two{#1}% \ifx\m_syst_string_two\empty \expandafter\syst_helpers_do_check_if_item_in_set \else \expandafter\syst_helpers_do_do_check_if_item_in_set \fi#2} \def\syst_helpers_do_do_check_if_item_in_set {\ifx\m_syst_string_two\v_syst_helpers_right_optional_bracket \expandafter\gobbletwoarguments \else \expandafter\syst_helpers_do_do_do_check_if_item_in_set \fi} \def\syst_helpers_do_do_do_check_if_item_in_set {\ifx\m_syst_string_one\m_syst_string_two \expandafter\syst_helpers_do_quit_if_item_in_set \else \expandafter\syst_helpers_do_check_if_item_in_set \fi} \def\syst_helpers_do_check_if_item_not_in_set#1,#2% #2 eats up preceding space {\edef\m_syst_string_two{#1}% \ifx\m_syst_string_two\empty \expandafter\syst_helpers_do_check_if_item_not_in_set \else \expandafter\syst_helpers_do_do_check_if_item_not_in_set \fi#2} \def\syst_helpers_do_do_check_if_item_not_in_set {\ifx\m_syst_string_two\v_syst_helpers_right_optional_bracket \expandafter\secondoftwoarguments \else \expandafter\syst_helpers_do_do_do_check_if_item_not_in_set \fi} \def\syst_helpers_do_do_do_check_if_item_not_in_set {\ifx\m_syst_string_one\m_syst_string_two \expandafter\syst_helpers_do_quit_if_item_not_in_set \else \expandafter\syst_helpers_do_check_if_item_not_in_set \fi} %D \macros %D {doifcommon,doifnotcommon,doifcommonelse} %D %D Probably the most time consuming tests are those that test for overlap in sets %D of strings. %D %D \starttyping %D \doifcommon {string,...} {string,...} {...} %D \doifnotcommon {string,...} {string,...} {...} %D \doifcommonelse {string,...} {string,...} {then ...} {else ...} %D \stoptyping % !1yes=\doifcommonelse{aaa,bbb,ccc}{aaa,bbb,ccc}{yes}{nop} % !2nop=\doifcommonelse{aaa,bbb,ccc}{ddd,eee,fff}{yes}{nop} % !3nop=\doifcommonelse{aaa}{ddd,eee,fff}{yes}{nop} % !4yes=\doifcommonelse{aaa}{aaa}{yes}{nop} % !5nop=\doifcommonelse{bbb}{aaa}{yes}{nop} % !6nop=\doifcommonelse{}{aaa,bbb,ccc}{yes}{nop} % !7nop=\doifcommonelse{aaa,bbb,ccc}{}{yes}{nop} % !8nop=\doifcommonelse{}{}{yes}{nop} % !9nop=\doifcommonelse{,,}{,,}{yes}{nop} % !9yes=\doifcommonelse{,a,}{,a,}{yes}{nop} % !9yes=\doifcommonelse{,,a,}{,a,}{yes}{nop} % !9yes=\doifcommonelse{,a,}{,,a,}{yes}{nop} % !9yes=\doifcommonelse{,a,}{,,,a,}{yes}{nop} % !9yes=\doifcommonelse{,,a,}{,,,a,}{yes}{nop} \let\m_syst_common_a\empty \let\m_syst_common_b\empty \let\m_syst_common_c\empty \def\syst_helpers_do_quit_if_common_else#1],\relax#2],\relax{\firstoftwoarguments} \def\syst_helpers_do_check_if_common_else_one#1,#2% {\edef\m_syst_common_c{#1}% \ifx\m_syst_common_c\v_syst_helpers_right_optional_bracket \expandafter\thirdofthreearguments \else \expandafter\syst_helpers_do_common_check \fi#2} \def\syst_helpers_do_check_if_common_else_two#1,#2% we can do an empty #1 check too {\edef\commalistelement{#1}% \ifx\commalistelement\v_syst_helpers_right_optional_bracket \expandafter\syst_helpers_re_do_check_if_common_else_one \else \expandafter\syst_helpers_do_do_check_if_common_else_two \fi#2} \def\syst_helpers_do_do_check_if_common_else_two {\ifx\commalistelement\empty \expandafter\syst_helpers_do_check_if_common_else_two \else \expandafter\syst_helpers_do_do_do_check_if_common_else_two \fi} \def\syst_helpers_do_do_do_check_if_common_else_two {\ifx\m_syst_common_c\commalistelement \expandafter\syst_helpers_do_quit_if_common_else \else \expandafter\syst_helpers_do_check_if_common_else_two \fi} \def\syst_helpers_re_do_check_if_common_else_one#1{\syst_helpers_do_check_if_common_else_one} \def\syst_helpers_do_common_check {\expandafter\syst_helpers_do_check_if_common_else_two\m_syst_common_b,],\relax}% \def\syst_helpers_do_do_do_if_common_else {\expandafter\syst_helpers_do_check_if_common_else_one\m_syst_common_a,],\relax} \def\syst_helpers_do_do_if_common_else#1#2#3#4% {\edef\m_syst_common_a{#3}% \edef\m_syst_common_b{#4}% \ifx\m_syst_common_a\empty \expandafter\secondoftwoarguments \else\ifx\m_syst_common_b\empty \expandafter\expandafter\expandafter\secondoftwoarguments \else \expandafter\expandafter\expandafter\syst_helpers_do_do_do_if_common_else \fi\fi #1#2} \unexpanded\def\doifcommonelse{\syst_helpers_do_do_if_common_else\firstoftwoarguments\secondoftwoarguments} \unexpanded\def\doifcommon {\syst_helpers_do_do_if_common_else\firstofoneargument \gobbleoneargument } \unexpanded\def\doifnotcommon {\syst_helpers_do_do_if_common_else\gobbleoneargument \firstofoneargument } %D \macros %D {processcommalist,processcommacommand,quitcommalist, %D processcommalistwithparameters} %D %D We've already seen some macros that take care of comma separated lists. Such %D list can be processed with %D %D \starttyping %D \processcommalist[string,string,...]\commando %D \stoptyping %D %D The user supplied command \type{\commando} receives one argument: the string. %D This command permits nesting and spaces after commas are skipped. Empty sets %D are no problem. %D %D \startbuffer %D \def\dosomething#1{(#1)} %D %D 1: \processcommalist [\hbox{$a,b,c,d,e,f$}] \dosomething \par %D 2: \processcommalist [{a,b,c,d,e,f}] \dosomething \par %D 3: \processcommalist [{a,b,c},d,e,f] \dosomething \par %D 4: \processcommalist [a,b,{c,d,e},f] \dosomething \par %D 5: \processcommalist [a{b,c},d,e,f] \dosomething \par %D 6: \processcommalist [{a,b}c,d,e,f] \dosomething \par %D 7: \processcommalist [] \dosomething \par %D 8: \processcommalist [{[}] \dosomething \par %D \stopbuffer %D %D \typebuffer %D %D Before we show the result, we present the macro's: \newcount\commalevel \installsystemnamespace{nextcommalevel} \def\syst_helpers_do_do_do_process_comma_item {\csname\??nextcommalevel\the\commalevel\endcsname} \def\syst_helpers_do_do_process_comma_item {\ifx\nexttoken\blankspace \expandafter\syst_helpers_re_do_process_comma_item \else \expandafter\syst_helpers_do_do_process_comma_item_indeed \fi} \def\syst_helpers_do_do_process_comma_item_indeed {\ifx\nexttoken]% \expandafter\gobbleoneargument \else \expandafter\syst_helpers_do_do_do_process_comma_item \fi} \def\syst_helpers_do_process_comma_item {\futurelet\nexttoken\syst_helpers_do_do_process_comma_item} %D Empty arguments are not processed. Empty items (\type {,,}) however are %D treated. We have to check for the special case \type {[{a,b,c}]}. \unexpanded\def\processcommalist[% {\futurelet\nexttoken\syst_helpers_do_check_comma_item} \def\syst_helpers_do_check_comma_item {\ifx\nexttoken]% \expandafter\gobblethreearguments \else \expandafter\syst_helpers_do_process_comma_list \fi \relax} % this one preserved the next {} \def\syst_helpers_do_process_comma_list#1]#2% {\global\advance\commalevel \plusone \expandafter\def\csname\??nextcommalevel\the\commalevel\endcsname##1,% {#2{##1}\syst_helpers_do_process_comma_item}% \expandafter\syst_helpers_do_do_process_comma_item\gobbleoneargument#1,]\relax \global\advance\commalevel \minusone } %D One way of quitting a commalist halfway is: \unexpanded\def\quitcommalist {\begingroup\let\syst_helpers_do_process_comma_item\syst_helpers_do_quit_comma_list} \def\syst_helpers_do_quit_comma_list#1]% {\endgroup} \unexpanded\def\quitprevcommalist {\begingroup\let\syst_helpers_do_process_comma_item\syst_helpers_do_quit_prev_comma_list} \def\syst_helpers_do_quit_prev_comma_list#1]% {\let\syst_helpers_do_process_comma_item\syst_helpers_do_quit_comma_list} %D The hack we used for checking the next character \type {\doifnextcharelse} %D is also used here. \let\next\: \def\:{\syst_helpers_re_do_process_comma_item} % \:not saved ? \expandafter\def\: {\futurelet\nexttoken\syst_helpers_do_do_process_comma_item} \let\:\next %D The previous examples lead to: %D %D \getbuffer %D When a list is saved in a macro, we can use a construction like: %D %D \starttyping %D \expandafter\processcommalist\expandafter[\list]\command %D \stoptyping %D %D Such solutions suit most situations, but we wanted a bit more. %D %D \starttyping %D \processcommacommand[string,\stringset,string]\commando %D \stoptyping %D %D where \type{\stringset} is a predefined set, like: %D %D \starttyping %D \def\first{aap,noot,mies} %D \def\second{laatste} %D %D \processcommacommand[\first]\message %D \processcommacommand[\first,second,third]\message %D \processcommacommand[\first,between,\second]\message %D \stoptyping %D %D Commands that are part of the list are expanded, so the use of %D this macro has its limits. \unexpanded\def\processcommacommand[#1]% {\normalexpanded{\processcommalist[#1]}} %D The argument to \type{\command} is not delimited. Because we often %D use \type{[]} as delimiters, we also have: %D %D \starttyping %D \processcommalistwithparameters[string,string,...]\command %D \stoptyping %D %D where \type{\command} looks like: %D %D \starttyping %D \def\command[#1]{... #1 ...} %D \stoptyping \unexpanded\def\processcommalistwithparameters[#1]#2% {\def\syst_helpers_do_process_comma_list_with_parameters##1{#2[##1]}% \processcommalist[#1]\syst_helpers_do_process_comma_list_with_parameters} %D \macros %D {startprocesscommalist,startprocesscommacommand} %D %D Two more: \let\syst_helpers_comma_list_step\relax \unexpanded\def\startprocesscommalist[#1]#2\stopprocesscommalist {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}% \processcommalist[#1]\syst_helpers_comma_list_step} \unexpanded\def\startprocesscommacommand[#1]#2\stopprocesscommacommand {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}% \normalexpanded{\processcommalist[#1]}\syst_helpers_comma_list_step} \let\stopprocesscommalist \relax \let\stopprocesscommacommand\relax %D \macros %D {processaction, %D processfirstactioninset, %D processallactionsinset} %D %D \CONTEXT\ makes extensive use of a sort of case or switch %D command. Depending of the presence of one or more provided %D items, some actions is taken. These macros can be nested %D without problems. %D %D \starttyping %D \processaction [x] [a=>\a,b=>\b,c=>\c] %D \processfirstactioninset [x,y,z] [a=>\a,b=>\b,c=>\c] %D \processallactionsinset [x,y,z] [a=>\a,b=>\b,c=>\c] %D \stoptyping %D %D We can supply both a \type{default} action and an action %D to be undertaken when an \type{unknown} value is met: %D %D \starttyping %D \processallactionsinset %D [x,y,z] %D [ a=>\a, %D b=>\b, %D c=>\c, %D default=>\default, %D unknown=>\unknown{... \commalistelement ...}] %D \stoptyping %D %D When \type{#1} is empty, this macro scans list \type{#2} for %D the keyword \type{default} and executed the related action %D if present. When \type{#1} is non empty and not in the list, %D the action related to \type{unknown} is executed. Both %D keywords must be at the end of list \type{#2}. Afterwards, %D the actually found keyword is available in %D \type{\commalistelement}. An advanced example of the use of %D this macro can be found in \PPCHTEX, where we completely %D rely on \TEX\ for interpreting user supplied keywords like %D \type{SB}, \type{SB1..6}, \type{SB125} etc. \newcount\processlevel % obsolete: \def\expandactions{\let\expandedaction\edef} \expandactions (see mkii) \unexpanded\def\syst_helpers_do_compare_process_action_a[#1=>#2][#3]% {\edef\m_syst_string_two{#1}% \ifx\m_syst_string_two\s!default \let\commalistelement\empty #2% \fi} % met \quitcommalist tot meer dan 25\% sneller \unexpanded\def\syst_helpers_do_compare_process_action_b[#1=>#2][#3]% {\edef\m_syst_string_two{#1}% \ifx\m_syst_string_one\m_syst_string_two \def\commalistelement{#3}% #2% \expandafter\quitcommalist \else\ifx\m_syst_string_two\s!unknown \def\commalistelement{#3}% beware of loops #2% \fi\fi} \unexpanded\def\processaction[#1]#2[% {\edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \let\syst_helpers_do_compare_process_action\syst_helpers_do_compare_process_action_a \else \let\syst_helpers_do_compare_process_action\syst_helpers_do_compare_process_action_b \fi \edef\syst_helpers_do_process_action##1{\syst_helpers_do_compare_process_action[##1][#1]}% expands #1 \processnextcommalist\relax\relax\syst_helpers_do_process_action[} \unexpanded\def\syst_helpers_do_compare_process_action_c[#1=>#2][#3]% {\edef\m_syst_string_one{#1}% \edef\m_syst_string_two{#3}% \ifx\m_syst_string_one\m_syst_string_two \def\commalistelement{#3}% #2% \expandafter\quitprevcommalist \else \edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\s!unknown \def\commalistelement{#3}% #2% \fi \fi} \unexpanded\def\processfirstactioninset[#1]% {\edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\processaction \else \expandafter\syst_helpers_process_first_action_in_set_indeed \fi [#1]} \unexpanded\def\syst_helpers_process_first_action_in_set_indeed[#1]#2[#3]% {\def\syst_helpers_do_process_action##1% {\def\syst_helpers_do_do_process_action####1{\syst_helpers_do_compare_process_action_c[####1][##1]}% \processcommalist[#3]\syst_helpers_do_do_process_action}% \normalexpanded{\processcommalist[#1]}\syst_helpers_do_process_action} \unexpanded\def\syst_helpers_do_compare_process_action_d[#1=>#2][#3]% {\edef\m_syst_string_one{#1}% \edef\m_syst_string_two{#3}% \ifx\m_syst_string_one\m_syst_string_two \def\commalistelement{#3}% #2% \expandafter\quitcommalist \else \edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\s!unknown \def\commalistelement{#3}% #2% \fi \fi} \installsystemnamespace{nextactionlevel} \unexpanded\def\syst_helpers_do_process_all_actions_in_set {\csname\??nextactionlevel\the\processlevel\endcsname} \unexpanded\def\processallactionsinset[#1]% {\edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\processaction \else \expandafter\syst_helpers_process_all_actions_in_set_indeed \fi [#1]} \unexpanded\def\syst_helpers_process_all_actions_in_set_indeed[#1]#2[#3]% {\advance\processlevel \plusone \expandafter\def\csname\??nextactionlevel\the\processlevel\endcsname##1% {\def\syst_helpers_do_do_process_action####1{\syst_helpers_do_compare_process_action_d[####1][##1]}% \processcommalist[#3]\syst_helpers_do_do_process_action}% \normalexpanded{\processcommalist[#1]}\syst_helpers_do_process_all_actions_in_set \advance\processlevel\minusone} %D These macros use: \unexpanded\def\processnextcommalist#1#2#3[#4#5]% {#1% \let\nexttoken#4% \global\advance\commalevel \plusone \expandafter\def\csname\??nextcommalevel\the\commalevel\endcsname##1,% {#3{##1}\syst_helpers_do_process_comma_item}% \syst_helpers_do_do_process_comma_item#4#5,]\relax \global\advance\commalevel\minusone #2} %D \macros %D {getfirstcharacter, firstcharacter, remainingcharacters, doiffirstcharacter} %D %D Sometimes the action to be undertaken depends on the %D next character. This macro get this character and puts it in %D \type{\firstcharacter}. %D %D \starttyping %D \getfirstcharacter {string} %D \stoptyping %D %D A two step expansion is used to prevent problems with %D complicated arguments, for instance arguments that %D consist of two or more expandable tokens. \def\getfirstcharacter #1{\ctxcommand{getfirstcharacter(\!!bs#1\!!es)}} \def\doiffirstcharelse#1#2{\ctxcommand{doiffirstcharelse(\!!bs#1\!!es,\!!bs#2\!!es)}} % chr str %D \macros %D {doifinstringelse, doifincsnameelse} %D %D We can check for the presence of a substring in a given %D sequence of characters. %D %D \starttyping %D \doifinsetelse {substring} {string} {then ...} {else ...} %D \stoptyping \let\m_syst_sub_string\empty \unexpanded\def\doifinstringelse#1% {\edef\m_syst_sub_string{#1}% expand #1 here \ifx\m_syst_sub_string\empty \expandafter\thirdofthreearguments \else \expandafter\syst_helpers_do_if_in_string_else_indeed \fi} \unexpanded\def\syst_helpers_do_if_in_string_else_indeed#1% {\syst_helpers_do_if_in_string_else\m_syst_sub_string{#1}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\doifinstring#1%% {\edef\m_syst_sub_string{#1}% expand #1 here \ifx\m_syst_sub_string\empty \expandafter\gobbletwoarguments \else \expandafter\syst_helpers_do_if_in_string_indeed \fi} \unexpanded\def\syst_helpers_do_if_in_string_indeed#1% {\syst_helpers_do_if_in_string_else\m_syst_sub_string{#1}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\doifnotinstring#1%% {\edef\m_syst_sub_string{#1}% expand #1 here \ifx\m_syst_sub_string\empty \expandafter\gobbletwoarguments \else \expandafter\syst_helpers_do_if_not_in_string_indeed \fi} \unexpanded\def\syst_helpers_do_if_not_in_string_indeed#1% {\syst_helpers_do_if_in_string_else\m_syst_sub_string{#1}% \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} % replaces prev \unexpanded\def\syst_helpers_do_if_in_string_else#1#2% ##2 can be {abc} {\expandafter\def\expandafter\syst_helpers_do_do_if_in_string_else \expandafter##\expandafter1#1##2##3\_e_o_s_{\unless\if##2@}% expand #1 here \expandafter\syst_helpers_do_do_if_in_string_else\normalexpanded{#2#1}@@\_e_o_s_} % expand #2 here %D The next alternative proved to be upto twice as fast on %D tasks like checking reserved words in pretty verbatim %D typesetting! This is mainly due to the fact that passing %D (expanded) strings is much slower that passing a macro. %D %D \starttyping %D \doifincsnameelse {substring} {\string} {then ...} {else ...} %D \stoptyping %D %D Where \type{\doifinstringelse} does as much expansion as %D possible, the latter alternative does minimal (one level) %D expansion. \unexpanded\def\syst_helpers_do_if_in_csname_else#1#2% {\def\syst_helpers_do_do_if_in_csname_else##1#1##2##3\_e_o_s_ {\unless\if##2@}% \expandafter\syst_helpers_do_do_if_in_csname_else#2#1@@\_e_o_s_} \unexpanded\def\doifincsnameelse#1#2% {\normalexpanded{\syst_helpers_do_if_in_csname_else{#1}}{#2}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} %D \macros %D {doifnumberelse,doifnumber,doifnotnumber} %D %D The next macro executes a command depending of the outcome of a test on %D numerals. This is probably one of the fastest test possible, exept from %D a less robust 10||step \type {\if}||ladder or some tricky \type {\lcode} %D checking. %D %D \starttyping %D \doifnumberelse {string} {then ...} {else ...} %D \stoptyping %D %D The macro accepts \type {123}, \type {abc}, \type {{}}, \type {\getal} and %D \type {\the\count...}. This macro is a rather dirty one. \def\doifnumberelse#1% does not accept counters (fully expandable) {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} \def\doifnumber#1% {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \def\doifnotnumber#1% {\ifcase0\ifcase1#1\or\or\or\or\or\or\or\or\or\else1\fi\space \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} %D \macros %D {setpercentdimen} %D %D \starttyping %D \scratchdimen=100pt \setpercentdimen\scratchdimen{10\letterpercent} %D \scratchdimen=100pt \setpercentdimen\scratchdimen{5pt} %D \scratchdimen \percentdimen \hsize {10\letterpercent} %D \stoptyping \def\percentdimen#1#2% dimen percentage (with %) {\dimexpr\ctxcommand{percentageof("#2",\number#1)}\relax} \unexpanded\def\setpercentdimen#1#2% dimen percentage (with %) {#1=\ctxcommand{percentageof("#2",\number#1)}\relax} %D \macros %D {makerawcommalist, %D rawdoinsetelse, %D rawprocesscommalist, %D rawprocessaction} %D %D Some of the commands mentioned earlier are effective but %D slow. When one is desperately in need of faster alternatives %D and when the conditions are predictable safe, the \type{\raw} %D alternatives come into focus. A major drawback is that %D they do not take \type{\c!constants} into account, simply %D because no expansion is done. This is no problem with %D \type{\rawprocesscommalist}, because this macro does not %D compare anything. Expandable macros are permitted as search %D string. %D %D \starttyping %D \makerawcommalist[string,string,...]\stringlist %D \rawdoifinsetelse{string}{string,...}{...}{...} %D \rawprocesscommalist[string,string,...]\commando %D \rawprocessaction[x][a=>\a,b=>\b,c=>\c] %D \stoptyping %D %D Spaces embedded in the list, for instance after commas, %D spoil the search process. The gain in speed depends on the %D length of the argument (the longer the argument, the less %D we gain). \unexpanded\def\makerawcommalist[#1]#2% use \processnext ... here {\def\syst_helpers_do_make_raw_comma_list##1% we don't expand ##1 {\ifx#2\empty \def#2{##1}% \else \expandafter\def\expandafter#2\expandafter{#2,##1}% \fi}% \let#2\empty \processcommalist[#1]\syst_helpers_do_make_raw_comma_list} \def\syst_helpers_raw_process_comma_item#1,#2% #2 eats up preceding space {\if]#1\else \csname\??nextcommalevel\the\commalevel\endcsname{#1}% \expandafter\syst_helpers_raw_process_comma_item \fi#2} \unexpanded\def\rawprocesscommalist[#1]#2% accepteert ook [\cs] {\global\advance\commalevel \plusone \expandafter\let\csname\??nextcommalevel\the\commalevel\endcsname#2% \expandafter\syst_helpers_raw_process_comma_item#1,],% \relax \global\advance\commalevel \minusone } \unexpanded\def\rawprocesscommacommand[#1]% not really needed {\normalexpanded{\rawprocesscommalist[#1]}} % \def\rawdoifinsetelse#1#2{\doifinstringelse{,#1,}{,#2,}} % \def\rawdoifinset #1#2{\doifinstring {,#1,}{,#2,}} \def\m_syst_two_commas{,,} \unexpanded\def\rawdoifinsetelse#1% {\edef\m_syst_sub_string{,#1,}% expand #1 here \ifx\m_syst_sub_string\m_syst_two_commas \expandafter\thirdofthreearguments \else \expandafter\syst_helpers_raw_do_if_in_set_else \fi} \unexpanded\def\syst_helpers_raw_do_if_in_set_else#1% {\syst_helpers_do_if_in_string_else\m_syst_sub_string{,#1,}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\rawdoifinset#1% {\edef\m_syst_sub_string{,#1,}% expand #1 here \ifx\m_syst_sub_string\m_syst_two_commas \expandafter\gobbletwoarguments \else \expandafter\syst_helpers_raw_do_if_in_set \fi} \unexpanded\def\syst_helpers_raw_do_if_in_set#1%% {\syst_helpers_do_if_in_string_else\m_syst_sub_string{,#1,}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} %D Some more raw material: \def\syst_helpers_do_raw_process_action[#1][#2]% {\def\syst_helpers_do_do_raw_process_action##1,#1=>##2,##3\_e_o_s_ {\if##3@\else \def\m_syst_helpers_process_action{##2}% \fi}% \syst_helpers_do_do_raw_process_action,#2,#1=>,@\_e_o_s_} \unexpanded\def\rawprocessaction[#1]#2[#3]% {\edef\m_syst_string_one{#1}% \edef\m_syst_string_two{undefined}% better \!!undefined \let\m_syst_helpers_process_action\m_syst_string_two \ifx\m_syst_string_one\empty \expandafter\syst_helpers_do_raw_process_action\expandafter[\s!default][#3]% \else \expandafter\syst_helpers_do_raw_process_action\expandafter[\m_syst_string_one][#3]% \ifx\m_syst_helpers_process_action\m_syst_string_two \expandafter\syst_helpers_do_raw_process_action\expandafter[\s!unknown][#3]% \fi \fi \ifx\m_syst_helpers_process_action\m_syst_string_two \else \m_syst_helpers_process_action \fi} %D When we process the list \type{a,b,c,d,e}, the raw routine takes over 30\% less %D time, when we feed $20+$ character strings we gain about 20\%. Alternatives which %D use \type{\futurelet} perform worse. Part of the speedup is due to the %D \type{\let} and \type{\expandafter} in the test. %D \macros %D {dosetvalue,dosetevalue,dosetgvalue,docopyvalue,doresetvalue, %D dogetvalue} %D %D When we are going to do assignments, we have to take multi||linguality into account. %D For the moment we keep things simple and single||lingual. %D %D \starttyping %D \dosetvalue {label} {variable} {value} %D \dosetevalue {label} {variable} {value} %D \dosetgvalue {label} {variable} {value} %D \docopyvalue {to label} {from label} {variable} %D \doresetvalue {label} {variable} %D \stoptyping %D %D These macros are in fact auxiliary ones and are not meant for use outside the %D assignment macros. \def\dosetvalue#1#2% #3 {\expandafter\def\csname#1#2\endcsname} % {#3}} \def\dosetevalue#1#2% #3 {\expandafter\edef\csname#1#2\endcsname} % {#3}} \def\dosetgvalue#1#2% #3 {\expandafter\gdef\csname#1#2\endcsname} % {#3}} \def\doresetvalue#1#2% {\expandafter\let\csname#1#2\endcsname\empty} \def\doignorevalue#1#2#3% {\expandafter\let\csname#1#2\endcsname\empty} \def\docopyvalue#1#2#3% {\expandafter\def\csname#1#3\endcsname{\csname#2#3\endcsname}} %D \macros %D {doassign,undoassign,doassignempty} %D %D Assignments are the backbone of \CONTEXT. Abhorred by the concept of style file %D hacking, we took a considerable effort in building a parameterized system. %D Unfortunately there is a price to pay in terms of speed. Compared to other %D packages and taking the functionality of \CONTEXT\ into account, the total size %D of the format file is still very acceptable. Now how are these assignments done. %D %D Assignments can be realized with: %D %D \starttyping %D \doassign[label][variable=value] %D \undoassign[label][variable=value] %D \stoptyping %D %D and: %D %D \starttyping %D \doassignempty[label][variable=value] %D \stoptyping %D %D Assignments like \type{\doassign} are compatible with: %D %D \starttyping %D \def\labelvariable{value} %D \stoptyping %D %D We do check for the presence of an \type{=} and loudly complain of it's missed. We %D will redefine this macro later on, when a more advanced message mechanism is %D implemented. \newif\iferrorisfatal \unexpanded\def\waitonfatalerror {\iferrorisfatal\wait\fi} \unexpanded\def\showassignerror#1#2% {\writestatus{setup}{missing or ungrouped '=' after '#1' in line #2}% \waitonfatalerror} \unexpanded\def\doassignempty[#1][#2=#3]% {\ifcsname#1#2\endcsname\else\dosetvalue{#1}{#2}{#3}\fi} %D \macros %D {getparameters,geteparameters,getgparameters, %D forgetparameters} %D %D Using the assignment commands directly is not our ideal of user friendly interfacing, %D so we take some further steps. %D %D \starttyping %D \getparameters [label] [...=...,...=...] %D \forgetparameters [label] [...=...,...=...] %D \stoptyping %D %D Again, the label identifies the category a variable belongs to. The second argument %D can be a comma separated list of assignments. %D %D \starttyping %D \getparameters %D [demo] %D [alfa=1, %D beta=2] %D \stoptyping %D %D is equivalent to %D %D \starttyping %D \def\demoalfa{1} %D \def\demobeta{2} %D \stoptyping %D %D %D In the pre||multi||lingual stadium \CONTEXT\ took the next approach. With %D %D \starttyping %D \def\??demo {@@demo} %D \def\!!alfa {alfa} %D \def\!!beta {beta} %D \stoptyping %D %D calling %D %D \starttyping %D \getparameters %D [\??demo] %D [\!!alfa=1, %D \!!beta=2] %D \stoptyping %D %D lead to: %D %D \starttyping %D \def\@@demoalfa{1} %D \def\@@demobeta{2} %D \stoptyping %D %D Because we want to be able to distinguish the \type{!!} pre||tagged user supplied %D variables from internal counterparts, we will introduce a slightly different tag %D in the multi||lingual modules. There we will use \type{c!} or \type{v!}, %D depending on the context. %D %D By calling \type{doassign} directly, we save ourselves some argument passing %D and gain some speed. Whatever optimizations we do, this command will always be %D one of the bigger bottlenecks. The alternative \type{\geteparameters} --- it's %D funny to see that this alternative saw the light so lately --- can be used to do %D expanded assigments. \let\currentvalue\empty \unexpanded\def\getparameters {\dogetparameters\dosetvalue} \unexpanded\def\geteparameters {\dogetparameters\dosetevalue} \unexpanded\def\getgparameters {\dogetparameters\dosetgvalue} \unexpanded\def\getxparameters {\dogetparameters\dosetxvalue} \unexpanded\def\forgetparameters{\dogetparameters\doignorevalue} \let\getexpandedparameters\geteparameters \unexpanded\def\dogetparameters#1[#2]#3[#4% {\if\noexpand#4]% \expandafter\gobbleoneargument \else \let\setsomevalue#1% \def\syst_helpers_get_parameters_assign{\syst_helpers_get_parameters_assign_indeed#2}% \expandafter\syst_helpers_get_parameters \fi#4} \def\syst_helpers_get_parameters#1]% {\xprocesscommaitem#1,],\_e_o_p_} \def\syst_helpers_process_comma_item#1,#2% #2 takes space before , {\if,#1,% dirty trick for testing #1=empty \expandafter\syst_helpers_process_comma_item \else\if]#1% \doubleexpandafter\gobbleoneargument \else \syst_helpers_get_parameters_assign\_e_o_p_#1==\empty\_e_o_p_ \doubleexpandafter\syst_helpers_process_comma_item \fi\fi#2} \def\syst_helpers_assign_error#1#2#3% {\showassignerror{#2}{\the\inputlineno\space(#1)}} \def\syst_helpers_get_parameters_assign_normal#1\_e_o_p_#2=#3=#4#5\_e_o_p_ {\ifx\empty#2\empty \expandafter\syst_helpers_assign_error \else\ifx#4\empty \doubleexpandafter\syst_helpers_assign_error \else \doubleexpandafter\setsomevalue \fi\fi {#1}{#2}{#3}} \def\syst_helpers_get_parameters_assign_error#1\_e_o_p_#2=#3=#4#5\_e_o_p_ {\ifx\empty#2\empty \expandafter\syst_helpers_assign_error \else\ifx#4\empty \doubleexpandafter\syst_helpers_assign_error \else \ifcsname#1#2\endcsname \expandafter\let\expandafter\currentvalue\csname#1#2\endcsname \else \let\currentvalue\empty \fi \doubleexpandafter\setsomevalue \fi\fi {#1}{#2}{#3}} \let\syst_helpers_get_parameters_assign_indeed\syst_helpers_get_parameters_assign_normal \unexpanded\def\doassign [#1][#2]{\let\setsomevalue\dosetvalue \syst_helpers_get_parameters_assign_indeed#1\_e_o_p_#2==\empty\_e_o_p_} \unexpanded\def\doeassign [#1][#2]{\let\setsomevalue\dosetevalue \syst_helpers_get_parameters_assign_indeed#1\_e_o_p_#2==\empty\_e_o_p_} \unexpanded\def\undoassign[#1][#2]{\let\setsomevalue\doresetvalue\syst_helpers_get_parameters_assign_indeed#1\_e_o_p_#2==\empty\_e_o_p_} %D \macros %D {processassignmentlist,processassignmentcommand, %D startprocessassignmentlist,startprocessassignmentcommand} %D %D For Wolfgang: %D %D \starttyping %D \def\showpair#1#2{key:#1, value:#2\par} %D \processassignmentlist[a=1,b=2]\showpair %D \stoptyping %D %D We can optimize this one if needed but it's not a core macro so hardly %D worth the trouble and tokens. \unexpanded\def\processassignmentlist[#1]#2% #2 == \command{key}{value] {\def\syst_helpers_process_assignment_entry##1{#2}% {##2}{##3} % namespace is ignored \dogetparameters\syst_helpers_process_assignment_entry[][#1]} \unexpanded\def\processassignmentcommand[#1]% {\normalexpanded{\processassignmentlist[#1]}} \unexpanded\def\startprocessassignmentlist[#1]#2\stopprocessassignmentlist {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}% \processassignmentlist[#1]\currentassignmentlistcommand} \unexpanded\def\startprocessassignmentcommand[#1]#2\stopprocessassignmentcommand {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}% \normalexpanded{\processassignmentlist[#1]}\currentassignmentlistcommand} %D \macros{currentvalue} %D %D Just in case a \type{\getparameter} argument itself ends up inside a \type %D {\write} or other expandable location, our new macro needs a default value. %D %D \starttyping %D \getparameters[xxx][aaa=bbb]\par %D \getparameters[xxx][=bbb]\par %D \getparameters[xxx][aaa=]\par %D \getparameters[xxx][=]\par %D \getparameters[xxx][aaa]\par %D \stoptyping %D \macros %D {expandparameters} %D %D Example usage: %D %D \startbuffer %D \getparameters[taco][name=taco] %D \convertcommand\taconame\to\ascii \ascii %D \expandparameters \getparameters[taco][name=\currentvalue\space hoekwater] %D \convertcommand\taconame\to\ascii \ascii %D \getparameters[taco][name=\currentvalue\space hoekwater] %D \convertcommand\taconame\to\ascii \ascii %D \stopbuffer %D %D \typebuffer %D \startlines %D \getbuffer %D \stoplines %D %D Here we hook in the code (beware, this is the optimized get **): \def\syst_helpers_get_parameters_normal#1]% {\syst_helpers_process_comma_item#1,],\_e_o_p_} \def\syst_helpers_get_parameters_expanded#1]% {\let\dosetnvalue\setsomevalue \let\setsomevalue\dosetevalue \let\syst_helpers_get_parameters_assign_indeed\syst_helpers_get_parameters_assign_error \let\setsomevalue\dosetevalue \syst_helpers_process_comma_item#1,],\_e_o_p_ \let\syst_helpers_get_parameters_assign_indeed\syst_helpers_get_parameters_assign_normal \let\setsomevalue\dosetnvalue \let\syst_helpers_get_parameters\syst_helpers_get_parameters_normal \let\currentvalue\empty} \let\syst_helpers_get_parameters\syst_helpers_get_parameters_normal % ** \unexpanded\def\expandparameters {\let\syst_helpers_get_parameters\syst_helpers_get_parameters_expanded} %D \macros %D {getemptyparameters} %D %D Sometimes we explicitly want variables to default to an %D empty string, so we welcome: %D %D \starttyping %D \getemptyparameters [label] [...=...,...=...] %D \stoptyping \unexpanded\def\getemptyparameters[#1]#2[#3]% {\def\syst_helpers_get_empty_parameters##1{\doassignempty[#1][##1]}% \processcommalist[#3]\syst_helpers_get_empty_parameters} %D \macros %D {copyparameters} %D %D Some \CONTEXT\ commands take their default setups from %D others. All commands that are able to provide backgounds %D or rules around some content, for instance default to the %D standard command for ruled boxes. Is situations like this %D we can use: %D %D \starttyping %D \copyparameters [to-label] [from-label] [name1,name2,...] %D \stoptyping %D %D For instance %D %D \starttyping %D \copyparameters %D [internal][external] %D [alfa,beta] %D \stoptyping %D %D Leads to: %D %D \starttyping %D \def\internalalfa {\externalalfa} %D \def\internalbeta {\externalbeta} %D \stoptyping %D %D By using \type{\docopyvalue} we've prepared this command %D for use in a multi||lingual environment. \unexpanded\def\copyparameters[#1]#2[#3]#4[#5]% {\doifnot{#1}{#3} {\def\syst_helpers_copy_parameter{\docopyvalue{#1}{#3}}% ##1 \processcommalist[#5]\syst_helpers_copy_parameter}} %D \macros %D {ifparameters,checkparameters} %D %D A slightly different one is \type{\checkparameters}, which %D also checks on the presence of a~\type{=}. %D %D The boolean \type{\ifparameters} can be used afterwards. %D Combining both in one \type{\if}||macro would lead to %D problems with nested \type{\if}'s. %D %D \starttyping %D \checkparameters[argument] %D \stoptyping \newif\ifparameters \def\syst_helpers_check_parameters#1=#2#3\_e_o_s_ {\if#2@\parametersfalse\else\parameterstrue\fi} \unexpanded\def\checkparameters[#1]% {\syst_helpers_check_parameters#1=@@\_e_o_s_} %D \macros %D {getfromcommalist,getfromcommacommand, %D commalistelement, %D getcommalistsize,getcommacommandsize} %D %D It's possible to get an element from a commalist or a command representing %D a commalist. %D %D \starttyping %D \getfromcommalist [string] [n] %D \getfromcommacommand [string,\strings,string,...] [n] %D \stoptyping %D %D The difference betwee the two of them is the same as the difference between %D \type {\processcomma...}. The found string is stored in \type %D {\commalistelement}. %D %D We can calculate the size of a comma separated list by using: %D %D \starttyping %D \getcommalistsize [string,string,...] %D \getcommacommandsize [string,\strings,string,...] %D \stoptyping %D %D Afterwards, the length is available in the macro \type {\commalistsize} %D (not a \COUNTER). \newcount\commalistcounter \def\commalistsize{0} \def\syst_helpers_get_comma_list_size#1% {\advance\commalistcounter\plusone} \unexpanded\def\getcommalistsize#1]% don't loose [{#1}] {\commalistcounter\zerocount \processcommalist#1]\syst_helpers_get_comma_list_size % was [{#1}] \edef\commalistsize{\the\commalistcounter}} % \def\getcommacommandsize[#1]% % {\edef\commacommand{#1}% % \scratchtoks\expandafter{\expandafter[\commacommand]}% % \expandafter\getcommalistsize\the\scratchtoks } \unexpanded\def\getcommacommandsize[#1]% {\normalexpanded{\getcommalistsize[#1]}} \def\syst_helpers_get_from_comma_list#1% {\advance\commalistcounter \minusone \ifcase\commalistcounter \def\commalistelement{#1}% \expandafter\quitcommalist \fi} \unexpanded\def\getfromcommalist[#1]#2[#3]% {\let\commalistelement\empty \commalistcounter#3\relax \processcommalist[#1]\syst_helpers_get_from_comma_list} \unexpanded\def\getfromcommacommand[#1]% {\normalexpanded{\getfromcommalist[#1]}} %D Watertight (and efficient) solutions are hard to find, due %D to the handling of braces during parameters passing and %D scanning. Nevertheless: %D %D \startbuffer %D \def\dosomething#1{(#1=\commalistsize) } %D %D \getcommalistsize [\hbox{$a,b,c,d,e,f$}] \dosomething 1 %D \getcommalistsize [{a,b,c,d,e,f}] \dosomething 1 %D \getcommalistsize [{a,b,c},d,e,f] \dosomething 4 %D \getcommalistsize [a,b,{c,d,e},f] \dosomething 4 %D \getcommalistsize [a{b,c},d,e,f] \dosomething 4 %D \getcommalistsize [{a,b}c,d,e,f] \dosomething 4 %D \getcommalistsize [] \dosomething 0 %D \getcommalistsize [{[}] \dosomething 1 %D \stopbuffer %D %D \typebuffer %D %D reports: %D %D \getbuffer %D \macros %D {dogetcommalistelement,dogetcommacommandelement} %D %D For low level (fast) purposes, we can also use the next %D alternative, which can handle 8~elements at most. %D %D \starttyping %D \dogetcommalistelement1\from a,b,c\to\commalistelement %D \stoptyping \def\syst_helpers_get_comma_list_element#1\from#2,#3,#4,#5,#6,#7,#8\to#9% {\edef#9{\ifcase#1\relax\or#2\or#3\or#4\or#5\or#6\or#7\or#8\fi}} \def\dogetcommacommandelement#1\from#2\to% {\expandafter\syst_helpers_get_comma_list_element\expandafter#1\expandafter\from#2,,,,,,\to} %D \macros %D {dosingleargument,dodoubleargument,dotripleargument, %D doquadrupleargument,doquintupleargument,dosixtupleargument, %D doseventupleargument} %D %D When working with delimited arguments, spaces and %D lineendings can interfere. The next set of macros uses %D \TEX' internal scanner for grabbing everything between %D arguments. Forgive me the funny names. %D %D \starttyping %D \dosingleargument\commando = \commando[#1] %D \dodoubleargument\commando = \commando[#1][#2] %D \dotripleargument\commando = \commando[#1][#2][#3] %D \doquadrupleargument\commando = \commando[#1][#2][#3][#4] %D \doquintupleargument\commando = \commando[#1][#2][#3][#4][#5] %D \dosixtupleargument\commando = \commando[#1][#2][#3][#4][#5][#6] %D \doseventupleargument\command = \commando[#1][#2][#3][#4][#5][#6][#7] %D \stoptyping %D %D These macros are used in the following way: %D %D \starttyping %D \def\dosetupsomething[#1][#2]% %D {... #1 ... #2 ...} %D %D \unexpanded\def\setupsomething %D {\dodoubleargument\dosetupsomething} %D \stoptyping %D %D The implementation can be surprisingly simple and needs no %D further explanation, like: %D %D \starttyping %D \def\dosingleargument#1[#2]% %D {#1[#2]} %D \def\dotripleargument#1[#2]#3[#4]#5[#6]% %D {#1[#2][#4][#6]} %D \def\doquintupleargument#1% %D {\def\dodoquintupleargument[##1]##2[##3]##4[##5]##6[##7]##8[##9]% %D {#1[##1][##3][##5][##7][##9]}% %D \dodoquintupleargument} %D \stoptyping %D %D Because \TEX\ accepts 9~arguments at most, we have to use %D two||step solution when getting five or more arguments. %D %D When developing more and more of the real \CONTEXT, we %D started using some alternatives that provided empty %D arguments (in fact optional ones) whenever the user failed %D to supply them. Because this more complicated macros enable %D us to do some checking, we reimplemented the non||empty %D ones. % no longer a mesage: % % \unexpanded\def\dosingleargument {\let\expectedarguments\plusone \dosingleempty } % \unexpanded\def\dodoubleargument {\let\expectedarguments\plustwo \dodoubleempty } % \unexpanded\def\dotripleargument {\let\expectedarguments\plusthree \dotripleempty } % \unexpanded\def\doquadrupleargument {\let\expectedarguments\plusfour \doquadrupleempty } % \unexpanded\def\doquintupleargument {\let\expectedarguments\plusfive \doquintupleempty } % \unexpanded\def\dosixtupleargument {\let\expectedarguments\plussix \dosixtupleempty } % \unexpanded\def\doseventupleargument{\let\expectedarguments\plusseven \doseventupleempty} %D \macros %D {iffirstagument,ifsecondargument,ifthirdargument, %D iffourthargument,iffifthargument,ifsixthargument, %D ifseventhargument} %D %D We use some signals for telling the calling macros if all %D wanted arguments are indeed supplied by the user. \newif\iffirstargument \newif\ifsecondargument \newif\ifthirdargument \newif\iffourthargument \newif\iffifthargument \newif\ifsixthargument \newif\ifseventhargument %D \macros %D {dosingleempty,dodoubleempty,dotripleempty, %D doquadrupleempty,doquintupleempty,dosixtupeempty, %D doseventupleempty} %D %D The empty argument supplying macros mentioned before, look like: %D %D \starttyping %D \dosingleempty \command %D \dodoubleempty \command %D \dotripleempty \command %D \doquadrupleempty \command %D \doquintupleempty \command %D \dosixtuple_empty \command %D \doseventupleempty\command %D \stoptyping %D %D So \type{\dodoubleempty} leads to: %D %D \starttyping %D \command[#1][#2] %D \command[#1][] %D \command[][] %D \stoptyping %D %D Depending of the generousity of the user. Afterwards one can %D use the \type{\if...argument} boolean. For novice: watch %D the stepwise doubling of \type{#}'s \setnewconstant\noexpectedarguments\zerocount \setnewconstant\expectedarguments \zerocount \unexpanded\def\showargumenterror#1#2% {\writestatus{system}{\number#1 argument(s) expected in line #2}} \unexpanded\def\syst_helpers_argument_error {\ifnum\expectedarguments>\noexpectedarguments \showargumenterror{\number\expectedarguments}{\number\inputlineno}% \fi \syst_helpers_argument_reset} \unexpanded\def\syst_helpers_argument_reset {\let\expectedarguments\noexpectedarguments} % \def\test[#1]{(#1)} % % \dosingleempty\test[] xxx\par % \dosingleempty\test xxx\par % % \def\test[#1][#2]{(#1,#2)} % % \dodoubleempty\test[][] xxx\par % \dodoubleempty\test[] xxx\par % \dodoubleempty\test xxx\par % % \def\test[#1][#2][#3]{(#1,#2,#3)} % % \dotripleempty\test[][][] xxx\par % \dotripleempty\test[][] xxx\par % \dotripleempty\test[] xxx\par % \dotripleempty\test xxx\par %D Single: \unexpanded\def\dosingleempty#1% {\syst_helpers_argument_reset \doifnextoptionalelse {\firstargumenttrue#1}% {\syst_helpers_single_empty_one_nop#1}} \def\syst_helpers_single_empty_one_nop#1% {\firstargumentfalse #1[]} %D Double \unexpanded\def\dodoubleempty#1% {\syst_helpers_argument_reset \doifnextoptionalelse {\syst_helpers_double_empty_one_yes#1}% {\syst_helpers_double_empty_one_nop#1}} \def\syst_helpers_double_empty_one_yes#1[#2]% {\firstargumenttrue \doifnextoptionalelse {\secondargumenttrue#1[{#2}]}% {\syst_helpers_double_empty_two_nop#1{#2}}} \def\syst_helpers_double_empty_one_nop#1% {\firstargumentfalse \secondargumentfalse #1[][]} \def\syst_helpers_double_empty_two_nop {\secondargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_double_empty_one_spaced \else \expandafter\syst_helpers_double_empty_one_normal \fi} \def\syst_helpers_double_empty_one_spaced#1#2{#1[{#2}][] } \def\syst_helpers_double_empty_one_normal#1#2{#1[{#2}][]} % Three \unexpanded\def\dotripleempty#1% {\syst_helpers_argument_reset \doifnextoptionalelse {\syst_helpers_triple_empty_one_yes#1}% {\syst_helpers_triple_empty_one_nop#1}} \def\syst_helpers_triple_empty_one_yes#1[#2]% {\firstargumenttrue \doifnextoptionalelse {\syst_helpers_triple_empty_two_yes#1{#2}}% {\syst_helpers_triple_empty_two_nop#1{#2}}} \def\syst_helpers_triple_empty_two_yes#1#2[#3]% {\secondargumenttrue \doifnextoptionalelse {\thirdargumenttrue#1[{#2}][{#3}]}% {\syst_helpers_triple_empty_three_nop#1{#2}{#3}}} \def\syst_helpers_triple_empty_one_nop#1% {\firstargumentfalse \secondargumentfalse \thirdargumentfalse #1[][][]} \def\syst_helpers_triple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_triple_empty_two_spaced \else \expandafter\syst_helpers_triple_empty_two_normal \fi} \def\syst_helpers_triple_empty_three_nop {\thirdargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_triple_empty_three_spaced \else \expandafter\syst_helpers_triple_empty_three_normal \fi} \def\syst_helpers_triple_empty_two_spaced #1#2{#1[{#2}][][] } \def\syst_helpers_triple_empty_two_normal #1#2{#1[{#2}][][]} \def\syst_helpers_triple_empty_three_spaced#1#2#3{#1[{#2}][{#3}][] } \def\syst_helpers_triple_empty_three_normal#1#2#3{#1[{#2}][{#3}][]} %D Four: \unexpanded\def\doquadrupleempty#1% {\syst_helpers_argument_reset \doifnextoptionalelse {\syst_helpers_quadruple_empty_one_yes#1}% {\syst_helpers_quadruple_empty_one_nop#1}} \def\syst_helpers_quadruple_empty_one_yes#1[#2]% {\firstargumenttrue \doifnextoptionalelse {\syst_helpers_quadruple_empty_two_yes#1{#2}}% {\syst_helpers_quadruple_empty_two_nop#1{#2}}} \def\syst_helpers_quadruple_empty_two_yes#1#2[#3]% {\secondargumenttrue \doifnextoptionalelse {\syst_helpers_quadruple_empty_three_yes#1{#2}{#3}}% {\syst_helpers_quadruple_empty_three_nop#1{#2}{#3}}} \def\syst_helpers_quadruple_empty_three_yes#1#2#3[#4]% {\thirdargumenttrue \doifnextoptionalelse {\fourthargumenttrue#1[{#2}][{#3}][{#4}]}% {\syst_helpers_quadruple_empty_four_nop#1{#2}{#3}{#4}}} \def\syst_helpers_quadruple_empty_one_nop#1% {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse #1[][][][]} \def\syst_helpers_quadruple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_quadruple_empty_two_spaced \else \expandafter\syst_helpers_quadruple_empty_two_normal \fi} \def\syst_helpers_quadruple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_quadruple_empty_three_spaced \else \expandafter\syst_helpers_quadruple_empty_three_normal \fi} \def\syst_helpers_quadruple_empty_four_nop {\fourthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_quadruple_empty_four_spaced \else \expandafter\syst_helpers_quadruple_empty_four_normal \fi} \def\syst_helpers_quadruple_empty_two_spaced #1#2{#1[{#2}][][][] } \def\syst_helpers_quadruple_empty_two_normal #1#2{#1[{#2}][][][]} \def\syst_helpers_quadruple_empty_three_spaced #1#2#3{#1[{#2}][{#3}][][] } \def\syst_helpers_quadruple_empty_three_normal #1#2#3{#1[{#2}][{#3}][][]} \def\syst_helpers_quadruple_empty_four_spaced #1#2#3#4{#1[{#2}][{#3}][{#4}][] } \def\syst_helpers_quadruple_empty_four_normal #1#2#3#4{#1[{#2}][{#3}][{#4}][]} %D Five: \unexpanded\def\doquintupleempty#1% {\syst_helpers_argument_reset \doifnextoptionalelse {\syst_helpers_quintuple_empty_one_yes#1}% {\syst_helpers_quintuple_empty_one_nop#1}} \def\syst_helpers_quintuple_empty_one_yes#1[#2]% {\firstargumenttrue \doifnextoptionalelse {\syst_helpers_quintuple_empty_two_yes#1{#2}}% {\syst_helpers_quintuple_empty_two_nop#1{#2}}} \def\syst_helpers_quintuple_empty_two_yes#1#2[#3]% {\secondargumenttrue \doifnextoptionalelse {\syst_helpers_quintuple_empty_three_yes#1{#2}{#3}}% {\syst_helpers_quintuple_empty_three_nop#1{#2}{#3}}} \def\syst_helpers_quintuple_empty_three_yes#1#2#3[#4]% {\thirdargumenttrue \doifnextoptionalelse {\syst_helpers_quintuple_empty_four_yes#1{#2}{#3}{#4}}% {\syst_helpers_quintuple_empty_four_nop#1{#2}{#3}{#4}}} \def\syst_helpers_quintuple_empty_four_yes#1#2#3#4[#5]% {\fourthargumenttrue \doifnextoptionalelse {\fifthargumenttrue#1[{#2}][{#3}][{#4}][{#5}]}% {\syst_helpers_quintuple_empty_five_nop#1{#2}{#3}{#4}{#5}}} \def\syst_helpers_quintuple_empty_one_nop#1% {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse #1[][][][][]} \def\syst_helpers_quintuple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_quintuple_empty_two_spaced \else \expandafter\syst_helpers_quintuple_empty_two_normal \fi} \def\syst_helpers_quintuple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_quintuple_empty_three_spaced \else \expandafter\syst_helpers_quintuple_empty_three_normal \fi} \def\syst_helpers_quintuple_empty_four_nop {\fourthargumentfalse \fifthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_quintuple_empty_four_spaced \else \expandafter\syst_helpers_quintuple_empty_four_normal \fi} \def\syst_helpers_quintuple_empty_five_nop {\fifthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_quintuple_empty_five_spaced \else \expandafter\syst_helpers_quintuple_empty_five_normal \fi} \def\syst_helpers_quintuple_empty_two_spaced #1#2{#1[{#2}][][][][] } \def\syst_helpers_quintuple_empty_two_normal #1#2{#1[{#2}][][][][]} \def\syst_helpers_quintuple_empty_three_spaced #1#2#3{#1[{#2}][{#3}][][][] } \def\syst_helpers_quintuple_empty_three_normal #1#2#3{#1[{#2}][{#3}][][][]} \def\syst_helpers_quintuple_empty_four_spaced #1#2#3#4{#1[{#2}][{#3}][{#4}][][] } \def\syst_helpers_quintuple_empty_four_normal #1#2#3#4{#1[{#2}][{#3}][{#4}][][]} \def\syst_helpers_quintuple_empty_five_spaced #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][] } \def\syst_helpers_quintuple_empty_five_normal #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][]} %D Six \unexpanded\def\dosixtupleempty#1% {\syst_helpers_argument_reset \doifnextoptionalelse {\syst_helpers_sixtuple_empty_one_yes#1} {\syst_helpers_sixtuple_empty_one_nop#1}} \def\syst_helpers_sixtuple_empty_one_yes#1[#2]% {\firstargumenttrue \doifnextoptionalelse {\syst_helpers_sixtuple_empty_two_yes#1{#2}}% {\syst_helpers_sixtuple_empty_two_nop#1{#2}}} \def\syst_helpers_sixtuple_empty_two_yes#1#2[#3]% {\secondargumenttrue \doifnextoptionalelse {\syst_helpers_sixtuple_empty_three_yes#1{#2}{#3}}% {\syst_helpers_sixtuple_empty_three_nop#1{#2}{#3}}} \def\syst_helpers_sixtuple_empty_three_yes#1#2#3[#4]% {\thirdargumenttrue \doifnextoptionalelse {\syst_helpers_sixtuple_empty_four_yes#1{#2}{#3}{#4}}% {\syst_helpers_sixtuple_empty_four_nop#1{#2}{#3}{#4}}} \def\syst_helpers_sixtuple_empty_four_yes#1#2#3#4[#5]% {\fourthargumenttrue \doifnextoptionalelse {\syst_helpers_sixtuple_empty_five_yes#1{#2}{#3}{#4}{#5}}% {\syst_helpers_sixtuple_empty_five_nop#1{#2}{#3}{#4}{#5}}} \def\syst_helpers_sixtuple_empty_five_yes#1#2#3#4#5[#6]% {\fifthargumenttrue \doifnextoptionalelse {\sixthargumenttrue#1[{#2}][{#3}][{#4}][{#5}][{#6}]}% {\syst_helpers_sixtuple_empty_six_nop#1{#2}{#3}{#4}{#5}{#6}}} \def\syst_helpers_sixtuple_empty_one_nop#1% {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse #1[][][][][][]} \def\syst_helpers_sixtuple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_sixtuple_empty_two_spaced \else \expandafter\syst_helpers_sixtuple_empty_two_normal \fi} \def\syst_helpers_sixtuple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_sixtuple_empty_three_spaced \else \expandafter\syst_helpers_sixtuple_empty_three_normal \fi} \def\syst_helpers_sixtuple_empty_four_nop {\fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_sixtuple_empty_four_spaced \else \expandafter\syst_helpers_sixtuple_empty_four_normal \fi} \def\syst_helpers_sixtuple_empty_five_nop {\fifthargumentfalse \sixthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_sixtuple_empty_five_spaced \else \expandafter\syst_helpers_sixtuple_empty_five_normal \fi} \def\syst_helpers_sixtuple_empty_six_nop {\sixthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_sixtuple_empty_six_spaced \else \expandafter\syst_helpers_sixtuple_empty_six_normal \fi} \def\syst_helpers_sixtuple_empty_two_spaced #1#2{#1[{#2}][][][][][] } \def\syst_helpers_sixtuple_empty_two_normal #1#2{#1[{#2}][][][][][]} \def\syst_helpers_sixtuple_empty_three_spaced #1#2#3{#1[{#2}][{#3}][][][][] } \def\syst_helpers_sixtuple_empty_three_normal #1#2#3{#1[{#2}][{#3}][][][][]} \def\syst_helpers_sixtuple_empty_four_spaced #1#2#3#4{#1[{#2}][{#3}][{#4}][][][] } \def\syst_helpers_sixtuple_empty_four_normal #1#2#3#4{#1[{#2}][{#3}][{#4}][][][]} \def\syst_helpers_sixtuple_empty_five_spaced #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][] } \def\syst_helpers_sixtuple_empty_five_normal #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][]} \def\syst_helpers_sixtuple_empty_six_spaced #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][] } \def\syst_helpers_sixtuple_empty_six_normal #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][]} %D Seven: \unexpanded\def\doseventupleempty#1% {\syst_helpers_argument_reset \doifnextoptionalelse {\syst_helpers_seventuple_empty_one_yes#1}% {\syst_helpers_seventuple_empty_one_nop#1}} \def\syst_helpers_seventuple_empty_one_yes#1[#2]% {\firstargumenttrue \doifnextoptionalelse {\syst_helpers_seventuple_empty_two_yes#1{#2}}% {\syst_helpers_seventuple_empty_two_nop#1{#2}}} \def\syst_helpers_seventuple_empty_two_yes#1#2[#3]% {\secondargumenttrue \doifnextoptionalelse {\syst_helpers_seventuple_empty_three_yes#1{#2}{#3}}% {\syst_helpers_seventuple_empty_three_nop#1{#2}{#3}}} \def\syst_helpers_seventuple_empty_three_yes#1#2#3[#4]% {\thirdargumenttrue \doifnextoptionalelse {\syst_helpers_seventuple_empty_four_yes#1{#2}{#3}{#4}}% {\syst_helpers_seventuple_empty_four_nop#1{#2}{#3}{#4}}} \def\syst_helpers_seventuple_empty_four_yes#1#2#3#4[#5]% {\fourthargumenttrue \doifnextoptionalelse {\syst_helpers_seventuple_empty_five_yes#1{#2}{#3}{#4}{#5}}% {\syst_helpers_seventuple_empty_five_nop#1{#2}{#3}{#4}{#5}}} \def\syst_helpers_seventuple_empty_five_yes#1#2#3#4#5[#6]% {\fifthargumenttrue \doifnextoptionalelse {\syst_helpers_seventuple_empty_six_yes#1{#2}{#3}{#4}{#5}{#6}}% {\syst_helpers_seventuple_empty_six_nop#1{#2}{#3}{#4}{#5}{#6}}} \def\syst_helpers_seventuple_empty_six_yes#1#2#3#4#5#6[#7]% {\sixthargumenttrue \doifnextoptionalelse {\seventhargumenttrue#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}]}% {\syst_helpers_seventuple_empty_seven_nop#1{#2}{#3}{#4}{#5}{#6}{#7}}} \def\syst_helpers_seventuple_empty_one_nop#1% {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \seventhargumentfalse #1[][][][][][][]} \def\syst_helpers_seventuple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_seventuple_empty_two_spaced \else \expandafter\syst_helpers_seventuple_empty_two_normal \fi} \def\syst_helpers_seventuple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_seventuple_empty_three_spaced \else \expandafter\syst_helpers_seventuple_empty_three_normal \fi} \def\syst_helpers_seventuple_empty_four_nop {\fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_seventuple_empty_four_spaced \else \expandafter\syst_helpers_seventuple_empty_four_normal \fi} \def\syst_helpers_seventuple_empty_five_nop {\fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_seventuple_empty_five_spaced \else \expandafter\syst_helpers_seventuple_empty_five_normal \fi} \def\syst_helpers_seventuple_empty_six_nop {\sixthargumentfalse \seventhargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_seventuple_empty_six_spaced \else \expandafter\syst_helpers_seventuple_empty_six_normal \fi} \def\syst_helpers_seventuple_empty_seven_nop {\seventhargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_seventuple_empty_seven_spaced \else \expandafter\syst_helpers_seventuple_empty_seven_normal \fi} \def\syst_helpers_seventuple_empty_two_spaced #1#2{#1[{#2}][][][][][][] } \def\syst_helpers_seventuple_empty_two_normal #1#2{#1[{#2}][][][][][][]} \def\syst_helpers_seventuple_empty_three_spaced #1#2#3{#1[{#2}][{#3}][][][][][] } \def\syst_helpers_seventuple_empty_three_normal #1#2#3{#1[{#2}][{#3}][][][][][]} \def\syst_helpers_seventuple_empty_four_spaced #1#2#3#4{#1[{#2}][{#3}][{#4}][][][][] } \def\syst_helpers_seventuple_empty_four_normal #1#2#3#4{#1[{#2}][{#3}][{#4}][][][][]} \def\syst_helpers_seventuple_empty_five_spaced #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][][] } \def\syst_helpers_seventuple_empty_five_normal #1#2#3#4#5{#1[{#2}][{#3}][{#4}][{#5}][][][]} \def\syst_helpers_seventuple_empty_six_spaced #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][][] } \def\syst_helpers_seventuple_empty_six_normal #1#2#3#4#5#6{#1[{#2}][{#3}][{#4}][{#5}][{#6}][][]} \def\syst_helpers_seventuple_empty_seven_spaced#1#2#3#4#5#6#7{#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}][] } \def\syst_helpers_seventuple_empty_seven_normal#1#2#3#4#5#6#7{#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}][]} \let\dosingleargument \dosingleempty \let\dodoubleargument \dodoubleempty \let\dotripleargument \dotripleempty \let\doquadrupleargument \doquadrupleempty \let\doquintupleargument \doquintupleempty \let\dosixtupleargument \dosixtupleempty \let\doseventupleargument\doseventupleempty %D \macros %D {strippedcsname} %D %D The next macro can be very useful when using \type{\csname} %D like in: %D %D \starttyping %D \csname if\strippedcsname\something\endcsname %D \stoptyping %D %D This expands to \type{\ifsomething}. \def\strippedcsname {\expandafter\gobbleoneargument\string} %D \macros %D {complexorsimple,complexorsimpleempty} %D %D Setups can be optional. A command expecting a setup is %D prefixed by \type{\complex}, a command without one gets the %D prefix \type{\simple}. Commands like this can be defined by: %D %D \starttyping %D \complexorsimple\command %D \stoptyping %D %D When \type{\command} is followed by a \type{[setup]}, then %D %D \starttyping %D \complexcommand [setup] %D \stoptyping %D %D executes, else we get %D %D \starttyping %D \simplecommand %D \stoptyping %D %D An alternative for \type{\complexorsimple} is: %D %D \starttyping %D \complexorsimpleempty {command} %D \stoptyping %D %D Depending on the presence of \type{[setup]}, this one %D leads to one of: %D %D \starttyping %D \complexcommando [setup] %D \complexcommando [] %D \stoptyping %D %D Many \CONTEXT\ commands started as complex or simple ones, %D but changed into more versatile (more object oriented) ones %D using the \type{\get..argument} commands. \unexpanded\def\complexorsimple#1% {% \relax % prevents lookahead, brrr \doifnextoptionalelse {\firstargumenttrue \csname\s!complex\strippedcsname#1\endcsname} {\firstargumentfalse\csname\s!simple \strippedcsname#1\endcsname}} \unexpanded\def\complexorsimpleempty#1% {% \relax % prevents lookahead, brrr \doifnextoptionalelse {\firstargumenttrue \csname\s!complex\strippedcsname#1\endcsname} {\firstargumentfalse\csname\s!complex\strippedcsname#1\endcsname[]}} %D \macros %D {definecomplexorsimple,definecomplexorsimpleempty} %D %D The previous commands are used that often that we found it %D worthwile to offer two more alternatives. Watch the build %D in protection. \unexpanded\def\syst_helpers_complex_or_simple#1#2% {\doifnextoptionalelse{\firstargumenttrue#1}{\firstargumentfalse#2}} \unexpanded\def\syst_helpers_complex_or_simple_empty#1% {\doifnextoptionalelse{\firstargumenttrue#1}{\firstargumentfalse#1[]}} \unexpanded\def\definecomplexorsimple#1% {\unexpanded\edef#1{\syst_helpers_complex_or_simple \expandafter\noexpand\csname\s!complex\strippedcsname#1\endcsname \expandafter\noexpand\csname\s!simple \strippedcsname#1\endcsname}} \unexpanded\def\definecomplexorsimpleempty#1% {\unexpanded\edef#1{\syst_helpers_complex_or_simple_empty \expandafter\noexpand\csname\s!complex\strippedcsname#1\endcsname}} %D These commands are called as: %D %D \starttyping %D \definecomplexorsimple\command %D \stoptyping %D %D Of course, we must have available %D %D \starttyping %D \def\complexcommand[#1]{...} %D \def\simplecommand {...} %D \stoptyping %D %D Using this construction saves a few string now and then. %D \macros %D {dosinglegroupempty,dodoublegroupempty,dotriplegroupempty, %D doquadruplegroupempty, doquintuplegroupempty} %D %D We've already seen some commands that take care of %D optional arguments between \type{[]}. The next two commands %D handle the ones with \type{{}}. They are called as: %D %D \starttyping %D \dosinglegroupempty \ineedONEargument %D \dodoublegroupempty \ineedTWOarguments %D \dotriplegroupempty \ineedTHREEarguments %D \doquadruplegroupempty \ineedFOURarguments %D \doquintuplegroupempty \ineedFIVEarguments %D \stoptyping %D We can add additional definitions later when we have defined %D \type {\appendtoks}. \newconditional\c_syst_helpers_permit_spaces_between_groups \unexpanded\def \permitspacesbetweengroups{\settrue \c_syst_helpers_permit_spaces_between_groups} \unexpanded\def\dontpermitspacesbetweengroups{\setfalse\c_syst_helpers_permit_spaces_between_groups} \dontpermitspacesbetweengroups %D We can avoid the nasty if handling in \type {syst-gen} by splitting %D the lot in pieces so that we have no nested \type {\nextarguments} %D potentially being an \type {conditional} token. Okay, these macros %D are not called that often but it saves crap when tracing. \unexpanded\def\syst_helpers_get_grouped_argument#1#2% {\let\syst_helpers_get_grouped_argument_yes#1% \let\syst_helpers_get_grouped_argument_nop#2% \futurelet\nextargument\syst_helpers_get_grouped_argument_indeed} \def\syst_helpers_get_grouped_argument_indeed {\ifx\nextargument\bgroup \expandafter\syst_helpers_get_grouped_argument_a \else \expandafter\syst_helpers_get_grouped_argument_b \fi} \def\syst_helpers_get_grouped_argument_a {\syst_helpers_argument_reset \syst_helpers_get_grouped_argument_yes\syst_helpers_get_grouped_argument_nested} \def\syst_helpers_get_grouped_argument_b {\ifconditional\c_syst_helpers_permit_spaces_between_groups \expandafter\syst_helpers_get_grouped_argument_f \else \expandafter\syst_helpers_get_grouped_argument_d \fi} \def\syst_helpers_get_grouped_argument_d {\syst_helpers_argument_error \syst_helpers_get_grouped_argument_nop\syst_helpers_get_grouped_argument_nested{}} \begingroup \def\\ {\syst_helpers_get_grouped_argument\syst_helpers_get_grouped_argument_yes\syst_helpers_get_grouped_argument_nop} \global\let\syst_helpers_get_grouped_argument_e\\ \endgroup \def\syst_helpers_get_grouped_argument_f {\ifx\nextargument\blankspace \expandafter\syst_helpers_get_grouped_argument_e % g \else \expandafter\syst_helpers_get_grouped_argument_d % h \fi} \def\dosinglegroupempty#1% {\def\syst_helpers_get_grouped_argument_nested {\dontpermitspacesbetweengroups #1}% \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse} \def\dodoublegroupempty#1% {\def\syst_helpers_get_grouped_argument_nested##1% {\def\syst_helpers_get_grouped_argument_nested {\dontpermitspacesbetweengroups #1{##1}}% \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}% \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse} \def\dotriplegroupempty#1% {\def\syst_helpers_get_grouped_argument_nested##1% {\def\syst_helpers_get_grouped_argument_nested####1% {\def\syst_helpers_get_grouped_argument_nested {\dontpermitspacesbetweengroups #1{##1}{####1}}% \syst_helpers_get_grouped_argument\thirdargumenttrue\thirdargumentfalse}% \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}% \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse} \def\doquadruplegroupempty#1% {\def\syst_helpers_get_grouped_argument_nested##1% {\def\syst_helpers_get_grouped_argument_nested####1% {\def\syst_helpers_get_grouped_argument_nested########1% {\def\syst_helpers_get_grouped_argument_nested {\dontpermitspacesbetweengroups #1{##1}{####1}{########1}}% \syst_helpers_get_grouped_argument\fourthargumenttrue\fourthargumentfalse}% \syst_helpers_get_grouped_argument\thirdargumenttrue\thirdargumentfalse}% \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}% \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse} \def\doquintuplegroupempty#1% {\def\syst_helpers_get_grouped_argument_nested##1% {\def\syst_helpers_get_grouped_argument_nested####1% {\def\syst_helpers_get_grouped_argument_nested########1% {\def\syst_helpers_get_grouped_argument_nested################1% {\def\syst_helpers_get_grouped_argument_nested {\dontpermitspacesbetweengroups #1{##1}{####1}{########1}{################1}}% \syst_helpers_get_grouped_argument\fifthargumenttrue\fifthargumentfalse}% \syst_helpers_get_grouped_argument\fourthargumenttrue\fourthargumentfalse}% \syst_helpers_get_grouped_argument\thirdargumenttrue\thirdargumentfalse}% \syst_helpers_get_grouped_argument\secondargumenttrue\secondargumentfalse}% \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse} %D These macros can explictly take care of spaces, which means %D that the next definition and calls are valid: %D %D \starttyping %D \def\test#1#2#3{[#1#2#3]} %D %D \dotriplegroupempty\test {a}{b}{c} %D \dotriplegroupempty\test {a}{b} %D \dotriplegroupempty\test {a} %D \dotriplegroupempty\test %D \dotriplegroupempty\test {a} {b} {c} %D \dotriplegroupempty\test {a} {b} %D \dotriplegroupempty\test %D {a} %D {b} %D \stoptyping %D %D And alike. %D \macros %D {firstofoneargument, firstoftwoarguments, firstofthreearguments %D secondoftwoarguments, secondofthreearguments, %D thirdofthreearguments} %D %D The next six macros (dedicated to Taco) can conveniently %D used to select arguments. Their names explain their %D functionality. \def\firstofoneargument #1{#1} \def\firstoftwoarguments #1#2{#1} \def\secondoftwoarguments #1#2{#2} \def\firstofthreearguments #1#2#3{#1} \def\secondofthreearguments #1#2#3{#2} \def\thirdofthreearguments #1#2#3{#3} \def\firstoffourarguments #1#2#3#4{#1} \def\secondoffourarguments #1#2#3#4{#2} \def\thirdoffourarguments #1#2#3#4{#3} \def\fourthoffourarguments #1#2#3#4{#4} \def\firstoffivearguments #1#2#3#4#5{#1} \def\secondoffivearguments #1#2#3#4#5{#2} \def\thirdoffivearguments #1#2#3#4#5{#3} \def\fourthoffivearguments #1#2#3#4#5{#4} \def\fifthoffivearguments #1#2#3#4#5{#5} \def\firstofsixarguments #1#2#3#4#5#6{#1} \def\secondofsixarguments#1#2#3#4#5#6{#2} \def\thirdofsixarguments #1#2#3#4#5#6{#3} \def\fourthofsixarguments#1#2#3#4#5#6{#4} \def\fifthofsixarguments #1#2#3#4#5#6{#5} \def\sixthofsixarguments #1#2#3#4#5#6{#6} \unexpanded\def\firstofoneunexpanded#1{#1} %D \macros %D {globalletempty,letempty, %D letvalueempty,letgvalueempty, %D letvaluerelax,letgvaluerelax} %D %D Trivial: \def\letempty #1{\let#1\empty} \def\globalletempty#1{\global\let#1\empty} \def\letvalueempty #1{\expandafter\let\csname#1\endcsname\empty} \def\letgvalueempty#1{\global\expandafter\let\csname#1\endcsname\empty} \def\letvaluerelax #1{\expandafter\let\csname#1\endcsname\relax} \def\letgvalurelax #1{\global\expandafter\let\csname#1\endcsname\relax} %D \macros %D {wait} %D %D The next macro hardly needs explanation. Because no %D nesting is to be expected, we can reuse \type{\wait} within %D \type{\wait} itself. \unexpanded\def\wait {\begingroup \read16 to \wait \endgroup} %D \macros %D {writestring,writeline,writebanner, %D writestatus,statuswidth,normalwritestatus} %D %D Maybe one didn't notice, but we've already introduced a %D macro for showing messages. In the multi||lingual modules, %D we will also introduce a mechanism for message passing. For %D the moment we stick to the core macros: %D %D \starttyping %D \writestring {string} %D \writeline %D \writestatus {category} {message} %D \stoptyping %D %D Messages are formatted. One can provide the maximum with %D of the identification string with the macro \type %D {\statuswidth}. \setnewconstant\statuswidth 15 \setnewconstant\statuswrite 16 \ifdefined\writestring \else \newtoks\everywritestring \def\writedirect {\immediate\write\statuswrite} \def\writeline {\writedirect{}} \unexpanded\def\writestring#1{\begingroup\the\everywritestring\writedirect{#1}\endgroup} \fi \unexpanded\def\normalwritestatus#1#2% {\writestring{\expandafter\syst_helpers_split_status_yes\expandafter\statuswidth#1% \space\space\space\space\space\space\space \space\space\space\space\space\space\space \space\space\space\space\space\space\end \space:\space#2}} \def\syst_helpers_split_status_yes#1#2% {\ifcase#1 \expandafter\syst_helpers_split_status_nop\fi#2% \expandafter\syst_helpers_split_status_yes\expandafter{\the\numexpr#1+\minusone\relax}} \def\syst_helpers_split_status_nop#1\end {} %D \macros %D {immediatemessage} %D %D A fully expandable message: \def\immediatemessage#1{\ctxlua{logs.status("message","#1")}} %D \macros %D {debuggerinfo} %D %D For debugging purposes we can enhance macros with the %D next alternative. Here \type{debuggerinfo} stands for both %D a macro accepting two arguments and a boolean (in fact a %D few macro's too). \newif\ifdebuggerinfo \unexpanded\def\debuggerinfo#1#2% {\ifdebuggerinfo \writestatus{debugger}{#1:: #2}% \fi} \ifdefined\writestatus \else \let\writestatus\normalwritestatus \fi \ifdefined\writebanner \else \unexpanded\def\writebanner{\writestring} \fi % % % % % % % % % % % % % % % % % % % % % % % % %D \macros %D {rawgetparameters} %D %D A raw and dirty alternative for \type {\getparameters}; no %D checking is done! \unexpanded\def\rawsetparameter#1=#2,% {\if]#1\else \expandafter\def\csname\rawparameterprefix#1\endcsname{#2}% \expandafter\rawsetparameter \fi} \unexpanded\def\rawgetparameters[#1][#2% some 5-10% faster {\ifx#2]% test is needed, else bomb on [#1][] \expandafter\gobbleoneargument \else \def\rawparameterprefix{#1}% \expandafter\dorawgetparameters \fi#2} \def\dorawgetparameters#1]% {\expandafter\rawsetparameter#1,]=,} %D \macros %D {doglobal, %D redoglobal,dodoglobal,resetglobal} %D %D The two macros \type {\redoglobal} and \type{\dodoglobal} are %D used in this and some other modules to enforce a user %D specified \type {\doglobal} action. The last and often only %D global assignment in a macro is done with %D \type {\dodoglobal}, but all preceding ones with %D \type {\redoglobal}. When using only alternatives, one can %D reset this mechanism with \type {\resetglobal}. \unexpanded\def\resetglobal {\let\redoglobal\relax \let\dodoglobal\relax} \resetglobal \unexpanded\def\doglobal {\ifx\redoglobal\relax \let\redoglobal\global \let\dodoglobal\syst_helpers_dodo_global \fi} \def\syst_helpers_dodo_global {\resetglobal\global} \def\saveglobal {\let\syst_helpers_dodo_global\dodoglobal \let\syst_helpers_redo_global\redoglobal} \def\restoreglobal {\let\redoglobal\syst_helpers_redo_global \let\dodoglobal\syst_helpers_dodo_global} %D A very useful application of this macro is \type {\newif}, %D \TEX's fake boolean type. Not being a primitive, %D \type {\global} hopelessly fails here. But a slight %D adaption of Knuth's original macro permits: %D %D \starttyping %D \doglobal\newif\iftest %D \stoptyping %D %D Of course one can still say: %D %D \starttyping %D \global\testtrue %D \global\testfalse %D \stoptyping %D %D Apart from the prefixes, a few more \type{\expandafters} %D are needed: \unexpanded\def\newif#1% {\scratchcounter\escapechar \escapechar\minusone \expandafter\expandafter\expandafter \redoglobal\expandafter\expandafter\expandafter \edef\@if#1{true}{\let\noexpand#1\noexpand\iftrue}% \expandafter\expandafter\expandafter \redoglobal\expandafter\expandafter\expandafter \edef\@if#1{false}{\let\noexpand#1\noexpand\iffalse}% \dodoglobal\@if#1{false}% \escapechar\scratchcounter} %D Also new: \unexpanded\def\define#1% {\ifdefined#1% \message{[\noexpand#1is already defined]}% \unexpanded\expandafter\def\expandafter\gobbleddefinition \else \unexpanded\expandafter\def \fi#1} \unexpanded\def\redefine#1% {\ifdefined#1% \message{[\noexpand#1is redefined]}% \fi \unexpanded\def#1} \unexpanded\def\definemacro#1% {\ifdefined#1% \message{[\noexpand#1is already defined]}% \unexpanded\expandafter\def\expandafter\gobbleddefinition \else \unexpanded\expandafter\def \fi#1} % \define\hans{hans} % \redefine\hans{hans} % \define\hans#1[]#2#3{hans} %D The next variant fits nicely in the setups syntax: %D %D \starttyping %D \starttexdefinition bagger [#1] #2 %D oeps %D #1 %D oeps %D \stoptexdefinition %D %D \bagger [a] {b} %D \stoptyping % \starttexdefinition test % oeps % \stoptexdefinition % % [\test] \def\s!unexpanded{unexpanded} \bgroup \obeylines \global\let\stoptexdefinition\relax \unexpanded\gdef\starttexdefinition% {\bgroup% \obeylines% \syst_helpers_start_tex_definition} \gdef\syst_helpers_start_tex_definition #1 {\catcode\endoflineasciicode\ignorecatcode% \doifinstringelse\letterhash{\detokenize{#1}}\syst_helpers_start_tex_definition_yes\syst_helpers_start_tex_definition_nop#1 } \gdef\syst_helpers_start_tex_definition_yes#1 #2 {\edef\texdefinitionname{#1}% \ifx\texdefinitionname\s!unexpanded% \expandafter\syst_helpers_start_tex_definition_yes_unexpanded% \else% \expandafter\syst_helpers_start_tex_definition_yes_normal% \fi% {#1}#2 } \gdef\syst_helpers_start_tex_definition_yes_unexpanded#1#2 #3 #4\stoptexdefinition% {\egroup% #1=unexpanded \unexpanded\expandafter\def\csname#2\endcsname#3{#4}} \gdef\syst_helpers_start_tex_definition_yes_normal#1#2 #3\stoptexdefinition% {\egroup% \expandafter\def\csname#1\endcsname#2{#3}} \gdef\syst_helpers_start_tex_definition_nop#1 {\syst_helpers_start_tex_definition_nop_indeed{#1}{}} \gdef\syst_helpers_start_tex_definition_nop_indeed#1#2#3\stoptexdefinition% {\egroup% \expandafter\def\csname#1\endcsname{#3}} \egroup \unexpanded\def\texdefinition#1{\csname\ifcsname#1\endcsname#1\else donothing\fi\endcsname} % This is a first variant, more might be added: \unexpanded\def\starttexcode{\unprotect} \unexpanded\def\stoptexcode {\protect} %D \macros %D {newcounter, %D increment,decrement} %D %D Unfortunately the number of \COUNTERS\ in \TEX\ is limited, %D but fortunately we can store numbers in a macro. We can %D increment such pseudo \COUNTERS\ with \type{\increment}. %D %D \starttyping %D \increment(\counter,20) %D \increment(\counter,-4) %D \increment(\counter) %D \increment\counter %D \stoptyping %D %D After this sequence of commands, the value of %D \type{\counter} is 20, 16, 17 and~18. Of course there is %D also the complementary command \type{\decrement}. %D %D Global assignments are possible too, using \type{\doglobal}: %D %D \starttyping %D \doglobal\increment\counter %D \stoptyping %D %D When \type{\counter} is undefined, it's value is initialized %D at~0. It is nevertheless better to define a \COUNTER\ %D explicitly. One reason could be that the \COUNTER\ can be %D part of a test with \type{\ifnum} and this conditional does %D not accept undefined macro's. The \COUNTER\ in our example %D can for instance be defined with: %D %D \starttyping %D \newcounter\counter %D \stoptyping %D %D The command \type{\newcounter} must not be confused with %D \type{\newcount}! Of course this mechanism is much slower %D than using \TEX's \COUNTERS\ directly. In practice %D \COUNTERS\ (and therefore our pseudo counters too) are %D seldom the bottleneck in the processing of a text. Apart %D from some other incompatilities we want to mention a pitfal %D when using \type{\ifnum}. %D %D \starttyping %D \ifnum\normalcounter=\pseudocounter \doif \else \doelse \fi %D \ifnum\pseudocounter=\normalcounter \doif \else \doelse \fi %D \stoptyping %D %D In the first test, \TEX\ continues it's search for the %D second number after reading \type{\pseudocounter}, while %D in the second test, it stops reading after having %D encountered a real one. Tests like the first one therefore %D can give unexpected results, for instance execution %D of \type{\doif} even if both numbers are unequal. \def\zerocountervalue{0} \unexpanded\def\newcounter#1% {\dodoglobal\let#1\zerocountervalue} %D Nowadays we don't mind a few more tokens if we can gain a %D bit of speed. \def\syst_helpers_do_increment#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\plusone \relax}} \def\syst_helpers_do_decrement#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\minusone\relax}} \def\syst_helpers_do_do_do_increment#1,#2){\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+#2\relax}} \def\syst_helpers_do_do_do_decrement#1,#2){\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi-#2\relax}} \def\syst_helpers_do_do_increment(#1{\doifnextcharelse,{\syst_helpers_do_do_do_increment#1}{\syst_helpers_do_do_do_increment#1,\plusone}} \def\syst_helpers_do_do_decrement(#1{\doifnextcharelse,{\syst_helpers_do_do_do_decrement#1}{\syst_helpers_do_do_do_decrement#1,\plusone}} \unexpanded\def\fastincrement#1{\dodoglobal\edef#1{\the\numexpr#1+\plusone \relax}} \unexpanded\def\fastdecrement#1{\dodoglobal\edef#1{\the\numexpr#1+\minusone\relax}} \unexpanded\def\increment{\doifnextcharelse(\syst_helpers_do_do_increment\syst_helpers_do_increment} \unexpanded\def\decrement{\doifnextcharelse(\syst_helpers_do_do_decrement\syst_helpers_do_decrement} \unexpanded\def\incrementvalue#1{\expandafter\increment\csname#1\endcsname} \unexpanded\def\decrementvalue#1{\expandafter\decrement\csname#1\endcsname} %D \macros %D {newsignal} %D %D When writing advanced macros, we cannot do without %D signaling. A signal is a small (invisible) kern or penalty %D that signals the next macro that something just happened. %D This macro can take any action depending on the previous %D signal. Signals must be unique and the next macro takes care %D of that. %D %D \starttyping %D \newsignal\somesignal %D \stoptyping %D %D Signals old dimensions and can be used in skips, kerns and %D tests like \type{\ifdim}. \newdimen\maximumsignal % step is about 0.00025pt \unexpanded\def\newsignal#1% {\ifdefined#1\else \advance\maximumsignal 2\scaledpoint % to be save in rounding \edef#1{\the\maximumsignal}% \fi} %D \macros %D {strippedcsname} %D %D The next macro can be very useful when using \type{\csname} %D like in: %D %D \starttyping %D \csname if\strippedcsname\something\endcsname %D \stoptyping \def\checkedstrippedcsname#1% this permits \strippedcsname{\xxx} and \strippedcsname{xxx} {\expandafter\syst_helpers_checked_stripped_csname\string#1} \def\syst_helpers_checked_stripped_csname#1% {\if\noexpand#1\letterbackslash\else#1\fi} %D \macros %D {savenormalmeaning} %D %D We will use this one in: \unexpanded\def\savenormalmeaning#1% {\ifcsname normal\strippedcsname#1\endcsname \else \expandafter\let\csname normal\strippedcsname#1\endcsname#1% \fi} %D \macros %D {dorecurse,recurselevel,recursedepth, %D dostepwiserecurse} %D %D \TEX\ does not offer us powerfull for||loop mechanisms. On %D the other hand its recursion engine is quite unique. We %D therefore identify the for||looping macros by this method. %D The most simple alternative is the one that only needs a %D number. %D %D \starttyping %D \dorecurse {n} {whatever we want} %D \stoptyping %D %D This macro can be nested without problems and therefore be %D used in situations where \PLAIN\ \TEX's \type{\loop} macro %D ungracefully fails. The current value of the counter is %D available in \type{\recurselevel}, before as well as after %D the \typ{whatever we wat} stuff. %D %D \starttyping %D \dorecurse % inner loop %D {10} %D {\recurselevel: % outer value %D \dorecurse % inner loop %D {\recurselevel} % outer value %D {\recurselevel} % inner value %D \dorecurse % inner loop %D {\recurselevel} % outer value %D {\recurselevel} % inner value %D \endgraf} %D \stoptyping %D %D In this example the first, second and fourth %D \type{\recurselevel} concern the outer loop, while the third %D and fifth one concern the inner loop. The depth of the %D nesting is available for inspection in \type{\recursedepth}. %D %D Both \type{\recurselevel} and \type{\recursedepth} are %D macros. The real \COUNTERS\ are hidden from the user because %D we don't want any interference. \newcount\outerrecurse \newcount\innerrecurse \def\recursedepth{\the\outerrecurse} \def\recurselevel{0} \let\syst_helpers_stepwise_next\relax \installsystemnamespace{recurseindex} \installsystemnamespace{recurseaction} \unexpanded\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 {\global\advance\outerrecurse \plusone \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname{#4}% \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel \ifnum#3>\zerocount\relax \ifnum#2<#1\relax \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit \else \let\syst_helpers_stepwise_next\syst_helpers_stepwise_recurse \fi \else \ifnum#3<\zerocount\relax \ifnum#1<#2\relax \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit \else \let\syst_helpers_stepwise_next\syst_helpers_stepwise_reverse \fi \else \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit \fi \fi\normalexpanded{\syst_helpers_stepwise_next{\number#1}{\number#2}{\number#3}}} \unexpanded\def\syst_helpers_stepwise_recurse#1#2#3% from to step {\ifnum#1>#2\relax \expandafter\syst_helpers_stepwise_recurse_nop \else \def\recurselevel{#1}% \doubleexpandafter\syst_helpers_stepwise_recurse_yes\expandafter \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}} \unexpanded\def\syst_helpers_recurse_content {\csname\??recurseaction\recursedepth\endcsname} \unexpanded\def\syst_helpers_stepwise_recurse_yes {\syst_helpers_recurse_content \syst_helpers_stepwise_recurse} \unexpanded\def\syst_helpers_stepwise_reverse#1#2#3% from to step {\ifnum#1<#2\relax \expandafter\syst_helpers_stepwise_recurse_nop \else \def\recurselevel{#1}% \innerrecurse#1\relax \advance\innerrecurse#3\relax \doubleexpandafter\syst_helpers_stepwise_reverse_yes\expandafter \fi\expandafter{\the\innerrecurse}{#2}{#3}} \unexpanded\def\syst_helpers_stepwise_reverse_yes {\syst_helpers_recurse_content \syst_helpers_stepwise_reverse} \unexpanded\def\syst_helpers_stepwise_exit {\syst_helpers_stepwise_recurse_nop\relax} \unexpanded\def\syst_helpers_stepwise_recurse_nop#1#2#3#4% {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\recursedepth\endcsname \global\advance\outerrecurse\minusone} % \unexpanded\def\nonostepwiserecurse#1#2#3% % {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\recursedepth\endcsname % \global\advance\outerrecurse\minusone} \unexpanded\def\dorecurse#1% {\dostepwiserecurse\plusone{#1}\plusone} \def\doexpandedrecurse#1#2% user macro (also was \doxprecurse) {\ifnum#1>\zerocount #2\expandafter\doexpandedrecurse\expandafter{\the\numexpr#1-\plusone\relax}{#2}% \fi} %D As we can see here, the simple command \type{\dorecurse} is %D a special case of the more general: %D %D \starttyping %D \dostepwiserecurse {from} {to} {step} {action} %D \stoptyping %D %D This commands accepts positive and negative steps. Illegal %D values are handles as good as possible and the macro accepts %D numbers and \COUNTERS. %D %D \starttyping %D \dostepwiserecurse {1} {10} {2} {...} %D \dostepwiserecurse {10} {1} {-2} {...} %D \stoptyping %D %D Because the simple case is used often, we implement it %D more efficiently: \unexpanded\def\dorecurse#1% {\ifcase#1\relax \expandafter\gobbletwoarguments \or \expandafter\syst_helpers_recurse_y \else \expandafter\syst_helpers_recurse_x \fi{#1}} \unexpanded\def\syst_helpers_recurse_x#1#2% {\global\advance\outerrecurse \plusone \expandafter\gdef\csname\??recurseaction\recursedepth\endcsname{#2}% \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}} \unexpanded\def\syst_helpers_recurse_y#1#2% {\global\advance\outerrecurse \plusone \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel \let\recurselevel\!!plusone #2% \expandafter\let\expandafter\recurselevel\csname\??recurseindex\recursedepth\endcsname \global\advance\outerrecurse \minusone} \unexpanded\def\syst_helpers_recurse_indeed#1#2% from to {\ifnum#1>#2\relax \expandafter\syst_helpers_recurse_indeed_nop \else \def\recurselevel{#1}% \doubleexpandafter\syst_helpers_recurse_indeed_yes \fi\expandafter{\the\numexpr\recurselevel+\plusone\relax}{#2}} \unexpanded\def\syst_helpers_recurse_indeed#1#2% from to {\ifnum#1>#2\relax \expandafter\syst_helpers_recurse_indeed_nop \else \def\recurselevel{#1}% \innerrecurse#1\advance\innerrecurse\plusone \doubleexpandafter\syst_helpers_recurse_indeed_yes \fi\expandafter{\the\innerrecurse}{#2}} \unexpanded\def\syst_helpers_recurse_indeed_yes {\syst_helpers_recurse_content \syst_helpers_recurse_indeed} \unexpanded\def\syst_helpers_recurse_indeed_nop#1#2#3% {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\recursedepth\endcsname \global\advance\outerrecurse \minusone } %D \macros %D {doloop,exitloop} %D %D Sometimes loops are not determined by counters, but by %D (a combinations of) conditions. We therefore implement a %D straightforward loop, which can only be left when we %D explictly exit it. Nesting is supported. First we present %D a more extensive alternative. %D %D \starttyping %D \doloop %D {Some kind of typesetting punishment \par %D \ifnum\pageno>100 \exitloop \fi} %D \stoptyping %D %D When needed, one can call for \type{\looplevel} and %D \type{\loopdepth}. \let\endofloop\donothing % maybe \syst_helpers_loop_end \unexpanded\def\doloop#1% {\global\advance\outerrecurse \plusone \expandafter\gdef\csname\??recurseaction\recursedepth\endcsname{#1}% \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel \let\endofloop\syst_helpers_loop \syst_helpers_loop1} % no \plusone else \recurselevel wrong \unexpanded\def\syst_helpers_loop#1% {\def\recurselevel{#1}% \expandafter\syst_helpers_loop_yes\expandafter{\the\numexpr\recurselevel+\plusone\relax}} \unexpanded\def\syst_helpers_loop_yes {\syst_helpers_recurse_content \endofloop} \unexpanded\def\syst_helpers_loop_nop#1% {\let\endofloop\syst_helpers_loop % new, permits nested \doloop's \expandafter\let\expandafter\recurselevel\csname\??recurseindex\recursedepth\endcsname \global\advance\outerrecurse\minusone} \unexpanded\def\exitloop % \exitloop quits at end {\let\endofloop\syst_helpers_loop_nop} \unexpanded\def\exitloopnow#1\endofloop % \exitloopnow quits directly {\syst_helpers_loop_nop} %D The loop is executed at least once, so beware of situations %D like: %D %D \starttyping %D \doloop {\exitloop some commands} %D \stoptyping %D %D It's just a matter of putting the text into the \type{\if} %D statement that should be there anyway, like in: %D %D \starttyping %D \doloop {\ifwhatever \exitloop \else some commands\fi} %D \stoptyping %D %D You can also quit a loop immediately, by using \type %D {\exitloopnow} instead. Beware, this is more sensitive %D for conditional errors. %D Krzysztof Leszczynski suggested to provide access to the level by %D means of a \type {#1}. I decided to pass the more frequently used %D level as \type {#1} and the less favoured depth as \type {#2}. The %D intended usage is: %D %D \starttyping %D \dorecurse{3}{\definesymbol[test-#1][xx-#1]} %D %D \def\test{\dorecurse{3}{\definesymbol[test-##1][xx-##1]}} \test %D %D \symbol[test-1]\quad\symbol[test-2]\quad\symbol[test-3] %D \stoptyping %D %D Since the hashed arguments are expanded, we don't need tricky %D expansion here. %D %D \starttyping %D \dorecurse{3}{\expanded{\definesymbol[test-\recurselevel][xx-\recurselevel]}} %D \stoptyping \def\syst_helpers_recurse_content {\csname\??recurseaction\recursedepth\expandafter\expandafter\expandafter\endcsname \expandafter\expandafter\expandafter{\expandafter\recurselevel\expandafter}\expandafter{\recursedepth}} \unexpanded\def\syst_helpers_recurse_x#1#2% {\global\advance\outerrecurse \plusone \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#2}% \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}} \unexpanded\def\syst_helpers_recurse_y#1#2% {\global\advance\outerrecurse \plusone \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel \let\recurselevel\!!plusone \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#2}% \syst_helpers_recurse_content \expandafter\let\expandafter\recurselevel\csname\??recurseindex\recursedepth\endcsname \global\advance\outerrecurse \minusone} \unexpanded\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 {\global\advance\outerrecurse \plusone \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#4}% \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel \ifnum#3>\zerocount\relax \ifnum#2<#1\relax \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit \else \let\syst_helpers_stepwise_next\syst_helpers_stepwise_recurse \fi \else \ifnum#3<\zerocount\relax \ifnum#1<#2\relax \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit \else \let\syst_helpers_stepwise_next\syst_helpers_stepwise_reverse \fi \else \let\syst_helpers_stepwise_next\syst_helpers_stepwise_exit \fi \fi\normalexpanded{\syst_helpers_stepwise_next{\number#1}{\number#2}{\number#3}}} \unexpanded\def\doloop#1% {\global\advance\outerrecurse \plusone \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#1}% \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel \let\endofloop\syst_helpers_loop \syst_helpers_loop1} % no \plusone else \recurselevel wrong % faster % \unexpanded\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 % {\global\advance\outerrecurse \plusone % \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#4}% % \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel % \csname @swr% % \ifnum#3>\zerocount % \ifnum#2<#1\else d\fi % \else\ifnum#3<\zerocount % \ifnum#1<#2\else r\fi % \fi\fi % \expandafter\endcsname\normalexpanded{{\number#1}{\number#2}{\number#3}}} % \let\@swr \syst_helpers_stepwise_exit % \let\@swrd\syst_helpers_stepwise_recurse % \let\@swrr\syst_helpers_stepwise_reverse \installsystemnamespace{recursestepwise} \unexpanded\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 {\global\advance\outerrecurse \plusone \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#4}% \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel \csname\??recursestepwise \ifnum#3>\zerocount \ifnum#2<#1\else d\fi \else\ifnum#3<\zerocount \ifnum#1<#2\else r\fi \fi\fi \expandafter\endcsname\normalexpanded{{\number#1}{\number#2}{\number#3}}} % \expandafter\endcsname\expandafter{\number#1\expandafter}\expandafter{\number#2\expandafter}\expandafter{\number#3}} \letvalue{\??recursestepwise }\syst_helpers_stepwise_exit \letvalue{\??recursestepwise d}\syst_helpers_stepwise_recurse \letvalue{\??recursestepwise r}\syst_helpers_stepwise_reverse % quite okay too, but untested % % \def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 % {\global\advance\outerrecurse \plusone % \global\expandafter\def\csname\??recurseaction\recursedepth\endcsname##1##2{#4}% % \global\expandafter\let\csname\??recurseindex\recursedepth\endcsname\recurselevel % \normalexpanded % {\ifnum#3>\zerocount % \ifnum#2<#1 % \syst_helpers_stepwise_exit % \else % \syst_helpers_stepwise_recurse % \fi % \else % \ifnum#3<\zerocount % \ifnum#1<#2 % \syst_helpers_stepwise_exit % \else % \syst_helpers_stepwise_reverse % \fi % \else % \syst_helpers_stepwise_exit % \fi % \fi{\number#1}{\number#2}{\number#3}}} %D For special purposes: \newcount\fastloopindex \newcount\fastloopfinal \let\m_syst_helpers_fast_loop_cs\relax \unexpanded\def\dofastloopcs#1#2% {\let\m_syst_helpers_fast_loop_cs#2% \fastloopindex\plusone \fastloopfinal#1\relax \syst_helpers_fast_loop_cs} \unexpanded\def\syst_helpers_fast_loop_cs {\ifnum\fastloopindex>\fastloopfinal \let\m_syst_helpers_fast_loop_cs\relax \else \m_syst_helpers_fast_loop_cs \advance\fastloopindex\plusone \expandafter\syst_helpers_fast_loop_cs \fi} % Helper: \unexpanded\def\resetrecurselevel{\let\recurselevel\!!zerocount} \let\recurselevel\!!zerocount % \appendtoks \resetrecurselevel \to \everydump %D \macros %D {doloopoverlist} %D %D \starttyping %D \doloopoverlist {red,green,blue} { %D \setuppalet[\recursestring] %D \doloopoverlist {light,normal,dark} { %D \blackrule[color=\recursestring,width=20cm,height=2cm,depth=0cm]\par %D } %D } %D \stoptyping %D %D or: %D %D \starttyping %D \doloopoverlist {red,green,blue} { %D \setuppalet[#1] %D \doloopoverlist {light,normal,dark} { %D \blackrule[color=##1,width=20cm,height=2cm,depth=0cm]\par %D } %D } %D \stoptyping \unexpanded\def\doloopoverlist#1#2% {\global\advance\outerrecurse\plusone \expandafter\gdef\csname\??recurseaction\recursedepth\endcsname##1{\edef\recursestring{##1}#2}% \expandafter\glet\csname\??recurseindex\recursedepth\endcsname\recursestring \normalexpanded{\processcommalist[#1]{\expandafter\noexpand\csname\??recurseaction\recursedepth\endcsname}}% \expandafter\let\expandafter\recursestring\csname\??recurseindex\recursedepth\endcsname \global\advance\outerrecurse\minusone} %D \macros %D {newevery,everyline,EveryLine,EveryPar} %D %D Lets skip to something quite different. It's common use %D to use \type {\everypar} for special purposes. In \CONTEXT\ %D we use this primitive for locating sidefloats. This means %D that when user assignments to \type {\everypar} can interfere %D with those of the package. We therefore introduce %D \type {\EveryPar}. %D %D The same goes for \type {\EveryLine}. Because \TEX\ offers %D no \type {\everyline} primitive, we have to call for %D \type {\everyline} when we are working on a line by line %D basis. Just by calling \type {\EveryPar{}} and %D \type {\EveryLine{}} we restore the old situation. % \dorecurse{2}{ % \expanded{\everypar{before \recurselevel\space}} % \EveryPar{x } [before \recurselevel\space x] \par % \EveryPar{y } [before \recurselevel\space y] \par % \EveryPar{} [before \recurselevel] \par % \EveryPar{x } \EveryPar{y } \EveryPar{} [before \recurselevel] \par % \EveryPar{y } \everypar{before } [before] \par % } \installsystemnamespace{extraevery} \unexpanded\def\newevery#1#2% {\ifx#1\everypar\else\newtoks#1\fi% we test for redefinition elsewhere \ifx#2\relax\else\ifdefined#2\else \expandafter\newtoks\csname\??extraevery\strippedcsname#1\endcsname \def#2{\syst_helpers_every#1}% \fi\fi} \unexpanded\def\syst_helpers_every#1% {\expandafter\removetoks\expandafter\the\csname\??extraevery\strippedcsname#1\endcsname\from#1% \expandafter\appendtoks\expandafter\the\csname\??extraevery\strippedcsname#1\endcsname\to #1% \csname\??extraevery\strippedcsname#1\endcsname} %D This one permits definitions like: \newevery \everypar \EveryPar % we get a warning which is ok \newevery \everyline \EveryLine %D and how about: % \newtoks \neverypar % \newtoks \neveryendpar % % \normalprotected\def\syst_helpers_forgotten_endpar % {\the\neveryendpar\normalpar} % % \unexpanded\def\forgeteverypar % {\everypar{\the\neverypar}% % \let\endpar\syst_helpers_forgotten_endpar} % % \unexpanded\def\finishpar % {\ifvmode\else\par\fi} \newtoks \neverypar \unexpanded\def\forgeteverypar {\everypar{\the\neverypar}} %D Which we're going to use indeed! When the second argument %D equals \type {\relax}, the first token list is created %D unless it is already defined. %D Technically spoken we could have used the method we are %D going to present in the visual debugger. First we save %D the primitive \type{\everypar}: %D %D \starttyping %D \let\normaleverypar=\everypar %D \stoptyping %D %D Next we allocate a \TOKENLIST\ named \type{\everypar}, %D which means that \type{\everypar} is no longer a primitive %D but something like \type{\toks44}. %D %D \starttyping %D \newtoks\everypar %D \stoptyping %D %D Because \TEX\ now executes \type{\normaleverypar} instead %D of \type{\everypar}, we are ready to assign some tokens to %D this internally known and used \TOKENLIST. %D %D \starttyping %D \normaleverypar={all the things the system wants to do \the\everypar} %D \stoptyping %D %D Where the user can provide his own tokens to be expanded %D every time he expects them to expand. %D %D \starttyping %D \everypar={something the user wants to do} %D \stoptyping %D %D We don't use this method because it undoubtly leads to %D confusing situations, especially when other packages are %D used, but it's this kind of tricks that make \TEX\ so %D powerful. %D \macros %D {convertargument,convertcommand,convertvalue} %D %D Some persistent experimenting led us to the next macro. This %D macro converts a parameter or an expanded macro to it's %D textual meaning. %D %D \starttyping %D \convertargument ... \to \command %D \stoptyping %D %D For example, %D %D \starttyping %D \convertargument{one \two \three{four}}\to\ascii %D \stoptyping %D %D The resulting macro \type{\ascii} can be written to a file %D or the terminal without problems. In \CONTEXT\ we use this %D macro for generating registers and tables of contents. %D %D The second conversion alternative accepts a command: %D %D \starttyping %D \convertcommand\command\to\ascii %D \stoptyping %D %D Both commands accept the prefix \type{\doglobal} for global %D assignments. \unexpanded\def\convertvalue#1\to {\expandafter\convertcommand\csname#1\endcsname\to} \unexpanded\def\defconvertedvalue#1#2% less sensitive for \to {\expandafter\defconvertedcommand\expandafter#1\csname#2\endcsname} %D \macros %D {doifassignmentelse} %D %D A lot of \CONTEXT\ commands take optional arguments, for %D instance: %D %D \starttyping %D \dothisorthat[alfa,beta] %D \dothisorthat[first=foo,second=bar] %D \dothisorthat[alfa,beta][first=foo,second=bar] %D \stoptyping %D %D Although a combined solution is possible, we prefer a %D seperation. The next command takes care of propper %D handling of such multi||faced commands. %D %D \starttyping %D \doifassignmentelse {...} {then ...} {else ...} %D \stoptyping \def\syst_helpers_check_if_assignment_else#1=#2#3\_e_o_p_{\if#2@}% \unexpanded\def\doifassignmentelse#1% expandable {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=@@\_e_o_p_ \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} \newif\ifassignment \unexpanded\def\docheckassignment#1% {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=@@\_e_o_p_ \assignmentfalse \else \assignmenttrue \fi} %D In \ETEX\ we can use \type {\detokenize} and gain some %D speed, but in general far less that 1\% for \type %D {\convertargument} and nil for \type {\convertcommand}. %D This macro is more robust than the pure \TEX\ one, %D something I found out when primitives like \type %D {\jobname} were fed (or something undefined). \unexpanded\def\convertargument#1\to#2{\dodoglobal\edef#2{\detokenize{#1}}} \unexpanded\def\convertcommand #1\to#2{\dodoglobal\edef#2{\expandafter\detokenize\expandafter{#1}}} % hm, only second is also ok \unexpanded\def\defconvertedargument #1#2{\edef#1{\detokenize{#2}}} \unexpanded\def\defconvertedcommand #1#2{\edef#1{\detokenize\expandafter{#2}}} \unexpanded\def\edefconvertedargument#1#2{\edef#1{#2}% \edef#1{\detokenize\expandafter{#1}}} \unexpanded\def\gdefconvertedargument#1#2{\xdef#1{\detokenize{#2}}} \unexpanded\def\gdefconvertedcommand #1#2{\xdef#1{\detokenize\expandafter{#2}}} \unexpanded\def\xdefconvertedargument#1#2{\xdef#1{#2}% \xdef#1{\detokenize\expandafter{#1}}} %D When you try to convert a primitive command, you'll find %D out that the \ETEX\ method fails on for instance \type %D {\jobname} in the sense that it returns the filename %D instead of just \type {\jobname}. So far this does not %D give real problems. %D This is typically a macro that one comes to after reading %D the \TEX book carefully. Even then, the definite solution %D was found after rereading the \TEX book. The first %D implementation was: %D %D \starttyping %D \def\doconvertargument#1->#2\\\\{#2} %D \stoptyping %D %D The \type{-}, the delimiter \type{\\\\} and the the second %D argument are completely redundant. %D \macros %D {showvalue} %D %D Ahandy macro, for testing purposes only: \unexpanded\def\showvalue#1% {\ifcsname#1\endcsname \expandafter\show\csname#1\endcsname \else \show\undefined \fi} %D \macros %D {doifmeaningelse} %D %D We can use both commands in testing, but alas, not all %D meanings expand to something \type {->}. This is no problem %D in the \ETEX\ implementation, but since we want %D compatibility, we need: %D %D \starttyping %D \doifmeaningelse {\next} {\something} {true} {false} %D \stoptyping %D %D Watch the one level expansion of the second argument. \unexpanded\def\doifmeaningelse#1#2% {\edef\m_syst_string_one{\meaning#1}% \def \m_syst_string_two{#2}% \edef\m_syst_string_two{\meaning\m_syst_string_two}% \ifx\m_syst_string_one\m_syst_string_two \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} %D \macros %D {doifsamestringselse,doifsamestring,doifnotsamestring} %D %D The next comparison macro converts the arguments into %D expanded strings. This command can be used to compare for %D instance \type {\jobname} with a name stored in a macro. %D %D \starttyping %D \doifelse {\jobname}{oeps}{YES}{NO} %D \doifsamestringelse{\jobname}{oeps}{YES}{NO} %D \stoptyping \def\syst_helpers_if_samestring_else#1#2#3#4% {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#3}}}% \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#4}}}% \ifx\m_syst_string_one\m_syst_string_two\expandafter#1\else\expandafter#2\fi} \unexpanded\def\doifsamestringelse{\syst_helpers_if_samestring_else\firstoftwoarguments\secondoftwoarguments} \unexpanded\def\doifsamestring {\syst_helpers_if_samestring_else\firstofoneargument \gobbleoneargument } \unexpanded\def\doifnotsamestring {\syst_helpers_if_samestring_else\gobbleoneargument \firstofoneargument } %D \macros %D {ConvertToConstant,ConvertConstantAfter} %D %D When comparing arguments with a constant, we can get into %D trouble when this argument consists of tricky expandable %D commands. One solution for this is converting the %D argument to a string of unexpandable characters. To make %D comparison possible, we have to convert the constant too %D %D \starttyping %D \ConvertToConstant\doifelse {...} {...} {then ...} {else ...} %D \stoptyping %D %D This construction is only needed when the first argument %D can give troubles. Misuse can slow down processing. %D %D \starttyping %D \ConvertToConstant\doifelse{\c!alfa} {\c!alfa}{...}{...} %D \ConvertToConstant\doifelse{alfa} {\c!alfa}{...}{...} %D \ConvertToConstant\doifelse{alfa} {alfa} {...}{...} %D \ConvertToConstant\doifelse{alfa \alfa test}{\c!alfa}{...}{...} %D \stoptyping %D %D In examples~2 and~3 both arguments equal, in~1 and~4 %D they differ. \unexpanded\def\ConvertToConstant#1#2#3% {\edef\m_syst_string_one{\expandafter\detokenize\expandafter{#2}}% \edef\m_syst_string_two{\expandafter\detokenize\expandafter{#3}}% #1{\m_syst_string_one}{\m_syst_string_two}} %D When the argument \type{#1} consists of commands, we had %D better use %D %D \starttyping %D \ConvertConstantAfter\processaction[#1][...] %D \ConvertConstantAfter\doifelse{#1}{\v!something}{}{} %D \stoptyping %D %D This commands accepts things like: %D %D \starttyping %D \v!constant %D constant %D \hbox to \hsize{\rubish} %D \stoptyping %D %D As we will see in the core modules, this macro permits %D constructions like: %D %D \starttyping %D \setupfootertexts[...][...] %D \setupfootertexts[margin][...][...] %D \setupfootertexts[\v!margin][...][...] %D \stoptyping %D %D where \type{...} can be anything legally \TEX. \unexpanded\def\CheckConstantAfter#1#2% {\expandafter\convertargument\v!prefix!\to\ascii \convertargument#1\to#2\relax \doifinstringelse\ascii{#2} {\expandafter\convertargument#1\to#2} {}} \unexpanded\def\ConvertConstantAfter#1#2#3% {\CheckConstantAfter{#2}\asciia \CheckConstantAfter{#3}\asciib #1{\asciia}{\asciib}} %D \macros %D {assignifempty} %D %D We can assign a default value to an empty macro using: %D %D \starttyping %D \assignifempty \macros {default value} %D \stoptyping %D %D We don't explicitly test if the macro is defined. \unexpanded\def\assignifempty#1#2% can be sped up {\doifsomething{#1}{\def#1{#2}}} % {\doifnot{#1}{}{\def#1{#2}}} %D \macros %D {gobbleuntil,grabuntil,gobbleuntilrelax, %D processbetween,processuntil} %D %D In \TEX\ gobbling usually stand for skipping arguments, so %D here are our gobbling macros. %D %D In \CONTEXT\ we use a lot of \type{\start}||\type{\stop} %D like constructions. Sometimes, the \type{\stop} is used as a %D hard coded delimiter like in: %D %D \starttyping %D \unexpanded\def\startcommand#1\stopcommand% %D {... #1 ...} %D \stoptyping %D %D In many cases the \type{\start}||\type{\stop} pair is %D defined at format generation time or during a job. This %D means that we cannot hardcode the \type{\stop} criterium. %D Only after completely understanding \type{\csname} and %D \type{\expandafter} I was able to to implement a solution, %D starting with: %D %D \starttyping %D \grabuntil{stop}\command %D \stoptyping %D %D This commands executes, after having encountered %D \type {\stop} the command \type {\command}. This command %D receives as argument the text preceding the \type {\stop}. %D This means that: %D %D \starttyping %D \unexpanded\def\starthello% %D {\grabuntil{stophello}\message} %D %D \starthello Hello world!\stophello %D \stoptyping %D %D results in: \type{\message{Hello world!}}. \let\syst_helpers_grab_indeed\relax \unexpanded\def\syst_helpers_grab#1#2% {\def\syst_helpers_grab_indeed##1#1{#2{##1}}\syst_helpers_grab_indeed} \unexpanded\def\grabuntil#1% {\expandafter\syst_helpers_grab\expandafter{\csname#1\endcsname}} %D The next command build on this mechanism: %D %D \starttyping %D \processbetween{string}\command %D \stoptyping %D %D Here: %D %D \starttyping %D \processbetween{hello}\message %D \starthello Hello again!\stophello %D \stoptyping %D %D leads to: \type{\message{Hello again!}}. The command %D %D \starttyping %D \gobbleuntil{sequence} %D \stoptyping %D %D is related to these commands. This one simply throws away %D everything preceding \type{\command}. \let\syst_helpers_gobble_indeed\relax \unexpanded\def\processbetween#1#2% {\setvalue{\s!start#1}{\grabuntil{\s!stop#1}{#2}}} \unexpanded\def\gobbleuntil#1% {\def\syst_helpers_gobble_indeed##1#1{}\syst_helpers_gobble_indeed} \unexpanded\def\gobbleuntilrelax#1\relax {} %D The next one simply expands the pickup up tokens. %D %D \starttyping %D \processuntil{sequence} %D \stoptyping \let\syst_helpers_until_indeed\relax \unexpanded\def\processuntil#1% {\def\syst_helpers_until_indeed##1#1{##1}\syst_helpers_until_indeed} %D \macros %D {groupedcommand} %D %D Commands often manipulate argument as in: %D %D \starttyping %D \def\doezomaarwat#1{....#1....} %D \stoptyping %D %D A disadvantage of this approach is that the tokens that %D form \type{#1} are fixed the the moment the argument is read %D in. Normally this is no problem, but for instance verbatim %D environments adapt the \CATCODES\ of characters and therefore %D are not always happy with already fixed tokens. %D %D Another problem arises when the argument is grouped not by %D \type{{}} but by \type{\bgroup} and \type{\egroup}. Such an %D argument fails, because the \type{\bgroup} is een as the %D argument (which is quite normal). %D %D The next macro offers a solution for both unwanted %D situations: %D %D \starttyping %D \groupedcommand {before} {after} %D \stoptyping %D %D Which can be used like: %D %D \starttyping %D \def\cite% %D {\groupedcommand{\rightquote\rightquote}{\leftquote\leftquote}} %D \stoptyping %D %D This command is equivalent to, but more 'robust' than: %D %D \starttyping %D \def\cite#1% %D {\rightquote\rightquote#1\leftquote\leftquote} %D \stoptyping %D %D \starttyping %D \def\rightword% %D {\groupedcommand{\hfill\hbox}{\parfillskip\zeropoint}} %D %D .......... \rightword{the right way} %D \stoptyping %D %D Here \TEX\ typesets \type{\bf the right way} unbreakable %D at the end of the line. The solution mentioned before does %D not work here. We also handle %D %D \starttyping %D to be \bold{bold} or not, that's the question %D \stoptyping %D %D and %D %D \starttyping %D to be {\bold bold} or not, that's the question %D \stoptyping %D %D This alternative checks for a \type {\bgroup} token first. The internal %D alternative does not accept the box handling mentioned before, but further %D nesting works all right. The extra \type {\bgroup}||\type {\egroup} is needed to %D keep \type {\m_syst_helpers_handle_group_after} both into sight and local. \let\m_syst_helpers_handle_group_after \relax \let\m_syst_helpers_handle_group_before\relax % keep: % % \unexpanded\def\syst_helpers_handle_group_normal#1#2% % {\bgroup % \def\m_syst_helpers_handle_group_before{\bgroup#1\bgroup\aftergroup\m_syst_helpers_handle_group_after}% can't we remove the second \bgroup % \def\m_syst_helpers_handle_group_after {#2\egroup\egroup}% and one \egroup here? % \afterassignment\m_syst_helpers_handle_group_before % \let\next=} \unexpanded\def\syst_helpers_handle_group_normal#1#2% {\bgroup \def\m_syst_helpers_handle_group_before{#1}% \def\m_syst_helpers_handle_group_after {#2}% \afterassignment\m_syst_helpers_handle_group_normal_before \let\next=} \def\m_syst_helpers_handle_group_normal_before {\bgroup \m_syst_helpers_handle_group_before \bgroup \aftergroup\m_syst_helpers_handle_group_normal_after} \def\m_syst_helpers_handle_group_normal_after {\m_syst_helpers_handle_group_after \egroup \egroup} % keep: % % \unexpanded\def\syst_helpers_handle_group_simple#1#2% no inner group (so no kerning interference) % {\bgroup % %def\m_syst_helpers_handle_group_before{\bgroup#1\aftergroup\m_syst_helpers_handle_group_after}% interferes % \def\m_syst_helpers_handle_group_before{\bgroup\aftergroup\m_syst_helpers_handle_group_after#1}% % \def\m_syst_helpers_handle_group_after {#2\egroup}% % \afterassignment\m_syst_helpers_handle_group_before % \let\next=} \unexpanded\def\syst_helpers_handle_group_simple#1#2% no inner group (so no kerning interference) {\bgroup \def\m_syst_helpers_handle_group_before{#1}% \def\m_syst_helpers_handle_group_after {#2}% \afterassignment\m_syst_helpers_handle_group_simple_before \let\next=} \def\m_syst_helpers_handle_group_simple_before {\bgroup \aftergroup\m_syst_helpers_handle_group_simple_after \m_syst_helpers_handle_group_before} \def\m_syst_helpers_handle_group_simple_after {\m_syst_helpers_handle_group_after \egroup}% \unexpanded\def\syst_helpers_handle_group_nop {\ifnum\currentgrouptype=\semisimplegroupcode \expandafter\syst_helpers_handle_group_nop_a \else \expandafter\syst_helpers_handle_group_nop_b \fi} \def\syst_helpers_handle_group_nop_a#1#2% {\def\m_syst_helpers_handle_group_after{#2\endgroup}% \begingroup \aftergroup\m_syst_helpers_handle_group_after #1} \def\syst_helpers_handle_group_nop_b#1#2% {\def\m_syst_helpers_handle_group_after{#2\egroup}% \bgroup \aftergroup\m_syst_helpers_handle_group_after #1} %D I considered it a nuisance that %D %D \starttyping %D \color[green] %D {as grass} %D \stoptyping %D %D was not interpreted as one would expect. This is due to the %D fact that \type{\futurelet} obeys blank spaces, and a %D line||ending token is treated as a blank space. So the final %D implementation became: \unexpanded\def\groupedcommand#1#2% {\doifnextbgroupelse{\syst_helpers_handle_group_normal{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}} \unexpanded\def\simplegroupedcommand#1#2% {\doifnextbgroupelse{\syst_helpers_handle_group_simple{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}} %D Users should be aware of the fact that grouping can %D interfere with ones paragraph settings that are executed %D after the paragraph is closed. One should therefore %D explictly close the paragraph with \type{\par}, else the %D settings will be forgotten and not applied. So it's: %D %D \starttyping %D \def\BoldRaggedCenter% %D {\groupedcommand{\raggedcenter\bf}{\par}} %D \stoptyping %D \macros %D {checkdefined} %D %D The bigger the system, the greater the change that %D user defined commands collide with those that are part of %D the system. The next macro gives a warning when a command is %D already defined. We considered blocking the definition, but %D this is not always what we want. %D %D \starttyping %D \checkdefined {category} {class} {command} %D \stoptyping %D %D The user is warned with the suggestion to use %D \type{CAPITALS}. This suggestion is feasible, because %D \CONTEXT only defines lowcased macros. \unexpanded\def\showdefinederror#1#2% {\writestatus\m!system{#1 #2 replaces a macro, use CAPITALS!}} \unexpanded\def\checkdefined#1#2#3% {\doifdefined{#3}{\showdefinederror{#2}{#3}}} %D \macros %D {GotoPar,GetPar} %D %D Typesetting a paragraph in a special way can be done by %D first grabbing the contents of the paragraph and processing %D this contents grouped. The next macro for instance typesets %D a paragraph in boldface. %D %D \starttyping %D \def\remark#1\par% %D {\bgroup\bf#1\egroup} %D \stoptyping %D %D This macro has to be called like %D %D \starttyping %D \remark some text ... ending with \par %D \stoptyping %D %D Instead of \type{\par} we can of course use an empty line. %D When we started typesetting with \TEX, we already had %D produced lots of text in plain \ASCII. In producing such %D simple formatted texts, we adopted an open layout, and when %D switching to \TEX, we continued this open habit. Although %D \TEX\ permits a cramped and badly formatted source, it adds %D to confusion and sometimes introduces errors. So we prefer: %D %D \starttyping %D \remark %D %D some text ... ending with an empty line %D \stoptyping %D %D We are going to implement a mechanism that allows such open %D specifications. The definition of the macro handling %D \type{\remark} becomes: %D %D \starttyping %D \def\remark% %D {\BeforePar{\bgroup\bf}% %D \AfterPar{\egroup}% %D \GetPar} %D \stoptyping %D %D A macro like \type{\GetPar} can be defined in several %D ways. The recent version, the fourth one in a row, %D originally was far more complicated, but some functionality %D has been moved to other macros. %D %D We start with the more simple but in some cases more %D appropriate alternative is \type{\GotoPar}. This one leaves %D \type{\par} unchanged and is therefore more robust. On the %D other hand, \type{\AfterPar} is not supported. \newtoks\BeforePar \newtoks\AfterPar \def\redowithpar\par {\doifnextcharelse\par\redowithpar\dodowithpar}% \def\dowithpar#1#2% {\def\dodowithpar##1\par{#1##1#2}% \redowithpar\par} \def\redogotopar\par {\doifnextcharelse\par\redogotopar\dodogotopar}% \def\dogotopar#1% {\def\dodogotopar{#1}% \redogotopar\par} \def\dogotoparcs#1% {\let\dodogotopar#1% \redogotopar\par} \unexpanded\def\GetPar {\expanded {\dowithpar {\the\BeforePar \BeforePar\emptytoks} {\the\AfterPar \BeforePar\emptytoks \AfterPar\emptytoks}}} \unexpanded\def\GotoPar {\expanded {\dogotopar {\the\BeforePar \BeforePar\emptytoks}}} %D \macros %D {dowithpargument,dowithwargument} %D %D The next macros are a variation on \type{\GetPar}. When %D macros expect an argument, it interprets a grouped sequence %D of characters a one token. While this adds to robustness and %D less ambiguous situations, we sometimes want to be a bit %D more flexible, or at least want to be a bit more tolerant %D to user input. %D %D We start with a commands that acts on paragraphs. This %D command is called as: %D %D \starttyping %D \dowithpargument\command %D \dowithpargument{\command ... } %D \stoptyping %D %D In \CONTEXT\ we use this one to read in the titles of %D chapters, sections etc. The commands responsible for these %D activities accept several alternative ways of argument %D passing. In these examples, the \type{\par} can be omitted %D when an empty line is present. %D %D \starttyping %D \command{...} %D \command ... \par %D \command %D {...} %D \command %D ... \par %D \stoptyping \let\syst_helpers_next_par\relax \let\syst_helpers_next_arg\relax \unexpanded\def\dowithpargument#1% {\def\syst_helpers_next_par##1 \par{#1{##1}}% \def\syst_helpers_next_arg##1{#1{##1}}% \doifnextbgroupelse\syst_helpers_next_arg{\doifnextcharelse\par{#1{}}\syst_helpers_next_par}} %D The \type{p} in the previous command stands for paragraph. %D When we want to act upon words we can use the \type{w} %D alternative. %D %D \starttyping %D \dowithwargument\command %D \dowithwargument{... \command ...} %D \stoptyping %D %D The main difference bwteen two alternatives is in the %D handling of \type{\par}'s. This time the space token acts %D as a delimiter. %D %D \starttyping %D \command{...} %D \command ... %D \command %D {...} %D \command %D ... %D \stoptyping \let\syst_helpers_next_war\relax \let\syst_helpers_next_arg\relax \unexpanded\def\dowithwargument#1% {\def\syst_helpers_next_war##1 {#1{##1}}% \def\syst_helpers_next_arg##1{#1{##1}}% \doifnextbgroupelse\syst_helpers_next_arg\syst_helpers_next_war} %D \macros %D {dorepeat,dorepeatwithcommand} %D %D When doing repetitive tasks, we stromgly advice to use %D \type{\dorecurse}. The next alternative however, suits %D better some of the \CONTEXT\ interface commands. %D %D \starttyping %D \dorepeat[n*\command] %D \stoptyping %D %D The value of the used \COUNTER\ can be called within %D \type{\command} by \type{\repeater}. %D %D A slightly different alternative is: %D %D \starttyping %D \dorepeatwithcommand[n*{...}]\command %D \stoptyping %D %D When we call for something like: %D %D \starttyping %D \dorepeatwithcommand[3*{Hello}]\message %D \stoptyping %D %D we get ourselves three \type{\message{Hello}} messages in %D a row. In both commands, the \type{n*} is optional. When this %D specification is missing, the command executes once. \unexpanded\def\dorepeatwithcommand[#1]% {\syst_helpers_repeat_with_command#1*\empty*\relax} \def\syst_helpers_repeat_with_command#1*#2#3*#4\relax#5% {\ifx#2\empty\syst_helpers_repeat_with_command_again[#1]#5\else\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5\fi} \def\syst_helpers_repeat_with_command_indeed#1#2#3#4% {\ifx#2\empty % redundant but gives cleaner extensions #4{#1}% \else\ifnum#1<\zerocount %\normalexpanded{\dorecurse{\number-\number#1}}{#4{-#2#3}}% \dorecurse{-#1}{#4{-#2#3}}% \else\ifx#2+% \dorecurse{#1}{#4{#3}}% \else \dorecurse{#1}{#4{#2#3}}% \fi\fi\fi} \def\syst_helpers_repeat_with_command_again[#1]#2% {#2{#1}} %D The extension hook permits something like: %D %D \starttyping %D \bgroup %D %D \catcode`\*=\superscriptcatcode %D %D \gdef\syst_helpers_repeat_with_command_again[#1]% %D {\redodorepeatwithcommand#1*\empty*\relax} %D %D \gdef\redodorepeatwithcommand#1*#2#3*#4\relax#5% %D {\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5} %D %D \egroup %D \stoptyping %D %D although one may wonder if changing the catcode of \type {*} is wise. %D \macros %D {doifstringinstringelse} %D %D The next macro is meant for situations where both strings %D are macros. This save some unneeded expansion. %D %D \starttyping %D \def\doifstringinstringelse#1#2% %D {\syst_helpers_do_if_in_string_else#1#2% %D \expandafter\firstoftwoarguments %D \else %D \expandafter\secondoftwoarguments %D \fi} %D \stoptyping %D %D A bit faster is: \def\syst_helpers_if_instring_else_indeed#1% {\if#1@% \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} \def\doifstringinstringelse#1#2% {\expandafter\def\expandafter\syst_helpers_if_instring_else\expandafter##\expandafter1#1##2##3\_e_o_s_ {\syst_helpers_if_instring_else_indeed##2}% \expandafter\expandafter\expandafter\syst_helpers_if_instring_else\expandafter#2#1@@\_e_o_s_} %D \macros %D {appendtoks,prependtoks,appendtoksonce,prependtoksonce, %D doifintokselse,flushtoks,dotoks} %D %D We use \TOKENLISTS\ sparsely within \CONTEXT, because the %D comma separated lists are more suitable for the user %D interface. Nevertheless we have: %D %D \starttyping %D (\doglobal) \appendtoks ... \to\tokenlist %D (\doglobal) \prependtoks ... \to\tokenlist %D (\doglobal) \flushtoks\tokenlist %D \dotoks\tokenlist %D \stoptyping %D %D These macros are clones of the ones implemented in page~378 of %D Knuth's \TeX book. \newtoks\t_syst_helpers_scratch \let \m_syst_helpers_scratch\empty % no longer \def but \let to target toks \unexpanded\def\appendtoks {\syst_helpers_append_toks \relax} \unexpanded\def\prependtoks {\syst_helpers_prepend_toks \relax} \unexpanded\def\appendtoksonce {\syst_helpers_append_toks_once \relax} \unexpanded\def\prependtoksonce{\syst_helpers_prepend_toks_once\relax} \def\syst_helpers_append_toks_indeed {\dodoglobal\m_syst_helpers_scratch\doubleexpandafter{\expandafter\the\expandafter\m_syst_helpers_scratch\the\t_syst_helpers_scratch}} \def\syst_helpers_prepend_toks_indeed {\dodoglobal\m_syst_helpers_scratch\doubleexpandafter{\expandafter\the\expandafter\t_syst_helpers_scratch\the\m_syst_helpers_scratch}} \def\syst_helpers_append_toks#1\to#2% {\let\m_syst_helpers_scratch#2% \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}\syst_helpers_append_toks_indeed} \def\syst_helpers_prepend_toks#1\to#2% {\let\m_syst_helpers_scratch#2% \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}\syst_helpers_prepend_toks_indeed} \def\syst_helpers_append_toks_once#1\to#2% {\let\m_syst_helpers_scratch#2% \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}% \doifintokselse\t_syst_helpers_scratch\m_syst_helpers_scratch\donothing\syst_helpers_append_toks_indeed} \def\syst_helpers_prepend_toks_once#1\to#2% {\let\m_syst_helpers_scratch#2% \t_syst_helpers_scratch\expandafter{\gobbleoneargument#1}% \doifintokselse\t_syst_helpers_scratch\m_syst_helpers_scratch\donothing\syst_helpers_prepend_toks_indeed} %D The test macro: \unexpanded\def\doifintokselse#1#2% #1 en #2 zijn toks {\edef\asciia{\detokenize\expandafter{\the#1}}% \edef\asciib{\detokenize\expandafter{\the#2}}% \doifstringinstringelse\asciia\asciib} %D A nice one too: % {\scratchtoks{abc} \removetoks b\from\scratchtoks [\the\scratchtoks]} % {\scratchtoks{abc} \removetoks x\from\scratchtoks [\the\scratchtoks]} % {\scratchtoks{} \removetoks x\from\scratchtoks [\the\scratchtoks]} % {\scratchtoks{xaa} \removetoks x\from\scratchtoks [\the\scratchtoks]} % {\scratchtoks{a\relax b} \removetoks \relax\from\scratchtoks [\showthe\scratchtoks]} \unexpanded\def\removetoks#1\from#2% {\def\syst_helpers_remove_toks##1#1##2\empty\empty\empty##3\_e_o_t_ {\def\m_syst_string_one{##3}% \ifx\m_syst_string_one\empty#2{##1}\else#2{##1##2}\fi}% \expandafter\syst_helpers_remove_toks\the#2\empty\empty\empty#1\empty\empty\empty\_e_o_t_} %D Also: \unexpanded\def\appendetoks #1\to{\normalexpanded{\appendtoks #1}\to} \unexpanded\def\prependetoks#1\to{\normalexpanded{\prependtoks#1}\to} %D Hm. \unexpanded\def\flushtoks#1% nb: can reassing to #1 again, hence the indirectness {\t_syst_helpers_scratch#1\relax \dodoglobal#1\emptytoks \the\t_syst_helpers_scratch\relax} % better: \def\flushtoks#1{\normalexpanded{\noexpand\dodoglobal#1\emptytoks\the#\relax}} \let\dotoks\the %D \macros %D {beforesplitstring,aftersplitstring} %D %D These both commands split a string at a given point in two %D parts, so \type{x.y} becomes \type{x} or \type{y}. %D %D \starttyping %D \beforesplitstring test.tex\at.\to\filename %D \aftersplitstring test.tex\at.\to\extension %D \stoptyping %D %D The first routine looks (and is indeed) a bit simpler than %D the second one. The alternative looking more or less like %D the first one did not always give the results we needed. %D Both implementations show some insight in the manipulation %D of arguments. \let\syst_helpers_split_string\relax \unexpanded\def\beforesplitstring#1\at#2\to#3% {\def\syst_helpers_split_string##1#2##2#2##3\\% {\def#3{##1}}% \expandafter\syst_helpers_split_string#1#2#2\\} \unexpanded\def\aftersplitstring#1\at#2\to#3% {\def\syst_helpers_split_string##1#2##2@@@##3\\% {\def#3{##2}}% \expandafter\syst_helpers_split_string#1@@@#2@@@\\} %D \macros %D {splitstring,greedysplitstring} %D %D A bonus macro. \unexpanded\def\splitstring#1\at#2\to#3\and#4% {\def\syst_helpers_split_string##1#2##2\empty\empty\empty##3\\% {\def#3{##1}% \def\syst_helpers_split_string{##3}% \ifx\syst_helpers_split_string\empty \let#4\empty \else \def#4{##2}% \fi}% \expandafter\syst_helpers_split_string#1\empty\empty\empty#2\empty\empty\empty\\} \unexpanded\def\greedysplitstring#1\at#2\to#3\and#4% {\edef\asciib{#1}% \let\asciic\asciib \let#3\empty \let#4\empty \doloop {\expandafter\splitstring\asciib\at#2\to\asciia\and\asciib \ifx\asciib\empty \exitloop \else % not \edef#3{\ifx#3\empty\else#3#2\fi\asciia} else % /root/path fails because then #3==empty \edef#3{\ifcase\recurselevel\or\else#3#2\fi\asciia}% \let#4\asciib \fi}% \ifx#3\empty\let#3\asciic\fi} %D \macros %D {beforetestandsplitstring, %D aftertestandsplitstring, %D testandsplitstring} \unexpanded\def\beforetestandsplitstring#1\at#2\to#3% {\def\syst_helpers_split_string##1#2##2#2##3##4\\% {\ifx##3\empty\let#3\empty\else\def#3{##1}\fi}% \expandafter\syst_helpers_split_string#1#2#2\empty\\} \unexpanded\def\aftertestandsplitstring#1\at#2\to#3% {\def\syst_helpers_split_string ##1#2##2@@@##3##4\\% {\ifx##3\empty\let#3\empty\else\def#3{##2}\fi}% \expandafter\syst_helpers_split_string #1@@@#2@@@\empty\\} \def\testandsplitstring#1\at#2\to#3\and#4% {\def\syst_helpers_split_string##1#2##2#2##3##4\\% {\ifx##3\empty\let#3\empty\let#4\empty\else\def#3{##1}\def#4{##2}\fi}% \expandafter\syst_helpers_split_string#1#2#2\empty\\} %D \macros %D {removesubstring} %D %D A first application of the two routines defined above is: %D %D \starttyping %D \removesubstring-\from first-last\to\nothyphenated %D \stoptyping %D %D Which in terms of \TEX\ looks like: \unexpanded\def\removesubstring#1\from#2\to#3% {\splitstring#2\to\m_syst_string_one\and\m_syst_string_two \dodoglobal#3{\m_syst_string_one\m_syst_string_two}} %D \macros %D {appendtocommalist,prependtocommalist, %D addtocommalist,removefromcommalist} %D %D When working with comma separated lists, one sooner or %D later want the tools to append or remove items from such a %D list. When we add an item, we first check if it's already %D there. This means that every item in the list is unique. %D %D \starttyping %D \addtocommalist {alfa} \name %D \addtocommalist {beta} \name %D \addtocommalist {gamma} \name %D \removefromcommalist {beta} \name %D \stoptyping %D %D These commands can be prefixed with \type{\doglobal}. The %D implementation of the second command is more complecated, %D because we have to take leading spaces into account. Keep in %D mind that users may provide lists with spaces after the %D commas. When one item is left, we also have to get rid of %D trailing spaces. %D %D \starttyping %D \def\words{alfa, beta, gamma, delta} %D \def\words{alfa,beta,gamma,delta} %D \stoptyping %D %D Removing an item takes more time than adding one. %D %D A fast appending alternative, without any testing, is %D also provided: %D %D \starttyping %D \appendtocommalist {something} \name %D \prependtocommalist {something} \name %D \stoptyping %D %D This can be implemented as follows: %D %D \starttyping %D \def\appendtocommalist#1#2% %D {\ifx#2\empty %D \dodoglobal\edef#2{#1}% %D \else % no test on empty %D \dodoglobal\edef#2{#2,#1}% %D \fi} %D %D \def\prependtocommalist#1#2% %D {\ifx#2\empty %D \dodoglobal\edef#2{#1}% %D \else % no test on empty %D \dodoglobal\edef#2{#1,#2}% %D \fi} %D \stoptyping %D %D The faster alternatives are: \unexpanded\def\appendtocommalist#1#2% {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}} \unexpanded\def\prependtocommalist#1#2% {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}} \unexpanded\def\addtocommalist#1#2% {item} \cs {\rawdoifinsetelse{#1}#2\resetglobal {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}} \unexpanded\def\pretocommalist#1#2% {item} \cs {\rawdoifinsetelse{#1}#2\resetglobal {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}} \unexpanded\def\robustdoifinsetelse#1#2% {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#1}}}% \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#2}}}% \rawdoifinsetelse\m_syst_string_one\m_syst_string_two} \unexpanded\def\robustaddtocommalist#1#2% {item} \cs {\robustdoifinsetelse{#1}#2\resetglobal {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}} \unexpanded\def\robustpretocommalist#1#2% {item} \cs {\robustdoifinsetelse{#1}#2\resetglobal {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}} \unexpanded\def\xsplitstring#1#2% \cs {str} {\def\syst_helpers_split_string##1,#2,##2,#2,##3\\% {\edef\m_syst_string_one{\bcleanedupcommalist##1\empty\empty\relax}% \edef\m_syst_string_two{\acleanedupcommalist##2,,\relax}}% \expandafter\syst_helpers_split_string\expandafter,#1,,#2,,#2,\\} \def\bcleanedupcommalist#1#2#3\relax{\if#1,\else#1\fi\if#2,\else#2\fi#3} \def\bcleanedupcommalist#1#2\relax{\if#1,\else#1\fi#2} \def\acleanedupcommalist#1,,#2\relax{#1} \unexpanded\def\removefromcommalist#1#2% to be sped up {\rawdoifinsetelse{#1}#2% {\normalexpanded{\xsplitstring\noexpand#2{#1}}% \dodoglobal\edef#2% {\ifx\m_syst_string_one\empty \m_syst_string_two \else \m_syst_string_one\ifx\m_syst_string_two\empty\else,\m_syst_string_two\fi \fi}} \resetglobal} % \unexpanded\def\addtocommalist#1#2% upto 3 times slower % {\dodoglobal\edef#2{\ctxcommand{addtocommalist(\!!bs#1\!!es,\!!bs#2\!!es)}}} % % \unexpanded\def\removefromcommalist#1#2% faster and more robust % {\dodoglobal\edef#2{\ctxcommand{addtocommalist(\!!bs#1\!!es,\!!bs#2\!!es)}}} %D \macros %D {substituteincommalist} %D %D Slow but seldom used, so for the moment we stick to this %D implementation. %D %D \starttyping %D \substituteincommalist{old}{new}{list} %D \stoptyping \def\syst_helpers_substitute_in_comma_list_step#1% {\edef\m_syst_string_three{#1}% \ifx\m_syst_string_one\m_syst_string_three \ifx\m_syst_string_two\empty \else \edef\m_syst_string_four{\ifx\m_syst_string_four\empty\else\m_syst_string_four,\fi\m_syst_string_two}% \fi \else \edef\m_syst_string_four{\ifx\m_syst_string_four\empty\else\m_syst_string_four,\fi#1}% \fi} \unexpanded\def\substituteincommalist#1#2#3% old, new, list (slooow) {\edef\m_syst_string_one{#1}% \edef\m_syst_string_two{#2}% \let\m_syst_string_four\empty \normalexpanded{\rawprocesscommacommand[#3]}\syst_helpers_substitute_in_comma_list_step \let#3\m_syst_string_four} %D \macros %D {replaceincommalist} %D %D The next macro can be used to replace an indexed element in a commalist: %D %D \starttyping %D \replaceincommalist\MyList{2} %D \stoptyping %D %D Element~2 will be replaced by the current meaning of the macro \type %D {\newcommalistelement}. The old meaning is saved in \type {\commalistelement}. %D The replacement honors grouped items, like in: %D %D \starttyping %D \def\MyList{a,b,c,d,e,f} \replaceincommalist\MyList{3} %D \def\MyList{a,b,c,d,e,f} \replaceincommalist\MyList{3} %D \def\MyList{a,{b,c},d,e,f} \replaceincommalist\MyList{3} %D \def\MyList{a,b,c,{d,e,f}} \replaceincommalist\MyList{3} %D \stoptyping %D %D This macro was used in the bibtex code (and is probably no longer needed). \newcount\c_syst_helpers_comma_list_index \let \m_syst_helpers_comma_list_target\empty \let\newcommalistelement\empty \def\syst_helpers_replace_in_comma_list_step#1% {\ifnum\commalistcounter=\c_syst_helpers_comma_list_index\relax \ifx\newcommalistelement\empty\else \ifx\m_syst_helpers_comma_list_target\empty \let\m_syst_helpers_comma_list_target\newcommalistelement \else \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter \m_syst_helpers_comma_list_target\expandafter\expandafter\expandafter {\expandafter\m_syst_helpers_comma_list_target\expandafter,\newcommalistelement}% \fi \fi \def\commalistelement{#1}% \else \ifx\m_syst_helpers_comma_list_target\empty \ifx\nexttoken\bgroup % is known -) \def\m_syst_helpers_comma_list_target{{#1}}% \else \def\m_syst_helpers_comma_list_target{#1}% \fi \else \ifx\nexttoken\bgroup % is known -) \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,{#1}}% \else \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,#1}% \fi \fi \fi \advance\commalistcounter\plusone} \unexpanded\def\replaceincommalist#1#2% #1 = commalistelement #2 = position starts at 1 {\c_syst_helpers_comma_list_index#2\relax \let\m_syst_helpers_comma_list_target\empty \let\commalistelement\empty \commalistcounter\plusone \expandafter\processcommalist\expandafter[#1]\syst_helpers_replace_in_comma_list_step \dodoglobal\let#1\m_syst_helpers_comma_list_target} %D \macros %D {globalprocesscommalist} %D %D The commalist processing commands are characterized by the %D fact that the way they handle expansion as well as the fact %D that they can be nested. This makes them kind of useless for %D handling comma lists in alignments. In these situations the %D next macro can be of use. \let\m_syst_helpers_comma_list_command_global\empty \def\syst_helpers_comma_list_command_global_step#1,% {\if]#1\else \m_syst_helpers_comma_list_command_global{#1}% \expandafter\syst_helpers_comma_list_command_global_step \fi} \unexpanded\def\globalprocesscommalist[#1]#2% {\global\let\m_syst_helpers_comma_list_command_global#2% \expandafter\syst_helpers_comma_list_command_global_step#1,],} %D \macros %D {withoutpt,PtToCm, %D numberofpoints,dimensiontocount} %D %D We can convert point into centimeters with: %D %D \starttyping %D \PtToCm{dimension} %D \stoptyping {\catcode`\.=\othercatcode \catcode`\p=\othercatcode \catcode`\t=\othercatcode \gdef\WITHOUTPT#1pt{#1}} \def\withoutpt#1% {\expandafter\WITHOUTPT#1} %D The capitals are needed because \type{p} and \type{t} have %D \CATCODE~12, while macronames only permit tokens with the %D \CATCODE~11. As a result we cannot use the \type{.group} %D primitives. Those who want to know more about this kind of %D manipulations, we advice to study the \TEX book in detail. %D Because this macro does not do any assignment, we can use it %D in the following way too. \def\PtToCm#1% {\withoutpt\the\dimexpr0.0351459804\dimexpr#1\relax\relax cm} %D We also support: %D %D \starttyping %D \numberofpoints {dimension} %D \dimensiontocount {dimension} {\count} %D \stoptyping %D %D Both macros return a rounded number. % \dimensiontocount{10.49pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.49pt} % \dimensiontocount{10.51pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.51pt} \def\dimensiontocount#1#2{#2\numexpr\dimexpr#1\relax/\maxcard\relax} \def\numberofpoints #1{\the\numexpr\dimexpr#1\relax/\maxcard\relax} %D \macros %D {swapdimens,swapcounts,swapmacros, %D globalswapdimens,globalswapcounts,globalswapmacros} %D %D Simple but effective are the next two macros. There name %D exactly states their purpose. \newdimen\d_syst_helpers_swapped \newcount\c_syst_helpers_swapped \let \m_syst_helpers_swapped\relax \unexpanded\def\swapdimens#1#2{\d_syst_helpers_swapped #1\relax#1#2\relax#2\d_syst_helpers_swapped} \unexpanded\def\swapcounts#1#2{\c_syst_helpers_swapped #1\relax#1#2\relax#2\c_syst_helpers_swapped} \unexpanded\def\swapmacros#1#2{\let\m_syst_helpers_swapped#1\let #1#2\let #2\m_syst_helpers_swapped} \unexpanded\def\globalswapdimens#1#2{\d_syst_helpers_swapped #1\global #1#2\global #2\d_syst_helpers_swapped} \unexpanded\def\globalswapcounts#1#2{\c_syst_helpers_swapped #1\global #1#2\global #2\c_syst_helpers_swapped} \unexpanded\def\globalswapmacros#1#2{\let\m_syst_helpers_swapped#1\global\let#1#2\global\let#2\m_syst_helpers_swapped} %D \macros %D {pushmacro,popmacro} %D %D Premature and a bit of beta, we offer: %D %D \starttyping %D \pushmacro\macro %D \popmacro\macro %D \stoptyping %D %D Beware: global! \installsystemnamespace{localpushedmacro} \installsystemnamespace{globalpushedmacro} \let\m_syst_helpers_push_macro\empty \unexpanded\def\globalpushmacro#1% {\xdef\m_syst_helpers_push_macro{\string#1}% \ifcsname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \else \expandafter\newcount\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \fi \global\advance\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \plusone \global\expandafter\let\csname\the\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname\m_syst_helpers_push_macro\endcsname#1} \unexpanded\def\globalpopmacro#1% {\xdef\m_syst_helpers_push_macro{\string#1}% \global\expandafter\let\expandafter#1\csname\the\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname\m_syst_helpers_push_macro\endcsname \global\advance\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \minusone} \unexpanded\def\localpushmacro#1% this one can be used to push a value over an \egroup {\xdef\m_syst_helpers_push_macro{\string#1}% \ifcsname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \else \expandafter\newcount\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \fi \global\advance\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \plusone \global\expandafter\let\csname\the\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname\m_syst_helpers_push_macro\endcsname#1} \unexpanded\def\localpopmacro#1% {\xdef\m_syst_helpers_push_macro{\string#1}% \expandafter\let\expandafter#1\csname\the\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname\m_syst_helpers_push_macro\endcsname \global\advance\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \minusone } \let\pushmacro\localpushmacro \let\popmacro \localpopmacro %D \macros %D {setlocalhsize} %D %D Sometimes we need to work with the \type{\hsize} that is %D corrected for indentation and left and right skips. The %D corrected value is available in \type{\localhsize}, which %D needs to be calculated with \type{\setlocalhsize} first. %D %D \starttyping %D \setlocalhsize \hbox to \localhsize{...} %D \setlocalhsize[-1em] \hbox to \localhsize{...} %D \setlocalhsize[.5ex] \hbox to \localhsize{...} %D \stoptyping %D %D These examples show us that an optional can be used. The %D value provided is added to \type{\localhsize}. \newdimen\localhsize \unexpanded\def\setlocalhsize % don't change ! {\doifnextoptionalelse \syst_helpers_set_local_hsize_yes \syst_helpers_set_local_hsize_nop} \def\syst_helpers_set_local_hsize_nop {\localhsize\availablehsize} \def\syst_helpers_set_local_hsize_yes[#1]% {\syst_helpers_set_local_hsize_nop \advance\localhsize#1\relax} \def\availablehsize {\dimexpr \hsize-\leftskip-\rightskip \ifnum\hangafter<\zerocount \ifdim\hangindent>\zeropoint-\else+\fi\hangindent \fi \relax} %D \macros %D {doifvalue,doifnotvalue,doifelsevalue, %D doifnothing,doifsomething,doifelsenothing, %D doifvaluenothing,doifvaluesomething,doifelsevaluenothing} %D %D These long named \type{\if} commands can be used to access %D macros (or variables) that are normally accessed by using %D \type{\getvalue}. Using these alternatives safes us three %D tokens per call. Anyone familiar with the not||values %D ones, can derive their meaning from the definitions. \unexpanded\def\doifvalue#1#2% {\edef\m_syst_string_one{\csname#1\endcsname}% \edef\m_syst_string_two{#2}% \ifx\m_syst_string_one\m_syst_string_two \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\doifnotvalue#1#2% {\edef\m_syst_string_one{\csname#1\endcsname}% \edef\m_syst_string_two{#2}% \ifx\m_syst_string_one\m_syst_string_two \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \unexpanded\def\doifelsevalue#1#2% {\edef\m_syst_string_one{\csname#1\endcsname}% \edef\m_syst_string_two{#2}% \ifx\m_syst_string_one\m_syst_string_two \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\doifnothing#1% {\edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\doifsomething#1% {\edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \unexpanded\def\doifelsenothing#1% {\edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\doifsomethingelse#1% {\edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} \unexpanded\def\doifvaluenothing#1% {\edef\m_syst_string_one{\csname#1\endcsname}% \ifx\m_syst_string_one\empty \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\doifvaluesomething#1% {\edef\m_syst_string_one{\csname#1\endcsname}% \ifx\m_syst_string_one\empty \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \unexpanded\def\doifelsevaluenothing#1% {\edef\m_syst_string_one{\csname#1\endcsname}% \ifx\m_syst_string_one\empty \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} %D \macros %D {doifemptyelsevalue, doifemptyvalue, doifnotemptyvalue} %D %D Also handy: \def\doifemptyelsevalue#1% {\expandafter\ifx\csname#1\endcsname\empty \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \def\doifemptyvalue#1% {\expandafter\ifx\csname#1\endcsname\empty \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \def\doifnotemptyvalue#1% {\expandafter\ifx\csname#1\endcsname\empty \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} %D \macros %D {doifallcommonelse} %D %D A complete match of two sets can be tested with %D \type {\doifallcommonelse}, where the first two %D arguments are sets. \def\syst_helpers_do_if_all_common_else#1#2#3#4% slow {\def\syst_helpers_do_common_check_all##1% {\doifnotinset{##1}{#4}\donefalse \ifdone\else\expandafter\quitcommalist\fi}% \donetrue \processcommalist[#3]\syst_helpers_do_common_check_all \ifdone\expandafter#1\else\expandafter#2\fi} \unexpanded\def\doifallcommonelse{\syst_helpers_do_if_all_common_else\firstoftwoarguments\secondoftwoarguments} \unexpanded\def\doifallcommon {\syst_helpers_do_if_all_common_else\firstofonearguments\gobbleoneargument } \unexpanded\def\doifnotallcommon {\syst_helpers_do_if_all_common_else\gobbleoneargument \firstofonearguments } %D \macros %D {DOIF,DOIFELSE,DOIFNOT} %D %D \TEX\ is case sensitive. When comparing arguments, this %D feature sometimes is less desirable, for instance when we %D compare filenames. The next three alternatives upcase their %D arguments before comparing them. %D %D \starttyping %D \DOIF {string1} {string2} {...} %D \DOIFNOT {string1} {string2} {...} %D \DOIFELSE {string1} {string2} {then ...}{else ...} %D \stoptyping %D %D We have to use a two||step implementation, because the %D expansion has to take place outside \type{\uppercase}. %D %D These might up as \LUA based helpers (i.e. considere these %D obsolete: \unexpanded\def\syst_helpers_do_IF#1#2% {\uppercase{\syst_helpers_do_if_in_string_else{$#1$}{$#2$}}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\syst_helpers_do_IF_NOT#1#2% {\uppercase{\syst_helpers_do_if_in_string_else{$#1$}{$#2$}}% \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \unexpanded\def\syst_helpers_do_IF_ELSE#1#2% {\uppercase{\syst_helpers_do_if_in_string_else{$#1$}{$#2$}}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\syst_helpers_do_IF_INSTRING_ELSE#1#2% {\uppercase{\syst_helpers_do_if_in_string_else{$#1$}{$#2$}}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\DOIF #1#2{\normalexpanded{\syst_helpers_do_IF {#1}{#2}}}% will become obsolete \unexpanded\def\DOIFNOT #1#2{\normalexpanded{\syst_helpers_do_IF_NOT {#1}{#2}}}% will become obsolete \unexpanded\def\DOIFELSE #1#2{\normalexpanded{\syst_helpers_do_IF_ELSE {#1}{#2}}}% will become obsolete \unexpanded\def\DOIFINSTRINGELSE #1#2{\normalexpanded{\syst_helpers_do_IF_INSTRING_ELSE{#1}{#2}}}% will become obsolete %D \macros %D {dosingleargumentwithset, %D dodoubleargumentwithset,dodoubleemptywithset, %D dotripleargumentwithset,dotripleemptywithset} %D %D These maybe too mysterious macros enable us to handle more %D than one setup at once. %D %D \starttyping %D \dosingleargumentwithset \command[#1] %D \dodoubleargumentwithset \command[#1][#2] %D \dotripleargumentwithset \command[#1][#2][#3] %D \dodoubleemptywithset \command[#1][#2] %D \dotripleemptywithset \command[#1][#2][#3] %D \stoptyping %D %D The first macro calls \type{\command[##1]} for each string %D in the set~\type{#1}. The second one calls for %D \type{\commando[##1][#2]} and the third, well one may guess. %D These commands support constructions like: %D %D \starttyping %D \def\dodefinesomething[#1][#2]% %D {\getparameters[\??xx#1][#2]} %D %D \unexpanded\def\definesomething% %D {\dodoubleargumentwithset\dodefinesomething} %D \stoptyping %D %D Which accepts calls like: %D %D \starttyping %D \definesomething[alfa,beta,...][variable=...,...] %D \stoptyping \let\m_syst_helpers_with_set_command\empty \let\syst_helpers_with_set_step \relax \def\syst_helpers_with_set_double[#1][#2]% {\doifsomething{#1} {\def\syst_helpers_with_set_step##1{\m_syst_helpers_with_set_command[##1][#2]}% \processcommalist[#1]\syst_helpers_with_set_step}} \def\syst_helpers_with_set_triple[#1][#2][#3]% {\doifsomething{#1} {\def\syst_helpers_with_set_step##1{\m_syst_helpers_with_set_command[##1][#2][#3]}% \processcommalist[#1]\syst_helpers_with_set_step}} \def\dodoubleemptywithset #1{\let\m_syst_helpers_with_set_command#1\dodoubleempty \syst_helpers_with_set_double} % \command \def\dodoubleargumentwithset#1{\let\m_syst_helpers_with_set_command#1\dodoubleargument\syst_helpers_with_set_double} % \command \def\dotripleemptywithset #1{\let\m_syst_helpers_with_set_command#1\dotripleempty \syst_helpers_with_set_triple} % \command \def\dotripleargumentwithset#1{\let\m_syst_helpers_with_set_command#1\dotripleargument\syst_helpers_with_set_triple} % \command %D \macros %D {stripcharacters,stripspaces} %D %D The next command was needed first when we implemented %D the \CONTEXT\ interactivity macros. When we use labeled %D destinations, we often cannot use all the characters we %D want. We therefore strip some of the troublemakers, like %D spaces, from the labels before we write them to the %D \DVI||file, which passes them to for instance a PostScript %D file. %D %D \starttyping %D \stripspaces\from\one\to\two %D \stoptyping %D %D Both the old string \type{\one} and the new one \type{\two} %D are expanded. This command is a special case of: %D %D \starttyping %D \stripcharacter\char\from\one\to\two %D \stoptyping %D %D As we can see below, spaces following a control sequence are %D to enclosed in \type{{}}. \let\m_syst_helpers_strip_character\empty \unexpanded\def\stripcharacter#1\from#2\to#3% {\def\syst_helpers_strip_character##1#1##2\end {\edef\m_syst_helpers_strip_character{\m_syst_helpers_strip_character##1}% \doifnotempty{##2}{\syst_helpers_strip_character##2\end}}% \let\m_syst_helpers_strip_character\empty \edef\m_syst_string_one{#2}% \expandafter\syst_helpers_strip_character\m_syst_string_one#1\end \dodoglobal\let#3\m_syst_helpers_strip_character} \unexpanded\def\stripspaces\from#1\to#2% will become \unspacestring#1\from#2 {\stripcharacter{ }\from#1\to#2} %D \macros %D {unspacestring} %D %D The next macro does the same but is more compatible with other macros, %D like \type {\convert...}. \unexpanded\def\unspacestring#1\to#2% {\stripcharacter{ }\from#1\to#2} %D \macros %D {executeifdefined} %D %D \CONTEXT\ uses one auxiliary file for all data concerning %D tables of contents, references, two||pass optimizations, %D sorted lists etc. This file is loaded as many times as %D needed. During such a pass we skip the commands thate are of %D no use at that moment. Because we don't want to come into %D trouble with undefined auxiliary commands, we call the %D macros in a way similar to \type{\getvalue}. The next macro %D take care of such executions and when not defined, gobbles %D the unwanted arguments. %D %D \starttyping %D \executeifdefined{name}\gobbleoneargument %D \stoptyping %D %D We can of course gobble more arguments using the %D appropriate gobbling command. \def\executeifdefined#1% #2 / never change this one again {\ifcsname#1\endcsname \csname#1\expandafter\expandafter\expandafter\endcsname\expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} %D This one also has the advantage that it is fully %D expandable and that it can be used after an assignment. %D \macros %D {doifsomespaceelse} %D %D The next command checks a string on the presence of a space %D and executed a command accordingly. %D %D \starttyping %D \doifsomespaceelse {tekst} {then ...} {else ...} %D \stoptyping %D %D We use this command in \CONTEXT\ for determing if an %D argument must be broken into words when made interactive. %D Watch the use of \type{\noexpand}. %D Is this one still needed? \def\syst_helpers_if_some_space_else#1 #2#3\_e_o_s_{\if\noexpand#2@} \def\doifsomespaceelse#1% % #2#3% {\syst_helpers_if_some_space_else#1 @ @\_e_o_s_ % #3\else#2\fi} \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} %D \macros %D {processseparatedlist} %D %D Maybe a bit late, but here is a more general version of the %D \type{\processcommalist} command. This time we don't handle %D nesting but accept arbitrary seperators. %D %D \starttyping %D \processseparatedlist[list][separator]\command %D \stoptyping %D %D One can think of things like: %D %D \starttyping %D \processseparatedlist[alfa+beta+gamma][+]\message %D \stoptyping %D %D We want to handle all situations, like: %D %D \startbuffer %D \processseparatedlist[{aap noot}] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} %D \processseparatedlist[{aap} {noot}][ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} %D \processseparatedlist[aap {noot}] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} %D \processseparatedlist[aap noot] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} %D \stopbuffer %D %D \typebuffer \getbuffer %D %D Therefore we smuggle a \type {\relax} in front of the %D argument, which we remove afterwards. \let\syst_helpers_process_separated_list_step\relax \def\syst_helpers_process_separated_list#1]#2[#3]#4% {\def\syst_helpers_process_separated_list_step##1##2#3% {\def\m_syst_string_one{##2}% suggested by VZ \if]##1% \let\syst_helpers_process_separated_list_step\relax \else\ifx\blankspace\m_syst_string_one #4{##1}% \else\if]##2% \let\syst_helpers_process_separated_list_step\relax \else #4{##1##2}% \fi\fi\fi \syst_helpers_process_separated_list_step}% \expandafter\syst_helpers_process_separated_list_step\gobbleoneargument#1#3]#3} \unexpanded\def\processseparatedlist[% {\syst_helpers_process_separated_list\relax} %D \macros %D {processlist} %D %D An even more general list processing macro is the following one: %D %D \starttyping %D \processlist{beginsym}{endsym}{separator}\docommand list %D \stoptyping %D %D This one supports arbitrary open and close symbols as well as user %D defined separators. %D %D \starttyping %D \processlist(){=>}\docommand(a=>b=>c=>d) %D \stoptyping \let\syst_helpers_process_any_list \relax \let\syst_helpers_process_any_list_indeed\relax \let\syst_helpers_process_any_list_step \relax \unexpanded\def\processlist#1#2#3#4% no blank skipping ! {\def\syst_helpers_process_any_list_indeed##1#2% {\def\syst_helpers_process_any_list_step####1####2#3% {\ifx#2####1% \let\syst_helpers_process_any_list_step\relax \else\ifx#2####2% \let\syst_helpers_process_any_list_step\relax \else #4{####1####2}% \fi\fi \syst_helpers_process_any_list_step}% \expandafter\syst_helpers_process_any_list_step\gobbleoneargument##1#3#2#3}% \def\syst_helpers_process_any_list#1% {\syst_helpers_process_any_list_indeed\relax}% \syst_helpers_process_any_list} %D \macros %D {processassignlist} %D %D Is possible to combine an assignment list with one %D containing keywords. Assignments are treated accordingly, %D keywords are treated by \type{\command}. %D %D \starttyping %D \processassignlist[...=...,...=...,...]\commando %D \stoptyping %D %D This command can be integrated in \type{\getparameters}, but %D we decided best not to do so. \unexpanded\def\processassignlist#1[#2]#3% {\def\syst_helpers_process_assign_list_assign[##1=##2=##3]% {\doifnot{##3}\relax{#3{##1}}}% \def\syst_helpers_process_assign_list_step##1% {\syst_helpers_process_assign_list_assign[##1==\relax]}% \processcommalist[#2]\syst_helpers_process_assign_list_step} %D \macros %D {untextargument %D untexcommand} %D %D When manipulating data(bases) and for instance generating %D index entries, the next three macros can be of help: %D %D \starttyping %D \untextargument{...}\to\name %D \untexcommand {...}\to\name %D \stoptyping %D %D They remove braces and backslashes and give us something to %D sort. \let\m_syst_helpers_untexed\empty \unexpanded\def\untexsomething {\begingroup \catcode\leftbraceasciicode \ignorecatcode \catcode\rightbraceasciicode\ignorecatcode \escapechar\minusone \syst_helpers_untex_something} \def\syst_helpers_untex_something#1#2\to#3% {\doglobal#1#2\to\m_syst_helpers_untexed \endgroup \let#3\m_syst_helpers_untexed} \unexpanded\def\untexargument{\untexsomething\convertargument} \unexpanded\def\untexcommand {\untexsomething\convertcommand} %D \macros %D {ScaledPointsToBigPoints,ScaledPointsToWholeBigPoints} %D %D One characteristic of \POSTSCRIPT\ and \PDF\ is that both %D used big points (\TEX's bp). The next macros convert points %D and scaled points into big points. %D %D \starttyping %D \ScaledPointsToBigPoints {number} \target %D \ScaledPointsToWholeBigPoints {number} \target %D \stoptyping %D %D The magic factor $72/72.27$ can be found in most \TEX\ %D related books. % \PointsToBigPoints{10.53940pt}\test \test % \PointsToBigPoints{10.53941pt}\test \test % \PointsToBigPoints{10.53942pt}\test \test % \PointsToWholeBigPoints{10.53940pt}\test \test % \PointsToWholeBigPoints{10.53941pt}\test \test % \PointsToWholeBigPoints{10.53942pt}\test \test \unexpanded\def\PointsToBigPoints#1#2% {\edef#2{\withoutpt\the\dimexpr.996264\dimexpr#1\relax\relax}} \unexpanded\def\PointsToWholeBigPoints#1#2% {\edef#2{\the\numexpr\dimexpr.996264\dimexpr#1\relax\relax/\maxcard\relax}} \unexpanded\def\ScaledPointsToBigPoints #1{\PointsToBigPoints {\number#1\scaledpoint}} \unexpanded\def\ScaledPointsToWholeBigPoints#1{\PointsToWholeBigPoints{\number#1\scaledpoint}} %D \macros %D {PointsToReal} %D %D Points can be stripped from their suffix by using %D \type{\withoutpt}. The next macro enveloppes this macro. %D %D \starttyping %D \PointsToReal {dimension} \target %D \stoptyping \unexpanded\def\PointsToReal#1#2% {\edef#2{\withoutpt\the\dimexpr#1}} %D \macros %D {dontleavehmode} %D %D Sometimes when we enter a paragraph with some command, the %D first token gets the whole first line. We can prevent this %D by saying: %D %D \starttyping %D \dontleavehmode %D \stoptyping %D %D This command is used in for instance the language module %D \type{lang-ini}. The first version was: %D %D \starttyping %D \def\dontleavehmode{\ifhmode\else\ifmmode\else$ $\fi\fi} %D \stoptyping %D %D Next, Taco came with a better alternative (using mathsurround): %D %D \starttyping %D \def\dontleavehmode %D {\ifhmode\else \ifmmode\else %D {\mathsurround\zeropoint\everymath\emptytoks$ $}% %D \fi \fi} %D \stoptyping %D %D And finaly we got the following alternative, one that avoids %D interfering grouping at the cost of a box. %D %D \starttyping %D \newbox\b_syst_helpers_dlh %D %D \unexpanded\def\dontleavehmode %D {\ifhmode\else \ifmmode\else %D \setbox\b_syst_helpers_dlh\hbox{\mathsurround\zeropoint\everymath\emptytoks$ $}\unhbox\b_syst_helpers_dlh %D \fi \fi} %D \stoptyping %D %D But, as we run a recent version of \TEX, we can use the new primitive: \ifdefined\normalquitvmode \let\dontleavehmode\normalquitvmode \fi %D \macros %D {uppercasestring,lowercasestring} %D %D The names tell what they do: %D %D \starttyping %D \uppercasestring somestring\to\somestring %D \lowercasestring somestring\to\somestring %D \stoptyping %D %D The first argument may be a \type{\macro}. \unexpanded\def\uppercasestring#1\to#2% {\uppercase\expandafter{\expandafter\dodoglobal\expandafter\edef\expandafter#2\expandafter{\normalexpanded{#1}}}} \unexpanded\def\lowercasestring#1\to#2% {\lowercase\expandafter{\expandafter\dodoglobal\expandafter\edef\expandafter#2\expandafter{\normalexpanded{#1}}}} %D These macros are sort of obsolete as we never use uppercase this %D way. %D \macros %D {handletokens} %D %D With the next macro we enter a critical area of macro %D expansion. What we want is a macro that looks like: %D %D \starttyping %D \handletokens some tokens\with \somemacro %D \stoptyping %D %D A bonus example: %D %D \starttyping %D \hbox{\handletokens tekst en meer tekst\with\ruledhbox} %D %D \def\weetikveel#1{\if#1\blankspace\space\else\ruledhbox{#1}\fi} %D %D \hbox{\handletokens tekst en meer tekst\with\weetikveel} %D \stoptyping %D \macros %D {counttoken,counttokens} %D %D For the few occasions that we want to know the number of %D specific tokens in a string, we can use: %D %D \starttyping %D \counttoken token\in string\to \somecount %D \counttokens string\to \somecount %D \stoptyping %D %D This macro, that for instance is used in \type{cont-tab}, %D takes a real counter. The macro can be preceded by \type %D {\doglobal}. \def\syst_helpers_count_token#1% obeys {} {\def\m_syst_string_three{#1}% \ifx\m_syst_string_two\m_syst_string_three \else \ifx\m_syst_string_one\m_syst_string_three \advance\scratchcounter\plusone \fi \expandafter\syst_helpers_count_token \fi} \unexpanded\def\counttoken#1\in#2\to#3% {\scratchcounter\zerocount \def\m_syst_string_one{#1}% \def\m_syst_string_two{\end}% \syst_helpers_count_token#2\end \dodoglobal#3\scratchcounter} \unexpanded\def\counttokens#1\to#2% {\scratchcounter\zerocount \def\syst_helpers_count_token##1{\advance\scratchcounter\plusone}% \handletokens#1\with\syst_helpers_count_token \dodoglobal#2\scratchcounter} %D \macros %D {splitofftokens} %D %D Running this one not always gives the expected results. %D Consider for instance the macro for which I originally %D wrote this token handler. \unexpanded\def\splitofftokens#1\from#2\to#3% slow but hardly used {\ifnum#1>\zerocount \scratchcounter#1\relax \def\syst_helpers_split_off_tokens##1% {\ifnum\scratchcounter>\zerocount \advance\scratchcounter \minusone \edef#3{#3##1}% \fi}% % \let#3\empty % #3 can be #2, so: \expandafter\let\expandafter#3\expandafter\empty \expandafter\handletokens#2\with\syst_helpers_split_off_tokens \else \edef#3{#2}% \fi} %D This macro can be called like: %D %D \startbuffer[example] %D \splitofftokens10\from01234567 890123456789\to\test [\test] %D \stopbuffer %D %D However, the characters that we expect to find in %D \type{\test} just don't show up there. The reason for this %D is not that logical but follows from \TEX's sometimes %D mysterious way of expanding. Look at this: %D %D \startbuffer[next] %D \def\next{a} \edef\test{\next} [\test] %D \let\next=b \edef\test{\test\next} [\test] %D \let\next=c \edef\test{\next} [\test] %D \let\next=d \edef\test{\test\next} [\test] %D \let\next=e \expandafter\edef\expandafter\test\expandafter{\test\next} [\test] %D \stopbuffer %D %D \typebuffer[next] %D %D Careful reading shows that inside an \type{\edef} macro's %D that are \type{\let} are not expanded! %D %D \unprotect\getbuffer[next]\protect %D %D That's why we finally end up with a macro that looks %D ahead by using an assignment, this time by using \type %D {\futurelet}, and grabbing an argument as well. That %D way we can handle the sentinal, a blank space and grouped %D tokens. \unexpanded\def\syst_helpers_handle_tokens % \nexthandledtoken is part of interface {\futurelet\nexthandledtoken\syst_helpers_handle_tokens_indeed} \def\handletokens#1\with#2% {\gdef\syst_helpers_handle_tokens_command{#2}% permits more complex #2's \syst_helpers_handle_tokens#1\end} \def\syst_helpers_handle_tokens_indeed {\ifx\nexthandledtoken\blankspace \expandafter\syst_helpers_handle_tokens_indeed_one \else\ifx\nexthandledtoken\end \expandafter\expandafter\expandafter\gobbletwoarguments % also gobble the \end \else \expandafter\expandafter\expandafter\syst_helpers_handle_tokens_indeed_two \fi\fi *} \def\syst_helpers_handle_tokens_indeed_one * % {\syst_helpers_handle_tokens_command{ }\syst_helpers_handle_tokens} \def\syst_helpers_handle_tokens_indeed_two *#1% {\syst_helpers_handle_tokens_command{#1}\syst_helpers_handle_tokens} %D This macro is tested on: %D %D \def\xxx#1{[#1]} %D %D \startlines %D \handletokens abc\with\xxx %D \handletokens a b c\with\xxx %D \handletokens a b c\with\xxx %D \handletokens a{bc}d\with\xxx %D \handletokens a\space bc \with\xxx %D \stoplines %D %D And our previous example shows up as: %D %D \getbuffer[example] %D \macros %D {iftrialtypesetting, ifvisible} %D %D The next boolean is at first sight a strange one. Sometimes %D one does a trial typesetting run, for instance to determine %D dimensions. Some mechanisms, like object inclusion, can fail %D on such trials. Temporary setting the next boolean to true, %D helps a lot. The second boolena can be used to inhibit %D processing completely. \newif\ifvisible \visibletrue \newtoks\everysettrialtypesetting \newtoks\everyresettrialtypesetting \unexpanded\def\settrialtypesetting {\the\everysettrialtypesetting } % obeys grouping so \unexpanded\def\resettrialtypesetting{\the\everyresettrialtypesetting} % this one is seldom needed \let\iftrialtypesetting\iffalse % so we have no \trialtypesettingtrue|false in mkiv ! \appendtoks \let\iftrialtypesetting\iftrue \to \everysettrialtypesetting \appendtoks \let\iftrialtypesetting\iffalse \to \everyresettrialtypesetting %D \macros %D {twodigitrounding} %D %D When using \type {\special}s or \type {\pdfliteral}s, it %D sometimes makes sense to limit the precission. The next %D macro rounds a real number to two digits. It takes one %D argument and only works in \ETEX. % \def\dointegerrounding #1.#2\relax {#1} % \def\doonedigitrounding #1.#2#3\relax {\ifx#2*#1\else#1.#2\fi} % \def\dotwodigitrounding #1.#2#3#4\relax {\ifx#2*#1\else#1.#2#3\fi} % \def\dothreedigitrounding#1.#2#3#4#5\relax{\ifx#2*#1\else#1.#2#3#4\fi} % % \def\integerrounding#1% % {\expandafter\expandafter\expandafter\dointegerrounding \expandafter\WITHOUTPT\the\dimexpr#1\points+.5\points \relax .\relax} % \def\onedigitrounding#1% % {\expandafter\expandafter\expandafter\doonedigitrounding \expandafter\WITHOUTPT\the\dimexpr#1\points+.05\points \relax 00.*0\relax} % \def\twodigitrounding#1% % {\expandafter\expandafter\expandafter\dotwodigitrounding \expandafter\WITHOUTPT\the\dimexpr#1\points+.005\points \relax 000.*00\relax} % \def\threedigitrounding#1% % {\expandafter\expandafter\expandafter\dothreedigitrounding\expandafter\WITHOUTPT\the\dimexpr#1\points+.0005\points\relax0000.*00\relax} \def\integerrounding #1{\cldcontext{"\letterpercent 0.0f",#1}} \def\onedigitrounding #1{\cldcontext{"\letterpercent 0.1f",#1}} \def\twodigitrounding #1{\cldcontext{"\letterpercent 0.2f",#1}} \def\threedigitrounding#1{\cldcontext{"\letterpercent 0.3f",#1}} %D \macros %D {processcontent} %D %D This is the first occasion where \TEX\ and \ETEX\ are no %D longer compatible, although in many cases things go ok. %D Beware of verbatim, i.e. catcode changes. %D %D \starttyping %D \unexpanded\def\starthans% %D {\processcontent{stophans}\test{\message{\test}\wait}} %D \stoptyping %D %D This macro is first used in the tabulation macros. \unexpanded\def\processcontent#1% {\begingroup\expandafter\syst_helpers_process_content\csname#1\endcsname} \unexpanded\def\syst_helpers_process_content#1#2#3% {\unexpanded\def\syst_helpers_process_content##1#1% {\endgroup\def#2{##1}#3}% \syst_helpers_process_content} %D \macros %D {dogobblesingleempty, dogobbledoubleempty} %D %D These two macros savely grab and dispose two arguments. \def\dogobblesingleempty{\dosingleempty\syst_helpers_gobble_single_empty} \def\dogobbledoubleempty{\dodoubleempty\syst_helpers_gobble_double_empty} \def\syst_helpers_gobble_single_empty [#1]{} \def\syst_helpers_gobble_double_empty[#1][#2]{} \let\gobblesingleempty\dogobblesingleempty % also used \let\gobbledoubleempty\dogobbledoubleempty % also used %D \macros %D {@True, @False, @Not, @And} %D %D Some predicate logic functions, used in for instance the %D math module. % These have rather ugly names ... will change: \def\@True {00} \def\@False {01} \def\@Not #1{0\ifcase#11 \or\expandafter 1\else \expandafter 0\fi} \def\@And #1#2{0\ifcase#1#2 \expandafter 0\else \expandafter 1\fi} %D \macros %D {setdimensionwithunit, freezedimensionwithunit} %D %D The next assignments are all valid: %D %D \starttyping %D \setdimensionwithunit\scratchdimen{10} {cm} %D \setdimensionwithunit\scratchdimen{10cm}{cm} %D \setdimensionwithunit\scratchdimen{10cm}{} %D \freezedimensionwithunit\SomeWidth{\textwidth} %D \freezedimensionwithunit\SomeDepth{\dp\strutbox} %D \stoptyping %D %D As an alternative for the next macro we can use a global %D assignment inside a box. The \type{\empty}'s permits %D gobbling while preventing spurious \type{\relax}'s. \unexpanded\def\setdimensionwithunit#1#2#3% number unit dimension / nice trick {\afterassignment\gobblefourarguments#1=#2#3pt\relax\empty\empty\empty\empty} \unexpanded\def\freezedimensionwithunit#1#2% {\setdimensionwithunit\scratchdimen#1{#2}\edef#1{\the\scratchdimen}} %D \macros %D {doifsometokselse, doifsometoks} %D %D Not that fast I guess, but here's a way to test for token %D registers being empty. \unexpanded\def\doifsometokselse#1% {\edef\m_syst_string_one{\the#1}% one level expansion so quite ok \ifx\m_syst_string_one\empty \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} \unexpanded\def\doifsometoks#1% {\edef\m_syst_string_one{\the#1}% one level expansion so quite ok \ifx\m_syst_string_one\empty \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \unexpanded\def\doifemptytoks#1% {\edef\m_syst_string_one{\the#1}% one level expansion so quite ok \ifx\m_syst_string_one\empty \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} %D \macros %D {startstrictinspectnextcharacter} %D %D This one if for Taco's bibliography module: \let\syst_helpers_normal_inspect_next_character\syst_helpers_inspect_next_character \def\syst_helpers_strict_inspect_next_character% no user macro ! {\ifx\nexttoken\charactertoken \expandafter\m_syst_action_yes \else \expandafter\m_syst_action_nop \fi} % better: push/pop \unexpanded\def\startstrictinspectnextcharacter {\let\syst_helpers_inspect_next_character\syst_helpers_strict_inspect_next_character} \unexpanded\def\stopstrictinspectnextcharacter {\let\syst_helpers_inspect_next_character\syst_helpers_normal_inspect_next_character} \def\strictdoifnextoptionalelse#1#2% {\startstrictinspectnextcharacter \doifnextcharelse[{\stopstrictinspectnextcharacter#1}{\stopstrictinspectnextcharacter#2}} %D \macros %D {gobblespacetokens} %D %D This macro needs a speed-up! %\def\gobblespacetokens % {\doifnextcharelse\empty\donothing\donothing} % no {}\do\do ! \def\gobblespacetokens {\afterassignment\nexttoken\let\nexttoken=} %D \macros %D {verbatimargument} %D %D As the name says, this macro converts its argument to a %D (rather safe) string. \let\verbatimstring\detokenize %D These are needed in ordinal number conversions: \def\lastdigit#1% {\expandafter\thelastdigit\number#1\relax} \def\thelastdigit#1#2% {\ifx#2\relax#1\else\expandafter\thelastdigit\expandafter#2\fi} \def\lasttwodigits#1% {\expandafter\thelasttwodigits\expandafter0\number#1\relax} \def\thelasttwodigits#1#2#3% 0 dig ... \relax {\ifx#3\relax#1#2\else\expandafter\thelasttwodigits\expandafter#2\expandafter#3\fi} %D \macros %D {serializecommalist} %D %D Concatenate commalists: \let\syst_helpers_serialize_comma_list_step\relax \def\syst_helpers_serialize_comma_list_step#1% {\edef\serializedcommalist{\serializedcommalist#1}} \unexpanded\def\serializecommalist[#1]% {\let\serializedcommalist\empty \processcommacommand[#1]\syst_helpers_serialize_comma_list_step} %D \macros %D {purenumber} %D %D Sometimes we need control over when \TEX\ stops reading a %D number, especially in full expandable macros where using %D \type {\relax} would lead to disasters. %D %D \starttyping %D \ifodd\purenumber{...}\space ... \else ... \fi %D \stoptyping %D %D Here we use a space as number delimiter in combination %D with a space- and relax-less \type {\purenumber}. This %D macro works ok with \type {\the}, \type {\number} as well %D as \ETEX's \type {\numexpr}. \def\purenumber#1{\expandafter\firstofoneargument\expandafter{\number#1}} %D \macros %D {filterfromvalue} %D %D \starttyping %D \setvalue{xx}{{A}{B}{C}} %D %D \filterfromvalue{xx}{3}{3} %D \filterfromvalue{xx}{3}{2} %D \filterfromvalue{xx}{3}{1} %D \stoptyping %D %D An alternative is to store 'max' in the list, say: %D %D \starttyping %D \setvalue{xx}{3{A}{B}{C}} %D %D \filterfromvalues{3}{xx}{3} %D \filterfromvalues{3}{xx}{2} %D \filterfromvalues{3}{xx}{1} %D \stoptyping %D %D I'll implement this when I'm in \quotation {writing dirty %D macros mood}. \def\dofilterfromstr#1#2% max n % no need to be fast {\expandafter \expandafter \expandafter \strippedcsname \ifcase#1\or \ifcase#2\or \firstofoneargument \else \gobbleoneargument \fi \or \ifcase#2\or \firstoftwoarguments \or \secondoftwoarguments \else \gobbletwoarguments \fi \or \ifcase#2\or \firstofthreearguments \or \secondofthreearguments \or \thirdofthreearguments \else \gobblethreearguments \fi \or \ifcase#2\or \firstoffourarguments \or \secondoffourarguments \or \thirdoffourarguments \or \fourthoffourarguments \else \gobblefourarguments \fi \or \ifcase#2\or \firstoffivearguments \or \secondoffivearguments \or \thirdoffivearguments \or \fourthoffivearguments \or \fifthoffivearguments \else \gobblefivearguments \fi \fi} \def\filterfromvalue#1#2#3% value max n {\expandafter\doubleexpandafter\csname % we use the fact that an \expandafter\ifx\csname#1\endcsname\relax % undefined cs has become \relax \strippedcsname\gobbleoneargument % which we then gobble here \else \dofilterfromstr{#2}{#3}% \fi \endcsname\csname#1\endcsname} \def\filterfromnext#1#2% max n {..}{..}{..}{..} {\csname\dofilterfromstr{#1}{#2}\endcsname} %D \macros %D {definemeasure} %D %D \starttyping %D \definemeasure[mywidth][\dimexpr(\textwidth-1cm)] %D %D ... \measure{mywidth} ... %D \stoptyping \installsystemnamespace{measure} \unexpanded\def\definemeasure {\dodoubleargument\syst_helpers_define_measure} \def\syst_helpers_define_measure[#1][#2]% {\expandafter\def\csname\??measure#1\endcsname{#2}} \unexpanded\def\freezemeasure {\dodoubleargument\syst_helpers_freeze_measure} \def\syst_helpers_freede_measure[#1][#2]% {\expandafter\edef\csname\??measure#1\endcsname{\the\dimexpr#2}} \unexpanded\def\setmeasure #1#2{\expandafter\def \csname\??measure#1\endcsname{#2}} % quick way \unexpanded\def\setgmeasure#1#2{\expandafter\gdef\csname\??measure#1\endcsname{#2}} % quick way \unexpanded\def\setemeasure#1#2{\expandafter\edef\csname\??measure#1\endcsname{\the\dimexpr#2}} % quick way \unexpanded\def\setxmeasure#1#2{\expandafter\xdef\csname\??measure#1\endcsname{\the\dimexpr#2}} % quick way \def\measure#1% maybe \dimexpr ... \relax {\ifcsname\??measure#1\endcsname\csname\??measure#1\endcsname\else\zeropoint\fi} % #2 could be omitted, but we want to support spaces % % \setmeasure {x} {1cm} % \setmeasure {xx} {1cm} % \setmeasure {xxx}{1cm} %D \macros %D {dividedsize} %D %D This one can be used inside a measure (used in m4all): %D %D \starttyping %D \definemeasure[columnwidth][\dividedsize\textwidth{1em}{3}] %D \stoptyping \def\dividedsize#1#2#3% size gap n {\dimexpr \ifnum\dimexpr#1\relax>\plusone (\dimexpr#1\relax-\numexpr#3-\plusone\relax\dimexpr#2\relax)/#3\else#1% \fi \relax} %D \macros %D {doifdimensionelse} %D %D This is a dirty one: we simply append a unit and discard it when needed. \def\doifdimensionelse#1% {\afterassignment\syst_helpers_if_dimension_else\scratchdimen#1pt\relax} \def\syst_helpers_if_dimension_else#1% {\ifx#1\relax \expandafter\secondoftwoarguments \else % #1=p ... t\relax \expandafter\thirdoffourarguments \fi} %D Ok, here's another one, slower but seldom used. This one scans the text. %D %D \starttabulate[|Tc|Tc|] %D \NC pt \NC \doifdimenstringelse {pt}{yes}{no} \NC \NR %D \NC 12pt \NC \doifdimenstringelse {-12pt}{yes}{no} \NC \NR %D \NC 1pt \NC \doifdimenstringelse {1pt}{yes}{no} \NC \NR %D \NC 12pt \NC \doifdimenstringelse {12pt}{yes}{no} \NC \NR %D \NC 12.0pt \NC \doifdimenstringelse {12.0pt}{yes}{no} \NC \NR %D \NC -.12pt \NC \doifdimenstringelse {-.12pt}{yes}{no} \NC \NR %D \NC .12pt \NC \doifdimenstringelse {.12pt}{yes}{no} \NC \NR %D \NC -12pt \NC \doifdimenstringelse {-12pt}{yes}{no} \NC \NR %D \NC -12.0pt \NC \doifdimenstringelse{-12.0pt}{yes}{no} \NC \NR %D \NC big \NC \doifdimenstringelse {big}{yes}{no} \NC \NR %D \NC 10 \NC \doifdimenstringelse {10}{yes}{no} \NC \NR %D \NC 1 \NC \doifdimenstringelse {1}{yes}{no} \NC \NR %D \stoptabulate \installsystemnamespace{dimenchecka} \installsystemnamespace{dimencheckb} \installsystemnamespace{dimencheckc} \def\doifdimenstringelse#1{\normalexpanded{\noexpand\dodimenteststageone#1}\empty\empty]} \def\dodimenteststageone #1#2{\csname \??dimenchecka\ifcsname \??dimenchecka#2\endcsname#2\else x\fi\endcsname#2} \def\dodimenteststagetwo #1#2{\csname \??dimencheckb\ifcsname \??dimencheckb#2\endcsname#2\else x\fi\endcsname#2} \def\dodimenteststagethree #1]{\csname \??dimencheckc\ifcsname \??dimencheckc#1\endcsname#1\else x\fi\endcsname} \expandafter\let\csname \??dimenchecka x\endcsname\dodimenteststagethree \expandafter\let\csname \??dimencheckb x\endcsname\dodimenteststagethree \expandafter\let\csname \??dimencheckc x\endcsname\secondoftwoarguments \expandafter\let\csname \??dimenchecka.\endcsname\dodimenteststagetwo \expandafter\let\csname \??dimenchecka,\endcsname\dodimenteststagetwo \expandafter\let\csname \??dimenchecka1\endcsname\dodimenteststageone \expandafter\let\csname \??dimenchecka2\endcsname\dodimenteststageone \expandafter\let\csname \??dimenchecka3\endcsname\dodimenteststageone \expandafter\let\csname \??dimenchecka4\endcsname\dodimenteststageone \expandafter\let\csname \??dimenchecka5\endcsname\dodimenteststageone \expandafter\let\csname \??dimenchecka6\endcsname\dodimenteststageone \expandafter\let\csname \??dimenchecka7\endcsname\dodimenteststageone \expandafter\let\csname \??dimenchecka8\endcsname\dodimenteststageone \expandafter\let\csname \??dimenchecka9\endcsname\dodimenteststageone \expandafter\let\csname \??dimenchecka0\endcsname\dodimenteststageone \expandafter\let\csname \??dimencheckb1\endcsname\dodimenteststagetwo \expandafter\let\csname \??dimencheckb2\endcsname\dodimenteststagetwo \expandafter\let\csname \??dimencheckb3\endcsname\dodimenteststagetwo \expandafter\let\csname \??dimencheckb4\endcsname\dodimenteststagetwo \expandafter\let\csname \??dimencheckb5\endcsname\dodimenteststagetwo \expandafter\let\csname \??dimencheckb6\endcsname\dodimenteststagetwo \expandafter\let\csname \??dimencheckb7\endcsname\dodimenteststagetwo \expandafter\let\csname \??dimencheckb8\endcsname\dodimenteststagetwo \expandafter\let\csname \??dimencheckb9\endcsname\dodimenteststagetwo \expandafter\let\csname \??dimencheckb0\endcsname\dodimenteststagetwo \expandafter\let\csname \??dimencheckc pt\endcsname\firstoftwoarguments \expandafter\let\csname \??dimencheckc pc\endcsname\firstoftwoarguments \expandafter\let\csname \??dimencheckc in\endcsname\firstoftwoarguments \expandafter\let\csname \??dimencheckc bp\endcsname\firstoftwoarguments \expandafter\let\csname \??dimencheckc cm\endcsname\firstoftwoarguments \expandafter\let\csname \??dimencheckc mm\endcsname\firstoftwoarguments \expandafter\let\csname \??dimencheckc dd\endcsname\firstoftwoarguments \expandafter\let\csname \??dimencheckc cc\endcsname\firstoftwoarguments \expandafter\let\csname \??dimencheckc sp\endcsname\firstoftwoarguments \expandafter\let\csname \??dimencheckc ex\endcsname\firstoftwoarguments \expandafter\let\csname \??dimencheckc em\endcsname\firstoftwoarguments \expandafter\let\csname \??dimencheckc nd\endcsname\firstoftwoarguments \expandafter\let\csname \??dimencheckc nc\endcsname\firstoftwoarguments %D \macros %D {comparedimension,comparedimensioneps} %D %D This is a dirty one: we simply append a unit and discard it when needed. \newdimen \roundingeps \roundingeps=10sp \newconstant\compresult \def\comparedimension#1#2% {\compresult \ifdim#1<#2% \zerocount \else\ifdim#1<#2% \plusone \else \plustwo \fi\fi} \def\comparedimensioneps#1#2% todo: use eps feature {\compresult \ifdim\dimexpr#1-#2\relax<\roudingeps \zerocount \else\ifdim\dimexpr#2-#1\relax<\roudingeps \zerocount \else\ifdim#1<#2% \plusone \else \plustwo \fi\fi\fi} % % % % % % % % % % % % % % % % % % % % % % % pretty ugly but fast % \copycsname xxx\endcsname\csname ..\endcsname \unexpanded\def\copycsname{\expandafter\expandafter\expandafter\let\expandafter\expandafter\csname} % \letcscsname \crap \csname ..\endcsname % \letcsnamecs \csname ..\endcsname\crap % \letcsnamecsname\csname ..\endcsname\csname ..\endcsname \unexpanded\def\letcscsname {\expandafter\let\expandafter} \unexpanded\def\letcsnamecs {\expandafter\let} \unexpanded\def\letcsnamecsname{\expandafter\expandafter\expandafter\let\expandafter\expandafter} % another one, add an item to a commalist \unexpanded\def\addvalue#1#2% cs item {\ifcsname#1\endcsname\else\expandafter\let\csname#1\endcsname\empty\fi \normalexpanded{\noexpand\addtocommalist{#2}\expandafter\noexpand\csname#1\endcsname}} \def\unspaced#1% {\syst_helpers_unspaced#1\end} \def\syst_helpers_unspaced#1% {\ifx#1\end \expandafter\gobbleoneargument \else \ifx#1\blankspace\else#1\fi \fi \syst_helpers_unspaced} \unexpanded\def\unspaceargument#1\to#2% {\scratchcounter\catcode\spaceasciicode \catcode\spaceasciicode\ignorecatcode \scantextokens{\edef#2{#1}}% \catcode\spaceasciicode\scratchcounter} \unexpanded\def\unspaceafter#1#2% {\unspaceargument#2\to\ascii \expandafter#1\expandafter{\ascii}} % sometimes handy: \unexpanded\def\doifhasspaceelse#1% {\edef\m_syst_string_one{#1}% \normalexpanded{\syst_helpers_if_has_space_else#1\space}\empty\relax} \unexpanded\def\syst_helpers_if_has_space_else#1 #2#3\relax % \space\empty\relax {\ifx\m_syst_string_one\space \expandafter\firstoftwoarguments \else\ifx#2\empty \doubleexpandafter\secondoftwoarguments \else \doubleexpandafter\firstoftwoarguments \fi\fi} % this will replace loadfile once and alike !!! todo \installsystemnamespace{flag} \unexpanded\def\setflag #1{\expandafter\dodoglobal\expandafter\let\csname\??flag#1\endcsname\zerocount} \unexpanded\def\resetflag#1{\expandafter\dodoglobal\expandafter\let\csname\??flag#1\endcsname\plusone} \def\flag#1{\csname\??flag#1\endcsname} \def\doifelseflagged#1% {\expandafter\ifx\csname\??flag#1\endcsname\relax \expandafter\secondoftwoarguments \else\ifcase\csname\??flag#1\endcsname \doubleexpandafter\firstoftwoarguments \else \doubleexpandafter\secondoftwoarguments \fi\fi} \def\doifnotflagged#1% {\expandafter\ifx\csname\??flag#1\endcsname\relax \expandafter\firstofoneargument \else\ifcase\csname\??flag#1\endcsname \doubleexpandafter\gobbleoneargument \else \doubleexpandafter\firstofoneargument \fi\fi} \unexpanded\def\inheritparameter[#1]#2[#3]#4[#5]% tag tokey fromkey {\expandafter\def\csname#1#3\expandafter\endcsname\expandafter{\csname#1#5\endcsname}} \def\syst_helpers_if_non_zero_positive_else#1#2\end % #3#4% {\ifx#1\relax \ifcase\scratchcounter \endgroup \doubleexpandafter\secondoftwoarguments \else \endgroup \doubleexpandafter\firstoftwoarguments \fi \else \endgroup \expandafter\secondoftwoarguments \fi} \def\doifnonzeropositiveelse#1% {\begingroup\afterassignment\syst_helpers_if_non_zero_positive_else\scratchcounter=0#1\relax\empty\end} % here ? \unexpanded\def\dosetrawvalue #1#2#3{\expandafter \def\csname#1#2\endcsname{#3}} \unexpanded\def\dosetrawevalue#1#2#3{\expandafter\edef\csname#1#2\endcsname{#3}} \unexpanded\def\dosetrawgvalue#1#2#3{\expandafter\gdef\csname#1#2\endcsname{#3}} \unexpanded\def\dosetrawxvalue#1#2#3{\expandafter\xdef\csname#1#2\endcsname{#3}} \unexpanded\def\getrawparameters {\dogetparameters\dosetrawvalue } \unexpanded\def\getraweparameters {\dogetparameters\dosetrawevalue} \unexpanded\def\getrawgparameters {\dogetparameters\dosetrawgvalue} \unexpanded\def\getrawxparameters {\dogetparameters\dosetrawxvalue} \unexpanded\def\globalgetrawparameters{\dogetparameters\dosetrawgvalue} % obsolete %D Sort of obsolete: \newcount\c_syst_helpers_mod \unexpanded\def\dosetmodulo#1#2#3% {\c_syst_helpers_mod#1\divide\c_syst_helpers_mod#2\multiply\c_syst_helpers_mod#2% #3#1\advance#3-\c_syst_helpers_mod} \unexpanded\def\dosetdivision#1#2#3% {#3#1\divide#3 #2\relax} \unexpanded\def\DoMod#1by#2to#3{\dosetmodulo {#1}{#2}{#3}} \unexpanded\def\DoDiv#1by#2to#3{\dosetdivision{#1}{#2}{#3}} \def\syst_helpers_unprotected#1\par {#1\protect} \unexpanded\def\unprotected {\unprotect \syst_helpers_unprotected} % awaiting the definitive implementation % \ifdefined\resettimer \else % \let\resettimer \relax % \newcount\elapsedtime % \fi % \def\elapsedseconds{\expandafter\withoutpt\the\dimexpr\elapsedtime sp\relax} \def\resettimer {\ctxcommand{resettimer()}} \def\elapsedtime {\ctxcommand{elapsedtime()}} \let\elapsedseconds \elapsedtime \newcount\c_syst_helpers_test_feature_n \unexpanded\def\testfeature#1#2% {\def\syst_helpers_test_feature_step {\advance\c_syst_helpers_test_feature_n\plusone \ifnum\c_syst_helpers_test_feature_n>#1\else#2\expandafter\syst_helpers_test_feature_step\fi}% \retestfeature} \unexpanded\def\retestfeature % timer support is new per 10/5/2005 {\bgroup \ifcase\interactionmode\let\wait\relax\fi \writestatus\m!system{starting feature test}\wait \resettimer \c_syst_helpers_test_feature_n\zerocount \syst_helpers_test_feature_step \writestatus\m!system{feature test done (\elapsedseconds s)}% \wait \egroup} \unexpanded\def\showtimer#1% {\writestatus{runtime}{\elapsedseconds\space s / #1}} \unexpanded\def\testfeatureonce#1#2% {\begingroup \let\wait\relax \testfeature{#1}{#2}% \endgroup} %D \macros %D {freezedimenmacro} %D %D This macro is use as: %D %D \starttyping %D \freezedimenmacro\leftmargindistance %D \stoptyping \unexpanded\def\freezedimenmacro#1% {\edef#1{\the\dimexpr#1}} %D The next macro negates a macro (dimension or number, or actually, whatever. %D It's a typical example of \type {\if} usage: %D %D \starttyping %D \if-\whatever \else-\whatever\fi => else => -whatever %D \if--\whatever\else-\whatever\fi => then => whatever %D \stoptyping \def\negated#1{\if-#1\else-#1\fi} % does only work in macros or text \def\gobbleassigndimen#1\\{} \def\assigndimen#1#2% {\afterassignment\gobbleassigndimen#1=#2\zeropoint\\} \unexpanded\def\appended#1#2#3{\expandafter#1\expandafter#2\expandafter{#2#3}} \unexpanded\def\appendvalue #1{\expandafter\appended\expandafter \def\csname#1\endcsname} \unexpanded\def\appendgvalue#1{\expandafter\appended\expandafter\gdef\csname#1\endcsname} \unexpanded\def\prepended#1#2#3% {\t_syst_helpers_scratch{#3}% \expandafter\expandafter\expandafter#1\expandafter\expandafter\expandafter#2\expandafter\expandafter\expandafter {\expandafter\the\expandafter\t_syst_helpers_scratch#2}} \unexpanded\def\prependvalue #1{\expandafter\prepended\expandafter \def\csname#1\endcsname} \unexpanded\def\prependgvalue#1{\expandafter\prepended\expandafter\gdef\csname#1\endcsname} %D \macros %D {dowithrange} %D %D This one is for Mojca Miklavec, who made me aware of the fact that %D \type {page-imp.tex} was not the best place to hide it. \unexpanded\def\dowithrange#1#2% #2 takes number {\splitstring#1\at:\to\fromrange\and\torange \ifx\torange\empty\let\torange\fromrange\fi \dostepwiserecurse\fromrange\torange1{#2{\recurselevel}}} %D \macros %D {ignoreimplicitspaces} %D %D \startbuffer %D \def\whatever[#1]{\expanded{\definedfont[#1 at 12pt]}\ignorespaces} %D {a\whatever[Serif]b a\whatever[Serif] b a\whatever[Serif]\space b} %D \def\whatever[#1]{\expanded{\definedfont[#1 at 12pt]}\ignoreimplicitspaces} %D {a\whatever[Serif]b a\whatever[Serif] b a\whatever[Serif]\space b} %D \stopbuffer %D %D \typebuffer \getbuffer \unexpanded\def\ignoreimplicitspaces {\doifnextcharelse\relax\relax\relax} %D \macros %D {processwords} %D %D Not that sophisticated but sometimes users (like in metafun). \def\syst_helpers_process_word#1 #2\_e_o_w_ {\doifsomething{#1}{\processword{#1} \syst_helpers_process_word#2 \_e_o_w_}} \def\processwords#1% {\syst_helpers_process_word#1 \_e_o_w_}% no \unskip \let\processword\relax %D \macros %D {startnointerference} %D %D \starttyping %D \startnointerference %D all kind of code %D \stopnointerference %D \stoptyping \newbox\b_syst_helpers_no_interference \unexpanded\def\startnointerference % not even grouped ! {\setbox\b_syst_helpers_no_interference\vbox \bgroup} \unexpanded\def\stopnointerference {\egroup \setbox\b_syst_helpers_no_interference\emptybox} %D A variant for \type {\executeifdefined}: \def\expandcheckedcsname#1#2% #2 is often a \xxxparameter so let's expand it once {\normalexpanded{\noexpand\syst_helpers_expand_checked_csname{#1}{#2}}} \def\syst_helpers_expand_checked_csname#1#2#3% {\csname#1\ifcsname#1#2\endcsname#2\else#3\fi\endcsname} %D Signal. Some fonts have a char0 rendering so we need to make sure that it %D is not set in the font! \unexpanded\def\signalcharacter{\char\zerocount} % \zwj %D A few secial variants of commands defined here. Some more will be moved here (e.g. %D from table modules. \def\dodirectdoubleempty#1#2% used in math (lookahead issues) {\ifx#2[% \expandafter\syst_helpers_direct_double_empty_one_yes \else \expandafter\syst_helpers_direct_double_empty_one_nop \fi#1#2} \def\syst_helpers_direct_double_empty_one_yes#1[#2]#3% {\ifx#3[\else\expandafter\syst_helpers_direct_double_empty_two_nop\fi#1[#2]#3} \def\syst_helpers_direct_double_empty_one_nop#1{#1[][]} \def\syst_helpers_direct_double_empty_two_nop#1[#2]{#1[#2][]} %D Used in math definitions (in an type {\edef}): %D \startbuffer %D [\docheckedpair{}] %D [\docheckedpair{a}] %D [\docheckedpair{a,b}] %D [\docheckedpair{a,b,c}] %D \stopbuffer %D %D \typebuffer \startlines \getbuffer \stoplines \def\docheckedpair#1% {\syst_helpers_checked_pair#1,,\_o_e_p_} \def\syst_helpers_checked_pair#1,#2,#3\_o_e_p_ {#1,#2} %D Here are some nasty helpers. They can be used to fill often expanded token %D lists efficiently (see tabulate for an example). \def\constantnumber#1% {\ifcase#1\zerocount \or \plusone \or \plustwo \or \plusthree \or \plusfour \or \plusfive \or \plussix \or \plusseven \or \pluseight \or \plusnine \or \plusten \else \number#1\relax\fi} \def\constantnumberargument#1% {\ifcase#1\zerocount \or \plusone \or \plustwo \or \plusthree \or \plusfour \or \plusfive \or \plussix \or \plusseven \or \pluseight \or \plusnine \or \plusten \else {\number#1}\fi} \def\constantdimen#1% {\ifdim#1=\zeropoint \zeropoint \else \the#1\relax \fi} \def\constantdimenargument#1% {\ifdim#1=\zeropoint \zeropoint \else {\the#1}% \fi} \def\constantemptyargument#1% {\ifx#1\empty \noexpand\empty \else {#1}% \fi} % %D Maybe some day (moved from cont-new): % %D % %D \starttyping % %D \the\dimexpr(\dimchoice {7pt}{{<10pt}{8pt}{<12pt}{9pt}{<15pt}{10pt}{=11pt}{12pt}}) % %D \the\dimexpr(\dimchoice{11pt}{{<10pt}{8pt}{<12pt}{9pt}{<15pt}{10pt}{=11pt}{12pt}}) % %D \the\dimexpr(\dimchoice{14pt}{{<10pt}{8pt}{<12pt}{9pt}{<15pt}{10pt}{=11pt}{12pt}}) % %D \stoptyping % % \def\syst_helpers_choice_finish#1\empty{} % % \def\syst_helpers_choice_dim#1#2#3% % {\ifdim#1#2% % #3\expandafter\syst_helpers_choice_finish % \else % \expandafter\syst_helpers_choice_dim % \fi{#1}} % % \def\syst_helpers_choice_num#1#2#3% % {\ifnum#1#2% % #3\expandafter\syst_helpers_choice_finish % \else % \expandafter\syst_helpers_choice_num % \fi{#1}} % % \def\dimchoice#1#2{\syst_helpers_choice_dim{#1}#2{=#1}{#1}\empty} % \def\numchoice#1#2{\syst_helpers_choice_num{#1}#2{=#1}{#1}\empty} %D \macros %D {getsubstring} %D \startbuffer %D %D \getsubstring{4}{}{Who Wants This} %D \getsubstring{4}{9}{Who Wants This} %D \getsubstring{9}{-2}{Who Wants This} %D \getsubstring{1}{5}{Who Wants This} %D \stopbuffer %D %D \typebuffer %D %D \startlines %D \getbuffer %D \stoplines % expandable: %def\getsubstring#1#2#3{\cldcontext{utf.sub([[#3]],tonumber("#1"),tonumber("#2"))}} \def\getsubstring#1#2#3{\ctxcommand{getsubstring(\!!bs#3\!!es,"#1","#2")}} \protect \endinput % \edef\choicetokenyes{+} % \edef\choicetokennop{-} % % \unexpanded\def\startchoice#1% % {\pushmacro\currentchoicevalue % \edef\currentchoicevalue{#1}% % \checkchoicetoken} % % \unexpanded\def\checkchoicetoken#1% % {\edef\choicetoken{#1}% % \ifx\choicetoken\choicetokenyes % \singleexpandafter\checkchoiceyes % \else\ifx\choicetoken\choicetokennop % \doubleexpandafter\checkchoicenop % \else % \doubleexpandafter\choicequit % \fi\fi} % % \def\checkchoiceyes#1% % {\edef\choicevalue{#1}% % \ifx\currentchoicevalue\choicevalue % \expandafter\choiceindeed % \else % \expandafter\choiceignore % \fi} % % \def\checkchoicenop % {\choiceindeed} % % \def\choiceabort#1\stopchoice % {\popmacro\currentchoicevalue} % % \def\choicequit % {\popmacro\currentchoicevalue} % % \def\choiceindeed#1#2\stopchoice % {\popmacro\currentchoicevalue % #1} % % \def\choiceignore#1% % {\checkchoicetoken} % % \let\stopchoice\relax % % \def\xxx{3} % \def\xxx{x} % % \startchoice {\xxx} % + {1} { % first % } + {2} { % second % } + {3} { % third % } - { % nothing % } % \stopchoice % \def\appendtovaluelist#1#2% % {\ifcsname#1\endcsname % \expandafter\ifx\csname#1\endcsname\empty % \expandafter\def\csname#1\endcsname{#2}% % \else % \expandafter\def\csname#1\expandafter\expandafter\expandafter\endcsname % \expandafter\expandafter\expandafter{\csname#1\endcsname,#2}% % \fi % \else % \expandafter\def\csname#1\endcsname{#2}% % \fi} % % or % % \def\appendtovaluelist#1% % {\ifcsname#1\endcsname % \expandafter\ifx\csname#1\endcsname\empty % \expandafter\noappendtovaluelist\csname#1\expandafter\expandafter\expandafter\endcsname % \else % \expandafter\doappendtovaluelist\csname#1\expandafter\expandafter\expandafter\endcsname % \fi % \else % \expandafter\noappendtovaluelist\csname#1\expandafter\endcsname % \fi} % % \def\doappendtovaluelist#1#2{\expandafter\def\expandafter#1\expandafter{#1,#2}} % \def\noappendtovaluelist#1#2{\def#1{#2}} % % \appendtovaluelist{mylist}{aap} % \appendtovaluelist{mylist}{noot} % \appendtovaluelist{mylist}{mies} % % \showvalue{mylist}