%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. \registerctxluafile{syst-aux}{} %D This file is a follow up in \type {syst-aux.mkii} and \type {syst-aux.mkiv} where %D you can find some more pure \TEX\ or \LUATEX\ variants. There are quite some %D helpers here and many no longer are used but we keep them around because they %D have always been there. However, the implementation in \LMTX\ can be somewhat %D different that in \MKIV, because it's also a kind of a playground for new %D features. Of course nostalgia also plays a role. %D %D For the original code we refer to \type {syst-aux.mkii} and its follow up \type %D {syst-aux.mkiv}. These also have historic versions. As usual, the upgrade went %D through stages. First I wrote variants usign the new \type {\ignorearguments} %D feature, then I decided to implement a few more conditionals and new fancy code %D could be ditched before it was even published. I kept some as examples. %D %D Of course I couldn't do this without Wolfgang looking over my shoulder. Changing %D core code is kind of tricky. \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 %D We're definitely in \LMTX\ mode here. \let\startlmtxmode\relax \let\stoplmtxmode \relax \def\startmkivmode#-\stopmkivmode{} \let\stopmkivmode \relax %D As we don't have namespace definers yet, we use a special one. Later we will %D do a better job. \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 this one: \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. These are kind of obsolete and %D eventually they might get dropped! \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. Of course, expanding them costs time, so %D that's the price to pay. More are defined elsewhere. Of course we later pay a %D price when they need to be expanded. \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 Sometimes we pass macros as arguments to commands that don't expand them before %D interpretation. Such commands can be enclosed with \type {\expanded}, like: %D %D \starttyping %D \expanded{\setupsomething[\alfa]} %D \stoptyping %D %D Such situations occur for instance when \type {\alfa} is a 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. The name clash is an %D unfortunate fact and in retrospect I should nto have agreed to a primitive having %D that same name. % 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. These zero references prevent intermediate storage. In principle that %D is more efficient but it probably won't be noticed. It's anyway cheaper on memory %D access. We use versions that don't even store the arguments. \def\gobbleoneargument #-{} \def\gobbletwoarguments #-#-{} \def\gobblethreearguments #-#-#-{} \def\gobblefourarguments #-#-#-#-{} \def\gobblefivearguments #-#-#-#-{} \def\gobblesixarguments #-#-#-#-{} \def\gobblesevenarguments #-#-#-#-#-{} \def\gobbleeightarguments #-#-#-#-#-#-{} \def\gobbleninearguments #-#-#-#-#-#-#-{} \def\gobbleoneoptional [#-]{} \def\gobbletwooptionals [#-][#-]{} \def\gobblethreeoptionals[#-][#-][#-]{} \def\gobblefouroptionals [#-][#-][#-][#-]{} \def\gobblefiveoptionals [#-][#-][#-][#-][#-]{} %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 This macro has a long history. Among the things we had to deal with was ignoring %D blank spaces but in the meantime we can use some \LUATEX\ trickery. Older versions %D use more code and can be find in the \MKIV\ and \MKII\ files. \let\next \relax \let\nextnext \relax % kind of obsolete \let\nextnextnext \relax % kind of obsolete \let\nexttoken \relax \let\m_syst_action_yes\relax \let\m_syst_action_nop\relax \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} \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. Here we use a lookahead primitive that ignores (blank) spaces %D which makes for less tracing clutter than the original definition. It's also %D faster bit that will probably go unnoticed. Of course, hard||core \TEX ies whose %D reputations depends on understanding obscure macro definitions will love the more %D low level variants. \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[} \let\doifnextoptionalelse \doifelsenextoptional \let\doifnextoptionalcselse\doifelsenextoptionalcs \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} \let\doifnextbgroupelse \doifelsenextbgroup \let\doifnextbgroupcselse\doifelsenextbgroupcs \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} \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} \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[} \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 %D %D like: %D %D \starttyping %D \def\somecommand{\dowithnextbox{\box\nextbox}\ruledhbox} %D %D \assumelongusagecs\somecommand %D %D \bgroup %D oeps %D \egroup %D \stoptyping %D %D The original kind of clumsy but working version is now replaced by a simple %D macro. And it can be even less code in \LUAMETATEX: \let\assumelongusagecs\expandafterpars %D It's used to skip over empty lines in some constructs that we like to set up %D spacy. We already permit par tokens (and equivalents) in math and some other %D places visa engine features. And, \type {\long} stuff has been dropped for a long %D time already (it is still optional in \lUATEX). But the \quote {long} in the %D name kind of stuck. %D \macros %D {blankspace} %D %D Here is how this works. The \type {\let} primitive first picks up the to be let %D name. Then it scans for an optional equal and when that is found it will skip the %D next space, which is why we need an extra one to achieve our goal. Such a \type %D {\blankspace} has the meaning \typ {blank space}. A typical \TEX ie definition: \normalexpanded{\let\noexpand\blankspace=\space\space} %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. But nowadays we can just alias to a primitive: \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 %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 %D %D Suppression of errors while constructing a control sequence is one of the (few) %D things that I remember being discussed in the perspective of \ETEX\ but it was %D rejected because it was not considered useful. Well, in \LUATEX\ we can surpress %D it and that is what we do in \MKIV. We're probably the only macro package that %D needs it. That kind of configuration happens elsewhere. These macros are (mostly %D for historic reasons) fully expandable. \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% {\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 %D %D Again, we use some of the new primitives in \LUAMETATEX. Using straightforward %D \type {\edef}'s and \type {\ifx} comparison works as well, but this saves tokens %D and, more important, tracing clutter. \unexpanded\def\doifelse#1#2% {\iftok{#1}{#2}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\doif#1#2% {\iftok{#1}{#2}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\doifnot#1#2% {\iftok{#1}{#2}% \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \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, but we use the dedicated empty checker %D here. \unexpanded\def\doifelseempty#1% {\def\m_syst_string_one{#1}% \ifempty\m_syst_string_one \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \let\doifemptyelse\doifelseempty \unexpanded\def\doifempty#1% {\def\m_syst_string_one{#1}% \ifempty\m_syst_string_one \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\doifnotempty#1% {\def\m_syst_string_one{#1}% \ifempty\m_syst_string_one \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} %D These are normally only used for keywords, i.e.\ strings so we can delegate %D the work to \LUA: \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 % These don't accept spaces after commas: % % \unexpanded\def\doifelseinset#1#2% % {\ifhasxtoks{,#1,}{,#2,}% % \expandafter\firstoftwoarguments % \else % \expandafter\secondoftwoarguments % \fi} % \unexpanded\def\doifinset#1#2% % {\ifhasxtoks{,#1,}{,#2,}% % \expandafter\firstofoneargument % \else % \expandafter\gobbleoneargument % \fi} % \unexpanded\def\doifnotinset#1#2% % {\ifhasxtoks{,#1,}{,#2,}% % \expandafter\gobbleoneargument % \else % \expandafter\firstofoneargument % \fi} % But these do: \edef\a!comma{\expandtoken \ignorecatcode \commaasciicode} \edef\a!space{\expandtoken \ignorecatcode \spaceasciicode} \normalexpanded { \unexpanded \def \noexpand \doifelseinset#1#2% {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}% \noexpand\expandafter\noexpand\firstoftwoarguments \noexpand\else \noexpand\expandafter\noexpand\secondoftwoarguments \noexpand\fi} \unexpanded \def \noexpand \doifinset#1#2% {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}% \noexpand\expandafter\noexpand\firstofoneargument \noexpand\else \noexpand\expandafter\noexpand\gobbleoneargument \noexpand\fi} \unexpanded \def \noexpand \doifnotinset#1#2% {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}% \noexpand\expandafter\noexpand\gobbleoneargument \noexpand\else \noexpand\expandafter\noexpand\firstofoneargument \noexpand\fi} } %D Done. \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} \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 Or expanded: %D %D \blank \getbuffer \blank %D %D The original definitions can be found elsewhere and need a bit more code. In case of %D issues, consult the \MKIV\ variant or previous \LMTX\ variants in the repository. %D %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. We use a new \LUAMETATEX\ feature that intercepts invalid macro arguments %D by quitting when \type {\ignorearguments} is seen. That's why we act on the %D arguments state. Again it permits leaner and meaner macro definitions with a bit %D less clutter in tracing. \unexpanded\def\syst_helpers_process_comma_item#1,% {\ifarguments \expandafter\syst_helpers_process_comma_item_gobble \or \commalistcommand{#1}% \expandafter\syst_helpers_process_comma_item_next \fi} \def\syst_helpers_process_comma_item_next {\expandafterspaces\syst_helpers_process_comma_item} \unexpanded\def\processcommalist[#1]#2% {\pushmacro\commalistcommand \def\commalistcommand{#2}% \expandafterspaces\syst_helpers_process_comma_item#1,\ignorearguments\ignorearguments\ignorearguments \popmacro\commalistcommand} \unexpanded\def\processcommacommand[#1]#2% {\pushmacro\commalistcommand \def\commalistcommand{#2}% \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_comma_item#1,}\ignorearguments\ignorearguments\ignorearguments \popmacro\commalistcommand} % \let\syst_helpers_process_comma_item_next_a \syst_helpers_process_comma_item_next % \def\syst_helpers_process_comma_item_next_b#0\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_a} % \def\syst_helpers_process_comma_item_next_c#0\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b} % \def\syst_helpers_process_comma_item_gobble#0\ignorearguments{} \let\syst_helpers_process_comma_item_next_a \syst_helpers_process_comma_item_next \def\syst_helpers_process_comma_item_next_b#-\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_a} \def\syst_helpers_process_comma_item_next_c#-\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b} \def\syst_helpers_process_comma_item_gobble#-\ignorearguments{} \unexpanded\def\quitcommalist {\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b} \unexpanded\def\quitprevcommalist{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_c} %D \startbuffer %D \def\foo#1{(#1)} %D <\processcommalist[a,b,c,d]\foo> %D %D \def\foo#1{(#1)} %D \def\oof#1{<\processcommalist[#1]\foo>} %D <\processcommalist[{a,b},{c,d}]\oof> %D %D \def\foo#1{(#1)\quitcommalist} %D <\processcommalist[a,b,c,d]\foo> %D %D \def\foo#1{(#1)} %D \def\oof#1{<\processcommalist[#1]\foo\quitcommalist>} %D <\processcommalist[{a,b},{c,d}]\oof> %D %D \def\foo#1{(#1)\quitcommalist} %D \def\oof#1{<\processcommalist[#1]\foo>} %D <\processcommalist[{a,b},{c,d},{e,f}]\oof> %D %D \def\foo#1{(#1)\quitprevcommalist} %D \def\oof#1{<\processcommalist[#1]\foo>} %D <\processcommalist[{a,b},{c,d},{e,f}]\oof> %D \stopbuffer %D %D \typebuffer \blank \getbuffer \blank %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] [x=>a,y=>b,z=>c] %D \processfirstactioninset [x,y,z] [x=>a,y=>b,z=>c] %D \processallactionsinset [x,y,z] [x=>a,y=>b,z=>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. %D %D In the meantime we follow a different approach, often somewhat more heavy on the %D number of control sequences used, but that is no lomger a real issue. The code %D has been simplified and nwo uses the macro stack mechanism. If needed I can make %D this more hip and a bit faster now but \unknown\ it's seldom used nowadays as we %D have better ways now. \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} \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 \orelse\ifx\m_syst_string_two\s!unknown \def\commalistelement{#3}% beware of loops #2% \fi} \unexpanded\def\processaction[#1]#2[% {\edef\m_syst_string_one{#1}% \ifempty\m_syst_string_one \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}% \ifempty\m_syst_string_one \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} \unexpanded\def\syst_helpers_process_all_actions_in_set_indeed[#1]#2[#3]% {\globalpushmacro\syst_process_action_in_set_all \def\syst_process_action_in_set##1% {\def\syst_process_action_in_set_one####1{\syst_helpers_do_compare_process_action_d[####1][##1]}% \processcommalist[#3]\syst_process_action_in_set_one}% \processcommacommand[#1]\syst_process_action_in_set \globalpopmacro\syst_process_action_in_set_all} \unexpanded\def\processallactionsinset[#1]% {\edef\m_syst_string_one{#1}% \ifempty\m_syst_string_one \expandafter\processaction \else \expandafter\syst_helpers_process_all_actions_in_set_indeed \fi [#1]} %D These macros use: \unexpanded\def\processnextcommalist#1#2#3[#4#5]% {#1\relax \pushmacro\commalistcommand \def\commalistcommand{#3}% \expandafterspaces\syst_helpers_process_comma_item#4#5\ignorearguments\ignorearguments\ignorearguments \popmacro\commalistcommand #2\relax} %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 \doifinstringelse {substring} {string} {then ...} {else ...} %D \stoptyping %D %D \startbuffer %D \doifinstringelse{abc}{foo bar abc}{Y}{N}=Y\par %D \doifinstringelse{abc}{foo bar cab}{Y}{N}=N\par %D \doifinstring {abc}{foo bar abc}{Y}=Y\par %D \doifinstring {abc}{foo bar cab}{Y}\par %D \doifnotinstring {abc}{foo bar abc}{Y}\par %D \doifnotinstring {abc}{foo bar cab}{Y}=Y\par %D \doifinstringelse{}{foo bar abc}{Y}{N}=N\par %D \doifinstring {}{foo bar abc}{N}\par %D \doifnotinstring {}{foo bar abc}{Y}=Y\par %D \doifinstringelse{x}{}{Y}{N}=N\par %D \doifinstring {x}{}{N}\par %D \doifnotinstring {x}{}{Y}=Y\par %D \doifinstringelse{}{}{Y}{N}=N\par %D \doifinstring {}{}{N}\par %D \doifnotinstring {}{}{Y}=Y\par %D \stopbuffer %D %D \typebuffer \blank \getbuffer \blank %D I keep the following as example code: % \let\syst_helpers_do_do_if_in_string_else\relax % \let\syst_helpers_do_do_if_in_string \relax % \let\syst_helpers_do_do_if_not_in_string \relax % % \let\m_syst_sub_string \empty % % \unexpanded\def\doifelseinstring#1% % {\edef\m_syst_sub_string{#1}% expand #1 here % \ifempty\m_syst_sub_string % \expandafter\thirdofthreearguments % \else % \expandafter\syst_helpers_do_if_in_string_else % \fi} % % \let\doifinstringelse\doifelseinstring % % \unexpanded\def\syst_helpers_do_if_in_string_else#1% ##2 can be {abc} % {\normalexpanded{\unexpanded\def\syst_helpers_do_do_if_in_string_else##1\m_syst_sub_string##2}% % {\ifarguments % \or % \expandafter\syst_helpers_do_if_in_string_else_nop % \or % \expandafter\syst_helpers_do_if_in_string_else_yes % \fi}% % \normalexpanded{\syst_helpers_do_do_if_in_string_else#1}\e_o_t\ignorearguments\ignorearguments} % % \unexpanded\def\syst_helpers_do_if_in_string_else_delimited#1% ##2 can be {abc} % {\normalexpanded{\unexpanded\def\syst_helpers_do_do_if_in_string_else##1,\m_syst_sub_string,##2}% % {\ifarguments % \or % \expandafter\syst_helpers_do_if_in_string_else_nop % \or % \expandafter\syst_helpers_do_if_in_string_else_yes % \fi}% % \normalexpanded{\syst_helpers_do_do_if_in_string_else,#1,}\e_o_t\ignorearguments\ignorearguments} % % \unexpanded\def\doifinstring#1% % {\edef\m_syst_sub_string{#1}% expand #1 here % \ifempty\m_syst_sub_string % \expandafter\gobbletwoarguments % \else % \expandafter\syst_helpers_do_if_in_string % \fi} % % \unexpanded\def\syst_helpers_do_if_in_string#1% ##2 can be {abc} % {\normalexpanded{\unexpanded\def\syst_helpers_do_do_if_in_string##1\m_syst_sub_string##2}% % {\ifarguments % \or % \expandafter\syst_helpers_do_if_in_string_nop % \or % \expandafter\syst_helpers_do_if_in_string_yes % \fi}% % \normalexpanded{\syst_helpers_do_do_if_in_string#1}\e_o_t\ignorearguments\ignorearguments} % % \unexpanded\def\doifnotinstring#1% % {\edef\m_syst_sub_string{#1}% expand #1 here % \ifempty\m_syst_sub_string % \expandafter\secondoftwoarguments % \else % \expandafter\syst_helpers_do_if_not_in_string % \fi} % % \unexpanded\def\syst_helpers_do_if_not_in_string#1% ##2 can be {abc} % {\normalexpanded{\unexpanded\def\syst_helpers_do_do_if_not_in_string##1\m_syst_sub_string##2}% % {\ifarguments % \or % \expandafter\syst_helpers_do_if_not_in_string_nop % \or % \expandafter\syst_helpers_do_if_not_in_string_yes % \fi}% % \normalexpanded{\syst_helpers_do_do_if_not_in_string#1}\e_o_t\ignorearguments\ignorearguments} % % \def\syst_helpers_do_if_in_string_else_yes#0\ignorearguments\ignorearguments#2#0{#2} % \def\syst_helpers_do_if_in_string_else_nop#0\ignorearguments#0#3{#3} % \def\syst_helpers_do_if_in_string_yes #0\ignorearguments\ignorearguments#2{#2} % \def\syst_helpers_do_if_in_string_nop #0\ignorearguments#0{} % \def\syst_helpers_do_if_not_in_string_yes #0\ignorearguments\ignorearguments#0{} % \def\syst_helpers_do_if_not_in_string_nop #0\ignorearguments#2{#2} \unexpanded\def\doifelseinstring#1#2% {\ifhasxtoks{#1}{#2}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\doifinstring#1#2% {\ifhasxtoks{#1}{#2}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\doifnotinstring#1#2% {\ifhasxtoks{#1}{#2}% \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \let\doifinstringelse\doifelseinstring %D The next one one of those variants that we used when speed was more of an issue %D that today. Now we just expand the lot. We just use an alias now: \let\doifelseincsname\doifelseinstring \let\doifincsnameelse\doifinstringelse %D \macros %D {doifnumberelse,doifnumber,doifnotnumber} %D %D The next macro executes a command depending of the outcome of a test on numerals. %D We now use a \LUATEX\ feature that permits a more robust checking, but you might %D want to take a look at the originals. It's typically one of these new features %D that probably only \CONTEXT\ will use, which is probably true for more such %D features that no one ever asked for (but they are pretty generic in nature %D anyway). \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} \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 % todo: use the push back dimen trickery \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). The question is: do we still need these raw variants? \unexpanded\def\makerawcommalist[#1]#2% use \processnext ... here {\scratchtoks\emptytoks \def\syst_helpers_do_make_raw_comma_list##1{\iftok\scratchtoks\emptytoks\scratchtoks{##1}\else\toksapp\scratchtoks{,##1}\fi}% \processcommalist[#1]\syst_helpers_do_make_raw_comma_list \edef#2{\the\scratchtoks}} \let\rawprocesscommalist \processcommalist \let\rawprocesscommacommand\processcommacommand %D Here is one without nesting: \unexpanded\def\syst_helpers_process_fast_comma_item#1,% {\ifarguments \expandafter\syst_helpers_process_comma_item_gobble \or \fastcommalistcommand{#1}% \expandafter\syst_helpers_process_fast_comma_item_next \fi} \unexpanded\def\syst_helpers_process_fast_comma_item_next {\expandafterspaces\syst_helpers_process_fast_comma_item} \unexpanded\def\fastprocesscommalist[#1]#2% {\let\fastcommalistcommand#2% \expandafterspaces\syst_helpers_process_fast_comma_item#1\ignorearguments\ignorearguments\ignorearguments} \unexpanded\def\fastprocesscommacommand[#1]#2% {\let\fastcommalistcommand#2% \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_fast_comma_item#1}\ignorearguments\ignorearguments\ignorearguments} % \def\rawdoifelseinset#1#2{\doifinstringelse{,#1,}{,#2,}} % \def\rawdoifinset #1#2{\doifinstring {,#1,}{,#2,}} \def\syst_helpers_do_if_else_in_set#1% {\ifhasxtoks{,\m_syst_sub_string,}{,#1,}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\rawdoifelseinset#1% {\edef\m_syst_sub_string{#1}% expand #1 here \ifx\m_syst_sub_string\empty \expandafter\thirdofthreearguments \else \expandafter\syst_helpers_do_if_else_in_set \fi} \let\rawdoifinsetelse\rawdoifelseinset \def\syst_helpers_do_if_in_set#1% {\ifhasxtoks{,\m_syst_sub_string,}{,#1,}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\rawdoifinset#1% or just alias this one {\edef\m_syst_sub_string{#1}% expand #1 here \ifx\m_syst_sub_string\m_syst_two_commas \expandafter\gobbletwoarguments \else \expandafter\syst_helpers_do_if_in_set \fi} %D Some more raw material: \def\syst_helpers_raw_process_action#1=>#2,% {\ifarguments %\expandafter\syst_helpers_raw_process_action_gobble \or \expandafter\syst_helpers_raw_process_action_gobble \or \edef\m_syst_string_two{#1}% \ifx\m_syst_string_one\m_syst_string_two \def\m_syst_helpers_process_action{#2}% \expandafter\expandafter\expandafter\syst_helpers_raw_process_action_gobble \else \ifx\s!unknown\m_syst_string_two \def\m_syst_helpers_process_action_unknown{#2}% \fi \expandafter\expandafter\expandafter\syst_helpers_raw_process_action_next \fi \fi} \def\syst_helpers_raw_process_action_gobble#-\ignorearguments {} \def\syst_helpers_raw_process_action_next {\expandafterspaces\syst_helpers_raw_process_action} \unexpanded\def\xrawprocessaction[#1]#2[#3]% {\edef\m_syst_string_one{#1}% \ifempty\m_syst_string_one \let\m_syst_string_one\s!default \fi \let\m_syst_helpers_process_action\relax \let\m_syst_helpers_process_action_unknown\relax \syst_helpers_raw_process_action#3\ignorearguments\ignorearguments\ignorearguments \ifx\m_syst_helpers_process_action\relax \m_syst_helpers_process_action_unknown \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{\expandafter\def \csname#1#2\endcsname} % takes #3 \def\dosetevalue #1#2{\expandafter\edef\csname#1#2\endcsname} % takes #3 \def\dosetgvalue #1#2{\expandafter\gdef\csname#1#2\endcsname} % takes #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. \unexpanded\def\showassignerror#1#2% {\writestatus{setup}{missing or ungrouped '=' after '#1' in line #2}} \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 \def\syst_helpers_grab_parameter_error#1% {\showassignerror{#1}{\the\inputlineno\space(\m_syst_parameter_n)}} \def\syst_helpers_grab_parameter#1,% {\ifarguments % done \else \syst_helpers_grab_parameter_okay#1,\ignorearguments \expandafter\syst_helpers_grab_parameter_next \fi} \def\syst_helpers_grab_parameter_okay#1=#2,% {\ifarguments \or \syst_helpers_grab_parameter_error{#1}% \or \m_syst_parameter_s\m_syst_parameter_n{#1}{#2}% \fi} \def\syst_helpers_grab_parameter_next {\expandafterspaces\syst_helpers_grab_parameter} \unexpanded\def\dogetparameters#1[#2]#-[#3]% {\def\m_syst_parameter_n{#2}% \let\m_syst_parameter_s#1% \expandafterspaces\syst_helpers_grab_parameter#3\ignorearguments\ignorearguments} %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 We now just alias these as there is no need for a speedup. These have not been used %D for a long time in core code. \let\doassign \getparameters \let\doeassign \geteparameters \let\undoassign\forgetparameters %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 %D {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 {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]% {\doifnot{#1}{#2} {\def\syst_helpers_copy_parameter{\docopyvalue{#1}{#2}}% ##1 \processcommalist[#3]\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 \unexpanded\def\checkparameters[#1]% {\ifhastok={#1}\parameterstrue\else\parametersfalse\fi} %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#0,% no #- as we need to count {\ifarguments\or \advance\commalistcounter\plusone \expandafter\syst_helpers_get_comma_list_size \fi} \unexpanded\def\getcommalistsize[% {\futureexpand]\syst_helpers_get_comma_list_size_nop\syst_helpers_get_comma_list_size_yes} \def\syst_helpers_get_comma_list_size_yes#+]% {\commalistcounter\zerocount \syst_helpers_get_comma_list_size #1,\ignorearguments\ignorearguments \edef\commalistsize{\the\commalistcounter}} \def\syst_helpers_get_comma_list_size_nop]% {\commalistcounter\zerocount \let\commalistsize\!!zerocount} \unexpanded\def\getcommacommandsize[#1]% {\normalexpanded{\getcommalistsize[#1]}} %D Filters: % \def\syst_helpers_gobble_comma_list#0\ignorearguments{} \def\syst_helpers_gobble_comma_list#-\ignorearguments{} \def\syst_helpers_get_from_comma_list#1,% {\ifarguments \or \advance\commalistcounter \minusone \ifcase\commalistcounter \def\commalistelement{#1}% \expandafter\expandafter\expandafter\syst_helpers_gobble_comma_list \else \expandafter\expandafter\expandafter\syst_helpers_get_from_comma_list_next \fi \fi} \def\syst_helpers_get_from_comma_list_next {\expandafterspaces\syst_helpers_get_from_comma_list} \unexpanded\def\getfromcommalist[#1]#2[#3]% {\let\commalistelement\empty \commalistcounter#3\relax \expandafterspaces\syst_helpers_get_from_comma_list#1\ignorearguments\ignorearguments} \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 9~elements at most. %D %D \starttyping %D \dogetcommalistelement1\from a,b,c\to\commalistelement %D \stoptyping \def\syst_helpers_get_comma_list_element#1,#2,#3,#4,#5,#6,#7,#8,#9,% {\ifcase\scratchcounter\or#1\or#2\or#3\or#4\or#5\or#6\or#7\or#8\or#9\fi \syst_helpers_gobble_comma_list} \unexpanded\def\dogetcommacommandelement#1\from#2\to#3% {\scratchcounter#1\relax \edef#3{\normalexpanded{\syst_helpers_get_comma_list_element#2\ignorearguments\ignorearguments}}} %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 \command = \command[#1] %D \dodoubleargument \command = \command[#1][#2] %D \dotripleargument \command = \command[#1][#2][#3] %D \doquadrupleargument \command = \command[#1][#2][#3][#4] %D \doquintupleargument \command = \command[#1][#2][#3][#4][#5] %D \dosixtupleargument \command = \command[#1][#2][#3][#4][#5][#6] %D \doseventupleargument\command = \command[#1][#2][#3][#4][#5][#6][#7] %D \stoptyping %D %D These macros can be 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 further explanation, %D 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. %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. %D NB : experimental versions in cont-exp.mkiv %D Common: \newtoks\t_syst_aux \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% {\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[]} %D Double \unexpanded\def\dodoubleempty#1% {\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[]} % Triple \unexpanded\def\dotripleempty#1% {\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[]} %D Quadruple: \unexpanded\def\doquadrupleempty#1% {\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[]} %D Quintuple: \unexpanded\def\doquintupleempty#1% {\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[]} %D Sixtuple: \unexpanded\def\dosixtupleempty#1% {\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[]} %D Seventuple: \unexpanded\def\doseventupleempty#1% {\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[]} %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}. \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% {\doifelsenextoptional {\firstargumenttrue \csname\s!complex\csstring#1\endcsname} {\firstargumentfalse\csname\s!simple \csstring#1\endcsname}} \unexpanded\def\complexorsimpleempty#1% {\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. %D NB: experimental code in cont-exp.mkiv \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{}} %D These macros can explictly take care of spaces, which means that the next %D 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#-{#1} \def\secondoftwoarguments #-#1{#1} \def\firstofthreearguments #1#-#-{#1} \def\secondofthreearguments #-#1#-{#1} \def\thirdofthreearguments #-#-#1{#1} \def\firstoffourarguments #1#-#-#-{#1} \def\secondoffourarguments #-#1#-#-{#1} \def\thirdoffourarguments #-#-#1#-{#1} \def\fourthoffourarguments #-#-#-#1{#1} \def\firstoffivearguments #1#-#-#-#-{#1} \def\secondoffivearguments #-#1#-#-#-{#1} \def\thirdoffivearguments #-#-#1#-#-{#1} \def\fourthoffivearguments #-#-#-#1#-{#1} \def\fifthoffivearguments #-#-#-#-#1{#1} \def\firstofsixarguments #1#-#-#-#-#-{#1} \def\secondofsixarguments#-#1#-#-#-#-{#1} \def\thirdofsixarguments #-#-#1#-#-#-{#1} \def\fourthofsixarguments#-#-#-#1#-#-{#1} \def\fifthofsixarguments #-#-#-#-#1#-{#1} \def\sixthofsixarguments #-#-#-#-#-#1{#1} \unexpanded\def\firstofoneunexpanded #1{#1} \unexpanded\def\firstoftwounexpanded #1#-{#1} \unexpanded\def\secondoftwounexpanded #-#1{#1} \unexpanded\def\firstofthreeunexpanded #1#-#-{#1} \unexpanded\def\secondofthreeunexpanded#-#1#-{#1} \unexpanded\def\thirdofthreeunexpanded #-#-#1{#1} %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! \def\syst_helpers_grab_raw_parameter#1=#2,% {\ifarguments\or\or \expandafter\def\csname\m_syst_parameter_n#1\endcsname{#2}% \expandafter\syst_helpers_grab_raw_parameter_next \fi} \def\syst_helpers_grab_raw_parameter_next {\expandafterspaces\syst_helpers_grab_raw_parameter} \unexpanded\def\rawgetparameters#1[#2]#-[#3]% {\def\m_syst_parameter_n{#2}% %\expandafterspaces\syst_helpers_grab_raw_parameter#3\ignorearguments\ignorearguments} \expandafter\expandafterspaces\expandafter\syst_helpers_grab_raw_parameter#3\ignorearguments\ignorearguments} %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% \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 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 \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. %D %D We now use the macro stack which is somewhat leaner and meaner and a little %D faster too. \newcount\outerrecurse \newcount\innerrecurse \def\recursedepth{\the\outerrecurse} \def\recurselevel{0} \let\syst_helpers_stepwise_next\relax \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_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} \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 a special case of the %D more general: %D %D \starttyping %D \dostepwiserecurse {from} {to} {step} {action} %D \stoptyping %D %D This commands accepts positive and negative steps. Illegal values are handles as %D good as possible and the macro accepts numbers and \COUNTERS. %D %D \starttyping %D \dostepwiserecurse {1} {10} {2} {...} %D \dostepwiserecurse {10} {1} {-2} {...} %D \stoptyping %D %D Because the simple case (n=1) is used often, we implement it 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_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% {} %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 \globalpushmacro\recurselevel \globalpushmacro\recurseaction \unexpanded\gdef\recurseaction##1##2{#1}% \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#0% {\let\endofloop\syst_helpers_loop \globalpopmacro\recurselevel \globalpopmacro\recurseaction \global\advance\outerrecurse\minusone} \unexpanded\def\exitloop % \exitloop quits at end {\let\endofloop\syst_helpers_loop_nop} \unexpanded\def\exitloopnow#0\endofloop % \exitloopnow quits directly {\syst_helpers_loop_nop} %D The loop is executed at least once, so beware of situations 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 {\normalexpanded{\recurseaction{\recurselevel}{\the\outerrecurse}}} \unexpanded\def\syst_helpers_recurse_x#1#2% {\global\advance\outerrecurse\plusone \globalpushmacro\recurseaction \globalpushmacro\recurselevel \unexpanded\gdef\recurseaction##1##2{#2}% \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}% \globalpopmacro\recurselevel \globalpopmacro\recurseaction \global\advance\outerrecurse\minusone} \unexpanded\def\syst_helpers_recurse_y#1#2% {\global\advance\outerrecurse\plusone \globalpushmacro\recurseaction \globalpushmacro\recurselevel \let\recurselevel\!!plusone \unexpanded\gdef\recurseaction##1##2{#2}% \syst_helpers_recurse_content \globalpopmacro\recurselevel \globalpopmacro\recurseaction \global\advance\outerrecurse\minusone} \unexpanded\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 {\global\advance\outerrecurse \plusone \globalpushmacro\recurselevel \globalpushmacro\recurseaction \unexpanded\gdef\recurseaction##1##2{#4}% \normalexpanded{\ifcmpnum#3\zerocount \ifnum#1<#2\relax \syst_helpers_stepwise_exit \else \syst_helpers_stepwise_reverse \fi \or \syst_helpers_stepwise_exit \or \ifnum#2<#1\relax \syst_helpers_stepwise_exit \else \syst_helpers_stepwise_recurse \fi \fi{\number#1}{\number#2}{\number#3}} \globalpopmacro\recurselevel \globalpopmacro\recurseaction \global\advance\outerrecurse\minusone} \unexpanded\def\syst_helpers_stepwise_recurse_nop#1#2#3#4% {} \newcount\fastloopindex \newcount\fastloopfinal \let\m_syst_helpers_fast_loop_cs\relax \unexpanded\def\dofastloopcs#1% {\fastloopfinal#1\relax \ifcase\fastloopfinal \expandafter\gobbleoneargument \else \expandafter\syst_helpers_fast_loop_cs \fi} \unexpanded\def\syst_helpers_fast_loop_cs#1% {\let\m_syst_helpers_fast_loop_cs#1% \fastloopindex\plusone \syst_helpers_fast_loop_cs_step} \unexpanded\def\syst_helpers_fast_loop_cs_step {\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_step \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 \globalpushmacro\recurseaction \globalpushmacro\recursestring \unexpanded\gdef\recurseaction##1{\edef\recursestring{##1}#2}% \processcommacommand[#1]\recurseaction \globalpopmacro\recursestring \globalpopmacro\recurseaction \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\orelse\ifdefined#2\else \expandafter\newtoks\csname\??extraevery\csstring#1\endcsname \edef#2{\syst_helpers_every#1\csname\??extraevery\csstring#1\endcsname}% \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 \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 %D %D So %D \startbuffer %D \doifelseassignment{a=b}{Y}{N} %D \doifelseassignment{a+b}{Y}{N} %D %D \ifcondition\validassignment {a=b}Y\else N\fi %D \ifcondition\novalidassignment{a=b}N\else Y\fi %D \ifcondition\novalidassignment {ab}Y\else N\fi %D \stopbuffer %D %D \typebuffer gives: \blank \getbuffer \blank \unexpanded\def\doifelseassignment#1% {\ifhastok={#1}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\doifelseassignmentcs#1#2#3% {\ifhastok={#1}% \expandafter#2% \else \expandafter#3% \fi} \let\doifassignmentelse \doifelseassignment \let\doifassignmentelsecs\doifelseassignmentcs \newif\ifassignment \unexpanded\def\docheckassignment#1% {\ifhastok={#1}% \assignmenttrue \else \assignmentfalse \fi} \unexpanded\def\validassignment #1{\ifhastok={#1}} \unexpanded\def\novalidassignment#1{\ifnum\ifhastok={#1}\zerocount\else\plusone\fi=\plusone} %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 % BEGIN OF OBSOLETE % %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}} % END OF OBSOLETE % %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% {\iftok{#1}\emptytoks \def#1{#2}\fi} %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{\begincsname#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 % {} \unexpanded\def\gobbleuntil#1% {\def\syst_helpers_gobble_indeed##-#1{}\syst_helpers_gobble_indeed} \unexpanded\def\gobbleuntilrelax#-\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 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 \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% {\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{#3}% \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} %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. \let\syst_helpers_par_before\relax \let\syst_helpers_par_around\relax \unexpanded\def\dowithpar#1#2% {\globalpushmacro\syst_helpers_par_around \def\syst_helpers_par_around##1\par{#1##1#2\globalpopmacro\syst_helpers_par_around}% \expandafter\syst_helpers_par_around\ignorepars} \unexpanded\def\dogotopar#1% {\globalpushmacro\syst_helpers_par_before \def\syst_helpers_par_before{#1\globalpopmacro\syst_helpers_par_before}% \expandafter\syst_helpers_par_before\ignorepars} \let\dogotoparcs\dogotopar \unexpanded\def\dogotoparstart {\ignorepars} %D This is old and kind of obsolete: \newtoks\BeforePar \newtoks\AfterPar \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}% \orelse\ifnum#1<\zerocount %\normalexpanded{\dorecurse{\number-\number#1}}{#4{-#2#3}}% \dorecurse{-#1}{#4{-#2#3}}% \orelse\ifx#2+% \dorecurse{#1}{#4{#3}}% \else \dorecurse{#1}{#4{#2#3}}% \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. But now we just alias. \let\doifelsestringinstring\doifelseinstring \let\doifstringinstringelse\doifelseinstring %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 \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}% \ifhasxtoks\t_syst_helpers_scratch\m_syst_helpers_scratch\else \syst_helpers_append_toks_indeed \fi} \unexpanded\def\prependtoksonce#1\to#2% {\let\m_syst_helpers_scratch#2% \t_syst_helpers_scratch{#1}% \ifhasxtoks\t_syst_helpers_scratch\m_syst_helpers_scratch\else \syst_helpers_prepend_toks_indeed \fi} %D The test macro: \unexpanded\def\doifelseintoks#1#2% #1 en #2 zijn toks {\ifhasxtoks#1#2% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \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^^^^0004% {\def\m_syst_string_one{##3}% \ifempty\m_syst_string_one#2{##1}\else#2{##1##2}\fi}% \expandafter\syst_helpers_remove_toks\the#2\empty\empty\empty#1\empty\empty\empty^^^^0004} %D Also: \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} \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##0^^^^0004% no #- as we need to count {\ifarguments \let#3\empty \or \let#3\empty \else \def#3{##1}% \fi}% \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments} \unexpanded\def\aftersplitstring#1\at#2\to#3% {\def\syst_helpers_split_string##0#2##2^^^^0004% no #- as we need to count {\ifarguments \let#3\empty \or \def#3{#1}% \else \def#3{##2}% \fi}% \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments} %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^^^^0004% {\ifarguments \let#3\empty \let#4\empty \or \def#3{#1}% \let#4\empty \else \def#3{##1}% \def#4{##2}% \fi}% \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments} \unexpanded\def\greedysplitstring#1\at#2\to#3\and#4% {\def\syst_helpers_split_string##1#2##2^^^^0004% {\ifarguments \let#3\empty \let#4\empty \or \def#3{#1}% \let#4\empty \else \def#3{##1}% \def#4{##2}% \def\syst_helpers_split_string####1#2####2^^^^0004% {\ifarguments \or \else \expandafter\def\expandafter#3\expandafter{#3####1}% \def#4{####2}% \syst_helpers_split_string####2^^^^0004\ignorearguments\ignorearguments \fi}% \syst_helpers_split_string##2^^^^0004\ignorearguments\ignorearguments \fi}% \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments} %D \macros %D {beforetestandsplitstring, %D aftertestandsplitstring, %D testandsplitstring} \let\beforetestandsplitstring\beforesplitstring \unexpanded\def\aftertestandsplitstring#1\at#2\to#3% {\def\syst_helpers_split_string##0#2##2^^^^0004% no #- as we need to count {\ifarguments \let#3\empty \or \let#3\empty \else \def#3{##2}% \fi}% \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments} \unexpanded\def\testandsplitstring#1\at#2\to#3\and#4% {\def\syst_helpers_split_string##1#2##2^^^^0004% {\ifarguments \let#3\empty \let#4\empty \or \let#3\empty \let#4\empty \else \def#3{##1}% \def#4{##2}% \fi}% \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments} %D \macros %D {splitatperiod, %D {splitatcomma, %D splitatasterisk, %D splitatcolon, %D splitatcolons} % \unexpanded\def\syst_helpers_splitatperiod #1.#2.#0^^^^0004#4#5{\def #4{#1}\def #5{#2}} % \unexpanded\def\syst_helpers_splitatcomma #1,#2,#0^^^^0004#4#5{\def #4{#1}\def #5{#2}} % \unexpanded\def\syst_helpers_splitatasterisk #1*#2*#0^^^^0004#4#5{\def #4{#1}\def #5{#2}} % \unexpanded\def\syst_helpers_splitatcolon #1:#2:#0^^^^0004#4#5{\def #4{#1}\def #5{#2}} % \unexpanded\def\syst_helpers_splitatcolons #1::#2::#0^^^^0004#4#5{\edef#4{#1}\edef#5{#2}} \unexpanded\def\syst_helpers_splitatperiod #1.#2.#-^^^^0004#3#4{\def #3{#1}\def #4{#2}} \unexpanded\def\syst_helpers_splitatcomma #1,#2,#-^^^^0004#3#4{\def #3{#1}\def #4{#2}} \unexpanded\def\syst_helpers_splitatasterisk #1*#2*#-^^^^0004#3#4{\def #3{#1}\def #4{#2}} \unexpanded\def\syst_helpers_splitatcolon #1:#2:#-^^^^0004#3#4{\def #3{#1}\def #4{#2}} \unexpanded\def\syst_helpers_splitatcolons #1::#2::#-^^^^0004#3#4{\edef#3{#1}\edef#4{#2}} \unexpanded\def\splitatperiod #1{\normalexpanded{\syst_helpers_splitatperiod #1}..^^^^0004} \unexpanded\def\splitatcomma #1{\normalexpanded{\syst_helpers_splitatcomma #1},,^^^^0004} % not at ", " \unexpanded\def\splitatasterisk#1{\normalexpanded{\syst_helpers_splitatasterisk#1}**^^^^0004} \unexpanded\def\splitatcolon #1{\normalexpanded{\syst_helpers_splitatcolon #1}::^^^^0004} \unexpanded\def\splitatcolons #1{\normalexpanded{\syst_helpers_splitatcolons #1}::::^^^^0004} %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{\ifempty#2\else#2,\fi#1}} \unexpanded\def\prependtocommalist#1#2% {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}} \unexpanded\def\addtocommalist#1#2% {item} \cs {\rawdoifelseinset{#1}#2\resetglobal {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}}} \unexpanded\def\pretocommalist#1#2% {item} \cs {\rawdoifelseinset{#1}#2\resetglobal {\dodoglobal\edef#2{#1\ifempty#2\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{\ifempty#2\else#2,\fi#1}}} \unexpanded\def\robustpretocommalist#1#2% {item} \cs {\robustdoifelseinset{#1}#2\resetglobal {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}}} \unexpanded\def\xsplitstring#1#2% \cs {str} %{\def\syst_helpers_split_string##1,#2,##2,#2,##3\\% {\def\syst_helpers_split_string##1,#2,##2,#2,##-\\% {\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} \def\acleanedupcommalist#1,,#-\relax{#1} \unexpanded\def\removefromcommalist#1#2% to be sped up {\rawdoifelseinset{#1}#2% {\normalexpanded{\xsplitstring\noexpand#2{#1}}% \dodoglobal\edef#2% {\ifempty\m_syst_string_one \m_syst_string_two \else \m_syst_string_one\ifempty\m_syst_string_two\else,\m_syst_string_two\fi \fi}} \resetglobal} %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 \ifempty\m_syst_string_two \else \edef\m_syst_string_four{\ifempty\m_syst_string_four\else\m_syst_string_four,\fi\m_syst_string_two}% \fi \else \edef\m_syst_string_four{\ifempty\m_syst_string_four\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 \ifempty\newcommalistelement\else \ifempty\m_syst_helpers_comma_list_target \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 \ifempty\m_syst_helpers_comma_list_target \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} % % Okay, not really an impressive extension to the engine, but better than some macro % that is demonstrating deeper understanding of \TEX. It's anyway a trivial extension % anyway. There are actually many examples of very advanced macros and exposure of % how clever one is that could be done cheap in the engine. It's just that at the time % that \TEX\ was written, it made no sense to add a lot of that. After decades we know % what extras we need. \def\withoutpt#1{\thewithoutunit\dimexpr#1} % best use the primitive directly %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} \def\PtToCm#1{\thewithoutunit\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#1#2#2\d_syst_helpers_swapped} \unexpanded\def\swapcounts#1#2{\c_syst_helpers_swapped#1#1#2#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 \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% {\iftok{\csname#1\endcsname}{#2}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\doifnotvalue#1#2% {\iftok{\csname#1\endcsname}{#2}% \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \unexpanded\def\doifelsevalue#1#2% {\iftok{\csname#1\endcsname}{#2}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\doifnothing#1% {\iftok{#1}\emptytoks \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\doifsomething#1% {\iftok{#1}\emptytoks \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \unexpanded\def\doifelsenothing#1% {\iftok{#1}\emptytoks \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\doifelsesomething#1% {\iftok{#1}\emptytoks \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} \unexpanded\def\doifvaluenothing#1% {\iftok{\csname#1\endcsname}\emptytoks \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\doifvaluesomething#1% {\iftok{\csname#1\endcsname}\emptytoks \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \unexpanded\def\doifelsevaluenothing#1% {\iftok{\csname#1\endcsname}\emptytoks \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\ifempty\csname#1\endcsname \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \let\doifemptyvalueelse\doifelseemptyvalue \def\doifemptyvalue#1% {\expandafter\ifempty\csname#1\endcsname \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \def\doifnotemptyvalue#1% {\expandafter\ifempty\csname#1\endcsname \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{\iftok{#1}{#2}}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \unexpanded\def\syst_helpers_do_IF_NOT#1#2% {\uppercase{\iftok{#1}{#2}}% \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \unexpanded\def\syst_helpers_do_IF_ELSE#1#2% {\uppercase{\iftok{#1}{#2}}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \unexpanded\def\syst_helpers_do_IF_INSTRING_ELSE#1#2% {\uppercase{\doifelseinstring{#1}{#2}}} \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^^^^0004{\if\noexpand#2@} % % \def\doifelsesomespace#1% % #2#3% % {\syst_helpers_if_some_space_else#1 @ @^^^^0004% #3\else#2\fi} % \expandafter\secondoftwoarguments % \else % \expandafter\firstoftwoarguments % \fi} \edef\doifelsesomespace#1% {\noexpand\ifhastok\space{#1}% \noexpand\expandafter\noexpand\firstoftwoarguments \noexpand\else \noexpand\expandafter\noexpand\secondoftwoarguments \noexpand\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 \orelse\ifx\blankspace\m_syst_string_one #4{##1}% \orelse\if]##2% \let\syst_helpers_process_separated_list_step\relax \else #4{##1##2}% \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 \orelse\ifx#2####2% \let\syst_helpers_process_any_list_step\relax \else #4{####1####2}% \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} \unexpanded\def\processassignlist#1[#2]#3% {\def\syst_helpers_process_assign_list_assign[##1=##-=##2]% {\doif{##2}\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 \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}} \unexpanded\def\PointsToReal#1#2% {\edef#2{\thewithoutunit\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}. %D %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} \unexpanded\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 \orelse\ifx\nexthandledtoken\end \expandafter\gobbletwoarguments % also gobble the \end \else \expandafter\syst_helpers_handle_tokens_indeed_two \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\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 {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 assignment inside a box. %D The \type {\empty}'s permits 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% {\iftok#1\emptytoks \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} \unexpanded\def\doifsometoks#1% {\iftok#1\emptytoks \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \unexpanded\def\doifemptytoks#1% {\iftok#1\emptytoks \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \let\doifsometokselse\doifelsesometoks %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 {definequantity} %D %D These do the same but for numbers. \installsystemnamespace{quantity} \unexpanded\def\definequantity {\dodoubleargument\syst_helpers_define_quantity} \def\syst_helpers_define_quantity[#1][#2]% {\expandafter\def\csname\??quantity#1\endcsname{#2}} \unexpanded\def\freezequantity {\dodoubleargument\syst_helpers_freeze_quantity} \def\syst_helpers_freeze_quantity[#1][#2]% {\expandafter\edef\csname\??quantity#1\endcsname{\the\numexpr#2}} \unexpanded\def\setquantity #1#2{\expandafter\def \csname\??quantity#1\endcsname{#2}} % quick way \unexpanded\def\setgquantity#1#2{\expandafter\gdef\csname\??quantity#1\endcsname{#2}} % quick way \unexpanded\def\setequantity#1#2{\expandafter\edef\csname\??quantity#1\endcsname{\the\numexpr#2}} % quick way \unexpanded\def\setxquantity#1#2{\expandafter\xdef\csname\??quantity#1\endcsname{\the\numexpr#2}} % quick way \def\quantity {\the\quantitied} \def\quantitied#1% {\numexpr\ifcsname\??quantity#1\endcsname\lastnamedcs\else\zeropoint\fi\relax} \def\directquantity#1% {\the\numexpr#1\relax} % let\quantified\quantitied %D \macros %D {dividedsize} %D %D This one can be used inside a measure (used in m4all): %D %D \starttyping %D \definemeasure[columnwidth][\dividedsize\textwidth{1em}{3}] %D \stoptyping \def\dividedsize#1#2#3% size gap n {\dimexpr \ifnum\dimexpr#1\relax>\plusone (\dimexpr#1\relax-\numexpr#3-\plusone\relax\dimexpr#2\relax)/#3\else#1% \fi \relax} %D \macros %D {doifdimensionelse} %D %D This is a dirty one: we simply append a unit and discard it when needed. \def\doifelsedimension#1% {\ifchkdim#1\or \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \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 \let\doifelsedimenstring\doifelsedimension \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 \orelse\ifdim#1<#2% \plusone \else \plustwo \fi} \def\comparedimensioneps#1#2% todo: use eps feature {\compresult \ifdim\dimexpr#1-#2\relax<\roudingeps \zerocount \orelse\ifdim\dimexpr#2-#1\relax<\roudingeps \zerocount \orelse\ifdim#1<#2% \plusone \else \plustwo \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{\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 \orelse\ifx#1\blankspace % go on \else #1% \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 \orelse\ifempty#2% \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \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 \orelse\ifcase\csname\??flag#1\endcsname \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \let\doifflaggedelse\doifelseflagged \def\doifnotflagged#1% {\expandafter\ifx\csname\??flag#1\endcsname\relax \expandafter\firstofoneargument \orelse\ifcase\csname\??flag#1\endcsname \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \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 \let\elapsedsteps\!!zerocount \unexpanded\def\elapsedsteptime % unexpanded ! a bit useless but who knows ... {\clf_elapsedsteptime\elapsedsteps\relax} \newcount\c_syst_helpers_test_feature_n \newcount\c_syst_helpers_test_feature_m \def\currentfeaturetest{\number\c_syst_helpers_test_feature_n} \unexpanded\def\testfeature#1#2% {\c_syst_helpers_test_feature_m#1\relax \xdef\elapsedsteps{\number\c_syst_helpers_test_feature_m}% \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: % \number\c_syst_helpers_test_feature_m\space steps}% \c_syst_helpers_test_feature_n\zerocount \syst_helpers_test_feature_yes \writestatus\m!system {feature test done: % \number\c_syst_helpers_test_feature_m\space steps, % \clf_elapsedtime\space seconds, % \clf_elapsedsteptime\elapsedsteps\space\space per step}% \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 \ifempty\m_syst_helpers_range_to\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 %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,#0\_o_e_p_ % {#1,#2} \def\syst_helpers_checked_pair#1,#2,#-\_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% {\ifempty#1% \noexpand\empty \else {#1}% \fi} %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 % \orelse\ifx\m_case_temp\s!default % \expandafter\firstofoneargument % \else % \expandafter\syst_aux_case_nop % \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 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}% \ifempty\m_syst_string_one} \protect \endinput