%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 (often old) %D experiments, moved from local test modules to here, cleaned up, but not really %D used. After all it's not that urgent and replacing helpers is a delicate process. %D Don't depend on it. \registerctxluafile{syst-aux}{} % A dedicated primitive \ifvoidmacro\cs == \ifx\cs\empty is some 10% faster but % probably not that noticeable in practice. An \ifvoidtoks might make sense but we % don't test that often for it (and it's more work to implement in the engine). %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, experiences, %D intermediate versions and cleaner variants there (and also non-\ETEX\ variants). %D %D Contrary to the older files, we now assume that this one is used in \CONTEXT\ and %D therefore we might also assume that some basic functionality is available. %D %D The original files contain previous implementations and notes about performance. %D This file will be stripped down in due time. %D %D Some of the macros here were only used in the bibliography module. They have been %D be moved to a separate syst module since the bib module is no longer using them. %D Some more will go away. \unprotect %D \macros %D {unexpanded} %D %D Because we use this module only in \MKIV, we have removed the old protection %D code. %D %D \starttyping %D \unexpanded\def\somecommand{... ... ...} %D \stoptyping %D %D This overloads the \ETEX\ primitive but as we already had an \MKII\ solution we %D keep the same name for a similar mechanism. \let\unexpanded\normalprotected \ifcase\contextlmtxmode \def\startlmtxmode#1\stoplmtxmode{} \let\stoplmtxmode \relax \let\startmkivmode\relax \let\stopmkivmode \relax \else \let\startlmtxmode\relax \let\stoplmtxmode \relax \def\startmkivmode#1\stopmkivmode{} \let\stopmkivmode \relax \fi % %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: \ifdefined\c_syst_helpers_n_of_namespaces % lets plug in a better error message \else \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 \fi \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 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 define some shortcuts to %D the local scatchregisters~0, 2, 4, 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} % ... \def\!!plusone {1} % ... \def\!!plustwo {2} % ... \def\!!plusthree {3} % ... \def\!!plusfour {4} % ... \def\!!plusfive {5} % ... \def\!!plussix {6} % ... \def\!!plusseven {7} % ... \def\!!pluseight {8} % ... \def\!!plusnine {9} % alongside \plusnine \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 variables). Redefining these %D constants can have disastrous 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 bytes come %D cheap). \let\@EA \singleexpandafter % obsolete \let\@EAEAEA \doubleexpandafter % obsolete \let\@EAEAEAEAEAEA\tripleexpandafter % obsolete %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 first experiences with %D programming concerned a simple shell around \LATEX. The commands probably use %D most at \PRAGMA, are the itemizing ones. One of those few shell commands took %D care of an optional argument, that enabled us to specify what kind of item symbol %D we wanted. Without understanding anything we were able to locate a \LATEX\ macro %D that could be used to inspect the next character. %D %D It's this macro that the ancester of the next one presented here. It executes one %D of two actions, dependant of the next character. Disturbing spaces and line %D endings, which are 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 {\localnext} because we %D don't want clashes with \type {\next}. \let\next \relax \let\nextnext \relax \let\nextnextnext \relax \let\nexttoken \relax \let\charactertoken\relax \let\m_syst_action_yes\relax \let\m_syst_action_nop\relax \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} \startmkivmode \unexpanded\def\doifelsenextchar#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} \unexpanded\def\doifelsenextcharcs#1#2#3% #1 should not be {} ! {\let\charactertoken=#1% = needed here \let\m_syst_action_yes#2% \let\m_syst_action_nop#3% \futurelet\nexttoken\syst_helpers_inspect_next_character} \stopmkivmode \startlmtxmode \unexpanded\def\doifelsenextchar#1#2#3% #1 should not be {} ! {\def\m_syst_action_yes{#2}% \def\m_syst_action_nop{#3}% \futureexpandis#1\m_syst_action_yes\m_syst_action_nop} \unexpanded\def\doifelsenextcharcs % #1#2#3% #1 should not be {} ! {\futureexpandis} \stoplmtxmode \let\doifnextcharelse \doifelsenextchar \let\doifnextcharcselse\doifelsenextcharcs %D Because we will mostly use this macro for testing if the next character is \type %D {[}, we also make a slightly faster variant as it is not uncommon to have tens of %D thousands of calls to this test in a run. Of course it also is more convenient to %D read a trace then. %D We could make variants without the \if_next_blank_space_token but the overhead is %D only .1 sec on 3.5 for 10^6 tests and often that branch is not entered anyway. The %D 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=[ \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} \startmkivmode \unexpanded\def\doifelsenextoptional#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\doifelsenextoptionalcs#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} \stopmkivmode \startlmtxmode \unexpanded\def\doifelsenextoptional#1#2% {\def\m_syst_action_yes{#1}% \def\m_syst_action_nop{#2}% \futureexpandis[\m_syst_action_yes\m_syst_action_nop} \unexpanded\def\doifelsenextoptionalcs {\futureexpandis[} \stoplmtxmode \let\doifnextoptionalelse \doifelsenextoptional \let\doifnextoptionalcselse\doifelsenextoptionalcs % fails on assignments % % \unexpanded\def\doifelsenextoptional {\afterassignment\doifelsenextoptional_n \def\m_syst_action_yes} % \def\doifelsenextoptional_n {\afterassignment\doifelsenextoptional_n_n\def\m_syst_action_nop} % \def\doifelsenextoptional_n_n {\let\if_next_blank_space_token\iffalse % \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} % % \unexpanded\def\doifelsenextoptionalcs {\afterassignment\doifelsenextoptionalcs_n \let\m_syst_action_yes} % \def\doifelsenextoptionalcs_n {\afterassignment\doifelsenextoptionalcs_n_n\let\m_syst_action_nop} % \def\doifelsenextoptionalcs_n_n{\let\if_next_blank_space_token\iffalse % \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \let\syst_helpers_next_bgroup_character_token\bgroup \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} \startmkivmode \unexpanded\def\doifelsenextbgroup#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} \unexpanded\def\doifelsenextbgroupcs#1#2% {\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_bgroup_character} \stopmkivmode \startlmtxmode \unexpanded\def\doifelsenextbgroup#1#2% {\def\m_syst_action_yes{#1}% \def\m_syst_action_nop{#2}% \futureexpandis\bgroup\m_syst_action_yes\m_syst_action_nop} \unexpanded\def\doifelsenextbgroupcs % #1#2 {\futureexpandis\bgroup} \stoplmtxmode \let\doifnextbgroupelse \doifelsenextbgroup \let\doifnextbgroupcselse\doifelsenextbgroupcs \let\syst_helpers_next_parenthesis_character_token( \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} \startmkivmode \unexpanded\def\doifelsenextparenthesis#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} \stopmkivmode \startlmtxmode \unexpanded\def\doifelsenextparenthesis#1#2% {\def\m_syst_action_yes{#1}% \def\m_syst_action_nop{#2}% \futureexpandis(\m_syst_action_yes\m_syst_action_nop} \stoplmtxmode \let\doifnextparenthesiselse\doifelsenextparenthesis %D The next one is handy in predictable situations: \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} \startmkivmode \unexpanded\def\doifelsefastoptionalcheck#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\doifelsefastoptionalcheckcs#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} \stopmkivmode \startlmtxmode \unexpanded\def\doifelsefastoptionalcheck#1#2% {\def\m_syst_action_yes{#1}% \def\m_syst_action_nop{#2}% \futureexpandis[\m_syst_action_yes\m_syst_action_nop} \unexpanded\def\doifelsefastoptionalcheckcs {\futureexpandis[} \stoplmtxmode \let\doiffastoptionalcheckelse \doifelsefastoptionalcheck \let\doiffastoptionalcheckcselse\doifelsefastoptionalcheckcs %D Here's one for skipping spaces and pars, handy for: %D %D \starttyping %D \hbox %D %D {a few lines later} %D \stoptyping % \unexpanded\def\assumelongusagecs#1% % {\let\m_syst_action#1% % \futurelet\nexttoken\syst_helpers_ignore_par_character} % % \def\syst_helpers_ignore_par_character % {\ifx\nexttoken\blankspace % \expandafter\syst_helpers_ignore_par_character_blankspace % \else % \expandafter\syst_helpers_ignore_par_character_followup % \fi} % % \def\syst_helpers_ignore_par_character_followup % {\ifx\nexttoken\par % \expandafter\syst_helpers_ignore_par_partoken % \else % \expandafter\m_syst_action % \fi} % % \def\syst_helpers_ignore_par_partoken % {\afterassignment\m_syst_action\let\nexttoken} \unexpanded\def\assumelongusagecs#1% {\let\m_syst_action#1% \futurelet\nexttoken\syst_helpers_ignore_spacing} \def\syst_helpers_ignore_spacing {\ifx\nexttoken\blankspace \singleexpandafter\syst_helpers_ignore_spacing_blankspace \else\ifx\nexttoken\par \doubleexpandafter\syst_helpers_ignore_spacing_partoken \else \doubleexpandafter\m_syst_action \fi\fi} \def\syst_helpers_ignore_spacing_partoken\par {\futurelet\nexttoken\syst_helpers_ignore_spacing} %D These macros use 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}. % \ifcase\contextlmtxmode \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} \def\:{\syst_helpers_ignore_spacing_blankspace} \expandafter\def\: {\futurelet\nexttoken\syst_helpers_ignore_spacing} \let\:\next % \else % % % nothing here % % \fi %D This is much nicer and works too: % \def\firstofoneargument#1{#1} % % \expandafter\let\firstofoneargument{\blankspace= } % % \expandafter\def\firstofoneargument{\syst_helpers_reinspect_next_character % } {\let\if_next_blank_space_token\iftrue % \futurelet\nexttoken\syst_helpers_inspect_next_character} % % \expandafter\def\firstofoneargument{\syst_helpers_reinspect_next_optional_character % } {\let\if_next_blank_space_token\iftrue % \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} % % \expandafter\def\firstofoneargument{\syst_helpers_reinspect_next_bgroup_character % } {\let\if_next_blank_space_token\iftrue % \futurelet\nexttoken\syst_helpers_inspect_next_bgroup_character} % % \expandafter\def\firstofoneargument{\syst_helpers_reinspect_next_parenthesis_character % } {\let\if_next_blank_space_token\iftrue % \futurelet\nexttoken\syst_helpers_inspect_next_parenthesis_character} % % \expandafter\def\firstofoneargument{\syst_helpers_ignore_spacing_blankspace % } {\futurelet\nexttoken\syst_helpers_ignore_spacing} %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}=\... = \glet\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} % maybe: \begincsname#1\endcsname \def\letvalue #1{\expandafter\let \csname#1\endcsname} \def\letgvalue #1{\expandafter\glet\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} \unexpanded\def\getuvalue#1{\csname#1\endcsname} %D \macros %D {globallet,glet} %D %D In \CONTEXT\ of May 2000 using \type {\globallet} instead of the two tokens will %D save us some $300\times4=1200$ bytes of format file on a 32~bit system. Not that %D it matters much today. This shortcut is already defined: \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 % already set \def\doifelseundefined#1% {\ifcsname#1\endcsname \expandafter\secondoftwoarguments\else\expandafter\firstoftwoarguments \fi} \def\doifelsedefined#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} \let\doifundefinedelse\doifelseundefined \let\doifdefinedelse \doifelsedefined %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: \unexpanded\def\letbeundefined#1% potential stack buildup when used \global {\expandafter\let\csname#1\endcsname\undefined} % or use \undefinevalue to match \setvalue \unexpanded\def\localundefine#1% conditional {\ifcsname#1\endcsname\expandafter\let\csname#1\endcsname\undefined\fi} \unexpanded\def\globalundefine#1% conditional {\ifcsname#1\endcsname\expandafter\glet\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\doifelsealldefined#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} \let\doifalldefinedelse\doifelsealldefined \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\doifelseempty#1% {\def\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \let\doifemptyelse\doifelseempty \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\doifelseinset#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} % % \let\doifinsetelse\doifelseinset % % \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} \unexpanded\def\doifelseinset#1#2{\clf_doifelseinset{#1}{#2}} \unexpanded\def\doifinset #1#2{\clf_doifinset {#1}{#2}} \unexpanded\def\doifnotinset #1#2{\clf_doifnotinset {#1}{#2}} % \let\firstinset \clf_firstinset \let\doifinsetelse\doifelseinset %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% hm, why #2 here and passed at end % {\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\doifelsecommon{\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 } % % \let\doifcommonelse\doifelsecommon \unexpanded\def\doifelsecommon#1#2{\clf_doifelsecommon{#1}{#2}} \unexpanded\def\doifcommon #1#2{\clf_doifcommon {#1}{#2}} \unexpanded\def\doifnotcommon #1#2{\clf_doifnotcommon {#1}{#2}} \let\doifcommonelse\doifelsecommon %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} % \ifcase\contextlmtxmode \def\syst_helpers_do_process_comma_item {\futurelet\nexttoken\syst_helpers_do_do_process_comma_item} \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} % \else % % \def\syst_helpers_do_process_comma_item % {\futureexpandis]\gobbleoneargument\syst_helpers_do_do_do_process_comma_item} % % \fi %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 } \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}% \syst_helpers_do_do_process_comma_item_gobble#1,]\relax \global\advance\commalevel \minusone } \def\syst_helpers_do_do_process_comma_item_gobble#1{\syst_helpers_do_do_process_comma_item} %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. % \ifcase\contextlmtxmode \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 % \fi %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 this macro has its %D limits. \unexpanded\def\processcommacommand[#1]% {\normalexpanded{\processcommalist[#1]}} %D The argument to \type{\command} is not delimited. Because we often use \type {[]} %D 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 command. Depending of %D the presence of one or more provided items, some actions is taken. These macros %D can be nested 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 to be undertaken when %D 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 the keyword \type %D {default} and executed the related action if present. When \type {#1} is non %D empty and not in the list, the action related to \type {unknown} is executed. %D Both keywords must be at the end of list \type{#2}. Afterwards, the actually %D found keyword is available in \type {\commalistelement}. An advanced example of %D the use of this macro can be found in \PPCHTEX, where we completely rely on \TEX\ %D for interpreting user supplied keywords like \type {SB}, \type {SB1..6}, \type %D {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 next character. This macro %D get this character and puts it in \type {\firstcharacter}. %D %D \starttyping %D \getfirstcharacter {string} %D \stoptyping %D %D A two step expansion is used to prevent problems with complicated arguments, for %D instance arguments that consist of two or more expandable tokens. \let\firstcharacter \empty \let\remainingcharacters\empty \unexpanded\def\getfirstcharacter #1{\clf_getfirstcharacter{#1}} \unexpanded\def\doifelsefirstchar #1#2{\clf_doifelsefirstchar{#1}{#2}} \unexpanded\def\thefirstcharacter #1{\clf_thefirstcharacter{#1}} \unexpanded\def\theremainingcharacters#1{\clf_theremainingcharacters{#1}} \let\doiffirstcharelse\doifelsefirstchar %D \macros %D {doifinstringelse, doifincsnameelse} %D %D We can check for the presence of a substring in a given sequence of characters. %D %D \starttyping %D \doifinsetelse {substring} {string} {then ...} {else ...} %D \stoptyping \let\m_syst_sub_string\empty \unexpanded\def\doifelseinstring#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} \let\doifinstringelse\doifelseinstring \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 tasks like checking %D reserved words in pretty verbatim typesetting! This is mainly due to the fact %D that passing (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 possible, the latter %D alternative does minimal (one level) 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\doifelseincsname#1#2% {\normalexpanded{\syst_helpers_do_if_in_csname_else{#1}}{#2}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \let\doifincsnameelse\doifelseincsname %D \macros %D {doifnumberelse,doifnumber,doifnotnumber} %D %D The next macro executes a command depending of the outcome of a test on numerals. %D This is probably one of the fastest test possible, exept from a less robust %D 10||step \type {\if}||ladder or some tricky \type {\lcode} checking. %D %D \starttyping %D \doifnumberelse {string} {then ...} {else ...} %D \stoptyping %D %D The macro accepts \type {123}, \type {abc}, \type {{}}, \type {\getal} and \type %D {\the\count...}. This macro is a rather dirty one. \startmkivmode \def\doifelsenumber#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} \stopmkivmode \startlmtxmode \def\doifelsenumber#1% {\ifchknum#1\or \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \def\doifnumber#1% {\ifchknum#1\or \expandafter\firstoftwoarguments \else \expandafter\gobbleoneargument \fi} \def\doifnotnumber#1% {\ifchknum#1\or \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \stoplmtxmode \let\doifnumberelse\doifelsenumber %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\clf_percentageof{#2}\dimexpr#1\relax} \unexpanded\def\setpercentdimen#1#2% dimen percentage (with %) {#1=\clf_percentageof{#2}\dimexpr#1\relax} %D \macros %D {makerawcommalist, %D rawdoinsetelse, %D rawprocesscommalist, %D rawprocessaction} %D %D Some of the commands mentioned earlier are effective but slow. When one is %D desperately in need of faster alternatives and when the conditions are %D predictable safe, the \type {\raw} alternatives come into focus. A major drawback %D is that they do not take \type {\c!constants} into account, simply because no %D expansion is done. This is no problem with \type {\rawprocesscommalist}, because %D this macro does not compare anything. Expandable macros are permitted as search %D string. %D %D \starttyping %D \makerawcommalist[string,string,...]\stringlist %D \rawdoifelseinset{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, spoil the search process. %D The gain in speed depends on the length of the argument (the longer the argument, %D the less 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]}} %D Here is one without nesting: \unexpanded\def\syst_helpers_fast_process_comma_item#1,#2% #2 eats up preceding space {\if]#1\else \syst_helpers_fast_process_comma_command{#1}% \expandafter\syst_helpers_fast_process_comma_item \fi#2} \unexpanded\def\fastprocesscommalist[#1]#2% accepteert ook [\cs] {\let\syst_helpers_fast_process_comma_command#2% \expandafter\syst_helpers_fast_process_comma_item#1,],}% \relax \unexpanded\def\fastprocesscommacommand[#1]#2% accepteert ook [\cs] {\let\syst_helpers_fast_process_comma_command#2% \normalexpanded{\syst_helpers_fast_process_comma_item#1},],}% \relax % \def\rawdoifelseinset#1#2{\doifinstringelse{,#1,}{,#2,}} % \def\rawdoifinset #1#2{\doifinstring {,#1,}{,#2,}} \def\m_syst_two_commas{,,} \unexpanded\def\rawdoifelseinset#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} \let\rawdoifinsetelse\rawdoifinsetelse \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 \type %D {\let} and \type {\expandafter} in the test. %D %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_t_} \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_t_#1==\empty\_e_o_t_ \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_t_#2=#3=#4#5\_e_o_t_ {\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_t_#2=#3=#4#5\_e_o_t_ {\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_t_#2==\empty\_e_o_t_} \unexpanded\def\doeassign [#1][#2]{\let\setsomevalue\dosetevalue \syst_helpers_get_parameters_assign_indeed#1\_e_o_t_#2==\empty\_e_o_t_} \unexpanded\def\undoassign[#1][#2]{\let\setsomevalue\doresetvalue\syst_helpers_get_parameters_assign_indeed#1\_e_o_t_#2==\empty\_e_o_t_} %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 worth the %D 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_t_} \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_t_ \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 empty string, so we %D 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 others. All commands that %D are able to provide backgounds or rules around some content, for instance default %D to the standard command for ruled boxes. Is situations like this 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 for use in a %D 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 also checks on the %D presence of a~\type {=}. %D %D The boolean \type {\ifparameters} can be used afterwards. Combining both in one %D \type {\if}||macro would lead to 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\_e_t_x_\parametersfalse\else\parameterstrue\fi} \unexpanded\def\checkparameters[#1]% {\syst_helpers_check_parameters#1=\_e_t_x_\_e_t_x_\_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 to the handling of %D braces during parameters passing and 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 alternative, which can %D 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 lineendings can interfere. The %D next set of macros uses \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 two||step solution when %D getting five or more arguments. %D %D When developing more and more of the real \CONTEXT, we started using some %D alternatives that provided empty arguments (in fact optional ones) whenever the %D user failed to supply them. Because this more complicated macros enable us to do %D some checking, we reimplemented the non||empty ones. % no longer a message: % % \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 wanted arguments are %D 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 use the \type %D {\if...argument} boolean. For novice: watch 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 Common: \newtoks\t_syst_aux % \def\syst_helpers_empty_spaced_six {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][][][] } % \def\syst_helpers_empty_normal_six {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][][][]} % \def\syst_helpers_empty_spaced_five {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][][] } % \def\syst_helpers_empty_normal_five {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][][]} % \def\syst_helpers_empty_spaced_four {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][] } % \def\syst_helpers_empty_normal_four {\expandafter\m_syst_aux_do\the\t_syst_aux[][][][]} % \def\syst_helpers_empty_spaced_three{\expandafter\m_syst_aux_do\the\t_syst_aux[][][] } % \def\syst_helpers_empty_normal_three{\expandafter\m_syst_aux_do\the\t_syst_aux[][][]} % \def\syst_helpers_empty_spaced_two {\expandafter\m_syst_aux_do\the\t_syst_aux[][] } % \def\syst_helpers_empty_normal_two {\expandafter\m_syst_aux_do\the\t_syst_aux[][]} % \def\syst_helpers_empty_spaced_one {\expandafter\m_syst_aux_do\the\t_syst_aux[] } % \def\syst_helpers_empty_normal_one {\expandafter\m_syst_aux_do\the\t_syst_aux[]} % % \def\syst_helpers_single_empty_one_yes {\firstargumenttrue \m_syst_aux_do} % \def\syst_helpers_double_empty_two_yes {\secondargumenttrue \expandafter\m_syst_aux_do\the\t_syst_aux} % \def\syst_helpers_triple_empty_three_yes {\thirdargumenttrue \expandafter\m_syst_aux_do\the\t_syst_aux} % \def\syst_helpers_quadruple_empty_four_yes {\fourthargumenttrue \expandafter\m_syst_aux_do\the\t_syst_aux} % \def\syst_helpers_quintuple_empty_five_yes {\fifthargumenttrue \expandafter\m_syst_aux_do\the\t_syst_aux} % \def\syst_helpers_sixtuple_empty_six_yes {\sixthargumenttrue \expandafter\m_syst_aux_do\the\t_syst_aux} % \def\syst_helpers_seventuple_empty_seven_yes{\seventhargumenttrue\expandafter\m_syst_aux_do\the\t_syst_aux} % % with % % \unexpanded\def\dodoubleempty#1% % {\syst_helpers_argument_reset % \let\m_syst_aux_do#1% alias % \let\m_syst_action_yes\syst_helpers_double_empty_one_yes % \let\m_syst_action_nop\syst_helpers_double_empty_one_nop % \let\if_next_blank_space_token\iffalse % \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} % % \def\syst_helpers_double_empty_one_yes[#1]% % {\firstargumenttrue % \t_syst_aux{[{#1}]}% assignment % \let\m_syst_action_yes\syst_helpers_double_empty_two_yes % \let\m_syst_action_nop\syst_helpers_double_empty_two_nop % \let\if_next_blank_space_token\iffalse % \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} % % But we use this as it keeps the original name visible: \startmkivmode \def\syst_helpers_empty_spaced_six {\the\t_syst_aux[][][][][][] } \def\syst_helpers_empty_normal_six {\the\t_syst_aux[][][][][][]} \def\syst_helpers_empty_spaced_five {\the\t_syst_aux[][][][][] } \def\syst_helpers_empty_normal_five {\the\t_syst_aux[][][][][]} \def\syst_helpers_empty_spaced_four {\the\t_syst_aux[][][][] } \def\syst_helpers_empty_normal_four {\the\t_syst_aux[][][][]} \def\syst_helpers_empty_spaced_three{\the\t_syst_aux[][][] } \def\syst_helpers_empty_normal_three{\the\t_syst_aux[][][]} \def\syst_helpers_empty_spaced_two {\the\t_syst_aux[][] } \def\syst_helpers_empty_normal_two {\the\t_syst_aux[][]} \def\syst_helpers_empty_spaced_one {\the\t_syst_aux[] } \def\syst_helpers_empty_normal_one {\the\t_syst_aux[]} \stopmkivmode \def\syst_helpers_single_empty_one_yes {\firstargumenttrue \the\t_syst_aux} \def\syst_helpers_double_empty_two_yes {\secondargumenttrue \the\t_syst_aux} \def\syst_helpers_triple_empty_three_yes {\thirdargumenttrue \the\t_syst_aux} \def\syst_helpers_quadruple_empty_four_yes {\fourthargumenttrue \the\t_syst_aux} \def\syst_helpers_quintuple_empty_five_yes {\fifthargumenttrue \the\t_syst_aux} \def\syst_helpers_sixtuple_empty_six_yes {\sixthargumenttrue \the\t_syst_aux} \def\syst_helpers_seventuple_empty_seven_yes{\seventhargumenttrue\the\t_syst_aux} %D Single: % \unexpanded\def\dosingleempty#1% % {\syst_helpers_argument_reset % \doifelsenextoptional % {\firstargumenttrue#1}% % {\syst_helpers_single_empty_one_nop#1}} % % \def\syst_helpers_single_empty_one_nop#1% % {\firstargumentfalse % #1[]} \startmkivmode \unexpanded\def\dosingleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \let\m_syst_action_yes\syst_helpers_single_empty_one_yes \let\m_syst_action_nop\syst_helpers_single_empty_one_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_single_empty_one_nop {\firstargumentfalse \the\t_syst_aux[]} \stopmkivmode \startlmtxmode \unexpanded\def\dosingleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \futureexpand[\syst_helpers_single_empty_one_yes\syst_helpers_single_empty_one_nop} \def\syst_helpers_single_empty_one_nop {\firstargumentfalse \the\t_syst_aux[]} \stoplmtxmode %D Double % \unexpanded\def\dodoubleempty#1% % {\syst_helpers_argument_reset % \doifelsenextoptional % {\syst_helpers_double_empty_one_yes#1}% % {\syst_helpers_double_empty_one_nop#1}} % % \def\syst_helpers_double_empty_one_yes#1[#2]% % {\firstargumenttrue % \doifelsenextoptional % {\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}][]} \startmkivmode \unexpanded\def\dodoubleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \let\m_syst_action_yes\syst_helpers_double_empty_one_yes \let\m_syst_action_nop\syst_helpers_double_empty_one_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_double_empty_one_yes[#1]% {\firstargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_double_empty_two_yes \let\m_syst_action_nop\syst_helpers_double_empty_two_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_double_empty_one_nop {\firstargumentfalse \secondargumentfalse \the\t_syst_aux[][]} \def\syst_helpers_double_empty_two_nop {\secondargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_one \else \expandafter\syst_helpers_empty_normal_one \fi} \stopmkivmode \startlmtxmode \unexpanded\def\dodoubleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \futureexpand[\syst_helpers_double_empty_one_yes\syst_helpers_double_empty_one_nop} \def\syst_helpers_double_empty_one_yes[#1]% {\firstargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_double_empty_two_yes\syst_helpers_double_empty_two_nop} \def\syst_helpers_double_empty_one_nop {\firstargumentfalse \secondargumentfalse \the\t_syst_aux[][]} \def\syst_helpers_double_empty_two_nop {\secondargumentfalse \the\t_syst_aux[]} \stoplmtxmode % Triple % \unexpanded\def\dotripleempty#1% % {\syst_helpers_argument_reset % \doifelsenextoptional % {\syst_helpers_triple_empty_one_yes#1}% % {\syst_helpers_triple_empty_one_nop#1}} % % \def\syst_helpers_triple_empty_one_yes#1[#2]% % {\firstargumenttrue % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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}][]} \startmkivmode \unexpanded\def\dotripleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \let\m_syst_action_yes\syst_helpers_triple_empty_one_yes \let\m_syst_action_nop\syst_helpers_triple_empty_one_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_triple_empty_one_yes[#1]% {\firstargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_triple_empty_two_yes \let\m_syst_action_nop\syst_helpers_triple_empty_two_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_triple_empty_two_yes[#1]% {\secondargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_triple_empty_three_yes \let\m_syst_action_nop\syst_helpers_triple_empty_three_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_triple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \the\t_syst_aux[][][]} \def\syst_helpers_triple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_two \else \expandafter\syst_helpers_empty_normal_two \fi} \def\syst_helpers_triple_empty_three_nop {\thirdargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_one \else \expandafter\syst_helpers_empty_normal_one \fi} \stopmkivmode \startlmtxmode \unexpanded\def\dotripleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \futureexpand[\syst_helpers_triple_empty_one_yes\syst_helpers_triple_empty_one_nop} \def\syst_helpers_triple_empty_one_yes[#1]% {\firstargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_triple_empty_two_yes\syst_helpers_triple_empty_two_nop} \def\syst_helpers_triple_empty_two_yes[#1]% {\secondargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_triple_empty_three_yes\syst_helpers_triple_empty_three_nop} \def\syst_helpers_triple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \the\t_syst_aux[][][]} \def\syst_helpers_triple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \the\t_syst_aux[][]} \def\syst_helpers_triple_empty_three_nop {\thirdargumentfalse \the\t_syst_aux[]} \stoplmtxmode %D Quadruple: % \unexpanded\def\doquadrupleempty#1% % {\syst_helpers_argument_reset % \doifelsenextoptional % {\syst_helpers_quadruple_empty_one_yes#1}% % {\syst_helpers_quadruple_empty_one_nop#1}} % % \def\syst_helpers_quadruple_empty_one_yes#1[#2]% % {\firstargumenttrue % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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}][]} \startmkivmode \unexpanded\def\doquadrupleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \let\m_syst_action_yes\syst_helpers_quadruple_empty_one_yes \let\m_syst_action_nop\syst_helpers_quadruple_empty_one_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_quadruple_empty_one_yes[#1]% {\firstargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_quadruple_empty_two_yes \let\m_syst_action_nop\syst_helpers_quadruple_empty_two_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_quadruple_empty_two_yes[#1]% {\secondargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_quadruple_empty_three_yes \let\m_syst_action_nop\syst_helpers_quadruple_empty_three_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_quadruple_empty_three_yes[#1]% {\thirdargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_quadruple_empty_four_yes \let\m_syst_action_nop\syst_helpers_quadruple_empty_four_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_quadruple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \the\t_syst_aux[][][][]} \def\syst_helpers_quadruple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_three \else \expandafter\syst_helpers_empty_normal_three \fi} \def\syst_helpers_quadruple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_two \else \expandafter\syst_helpers_empty_normal_two \fi} \def\syst_helpers_quadruple_empty_four_nop {\fourthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_one \else \expandafter\syst_helpers_empty_normal_one \fi} \stopmkivmode \startlmtxmode \unexpanded\def\doquadrupleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \futureexpand[\syst_helpers_quadruple_empty_one_yes\syst_helpers_quadruple_empty_one_nop} \def\syst_helpers_quadruple_empty_one_yes[#1]% {\firstargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_quadruple_empty_two_yes\syst_helpers_quadruple_empty_two_nop} \def\syst_helpers_quadruple_empty_two_yes[#1]% {\secondargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_quadruple_empty_three_yes\syst_helpers_quadruple_empty_three_nop} \def\syst_helpers_quadruple_empty_three_yes[#1]% {\thirdargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_quadruple_empty_four_yes\syst_helpers_quadruple_empty_four_nop} \def\syst_helpers_quadruple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \the\t_syst_aux[][][][]} \def\syst_helpers_quadruple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \the\t_syst_aux[][][]} \def\syst_helpers_quadruple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \the\t_syst_aux[][]} \def\syst_helpers_quadruple_empty_four_nop {\fourthargumentfalse \the\t_syst_aux[]} \stoplmtxmode %D Quintuple: % \unexpanded\def\doquintupleempty#1% % {\syst_helpers_argument_reset % \doifelsenextoptional % {\syst_helpers_quintuple_empty_one_yes#1}% % {\syst_helpers_quintuple_empty_one_nop#1}} % % \def\syst_helpers_quintuple_empty_one_yes#1[#2]% % {\firstargumenttrue % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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}][]} \startmkivmode \unexpanded\def\doquintupleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \let\m_syst_action_yes\syst_helpers_quintuple_empty_one_yes \let\m_syst_action_nop\syst_helpers_quintuple_empty_one_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_quintuple_empty_one_yes[#1]% {\firstargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_quintuple_empty_two_yes \let\m_syst_action_nop\syst_helpers_quintuple_empty_two_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_quintuple_empty_two_yes[#1]% {\secondargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_quintuple_empty_three_yes \let\m_syst_action_nop\syst_helpers_quintuple_empty_three_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_quintuple_empty_three_yes[#1]% {\thirdargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_quintuple_empty_four_yes \let\m_syst_action_nop\syst_helpers_quintuple_empty_four_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_quintuple_empty_four_yes[#1]% {\fourthargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_quintuple_empty_five_yes \let\m_syst_action_nop\syst_helpers_quintuple_empty_five_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_quintuple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \the\t_syst_aux[][][][][]} \def\syst_helpers_quintuple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_four \else \expandafter\syst_helpers_empty_normal_four \fi} \def\syst_helpers_quintuple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_three \else \expandafter\syst_helpers_empty_normal_three \fi} \def\syst_helpers_quintuple_empty_four_nop {\fourthargumentfalse \fifthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_two \else \expandafter\syst_helpers_empty_normal_two \fi} \def\syst_helpers_quintuple_empty_five_nop {\fifthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_one \else \expandafter\syst_helpers_empty_normal_one \fi} \stopmkivmode \startlmtxmode \unexpanded\def\doquintupleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \futureexpand[\syst_helpers_quintuple_empty_one_yes\syst_helpers_quintuple_empty_one_nop} \def\syst_helpers_quintuple_empty_one_yes[#1]% {\firstargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_quintuple_empty_two_yes\syst_helpers_quintuple_empty_two_nop} \def\syst_helpers_quintuple_empty_two_yes[#1]% {\secondargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_quintuple_empty_three_yes\syst_helpers_quintuple_empty_three_nop} \def\syst_helpers_quintuple_empty_three_yes[#1]% {\thirdargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_quintuple_empty_four_yes\syst_helpers_quintuple_empty_four_nop} \def\syst_helpers_quintuple_empty_four_yes[#1]% {\fourthargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_quintuple_empty_five_yes\syst_helpers_quintuple_empty_five_nop} \def\syst_helpers_quintuple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \the\t_syst_aux[][][][][]} \def\syst_helpers_quintuple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \the\t_syst_aux[][][][]} \def\syst_helpers_quintuple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \the\t_syst_aux[][][]} \def\syst_helpers_quintuple_empty_four_nop {\fourthargumentfalse \fifthargumentfalse \the\t_syst_aux[][]} \def\syst_helpers_quintuple_empty_five_nop {\fifthargumentfalse \the\t_syst_aux[]} \stoplmtxmode %D Sixtuple: % \unexpanded\def\dosixtupleempty#1% % {\syst_helpers_argument_reset % \doifelsenextoptional % {\syst_helpers_sixtuple_empty_one_yes#1} % {\syst_helpers_sixtuple_empty_one_nop#1}} % % \def\syst_helpers_sixtuple_empty_one_yes#1[#2]% % {\firstargumenttrue % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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}][]} \startmkivmode \unexpanded\def\dosixtupleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \let\m_syst_action_yes\syst_helpers_sixtuple_empty_one_yes \let\m_syst_action_nop\syst_helpers_sixtuple_empty_one_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_sixtuple_empty_one_yes[#1]% {\firstargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_sixtuple_empty_two_yes \let\m_syst_action_nop\syst_helpers_sixtuple_empty_two_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_sixtuple_empty_two_yes[#1]% {\secondargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_sixtuple_empty_three_yes \let\m_syst_action_nop\syst_helpers_sixtuple_empty_three_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_sixtuple_empty_three_yes[#1]% {\thirdargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_sixtuple_empty_four_yes \let\m_syst_action_nop\syst_helpers_sixtuple_empty_four_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_sixtuple_empty_four_yes[#1]% {\fourthargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_sixtuple_empty_five_yes \let\m_syst_action_nop\syst_helpers_sixtuple_empty_five_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_sixtuple_empty_five_yes[#1]% {\fifthargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_sixtuple_empty_six_yes \let\m_syst_action_nop\syst_helpers_sixtuple_empty_six_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_sixtuple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \the\t_syst_aux[][][][][][]} \def\syst_helpers_sixtuple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_five \else \expandafter\syst_helpers_empty_normal_five \fi} \def\syst_helpers_sixtuple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_four \else \expandafter\syst_helpers_empty_normal_four \fi} \def\syst_helpers_sixtuple_empty_four_nop {\fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_three \else \expandafter\syst_helpers_empty_normal_three \fi} \def\syst_helpers_sixtuple_empty_five_nop {\fifthargumentfalse \sixthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_two \else \expandafter\syst_helpers_empty_normal_two \fi} \def\syst_helpers_sixtuple_empty_six_nop {\sixthargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_one \else \expandafter\syst_helpers_empty_normal_one \fi} \stopmkivmode \startlmtxmode \unexpanded\def\dosixtupleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \futureexpand[\syst_helpers_sixtuple_empty_one_yes\syst_helpers_sixtuple_empty_one_nop} \def\syst_helpers_sixtuple_empty_one_yes[#1]% {\firstargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_sixtuple_empty_two_yes\syst_helpers_sixtuple_empty_two_nop} \def\syst_helpers_sixtuple_empty_two_yes[#1]% {\secondargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_sixtuple_empty_three_yes\syst_helpers_sixtuple_empty_three_nop} \def\syst_helpers_sixtuple_empty_three_yes[#1]% {\thirdargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_sixtuple_empty_four_yes\syst_helpers_sixtuple_empty_four_nop} \def\syst_helpers_sixtuple_empty_four_yes[#1]% {\fourthargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_sixtuple_empty_five_yes\syst_helpers_sixtuple_empty_five_nop} \def\syst_helpers_sixtuple_empty_five_yes[#1]% {\fifthargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_sixtuple_empty_six_yes\syst_helpers_sixtuple_empty_six_nop} \def\syst_helpers_sixtuple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \the\t_syst_aux[][][][][][]} \def\syst_helpers_sixtuple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \the\t_syst_aux[][][][][]} \def\syst_helpers_sixtuple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \the\t_syst_aux[][][][]} \def\syst_helpers_sixtuple_empty_four_nop {\fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \the\t_syst_aux[][][]} \def\syst_helpers_sixtuple_empty_five_nop {\fifthargumentfalse \sixthargumentfalse \the\t_syst_aux[][]} \def\syst_helpers_sixtuple_empty_six_nop {\sixthargumentfalse \the\t_syst_aux[]} \stoplmtxmode %D Seventuple: % \unexpanded\def\doseventupleempty#1% % {\syst_helpers_argument_reset % \doifelsenextoptional % {\syst_helpers_seventuple_empty_one_yes#1}% % {\syst_helpers_seventuple_empty_one_nop#1}} % % \def\syst_helpers_seventuple_empty_one_yes#1[#2]% % {\firstargumenttrue % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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 % \doifelsenextoptional % {\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}][]} \startmkivmode \unexpanded\def\doseventupleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \let\m_syst_action_yes\syst_helpers_seventuple_empty_one_yes \let\m_syst_action_nop\syst_helpers_seventuple_empty_one_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_seventuple_empty_one_yes[#1]% {\firstargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_seventuple_empty_two_yes \let\m_syst_action_nop\syst_helpers_seventuple_empty_two_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_seventuple_empty_two_yes[#1]% {\secondargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_seventuple_empty_three_yes \let\m_syst_action_nop\syst_helpers_seventuple_empty_three_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_seventuple_empty_three_yes[#1]% {\thirdargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_seventuple_empty_four_yes \let\m_syst_action_nop\syst_helpers_seventuple_empty_four_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_seventuple_empty_four_yes[#1]% {\fourthargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_seventuple_empty_five_yes \let\m_syst_action_nop\syst_helpers_seventuple_empty_five_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_seventuple_empty_five_yes[#1]% {\fifthargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_seventuple_empty_six_yes \let\m_syst_action_nop\syst_helpers_seventuple_empty_six_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_seventuple_empty_six_yes[#1]% {\sixthargumenttrue \toksapp\t_syst_aux{[{#1}]}% \let\m_syst_action_yes\syst_helpers_seventuple_empty_seven_yes \let\m_syst_action_nop\syst_helpers_seventuple_empty_seven_nop \let\if_next_blank_space_token\iffalse \futurelet\nexttoken\syst_helpers_inspect_next_optional_character} \def\syst_helpers_seventuple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \the\t_syst_aux[][][][][][][]} \def\syst_helpers_seventuple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_six \else \expandafter\syst_helpers_empty_normal_six \fi} \def\syst_helpers_seventuple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_five \else \expandafter\syst_helpers_empty_normal_five \fi} \def\syst_helpers_seventuple_empty_four_nop {\fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_four \else \expandafter\syst_helpers_empty_normal_four \fi} \def\syst_helpers_seventuple_empty_five_nop {\fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_three \else \expandafter\syst_helpers_empty_normal_three \fi} \def\syst_helpers_seventuple_empty_six_nop {\sixthargumentfalse \seventhargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_two \else \expandafter\syst_helpers_empty_normal_two \fi} \def\syst_helpers_seventuple_empty_seven_nop {\seventhargumentfalse \if_next_blank_space_token \expandafter\syst_helpers_empty_spaced_one \else \expandafter\syst_helpers_empty_normal_one \fi} \stopmkivmode \startlmtxmode \unexpanded\def\doseventupleempty#1% {%syst_helpers_argument_reset \t_syst_aux{#1}% \futureexpand[\syst_helpers_seventuple_empty_one_yes\syst_helpers_seventuple_empty_one_nop} \def\syst_helpers_seventuple_empty_one_yes[#1]% {\firstargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_seventuple_empty_two_yes\syst_helpers_seventuple_empty_two_nop} \def\syst_helpers_seventuple_empty_two_yes[#1]% {\secondargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_seventuple_empty_three_yes\syst_helpers_seventuple_empty_three_nop} \def\syst_helpers_seventuple_empty_three_yes[#1]% {\thirdargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_seventuple_empty_four_yes\syst_helpers_seventuple_empty_four_nop} \def\syst_helpers_seventuple_empty_four_yes[#1]% {\fourthargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_seventuple_empty_five_yes\syst_helpers_seventuple_empty_five_nop} \def\syst_helpers_seventuple_empty_five_yes[#1]% {\fifthargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_seventuple_empty_six_yes\syst_helpers_seventuple_empty_six_nop} \def\syst_helpers_seventuple_empty_six_yes[#1]% {\sixthargumenttrue \toksapp\t_syst_aux{[{#1}]}% \futureexpand[\syst_helpers_seventuple_empty_seven_yes\syst_helpers_seventuple_empty_seven_nop} \def\syst_helpers_seventuple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \the\t_syst_aux[][][][][][][]} \def\syst_helpers_seventuple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \the\t_syst_aux[][][][][][]} \def\syst_helpers_seventuple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \the\t_syst_aux[][][][][]} \def\syst_helpers_seventuple_empty_four_nop {\fourthargumentfalse \fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \the\t_syst_aux[][][][]} \def\syst_helpers_seventuple_empty_five_nop {\fifthargumentfalse \sixthargumentfalse \seventhargumentfalse \the\t_syst_aux[][][]} \def\syst_helpers_seventuple_empty_six_nop {\sixthargumentfalse \seventhargumentfalse \the\t_syst_aux[][]} \def\syst_helpers_seventuple_empty_seven_nop {\seventhargumentfalse \the\t_syst_aux[]} \stoplmtxmode %D Aliases: \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} 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} \let\strippedcsname\csstring %D \macros %D {complexorsimple,complexorsimpleempty} %D %D Setups can be optional. A command expecting a setup is prefixed by \type %D {\complex}, a command without one gets the prefix \type {\simple}. Commands like %D 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 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, but changed into more %D versatile (more object oriented) ones using the \type {\get..argument} commands. \unexpanded\def\complexorsimple#1% {% \relax % prevents lookahead, brrr \doifelsenextoptional {\firstargumenttrue \csname\s!complex\csstring#1\endcsname} {\firstargumentfalse\csname\s!simple \csstring#1\endcsname}} \unexpanded\def\complexorsimpleempty#1% {% \relax % prevents lookahead, brrr \doifelsenextoptional {\firstargumenttrue \csname\s!complex\csstring#1\endcsname} {\firstargumentfalse\csname\s!complex\csstring#1\endcsname[]}} %D \macros %D {definecomplexorsimple,definecomplexorsimpleempty} %D %D The previous commands are used that often that we found it worthwile to offer two %D more alternatives. Watch the build in protection. \unexpanded\def\syst_helpers_complex_or_simple#1#2% {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#2}} \unexpanded\def\syst_helpers_complex_or_simple_empty#1% {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#1[]}} \unexpanded\def\definecomplexorsimple#1% {\unexpanded\edef#1{\syst_helpers_complex_or_simple \expandafter\noexpand\csname\s!complex\csstring#1\endcsname \expandafter\noexpand\csname\s!simple \csstring#1\endcsname}} \unexpanded\def\definecomplexorsimpleempty#1% {\unexpanded\edef#1{\syst_helpers_complex_or_simple_empty \expandafter\noexpand\csname\s!complex\csstring#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 %D We can add additional definitions later when we have defined \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 the lot in %D pieces so that we have no nested \type {\nextarguments} potentially being an %D \type {conditional} token. Okay, these macros are not called that often but it %D saves crap when tracing. \startmkivmode \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} \glet\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} \unexpanded\def\dosinglegroupempty#1% {\def\syst_helpers_get_grouped_argument_nested {\dontpermitspacesbetweengroups #1}% \syst_helpers_get_grouped_argument\firstargumenttrue\firstargumentfalse} \unexpanded\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} \unexpanded\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} \unexpanded\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} \unexpanded\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} \stopmkivmode \startlmtxmode \unexpanded\def\dosinglegroupempty#1% {\t_syst_aux{#1}% \futureexpand\bgroup\syst_helpers_single_empty_one_yes\syst_helpers_single_group_empty_one_nop} \def\syst_helpers_single_group_empty_one_nop {\firstargumentfalse \the\t_syst_aux{}} \unexpanded\def\dodoublegroupempty#1% {\t_syst_aux{#1}% \futureexpand\bgroup\syst_helpers_group_double_empty_one_yes\syst_helpers_group_double_empty_one_nop} \def\syst_helpers_group_double_empty_one_yes#1% {\firstargumenttrue \toksapp\t_syst_aux{{#1}}% \futureexpand\bgroup\syst_helpers_double_empty_two_yes\syst_helpers_group_double_empty_two_nop} \def\syst_helpers_group_double_empty_one_nop {\firstargumentfalse \secondargumentfalse \the\t_syst_aux{}{}} \def\syst_helpers_group_double_empty_two_nop {\secondargumentfalse \the\t_syst_aux{}} \unexpanded\def\dotriplegroupempty#1% {\t_syst_aux{#1}% \futureexpand\bgroup\syst_helpers_group_triple_empty_one_yes\syst_helpers_group_triple_empty_one_nop} \def\syst_helpers_group_triple_empty_one_yes#1% {\firstargumenttrue \toksapp\t_syst_aux{{#1}}% \futureexpand\bgroup\syst_helpers_group_triple_empty_two_yes\syst_helpers_group_triple_empty_two_nop} \def\syst_helpers_group_triple_empty_two_yes#1% {\secondargumenttrue \toksapp\t_syst_aux{{#1}}% \futureexpand\bgroup\syst_helpers_triple_empty_three_yes\syst_helpers_group_triple_empty_three_nop} \def\syst_helpers_group_triple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \the\t_syst_aux{}{}{}} \def\syst_helpers_group_triple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \the\t_syst_aux{}{}} \def\syst_helpers_group_triple_empty_three_nop {\thirdargumentfalse \the\t_syst_aux{}} \unexpanded\def\doquadruplegroupempty#1% {\t_syst_aux{#1}% \futureexpand\bgroup\syst_helpers_group_quadruple_empty_one_yes\syst_helpers_group_quadruple_empty_one_nop} \def\syst_helpers_group_quadruple_empty_one_yes#1% {\firstargumenttrue \toksapp\t_syst_aux{{#1}}% \futureexpand\bgroup\syst_helpers_group_quadruple_empty_two_yes\syst_helpers_group_quadruple_empty_two_nop} \def\syst_helpers_group_quadruple_empty_two_yes#1% {\secondargumenttrue \toksapp\t_syst_aux{{#1}}% \futureexpand\bgroup\syst_helpers_group_quadruple_empty_three_yes\syst_helpers_group_quadruple_empty_three_nop} \def\syst_helpers_group_quadruple_empty_three_yes#1% {\thirdargumenttrue \toksapp\t_syst_aux{{#1}}% \futureexpand\bgroup\syst_helpers_quadruple_empty_four_yes\syst_helpers_group_quadruple_empty_four_nop} \def\syst_helpers_group_quadruple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \the\t_syst_aux{}{}{}{}} \def\syst_helpers_group_quadruple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \the\t_syst_aux{}{}{}} \def\syst_helpers_group_quadruple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \the\t_syst_aux{}{}} \def\syst_helpers_group_quadruple_empty_four_nop {\fourthargumentfalse \the\t_syst_aux{}} \unexpanded\def\doquintuplegroupempty#1% {\t_syst_aux{#1}% \futureexpand\bgroup\syst_helpers_group_quintuple_empty_one_yes\syst_helpers_group_quintuple_empty_one_nop} \def\syst_helpers_group_quintuple_empty_one_yes#1% {\firstargumenttrue \toksapp\t_syst_aux{{#1}}% \futureexpand\bgroup\syst_helpers_group_quintuple_empty_two_yes\syst_helpers_group_quintuple_empty_two_nop} \def\syst_helpers_group_quintuple_empty_two_yes#1% {\secondargumenttrue \toksapp\t_syst_aux{{#1}}% \futureexpand\bgroup\syst_helpers_group_quintuple_empty_three_yes\syst_helpers_group_quintuple_empty_three_nop} \def\syst_helpers_group_quintuple_empty_three_yes#1% {\thirdargumenttrue \toksapp\t_syst_aux{{#1}}% \futureexpand\bgroup\syst_helpers_group_quintuple_empty_four_yes\syst_helpers_group_quintuple_empty_four_nop} \def\syst_helpers_group_quintuple_empty_four_yes#1% {\fourthargumenttrue \toksapp\t_syst_aux{{#1}}% \futureexpand\bgroup\syst_helpers_quintuple_empty_five_yes\syst_helpers_group_quintuple_empty_five_nop} \def\syst_helpers_group_quintuple_empty_one_nop {\firstargumentfalse \secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \the\t_syst_aux{}{}{}{}{}} \def\syst_helpers_group_quintuple_empty_two_nop {\secondargumentfalse \thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \the\t_syst_aux{}{}{}{}} \def\syst_helpers_group_quintuple_empty_three_nop {\thirdargumentfalse \fourthargumentfalse \fifthargumentfalse \the\t_syst_aux{}{}{}} \def\syst_helpers_group_quintuple_empty_four_nop {\fourthargumentfalse \fifthargumentfalse \the\t_syst_aux{}{}} \def\syst_helpers_group_quintuple_empty_five_nop {\fifthargumentfalse \the\t_syst_aux{}} \stoplmtxmode %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 used to select %D arguments. Their names explain their 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} \unexpanded\def\firstoftwounexpanded #1#2{#1} \unexpanded\def\secondoftwounexpanded #1#2{#2} \unexpanded\def\firstofthreeunexpanded #1#2#3{#1} \unexpanded\def\secondofthreeunexpanded#1#2#3{#2} \unexpanded\def\thirdofthreeunexpanded #1#2#3{#3} %D \macros %D {globalletempty,letempty, %D letvalueempty,letgvalueempty, %D letvaluerelax,letgvaluerelax} %D %D Trivial: \unexpanded\def\letempty #1{\let #1\empty} \unexpanded\def\globalletempty#1{\glet#1\empty} \unexpanded\def\letvalueempty #1{\expandafter\let \csname#1\endcsname\empty} \unexpanded\def\letgvalueempty#1{\expandafter\glet\csname#1\endcsname\empty} \unexpanded\def\letvaluerelax #1{\expandafter\let \csname#1\endcsname\relax} \unexpanded\def\letgvalurelax #1{\expandafter\glet\csname#1\endcsname\relax} \unexpanded\def\relaxvalueifundefined#1% {\ifcsname#1\endcsname \else \expandafter\let\csname#1\endcsname\relax \fi} %D \macros %D {wait} %D %D The next macro hardly needs explanation. Because no nesting is to be expected, we %D can reuse \type {\wait} within \type {\wait} itself. \unexpanded\def\wait {\begingroup \read16 to \wait \endgroup} %D \macros %D {writestring,writeline, %D writestatus,statuswidth,normalwritestatus} %D %D Maybe one didn't notice, but we've already introduced a macro for showing %D messages. In the multi||lingual modules, we will also introduce a mechanism for %D message passing. For 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 of the identification %D string with the macro \type {\statuswidth}. \setnewconstant\statuswidth 15 \setnewconstant\statuswrite 128 % \pluscxxviii \ifdefined\writestring \else \unexpanded\def\writestring{\immediate\write\statuswrite} \unexpanded\def\writeline {\writestring{}} \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: \let\immediatemessage\clf_immediatemessage % {} mandate %D \macros %D {rawgetparameters} %D %D A raw and dirty alternative for \type {\getparameters}; no 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 used in this and %D some other modules to enforce a user specified \type {\doglobal} action. The last %D and often only global assignment in a macro is done with \type {\dodoglobal}, but %D all preceding ones with \type {\redoglobal}. When using only alternatives, one %D can 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}, \TEX's fake boolean %D type. Not being a primitive, \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} are needed: % \unexpanded\def\newif#1% uses the original plain \@if % {\privatescratchcounter\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\privatescratchcounter} \normalprotected\def\newif#1% see syst-ini.mkiv {\let\new_if_saved\newif \let\newif\new_if_check \expandafter\redoglobal\expandafter\def\csname\expandafter\newif\csstring#1true\endcsname {\let#1\iftrue }% \expandafter\redoglobal\expandafter\def\csname\expandafter\newif\csstring#1false\endcsname{\let#1\iffalse}% \dodoglobal\csname\expandafter\newif\csstring#1false\endcsname \let\newif\new_if_saved} %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 % % \glet\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 % \starttexdefinition unexpanded test #1 % [here #1] % \stoptexdefinition % % \starttexdefinition global unexpanded test % [here test] % \stoptexdefinition % % \scratchcounter=123 % % \starttexdefinition global unexpanded expanded test #oeps % [here #oeps: \the\scratchcounter] % \stoptexdefinition % \bgroup \obeylines % % \glet\stoptexdefinition\relax % % \unexpanded\gdef\starttexdefinition% % {\bgroup% % \obeylines% % \syst_helpers_start_tex_definition_one} % % \gdef\syst_helpers_start_tex_definition_one#1 % {\catcode\endoflineasciicode\ignorecatcode% % \syst_helpers_start_tex_definition_two{#1}} % % \gdef\syst_helpers_start_tex_definition_two#1#2\stoptexdefinition% % {\egroup% % \ctxcommand{thetexdefinition("#1")}{#2}} % % \egroup \bgroup \obeylines \glet\stoptexdefinition\relax \unexpanded\gdef\starttexdefinition% {\bgroup% \obeylines% \syst_helpers_start_tex_definition} \gdef\syst_helpers_start_tex_definition#1 {\catcode\endoflineasciicode\ignorecatcode% \clf_texdefinition_one{#1}} \gdef\dostarttexdefinition#1\stoptexdefinition% {\egroup% \clf_texdefinition_two{#1}} \egroup % \unexpanded\def\texdefinition#1{\csname\ifcsname#1\endcsname#1\else donothing\fi\endcsname} % todo: a nop cs: char 0 or some corenamespace \unexpanded\def\texdefinition#1{\begincsname#1\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, but fortunately we %D can store numbers in a macro. We can increment such pseudo \COUNTERS\ with \type %D {\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 \type {\counter} is 20, 16, 17 %D and~18. Of course there is 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 at~0. It is %D nevertheless better to define a \COUNTER\ explicitly. One reason could be that %D the \COUNTER\ can be part of a test with \type {\ifnum} and this conditional does %D not accept undefined macro's. The \COUNTER\ in our example can for instance be %D defined with: %D %D \starttyping %D \newcounter\counter %D \stoptyping %D %D The command \type {\newcounter} must not be confused with \type {\newcount}! Of %D course this mechanism is much slower than using \TEX's \COUNTERS\ directly. In %D practice \COUNTERS\ (and therefore our pseudo counters too) are seldom the %D bottleneck in the processing of a text. Apart from some other incompatilities we %D want to mention a pitfal 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 second number after %D reading \type {\pseudocounter}, while in the second test, it stops reading after %D having encountered a real one. Tests like the first one therefore can give %D unexpected results, for instance execution of \type {\doif} even if both numbers %D 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% {\def\m_syst_action_yes{\syst_helpers_do_do_do_increment#1}% \def\m_syst_action_nop{\syst_helpers_do_do_do_increment#1,\plusone}% \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop} \def\syst_helpers_do_do_decrement(#1% {\def\m_syst_action_yes{\syst_helpers_do_do_do_decrement#1}% \def\m_syst_action_nop{\syst_helpers_do_do_do_decrement#1,\plusone}% \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop} \unexpanded\def\increment{\doifelsenextcharcs(\syst_helpers_do_do_increment\syst_helpers_do_increment} \unexpanded\def\decrement{\doifelsenextcharcs(\syst_helpers_do_do_decrement\syst_helpers_do_decrement} \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\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 signaling. A signal is a small %D (invisible) kern or penalty that signals the next macro that something just %D happened. This macro can take any action depending on the previous signal. %D Signals must be unique and the next macro takes care of that. %D %D \starttyping %D \newsignal\somesignal %D \stoptyping %D %D Signals old dimensions and can be used in skips, kerns and tests like \type %D {\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} 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% % %{\ifx#1\letterbackslash\else#1\fi} % {\if\noexpand#1\letterbackslash\else#1\fi} \let\checkedstrippedcsname\csstring %D \macros %D {savenormalmeaning} %D %D We will use this one in: \unexpanded\def\savenormalmeaning#1% {\ifcsname normal\csstring#1\endcsname \else \expandafter\let\csname normal\csstring#1\endcsname#1% \fi} %D \macros %D {dorecurse,recurselevel,recursedepth, %D dostepwiserecurse} %D %D \TEX\ does not offer us powerfull for||loop mechanisms. On the other hand its %D recursion engine is quite unique. We therefore identify the for||looping macros %D by this method. The most simple alternative is the one that only needs a number. %D %D \starttyping %D \dorecurse {n} {whatever we want} %D \stoptyping %D %D This macro can be nested without problems and therefore be used in situations %D where \PLAIN\ \TEX's \type {\loop} macro ungracefully fails. The current value of %D the counter is available in \type {\recurselevel}, before as well as after the %D \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 \type {\recurselevel} concern the %D outer loop, while the third and fifth one concern the inner loop. The depth of %D the nesting is available for inspection in \type {\recursedepth}. %D %D Both \type {\recurselevel} and \type {\recursedepth} are macros. The real %D \COUNTERS\ are hidden from the user because 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 \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname{#4}% \expandafter\glet\csname\??recurseindex \the\outerrecurse\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\the\outerrecurse\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\the\outerrecurse\endcsname \global\advance\outerrecurse\minusone} % \unexpanded\def\nonostepwiserecurse#1#2#3% % {\expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\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\the\outerrecurse\endcsname{#2}% \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}} \unexpanded\def\syst_helpers_recurse_y#1#2% {\global\advance\outerrecurse \plusone \expandafter\glet\csname\??recurseindex\the\outerrecurse\endcsname\recurselevel \let\recurselevel\!!plusone #2% \expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\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\the\outerrecurse\endcsname \global\advance\outerrecurse \minusone } %D \macros %D {dowith} %D %D Here's a loop over whatever is in a list: %D %D \starttyping %D \dowith{a,b,c}{[#1]} %D \stoptyping \unexpanded\def\dowith#1#2% {\def\syst_helpers_with##1{#2}% \normalexpanded{\processcommalist[#1]}\syst_helpers_with} %D \macros %D {doloop,exitloop} %D %D Sometimes loops are not determined by counters, but by (a combinations of) %D conditions. We therefore implement a straightforward loop, which can only be left %D when we explictly exit it. Nesting is supported. First we present a more %D 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 \type {\loopdepth}. \let\endofloop\donothing % maybe \syst_helpers_loop_end \unexpanded\def\doloop#1% {\global\advance\outerrecurse \plusone \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname{#1}% \expandafter\glet\csname\??recurseindex \the\outerrecurse\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\the\outerrecurse\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} statement that should %D 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 means of a %D \type {#1}. I decided to pass the more frequently used level as \type {#1} and %D the less favoured depth as \type {#2}. The 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 expansion here. %D %D \starttyping %D \dorecurse{3}{\expanded{\definesymbol[test-\recurselevel][xx-\recurselevel]}} %D \stoptyping \def\syst_helpers_recurse_content {\csname\??recurseaction\the\outerrecurse\expandafter\expandafter\expandafter\endcsname \expandafter\expandafter\expandafter{\expandafter\recurselevel\expandafter}\expandafter{\the\outerrecurse}} \unexpanded\def\syst_helpers_recurse_x#1#2% {\global\advance\outerrecurse \plusone \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#2}% \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}} \unexpanded\def\syst_helpers_recurse_y#1#2% {\global\advance\outerrecurse \plusone \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel \let\recurselevel\!!plusone \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#2}% \syst_helpers_recurse_content \expandafter\let\expandafter\recurselevel\csname\??recurseindex\the\outerrecurse\endcsname \global\advance\outerrecurse \minusone} \unexpanded\def\doloop#1% {\global\advance\outerrecurse \plusone \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#1}% \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel \let\endofloop\syst_helpers_loop \syst_helpers_loop1} % no \plusone else \recurselevel wrong % for instance: % % \unexpanded\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 % {\global\advance\outerrecurse \plusone % \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#4}% % \expandafter\glet\csname\??recurseindex \the\outerrecurse\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}}} % % faster: % % \unexpanded\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 % {\global\advance\outerrecurse \plusone % \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#4}% % \expandafter\glet\csname\??recurseindex \the\outerrecurse\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 % % nicer: \installsystemnamespace{recursestepwise} \unexpanded\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 {\global\advance\outerrecurse \plusone \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#4}% \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recurselevel \csname\??recursestepwise % we need the x in order to avoid the \relax that tex adds \ifnum#3>\zerocount \ifnum#2<#1x\else d\fi \else\ifnum#3<\zerocount \ifnum#1<#2x\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 x}\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 % \expandafter\gdef\csname\??recurseaction\the\outerrecurse\endcsname##1##2{#4}% % \expandafter\glet\csname\??recurseindex \the\outerrecurse\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\the\outerrecurse\endcsname##1{\edef\recursestring{##1}#2}% \expandafter\glet\csname\??recurseindex \the\outerrecurse\endcsname\recursestring \normalexpanded{\processcommalist[#1]{\expandafter\noexpand\csname\??recurseaction\the\outerrecurse\endcsname}}% \expandafter\let\expandafter\recursestring\csname\??recurseindex\the\outerrecurse\endcsname \global\advance\outerrecurse\minusone} %D \macros %D {newevery,everyline,EveryLine,EveryPar} %D %D Lets skip to something quite different. It's common use to use \type {\everypar} %D for special purposes. In \CONTEXT\ we use this primitive for locating sidefloats. %D This means that when user assignments to \type {\everypar} can interfere with %D those of the package. We therefore introduce \type {\EveryPar}. %D %D The same goes for \type {\EveryLine}. Because \TEX\ offers no \type {\everyline} %D primitive, we have to call for \type {\everyline} when we are working on a line %D by line basis. Just by calling \type {\EveryPar{}} and \type {\EveryLine{}} we %D 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\csstring#1\endcsname % \def#2{\syst_helpers_every#1}% % \fi\fi} % % \unexpanded\def\syst_helpers_every#1% % {\expandafter\removetoks\expandafter\the\csname\??extraevery\csstring#1\endcsname\from#1% % \expandafter\appendtoks\expandafter\the\csname\??extraevery\csstring#1\endcsname\to #1% % \csname\??extraevery\csstring#1\endcsname} \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\csstring#1\endcsname \edef#2{\syst_helpers_every#1\csname\??extraevery\csstring#1\endcsname}% \fi\fi} \unexpanded\def\syst_helpers_every#1#2% {\removetoks\the#2\from#1% \appendtoks\the#2\to #1% #2} %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 equals \type {\relax}, %D the first token list is created unless it is already defined. %D %D Technically spoken we could have used the method we are going to present in the %D visual debugger. First we save the primitive \type{\everypar}: %D %D \starttyping %D \let\normaleverypar=\everypar %D \stoptyping %D %D Next we allocate a \TOKENLIST\ named \type{\everypar}, which means that %D \type{\everypar} is no longer a primitive but something like \type{\toks44}. %D %D \starttyping %D \newtoks\everypar %D \stoptyping %D %D Because \TEX\ now executes \type{\normaleverypar} instead of \type{\everypar}, we %D are ready to assign some tokens to 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 every time he expects %D 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 confusing situations, %D especially when other packages are used, but it's this kind of tricks that make %D \TEX\ so powerful. %D \macros %D {convertargument,convertcommand,convertvalue} %D %D Some persistent experimenting led us to the next macro. This macro converts a %D parameter or an expanded macro to it's 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 or the terminal %D without problems. In \CONTEXT\ we use this macro for generating registers and %D 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 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 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 seperation. The next %D command takes care of propper 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_t_{\if#2\_e_t_x_}% \def\syst_helpers_check_else_assignment_if#1=#2#3\_e_o_t_{\unless\if#2\_e_t_x_}% \unexpanded\def\doifelseassignment#1% {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=\_e_t_x_\_e_t_x_\_e_o_t_ \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} \unexpanded\def\doifelseassignmentcs#1#2#3% {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=\_e_t_x_\_e_t_x_\_e_o_t_ \expandafter#3% \else \expandafter#2% \fi} \let\doifassignmentelse \doifelseassignment \let\doifassignmentelsecs\doifelseassignmentcs \newif\ifassignment \unexpanded\def\docheckassignment#1% {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=\_e_t_x_\_e_t_x_\_e_o_t_ \assignmentfalse \else \assignmenttrue \fi} %D These can be used for cases where we want less tracing noise. \unexpanded\def\validassignment#1% {\expandafter\syst_helpers_check_else_assignment_if\detokenize{#1}=\_e_t_x_\_e_t_x_\_e_o_t_} \unexpanded\def\novalidassignment#1% {\expandafter\syst_helpers_check_if_assignment_else\detokenize{#1}=\_e_t_x_\_e_t_x_\_e_o_t_} %D In \ETEX\ we can use \type {\detokenize} and gain some speed, but in general far %D less that 1\% for \type {\convertargument} and nil for \type {\convertcommand}. %D This macro is more robust than the pure \TEX\ one, something I found out when %D primitives like \type {\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 out that the \ETEX\ %D method fails on for instance \type {\jobname} in the sense that it returns the %D filename instead of just \type {\jobname}. So far this does not give real %D problems. %D This is typically a macro that one comes to after reading the \TEX book %D carefully. Even then, the definite solution was found after rereading the \TEX %D book. The first implementation was: %D %D \starttyping %D \def\doconvertargument#1->#2\\\\{#2} %D \stoptyping %D %D The \type {-}, the delimiter \type {\\\\} and the the second argument are %D 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 meanings expand to %D something \type {->}. This is no problem in the \ETEX\ implementation, but since %D we want 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\doifelsemeaning#1#2% {\edef\m_syst_string_one{\normalmeaning#1}% \def \m_syst_string_two{#2}% \edef\m_syst_string_two{\normalmeaning\m_syst_string_two}% \ifx\m_syst_string_one\m_syst_string_two \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \let\doifmeaningelse\doifelsemeaning %D \macros %D {doifsamestringselse,doifsamestring,doifnotsamestring} %D %D The next comparison macro converts the arguments into expanded strings. This %D command can be used to compare for instance \type {\jobname} with a name stored %D 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\doifelsesamestring{\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 } \let\doifsamestringelse\doifelsesamestring %D \macros %D {ConvertToConstant,ConvertConstantAfter} %D %D When comparing arguments with a constant, we can get into trouble when this %D argument consists of tricky expandable commands. One solution for this is %D converting the 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 can give troubles. %D 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 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 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 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 \doifelseinstring\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 here are our gobbling %D macros. %D %D In \CONTEXT\ we use a lot of \type {\start}||\type {\stop} like constructions. %D Sometimes, the \type {\stop} is used as a 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 defined at format %D generation time or during a job. This means that we cannot hardcode the \type %D {\stop} criterium. Only after completely understanding \type {\csname} and \type %D {\expandafter} I was able to to implement a solution, starting with: %D %D \starttyping %D \grabuntil{stop}\command %D \stoptyping %D %D This commands executes, after having encountered \type {\stop} the command \type %D {\command}. This command receives as argument the text preceding the \type %D {\stop}. 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 form \type{#1} are fixed %D the the moment the argument is read in. Normally this is no problem, but for %D instance verbatim 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 \type {{}} but by %D \type {\bgroup} and \type {\egroup}. Such an argument fails, because the \type %D {\bgroup} is een as the 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 at the end of the line. %D The solution mentioned before does 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} % % \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_pickup#1#2#3% no inner group (so no kerning interference) % {\bgroup % \def\m_syst_helpers_handle_group_before{#1}% % \def\m_syst_helpers_handle_group_after {#2\egroup#3}% % \afterassignment\m_syst_helpers_handle_group_pickup_before % \let\next=} % % \def\m_syst_helpers_handle_group_pickup_before % {\bgroup % \aftergroup\m_syst_helpers_handle_group_after % \m_syst_helpers_handle_group_before} % % \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} \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 {\begingroup \aftergroup\m_syst_helpers_handle_group_a \aftergroup\endgroup \m_syst_helpers_handle_group_b} \def\syst_helpers_handle_group_nop_b {\bgroup \aftergroup\m_syst_helpers_handle_group_a \aftergroup\egroup \m_syst_helpers_handle_group_b} \unexpanded\def\syst_helpers_handle_group_normal {\bgroup \afterassignment\m_syst_helpers_handle_group_normal_before \let\next=} \def\m_syst_helpers_handle_group_normal_before {\bgroup \m_syst_helpers_handle_group_b \bgroup \aftergroup\m_syst_helpers_handle_group_a \aftergroup\egroup \aftergroup\egroup} \unexpanded\def\syst_helpers_handle_group_simple% no inner group (so no kerning interference) {\bgroup \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_b} \def\m_syst_helpers_handle_group_simple_after {\m_syst_helpers_handle_group_a \egroup}% \unexpanded\def\syst_helpers_handle_group_pickup% no inner group (so no kerning interference) {\bgroup \afterassignment\m_syst_helpers_handle_group_pickup_before \let\next=} \def\m_syst_helpers_handle_group_pickup_before {\bgroup \aftergroup\m_syst_helpers_handle_group_a \aftergroup\egroup \aftergroup\m_syst_helpers_handle_group_p \m_syst_helpers_handle_group_b} \unexpanded\def\syst_helpers_handle_group_nop_x {\ifnum\currentgrouptype=\semisimplegroupcode \begingroup \aftergroup\endgroup \else \bgroup \aftergroup\egroup \fi \m_syst_helpers_handle_group_b} \unexpanded\def\syst_helpers_handle_group_normal_x {\bgroup \afterassignment\m_syst_helpers_handle_group_normal_before_x \let\next=} \def\m_syst_helpers_handle_group_normal_before_x {\bgroup \m_syst_helpers_handle_group_b \bgroup \aftergroup\egroup \aftergroup\egroup} %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 fact that \type %D {\futurelet} obeys blank spaces, and a line||ending token is treated as a blank %D space. So the final implementation became: % \unexpanded\def\groupedcommand#1#2% % {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}} % % \unexpanded\def\groupedcommandcs#1#2% % {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}} % % \unexpanded\def\triggergroupedcommand#1% % {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{}}{\syst_helpers_handle_group_nop{#1}{}}} % % \unexpanded\def\triggergroupedcommandcs#1% % {\doifelsenextbgroup{\syst_helpers_handle_group_normal{#1}{}}{\syst_helpers_handle_group_nop{#1}{}}} % % \unexpanded\def\simplegroupedcommand#1#2% % {\doifelsenextbgroup{\syst_helpers_handle_group_simple{#1}{#2}}{\syst_helpers_handle_group_nop{#1}{#2}}} % % \unexpanded\def\pickupgroupedcommand#1#2#3% % {\doifelsenextbgroup{\syst_helpers_handle_group_pickup{#1}{#2}{#3}}{\syst_helpers_handle_group_nop{#1}{#2}}} \startmkivmode \unexpanded\def\groupedcommand#1#2% {\def\m_syst_helpers_handle_group_b{#1}% \def\m_syst_helpers_handle_group_a{#2}% \doifelsenextbgroupcs\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop} \unexpanded\def\groupedcommandcs#1#2% {\let\m_syst_helpers_handle_group_b#1% \let\m_syst_helpers_handle_group_a#2% \doifelsenextbgroupcs\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop} \unexpanded\def\simplegroupedcommand#1#2% {\def\m_syst_helpers_handle_group_b{#1}% \def\m_syst_helpers_handle_group_a{#2}% \doifelsenextbgroupcs\syst_helpers_handle_group_simple\syst_helpers_handle_group_nop} \unexpanded\def\pickupgroupedcommand#1#2#3% {\def\m_syst_helpers_handle_group_b{#1}% \def\m_syst_helpers_handle_group_a{#2}% \def\m_syst_helpers_handle_group_p{#2}% \doifelsenextbgroupcs\syst_helpers_handle_group_pickup\syst_helpers_handle_group_nop} \unexpanded\def\triggergroupedcommand#1% {\def\m_syst_helpers_handle_group_b{#1}% \doifelsenextbgroupcs\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x} \unexpanded\def\triggergroupedcommandcs#1% {\let\m_syst_helpers_handle_group_b#1% \doifelsenextbgroupcs\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x} \stopmkivmode \startlmtxmode \unexpanded\def\groupedcommand#1#2% {\def\m_syst_helpers_handle_group_b{#1}% \def\m_syst_helpers_handle_group_a{#2}% \futureexpandis\bgroup\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop} \unexpanded\def\groupedcommandcs#1#2% {\let\m_syst_helpers_handle_group_b#1% \let\m_syst_helpers_handle_group_a#2% \futureexpandis\bgroup\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop} \unexpanded\def\simplegroupedcommand#1#2% {\def\m_syst_helpers_handle_group_b{#1}% \def\m_syst_helpers_handle_group_a{#2}% \futureexpandis\bgroup\syst_helpers_handle_group_simple\syst_helpers_handle_group_nop} \unexpanded\def\pickupgroupedcommand#1#2#3% {\def\m_syst_helpers_handle_group_b{#1}% \def\m_syst_helpers_handle_group_a{#2}% \def\m_syst_helpers_handle_group_p{#2}% \futureexpandis\bgroup\syst_helpers_handle_group_pickup\syst_helpers_handle_group_nop} \unexpanded\def\triggergroupedcommand#1% {\def\m_syst_helpers_handle_group_b{#1}% \futureexpandis\bgroup\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x} \unexpanded\def\triggergroupedcommandcs#1% {\let\m_syst_helpers_handle_group_b#1% \futureexpandis\bgroup\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x} \stoplmtxmode %D Users should be aware of the fact that grouping can interfere with ones paragraph %D settings that are executed after the paragraph is closed. One should therefore %D explictly close the paragraph with \type {\par}, else the settings will be %D 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 user defined commands collide %D with those that are part of the system. The next macro gives a warning when a %D command is already defined. We considered blocking the definition, but this is %D 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 \type {CAPITALS}. This suggestion %D is feasible, because \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 first grabbing the %D contents of the paragraph and processing this contents grouped. The next macro %D for instance typesets 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. When we started %D typesetting with \TEX, we already had produced lots of text in plain \ASCII. In %D producing such simple formatted texts, we adopted an open layout, and when %D switching to \TEX, we continued this open habit. Although \TEX\ permits a cramped %D and badly formatted source, it adds to confusion and sometimes introduces errors. %D 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 specifications. The %D definition of the macro handling \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 ways. The recent version, %D the fourth one in a row, originally was far more complicated, but some %D functionality has been moved to other macros. %D %D We start with the more simple but in some cases more appropriate alternative is %D \type {\GotoPar}. This one leaves \type {\par} unchanged and is therefore more %D robust. On the other hand, \type {\AfterPar} is not supported. \newtoks\BeforePar \newtoks\AfterPar \def\redowithpar\par {\doifelsenextchar\par\redowithpar\dodowithpar}% \def\dowithpar#1#2% {\def\dodowithpar##1\par{#1##1#2}% \redowithpar\par} \def\redogotopar\par {\doifelsenextchar\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 macros expect an %D argument, it interprets a grouped sequence of characters a one token. While this %D adds to robustness and less ambiguous situations, we sometimes want to be a bit %D more flexible, or at least want to be a bit more tolerant 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 chapters, sections etc. The %D commands responsible for these activities accept several alternative ways of %D argument passing. In these examples, the \type {\par} can be omitted when an %D 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}}% \doifelsenextbgroup\syst_helpers_next_arg{\doifelsenextchar\par{#1{}}\syst_helpers_next_par}} %D The \type {p} in the previous command stands for paragraph. When we want to act %D upon words we can use the \type{w} alternative. %D %D \starttyping %D \dowithwargument\command %D \dowithwargument{... \command ...} %D \stoptyping %D %D The main difference bwteen two alternatives is in the handling of \type {\par}'s. %D This time the space token acts 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}}% \doifelsenextbgroup\syst_helpers_next_arg\syst_helpers_next_war} %D \macros %D {dorepeat,dorepeatwithcommand} %D %D When doing repetitive tasks, we stromgly advice to use \type {\dorecurse}. The %D next alternative however, suits 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 a row. In both %D commands, the \type {n*} is optional. When this specification is missing, the %D 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 are macros. This save %D 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\doifelsestringinstring#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_} \let\doifstringinstringelse\doifelsestringinstring %D \macros %D {appendtoks,prependtoks,appendtoksonce,prependtoksonce, %D doifintokselse,flushtoks,dotoks} %D %D We use tokenlists sparsely within \CONTEXT, because the comma separated lists are %D more suitable for the user 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 Knuth's \TEX book. \newtoks\t_syst_helpers_scratch \let \m_syst_helpers_scratch\empty % no longer \def but \let to target toks .. the space gobbling \relax will go % \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}% % \doifelseintoks\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}% % \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch % \donothing % \syst_helpers_prepend_toks_indeed} % \unexpanded\def\appendtoks#1\to#2% % {\toksapp#2{#1}% % \ifx\dodoglobal\relax\else % \dodoglobal#2#2% % \fi} % % \unexpanded\def\prependtoks#1\to#2% % {\tokspre#2{#1}% % \ifx\dodoglobal\relax\else % \dodoglobal#2#2% % \fi} % % \def\syst_helpers_append_toks_indeed % {\toksapp\m_syst_helpers_scratch\t_syst_helpers_scratch % \ifx\dodoglobal\relax\else % \dodoglobal\m_syst_helpers_scratch\m_syst_helpers_scratch % \fi} % % \def\syst_helpers_prepend_toks_indeed % {\tokspre\m_syst_helpers_scratch\t_syst_helpers_scratch % \ifx\dodoglobal\relax\else % \dodoglobal\m_syst_helpers_scratch\m_syst_helpers_scratch % \fi} \unexpanded\def\appendtoks#1\to#2% {\ifx\dodoglobal\relax \expandafter\toksapp \else \resetglobal \expandafter\gtoksapp \fi#2{#1}} \unexpanded\def\prependtoks#1\to#2% {\ifx\dodoglobal\relax \expandafter\tokspre \else \resetglobal \expandafter\gtokspre \fi#2{#1}} \def\syst_helpers_append_toks_indeed {\ifx\dodoglobal\relax \expandafter\toksapp \else \resetglobal \expandafter\gtoksapp \fi\m_syst_helpers_scratch\t_syst_helpers_scratch} \def\syst_helpers_prepend_toks_indeed {\ifx\dodoglobal\relax \expandafter\tokspre \else \resetglobal \expandafter\gtokspre \fi\m_syst_helpers_scratch\t_syst_helpers_scratch} \unexpanded\def\appendtoksonce#1\to#2% {\let\m_syst_helpers_scratch#2% \t_syst_helpers_scratch{#1}% \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch \donothing \syst_helpers_append_toks_indeed} \unexpanded\def\prependtoksonce#1\to#2% {\let\m_syst_helpers_scratch#2% \t_syst_helpers_scratch{#1}% \doifelseintoks\t_syst_helpers_scratch\m_syst_helpers_scratch \donothing \syst_helpers_prepend_toks_indeed} %D The test macro: \unexpanded\def\doifelseintoks#1#2% #1 en #2 zijn toks {\edef\asciia{\detokenize\expandafter{\the#1}}% \edef\asciib{\detokenize\expandafter{\the#2}}% \doifelsestringinstring\asciia\asciib} \let\doifintokselse\doifelseintoks %D Moved from \type {lxml-ini.tex} to here. This one is for generators that collect %D stuff piecewise, which is sometimes hard on mechanisms that grab content using %D delimiters: %D %D \starttyping %D \startcollecting %D \startcollect \bTABLE \stopcollect %D \startcollect \bTR \stopcollect %D \startcollect \bTD \stopcollect %D \startcollect foo\stopcollect %D \startcollect \eTD \stopcollect %D \startcollect \bTD \stopcollect %D \startcollect bar\stopcollect %D \startcollect \eTD \stopcollect %D \startcollect \eTR \stopcollect %D \startcollect \eTABLE \stopcollect %D \stopcollecting %D \stoptyping \newtoks \collectingtoks \unexpanded\def\startcollect #1\stopcollect {\toksapp \collectingtoks{#1}} \unexpanded\def\startexpandedcollect#1\stopexpandedcollect{\etoksapp\collectingtoks{#1}} \unexpanded\def\startcollecting{\collectingtoks\emptytoks} \unexpanded\def\stopcollecting {\the\collectingtoks} \unexpanded\def\collect {\toksapp \collectingtoks} \unexpanded\def\collectexpanded{\etoksapp\collectingtoks} %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} % \unexpanded\def\appendetoks#1\to#2% % {\etoksapp#2{#1}% % \ifx\dodoglobal\relax\else % \global#2#2% % \fi} % % \unexpanded\def\prependetoks#1\to#2% % {\etokspre#2{#1}% % \ifx\dodoglobal\relax\else % \global#2#2% % \fi} \unexpanded\def\appendetoks#1\to#2% {\ifx\dodoglobal\relax \expandafter\etoksapp \else \resetglobal \expandafter\xtoksapp \fi#2{#1}} \unexpanded\def\prependetoks#1\to#2% {\ifx\dodoglobal\relax \expandafter\etokspre \else \resetglobal \expandafter\xtokspre \fi#2{#1}} %D Hm. \unexpanded\def\flushtoks#1% nb: can reassign 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 the second one. The %D alternative looking more or less like the first one did not always give the %D results we needed. Both implementations show some insight in the manipulation of %D 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 {splitatperiod, %D {splitatcomma, %D splitatasterisk, %D splitatcolon, %D splitatcolons} \unexpanded\def\splitatperiod #1{\normalexpanded{\syst_helpers_splitatperiod #1}..\relax} \unexpanded\def\splitatcomma #1{\normalexpanded{\syst_helpers_splitatcomma #1},,\relax} % not at ", " \unexpanded\def\splitatasterisk#1{\normalexpanded{\syst_helpers_splitatasterisk#1}**\relax} \unexpanded\def\splitatcolon #1{\normalexpanded{\syst_helpers_splitatcolon #1}::\relax} \unexpanded\def\splitatcolons #1{\normalexpanded{\syst_helpers_splitatcolons #1}::::\relax} \unexpanded\def\syst_helpers_splitatperiod #1.#2.#3\relax#4#5{\def#4{#1}\def#5{#2}} \unexpanded\def\syst_helpers_splitatcomma #1,#2,#3\relax#4#5{\def#4{#1}\def#5{#2}} \unexpanded\def\syst_helpers_splitatasterisk #1*#2*#3\relax#4#5{\def#4{#1}\def#5{#2}} \unexpanded\def\syst_helpers_splitatcolon #1:#2:#3\relax#4#5{\def#4{#1}\def#5{#2}} \unexpanded\def\syst_helpers_splitatcolons #1::#2::#3\relax#4#5{\edef#4{#1}\edef#5{#2}} %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 later want the tools to %D append or remove items from such a list. When we add an item, we first check if %D it's already 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 implementation of the %D second command is more complecated, because we have to take leading spaces into %D account. Keep in mind that users may provide lists with spaces after the commas. %D When one item is left, we also have to get rid of 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. A fast appending alternative, %D without any testing, is 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 {\rawdoifelseinset{#1}#2\resetglobal {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}} \unexpanded\def\pretocommalist#1#2% {item} \cs {\rawdoifelseinset{#1}#2\resetglobal {\dodoglobal\edef#2{#1\ifx#2\empty\else,#2\fi}}} \unexpanded\def\robustdoifelseinset#1#2% {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#1}}}% \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#2}}}% \rawdoifelseinset\m_syst_string_one\m_syst_string_two} \let\robustdoifinsetelse\robustdoifelseinset \unexpanded\def\robustaddtocommalist#1#2% {item} \cs {\robustdoifelseinset{#1}#2\resetglobal {\dodoglobal\edef#2{\ifx#2\empty\else#2,\fi#1}}} \unexpanded\def\robustpretocommalist#1#2% {item} \cs {\robustdoifelseinset{#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 {\rawdoifelseinset{#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 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 fact that the way they %D handle expansion as well as the fact that they can be nested. This makes them %D kind of useless for 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% {\glet\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 catcode~12, while %D macronames only permit tokens with the catcode~11. As a result we cannot use the %D \type {.group} primitives. Those who want to know more about this kind of %D manipulations, we advice to study the \TEX book in detail. Because this macro %D does not do any assignment, we can use it 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 exactly states their %D 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\glet #1#2\glet #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 % \expandafter\glet\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}% % \expandafter\glet\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 % \expandafter\glet\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 % % slightly faster but more important: less tracing % % possible optimization \installmacrostack\foo: \syst_push_foo \syst_pop_foo \let\m_syst_helpers_push_macro\empty \newcount\c_syst_helpers_pop_count \def\syst_helpers_push_macro_new_global {\expandafter\newcount\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \global\advance\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname\plusone} \def\syst_helpers_push_macro_new_local {\expandafter\newcount\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \global\advance\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname\plusone} \unexpanded\def\globalpushmacro#1% {\xdef\m_syst_helpers_push_macro{\csstring#1}% \ifcsname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \global\advance\lastnamedcs\plusone \else \syst_helpers_push_macro_new_global \fi \expandafter\glet\csname\the\lastnamedcs\m_syst_helpers_push_macro\endcsname#1} \unexpanded\def\localpushmacro#1% this one can be used to push a value over an \egroup {\xdef\m_syst_helpers_push_macro{\csstring#1}% \ifcsname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \global\advance\lastnamedcs\plusone \else \syst_helpers_push_macro_new_local \fi \expandafter\glet\csname\the\lastnamedcs\m_syst_helpers_push_macro\endcsname#1} \unexpanded\def\globalpopmacro#1% {\xdef\m_syst_helpers_push_macro{\csstring#1}% \c_syst_helpers_pop_count\csname\??globalpushedmacro\m_syst_helpers_push_macro\endcsname \global\advance\lastnamedcs \minusone \expandafter\glet\expandafter#1\csname\the\c_syst_helpers_pop_count\m_syst_helpers_push_macro\endcsname} \unexpanded\def\localpopmacro#1% {\xdef\m_syst_helpers_push_macro{\csstring#1}% \c_syst_helpers_pop_count\csname\??localpushedmacro\m_syst_helpers_push_macro\endcsname \global\advance\lastnamedcs \minusone \expandafter\let\expandafter#1\csname\the\c_syst_helpers_pop_count\m_syst_helpers_push_macro\endcsname} \let\pushmacro\localpushmacro \let\popmacro \localpopmacro %D \macros %D {setlocalhsize,distributedhsize} %D %D Sometimes we need to work with the \type{ \hsize} that is corrected for %D indentation and left and right skips. The corrected value is available in \type %D {\localhsize}, which needs to be calculated with \type {\setlocalhsize} first. %D %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 value provided is added %D to \type {\localhsize}. \newdimen\localhsize \unexpanded\def\setlocalhsize % don't change ! {\doifelsenextoptional \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} \def\distributedhsize#1#2#3% {\dimexpr(#1-\numexpr#3-1\relax\dimexpr#2\relax)/#3\relax} \def\hsizefraction#1#2% {\dimexpr#1/#2\relax} %D \macros %D {doifvalue,doifnotvalue,doifelsevalue, %D doifnothing,doifsomething,doifelsenothing, %D doifvaluenothing,doifvaluesomething,doifelsevaluenothing} %D %D These \type {\if} commands can be used to access macros (or variables) that are %D normally accessed by using \type {\getvalue}. Using these alternatives safes us %D three tokens per call. Anyone familiar with the not||values ones, can derive %D 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\doifelsesomething#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} \let\doifvalueelse \doifelsevalue \let\doifnothingelse \doifelsenothing \let\doifsomethingelse \doifelsesomething \let\doifvaluenothingelse\doifelsevaluenothing %D \macros %D {doifemptyelsevalue, doifemptyvalue, doifnotemptyvalue} %D %D Also handy: \def\doifelseemptyvalue#1% {\expandafter\ifx\csname#1\endcsname\empty \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \let\doifemptyvalueelse\doifelseemptyvalue \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 \type {\doifallcommonelse}, where %D the first two 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\doifelseallcommon{\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 } \let\doifallcommonelse\doifelseallcommon %D \macros %D {DOIF,DOIFELSE,DOIFNOT} %D %D \TEX\ is case sensitive. When comparing arguments, this feature sometimes is less %D desirable, for instance when we compare filenames. The next three alternatives %D upcase their 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 end up as \LUA based helpers (i.e. consider 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 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 in the set~\type %D {#1}. The second one calls for \typ {\command [##1][#2]} and the third, well one %D may guess. 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 the \CONTEXT\ interactivity %D macros. When we use labeled destinations, we often cannot use all the characters %D we want. We therefore strip some of the troublemakers, like spaces, from the %D labels before we write them to the \DVI||file, which passes them to for instance %D a \POSTSCRIPT\ 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 to enclosed in \type %D {{}}. \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, like \type %D {\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 tables of contents, %D references, two||pass optimizations, sorted lists etc. This file is loaded as %D many times as needed. During such a pass we skip the commands thate are of no use %D at that moment. Because we don't want to come into trouble with undefined %D auxiliary commands, we call the macros in a way similar to \type {\getvalue}. The %D next macro take care of such executions and when not defined, gobbles the %D unwanted arguments. %D %D \starttyping %D \executeifdefined{name}\gobbleoneargument %D \stoptyping %D %D We can of course gobble more arguments using the appropriate gobbling command. \def\executeifdefined#1% #2 / never change this one again {\ifcsname#1\endcsname % \csname#1\expandafter\expandafter\expandafter\endcsname\expandafter\gobbleoneargument \expandafter\expandafter\expandafter\lastnamedcs\expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} %D This one also has the advantage that it is fully expandable and that it can be %D used after an assignment. %D \macros %D {doifsomespaceelse} %D %D The next command checks a string on the presence of a space and executed a %D command accordingly. %D %D \starttyping %D \doifsomespaceelse {tekst} {then ...} {else ...} %D \stoptyping %D %D We use this command in \CONTEXT\ for determing if an argument must be broken into %D words when made interactive. 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\doifelsesomespace#1% % #2#3% {\syst_helpers_if_some_space_else#1 @ @\_e_o_s_ % #3\else#2\fi} \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} \let\doifsomespaceelse\doifelsesomespace %D \macros %D {processseparatedlist} %D %D Maybe a bit late, but here is a more general version of the \type %D {\processcommalist} command. This time we don't handle nesting but accept %D 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 argument, which we remove %D 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 defined %D 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 containing keywords. %D Assignments are treated accordingly, 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 we decided best not %D to do so. \unexpanded\def\processassignlist#1[#2]#3% {\def\syst_helpers_process_assign_list_assign[##1=##2=##3]% {\doif{##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 index entries, the next %D 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 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 used big points (\TEX's %D bp). The next macros convert points and scaled points into big points. The magic %D factor $72/72.27$ can be found in most \TEX\ related books. %D %D \starttyping %D \ScaledPointsToBigPoints {number} \target %D \ScaledPointsToWholeBigPoints {number} \target %D \stoptyping %D %D In pure \TEX: % \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% % #2 % {\PointsToBigPoints{\number#1\scaledpoint}} % {#2} % % \unexpanded\def\ScaledPointsToWholeBigPoints#1% % #2 % {\PointsToWholeBigPoints{\number#1\scaledpoint}} % {#2} %D This is slower but cleaner and more accurate too. The only place these are still %D used is in a few backend related macros. \let\tobigpoints \clf_tobigpoints \let\towholebigpoints\clf_towholebigpoints \unexpanded\def\PointsToBigPoints #1#2{\edef#2{\tobigpoints #1}} % can be avoided \unexpanded\def\PointsToWholeBigPoints #1#2{\edef#2{\towholebigpoints#1}} % can be avoided \unexpanded\def\ScaledPointsToBigPoints #1#2{\edef#2{\tobigpoints #1\scaledpoint}} % obsolete \unexpanded\def\ScaledPointsToWholeBigPoints#1#2{\edef#2{\towholebigpoints#1\scaledpoint}} % obsolete %D \macros %D {PointsToReal} %D %D Points can be stripped from their suffix by using \type {\withoutpt}. The next %D 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 first token gets the %D whole first line. We can prevent this by saying: %D %D \starttyping %D \dontleavehmode %D \stoptyping %D %D This command is used in for instance the language module \type {lang-ini}. The %D 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 interfering grouping %D 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 {utfupper, utflower, 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 way. But %D nevertheless we provide them: \def\utfupper#1{\clf_upper{#1}} % expandable \def\utflower#1{\clf_lower{#1}} % expandable \unexpanded\def\uppercasestring#1\to#2{\dodoglobal\edef#2{\clf_upper{#1}}} \unexpanded\def\lowercasestring#1\to#2{\dodoglobal\edef#2{\clf_lower{#1}}} %D \macros %D {handletokens} %D %D With the next macro we enter a critical area of macro expansion. What we want is %D 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 specific tokens in a %D 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}, takes a real counter. %D The macro can be preceded by \type {\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\privatescratchcounter\plusone \fi \expandafter\syst_helpers_count_token \fi} \unexpanded\def\counttoken#1\in#2\to#3% {\privatescratchcounter\zerocount \def\m_syst_string_one{#1}% \def\m_syst_string_two{\end}% \syst_helpers_count_token#2\end \dodoglobal#3\privatescratchcounter} \unexpanded\def\counttokens#1\to#2% {\privatescratchcounter\zerocount \def\syst_helpers_count_token##1{\advance\privatescratchcounter\plusone}% \handletokens#1\with\syst_helpers_count_token \dodoglobal#2\privatescratchcounter} %D \macros %D {splitofftokens} %D %D Running this one not always gives the expected results. Consider for instance the %D macro for which I originally wrote this token handler. \unexpanded\def\splitofftokens#1\from#2\to#3% slow but hardly used {\ifnum#1>\zerocount \privatescratchcounter#1\relax \def\syst_helpers_split_off_tokens##1% {\ifnum\privatescratchcounter>\zerocount \advance\privatescratchcounter \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 up there. The reason for this is not that logical but follows from \TEX's %D However, the characters that we expect to find in \type {\test} just don't show %D sometimes 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 that are \type {\let} %D are not expanded! %D %D \unprotect\getbuffer[next]\protect %D %D That's why we finally end up with a macro that looks ahead by using an %D assignment, this time by using \type {\futurelet}, and grabbing an argument as %D well. That way we can handle the sentinal, a blank space and grouped 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 one does a trial %D typesetting run, for instance to determine dimensions. Some mechanisms, like %D object inclusion, can fail on such trials. Temporary setting the next boolean to %D true, helps a lot. The second boolena can be used to inhibit processing %D 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 sometimes makes sense to %D limit the precission. The next macro rounds a real number to two digits. It takes %D one 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{\clf_rounded\zerocount\numexpr#1\relax} \def\onedigitrounding #1{\clf_rounded\plusone \numexpr#1\relax} \def\twodigitrounding #1{\clf_rounded\plustwo \numexpr#1\relax} \def\threedigitrounding#1{\clf_rounded\plusthree\numexpr#1\relax} %D \macros %D {processcontent} %D %D This macro is first used in the tabulation macros. %D %D \starttyping %D \unexpanded\def\starthans% %D {\processcontent{stophans}\test{\message{\test}\wait}} %D \stoptyping \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 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\privatescratchdimen#1{#2}\edef#1{\the\privatescratchdimen}} %D \macros %D {doifsometokselse, doifsometoks} %D %D Not that fast I guess, but here's a way to test for token registers being empty. \unexpanded\def\doifelsesometoks#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} \let\doifsometokselse\doifelsesometoks \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 is for the bibliography module (still?): \let\syst_helpers_strict_inspect_next_character[ \def\syst_helpers_strict_inspect_next_character% no user macro ! {\ifx\nexttoken[% \expandafter\m_syst_action_yes \else \expandafter\m_syst_action_nop \fi} \unexpanded\def\strictdoifelsenextoptional#1#2% {\def\m_syst_action_yes{#1}% \def\m_syst_action_nop{#2}% \futurelet\nexttoken\syst_helpers_strict_inspect_next_character} \let\strictdoifnextoptionalelse\strictdoifelsenextoptional %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 (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 number, especially in %D full expandable macros where using \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 with a space- and %D relax-less \type {\purenumber}. This macro works ok with \type {\the}, \type %D {\number} as well 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 macros mood}. \def\dofilterfromstr#1#2% max n % no need to be fast {\expandafter \expandafter \expandafter \csstring \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 \csstring\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_freeze_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 {\the\measured} \def\measured#1% %{\dimexpr\ifcsname\??measure#1\endcsname\csname\??measure#1\endcsname\else\zeropoint\fi\relax} {\dimexpr\ifcsname\??measure#1\endcsname\lastnamedcs\else\zeropoint\fi\relax} % #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. \startmkivmode \def\doifelsedimension#1% {\afterassignment\syst_helpers_if_dimension_else\privatescratchdimen#1pt\relax} % \def\doifelsedimension#1% % {\expandafter\syst_helpers_if_dimension_else\immediateassignment\privatescratchdimen#1pt\relax} \def\syst_helpers_if_dimension_else#1% {\ifx#1\relax \expandafter\secondoftwoarguments \else % #1=p ... t\relax \expandafter\thirdoffourarguments \fi} \stopmkivmode \startlmtxmode \def\doifelsedimension#1% {\ifchkdim#1\or \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \stoplmtxmode \let\doifdimensionelse\doifelsedimension %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 \startmkivmode \installsystemnamespace{dimenchecka} \installsystemnamespace{dimencheckb} \installsystemnamespace{dimencheckc} \def\doifelsedimenstring#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 \stopmkivmode \startlmtxmode \let\doifelsedimenstring\doifelsedimension \stoplmtxmode \let\doifdimenstringelse\doifelsedimenstring %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% {\privatescratchcounter\catcode\spaceasciicode \catcode\spaceasciicode\ignorecatcode \scantextokens{\edef#2{#1}}% \catcode\spaceasciicode\privatescratchcounter} \unexpanded\def\unspaceafter#1#2% {\unspaceargument#2\to\ascii \expandafter#1\expandafter{\ascii}} % sometimes handy: \unexpanded\def\doifelsehasspace#1% {\edef\m_syst_string_one{#1}% \normalexpanded{\syst_helpers_if_has_space_else#1\space}\empty\relax} \let\doifhasspaceelse\doifelsehasspace \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} \let\doifflaggedelse\doifelseflagged \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\privatescratchcounter \endgroup \doubleexpandafter\secondoftwoarguments \else \endgroup \doubleexpandafter\firstoftwoarguments \fi \else \endgroup \expandafter\secondoftwoarguments \fi} % used ? \def\doifelsenonzeropositive#1% {\begingroup\afterassignment\syst_helpers_if_non_zero_positive_else\privatescratchcounter=0#1\relax\empty\end} \let\doifnonzeropositiveelse\doifelsenonzeropositive % 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} \let\resettimer \clf_resettimer \let\elapsedtime \clf_elapsedtime \let\elapsedseconds \elapsedtime \newcount\c_syst_helpers_test_feature_n \chardef \c_syst_helpers_test_feature_m\zerocount \def\currentfeaturetest{\number\c_syst_helpers_test_feature_n} \unexpanded\def\testfeature#1#2% {\chardef\c_syst_helpers_test_feature_m#1\relax \def\syst_helpers_test_feature_yes {\advance\c_syst_helpers_test_feature_n\plusone \ifnum\c_syst_helpers_test_feature_n>\c_syst_helpers_test_feature_m\else #2\expandafter\syst_helpers_test_feature_yes \fi}% \def\syst_helpers_test_feature_nop {\advance\c_syst_helpers_test_feature_n\plusone \ifnum\c_syst_helpers_test_feature_n>\c_syst_helpers_test_feature_m\else \expandafter\syst_helpers_test_feature_nop \fi}% \retestfeature} \unexpanded\def\retestfeature % timer support is new per 10/5/2005 {\bgroup \ifcase\interactionmode\let\wait\relax\fi \clf_resettimer \c_syst_helpers_test_feature_n\zerocount \syst_helpers_test_feature_nop \clf_benchmarktimer \writestatus\m!system{starting feature test (n=\number\c_syst_helpers_test_feature_m)}\wait \c_syst_helpers_test_feature_n\zerocount \syst_helpers_test_feature_yes \writestatus\m!system{\number\c_syst_helpers_test_feature_m\space feature tests 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. It's %D 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 \type %D {page-imp.tex} was not the best place to hide it. %D %D \startbuffer %D \def\DoSomething#1{ [item #1] } %D %D \processranges[1,4:5]\DoSomething \par %D \dowithrange {1,4:5}\DoSomething \par %D \stopbuffer %D %D \typebuffer \blank \getbuffer \blank \def\syst_helpers_with_range#1% {\splitstring#1\at:\to\m_syst_helpers_range_from\and\m_syst_helpers_range_to \ifx\m_syst_helpers_range_to\empty\let\m_syst_helpers_range_to\m_syst_helpers_range_from\fi \dostepwiserecurse\m_syst_helpers_range_from\m_syst_helpers_range_to\plusone{\m_helpers_range_action{##1}}}% \unexpanded\def\processranges[#1]#2% #1= n:m,p,q:r {\def\m_helpers_range_action{#2}% \processcommacommand[#1]\syst_helpers_with_range} \unexpanded\def\dowithrange#1#2% {\def\m_helpers_range_action{#2}% \processcommacommand[#1]\syst_helpers_with_range} % \def\DoSomething#1{ [item #1] } % \dowithrange[1,4:5]\DoSomething %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 {\doifelsenextchar\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 is not %D set in the font! (This will be overloaded) \unexpanded\def\signalcharacter{\char\zerocount} % \zwj % \unexpanded\def\signalcharacter % {\scratchcounter\normallanguage % \normallanguage\zerocount % \char\zerocount % \normallanguage\scratchcounter} %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{\clf_getsubstring{#3}{#1}{#2}} %D Other dimensions than pt (used in mb-mp) \def\converteddimen#1#2{\clf_converteddimen\dimexpr#1\relax{#2}} %D Maybe (looks ugly): %D %D \starttyping %D \doifcase {foo} %D {bar} {BAR} %D {foo} {FOO} %D {default} {DEFAULT} %D %D \doifcase {foo} %D {bar} {BAR} %D {foo} {\doifcase {bar} %D {bar} {BAR} %D {foo} {FOO} %D {default} {DEFAULT} %D } %D {default} {DEFAULT} %D \stoptyping % \doifcase {\btxfoundname{author}} % {author} {\btxflush{author}} % {editor} {\texdefinition{btx:apa:editor-or-editors}} % {title} {\texdefinition{btx:apa:title-subtitle-type}} % {default} {\btxflush{author}} % \unexpanded\def\doifcase#1% % {\edef\m_case_asked{#1}% % \syst_aux_case} % % \def\syst_aux_case#1% % {\edef\m_case_temp{#1}% % \ifx\m_case_temp\m_case_asked % \expandafter\syst_aux_case_yes % \else\ifx\m_case_temp\s!default % \doubleexpandafter\firstofoneargument % \else % \doubleexpandafter\syst_aux_case_nop % \fi\fi} % % \def\syst_aux_skip#1#2% % {\edef\m_case_temp{#1}% % \ifx\m_case_temp\s!default % \expandafter\syst_aux_done % \else % \expandafter\syst_aux_skip % \fi} % % \def\syst_aux_case_yes#1% % {\def\syst_aux_done{#1}% % \syst_aux_skip} % % \def\syst_aux_case_nop#1% % {\syst_aux_case} %D \macros %D {ntimes} %D %D some repetition: %D %D \startbuffer %D \ntimes{*}{20} %D \stopbuffer %D %D \typebuffer \blank gives: \getbuffer \blank %D %D This is not real fast but quite okay: %def\ntimes#1#2{\ifnum#2>\zerocount#1\ntimes{#1}{\numexpr#2-\plusone\relax}\fi} % 1.72 \def\ntimes#1#2{\clf_ntimes{#1}\numexpr#2\relax} % 0.33 %D Not that useful: % \unexpanded\def\doifelsestringbefore{\clf_doifelsestringbefore} % \unexpanded\def\doifelsestringafter {\clf_doifelsestringafter} % Could have been useful but not faster that addtocommalist and also expansion mess: % % \def\additemtolist#1#2{\normalexpanded{\noexpand\clf_additemtolist{#1}{#2}}} %D Experiment (sometimes looks nicer in code): \unexpanded\def\sameargumentscondition#1#2% {\edef\m_syst_string_one{#1}% \edef\m_syst_string_two{#2}% \ifx\m_syst_string_one\m_syst_string_two} \unexpanded\def\emptyargumentcondition#1% {\edef\m_syst_string_one{#1}% \ifx\m_syst_string_one\empty} % \unexpanded\def\hascommonargumentcondition#1#2{\clf_hascommonargumentcondition{#1}{#2}} % \let\hascommonargumentcondition \clf_hascommonargumentcondition % \unexpanded\def\hascommonargumentcondition{\clf_hascommonargumentcondition} \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} % \unexpanded\def\showtokenlist#1% % {\begingroup % \edef\tempstring{\the#1}% % \tx\ttbf\string#1: \tttf\meaning\tempstring % \endgroup} %D Not needed now, but more efficient that expanding % % \chardef\_E_O_T_0 % % \unexpanded\def\doifelsetokens#1% % {\dodoifelsetokens#1\_E_O_T_\_e_o_t_} % % \def\dodoifelsetokens#1#2\_e_o_t_ % {\ifx#1\_E_O_T_ % \expandafter\secondoftwoarguments % \else % \expandafter\firstoftwoarguments % \fi} % % \def\emptytokscondition#1% % {\doemptytokscondition#1\_E_O_T_\_e_o_t_} % % \def\doemptytokscondition#1#2\_e_o_t_ % {\ifx#1\_E_O_T_}