%D \module %D [ file=syst-aux, % merge of syst-gen cum suis %D version=1996.03.20, %D title=\CONTEXT\ System Macros, %D subtitle=General, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. %D In the process of being adapted to \LMTX\ flags so a bit of a mess now. Also %D our playground. \registerctxluafile{syst-aux}{autosuffix} \registerctxluafile{syst-mac}{autosuffix} %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 These are for local helpers, the names are historic: \mutable\let\docommand \relax \mutable\let\dodocommand\relax %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 \protected\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. So, effectively we have two ways to %D protect a macro. %D %D In \LMTX\ we try to avoid this and use \type {\protected} instead, which is %D possible because over time we got rid of using the \CONTEXT\ macro with that %D name. \pushoverloadmode \aliased\let\unexpanded\normalprotected \popoverloadmode %D We're definitely in \LMTX\ mode here. \permanent\protected\lettonothing\startlmtxmode \permanent\protected\lettonothing\stoplmtxmode \permanent \def\startmkivmode#-\stopmkivmode{} \permanent\protected\lettonothing\stopmkivmode %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 \permanent\protected\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 \immutable\edefcsname ??#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: \permanent\def\normalspace{ } \newif\if!!donea \newif\if!!doneb \newif\if!!donec % soon obsolete in lmtx \newif\if!!doned \newif\if!!donee \newif\if!!donef % soon obsolete in lmtx \immutable\def\!!zerocount {0} % alongside \zerocount \immutable\def\!!minusone {-1} % ... \immutable\def\!!plusone {1} % ... \immutable\def\!!plustwo {2} % ... \immutable\def\!!plusthree {3} % ... \immutable\def\!!plusfour {4} % ... \immutable\def\!!plusfive {5} % ... \immutable\def\!!plussix {6} % ... \immutable\def\!!plusseven {7} % ... \immutable\def\!!pluseight {8} % ... \immutable\def\!!plusnine {9} % alongside \plusnine \setnewconstant \uprotationangle 0 \setnewconstant\rightrotationangle 90 \setnewconstant \downrotationangle 180 \setnewconstant \leftrotationangle 270 %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!simple {simple} % % \def\s!start{start} % \def\s!stop {stop} % \immutable\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 \pushoverloadmode \permanent\protected\def\expanded#1% {\xdef\m_syst_helpers_expanded{\noexpand#1}\m_syst_helpers_expanded} \popoverloadmode \permanent\protected\def\startexpanded#1\stopexpanded {\xdef\m_syst_helpers_expanded{#1}\m_syst_helpers_expanded} \permanent\protected\lettonothing\stopexpanded %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. %permanent\def\gobbleoneargument #-{} \permanent\def\gobbletwoarguments #-#-{} \permanent\def\gobblethreearguments #-#-#-{} \permanent\def\gobblefourarguments #-#-#-#-{} \permanent\def\gobblefivearguments #-#-#-#-{} \permanent\def\gobblesixarguments #-#-#-#-{} \permanent\def\gobblesevenarguments #-#-#-#-#-{} \permanent\def\gobbleeightarguments #-#-#-#-#-#-{} \permanent\def\gobbleninearguments #-#-#-#-#-#-#-{} \permanent\tolerant\def\gobbleoneoptional [#-]{} \permanent\tolerant\def\gobbletwooptionals [#-]#*[#-]{} \permanent\tolerant\def\gobblethreeoptionals[#-]#*[#-]#*[#-]{} \permanent\tolerant\def\gobblefouroptionals [#-]#*[#-]#*[#-]#*[#-]{} \permanent\tolerant\def\gobblefiveoptionals [#-]#*[#-]#*[#-]#*[#-]#*[#-]{} %D Reserved macros for tests: %D A \type \type {\let} is more efficient than a \type {\def} so we often let something %D to relax or do nothing. However, we then loose the name in tracing. For that we now %D have \type {\lettonothing \foo} which is efficient but also keeps the name. Of course %D this is no solution for commands that take arguments but at least it helps tracing a %D bit. We keep of course \type {\donothing}. %D Maybe even nicer is a let that aliases but keeps the name. % \aliased\let\donothing\empty \immutable\def\donothing {} % better in tracing %untraced\immutable\def\untraceddonothing{} \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. % \mutable\let\next \relax % \mutable\let\nextnext \relax % kind of obsolete % \mutable\let\nextnextnext \relax % kind of obsolete % \mutable\let\nexttoken \relax \permanent\protected\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} \permanent\protected\def\doifelsenextcharcs % #1#2#3% #1 should not be {} ! {\futureexpandis} \aliased\let\doifnextcharelse \doifelsenextchar \aliased\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. \permanent\protected\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} \permanent\protected\def\doifelsenextoptionalcs {\futureexpandis[} \aliased\let\doifnextoptionalelse \doifelsenextoptional \aliased\let\doifnextoptionalcselse\doifelsenextoptionalcs \permanent\protected\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} \permanent\protected\def\doifelsenextbgroupcs % #1#2 {\futureexpandis\bgroup} \aliased\let\doifnextbgroupelse \doifelsenextbgroup \aliased\let\doifnextbgroupcselse\doifelsenextbgroupcs \permanent\protected\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} \aliased\let\doifnextparenthesiselse\doifelsenextparenthesis %D The next one is handy in predictable situations: \permanent\protected\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} \permanent\protected\def\doifelsefastoptionalcheckcs {\futureexpandis[} \aliased\let\doiffastoptionalcheckelse \doifelsefastoptionalcheck \aliased\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: \aliased\let\assumelongusagecs\expandafterpars % so we can replace it %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{\permanent\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. \permanent\def\setvalue #1{\defcsname #1\endcsname} \permanent\def\setgvalue #1{\gdefcsname#1\endcsname} \permanent\def\setevalue #1{\edefcsname#1\endcsname} \permanent\def\setxvalue #1{\xdefcsname#1\endcsname} \permanent\def\getvalue #1{\csname#1\endcsname} % maybe: \begincsname#1\endcsname \permanent\def\letvalue #1{\letcsname #1\endcsname} \permanent\def\letgvalue #1{\gletcsname#1\endcsname} \permanent\def\resetvalue #1{\letcsname#1\endcsname\empty} \permanent\def\undefinevalue#1{\letcsname#1\endcsname\undefined} \permanent\def\ignorevalue#1#2{\letcsname#1\endcsname\empty} \permanent\def\setuvalue #1{\protected\defcsname #1\endcsname} \permanent\def\setuevalue #1{\protected\edefcsname#1\endcsname} \permanent\def\setugvalue #1{\protected\gdefcsname#1\endcsname} \permanent\def\setuxvalue #1{\protected\xdefcsname#1\endcsname} \permanent\protected\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: \aliased\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\ifrelax\csname NameA\endcsname ... \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. \permanent\def\doifelseundefined#1% {\ifcsname#1\endcsname \expandafter\secondoftwoarguments\else\expandafter\firstoftwoarguments \fi} \permanent\def\doifelsedefined#1% {\ifcsname#1\endcsname \expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments \fi} \permanent\def\doifundefined#1% {\ifcsname#1\endcsname \expandafter\gobbleoneargument\else\expandafter\firstofoneargument \fi} \permanent\def\doifdefined#1% {\ifcsname#1\endcsname \expandafter\firstofoneargument\else\expandafter\gobbleoneargument \fi} \aliased\let\doifundefinedelse\doifelseundefined \aliased\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: \permanent\protected\def\letbeundefined#1% {\letcsname#1\endcsname\undefined} \permanent\protected\def\localundefine#1% conditional {\ifcsname#1\endcsname\letcsname#1\endcsname\undefined\fi} \permanent\protected\def\globalundefine#1% conditional {\ifcsname#1\endcsname\gletcsname#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}. %D %D \stoptyping %D \doifelsealldefined {foo,ofo} {YES}{NOP} %D \doifelseallundefined{foo,ofo} {YES}{NOP} %D \doifelsealldefined {relax,ofo}{YES}{NOP} %D \doifelseallundefined{foo,relax}{YES}{NOP} %D \stoptyping \def\syst_helpers_do_if_all_defined_else#1% {\ifcsname#1\endcsname\else \donefalse \expandafter\quitcommalist % added \fi} \def\syst_helpers_do_if_all_undefined_else#1% {\ifcsname#1\endcsname \donefalse \expandafter\quitcommalist \fi} \permanent\protected\def\syst_helpers_do_if_all_else#1#2% {\begingroup \donetrue % we could use a reserved one and avoid the group \processcommalist[#2]#1% \ifdone \endgroup\expandafter\firstoftwoarguments \else \endgroup\expandafter\secondoftwoarguments \fi} \permanent\protected\def\doifelsealldefined {\syst_helpers_do_if_all_else\syst_helpers_do_if_all_defined_else} \permanent\protected\def\doifelseallundefined{\syst_helpers_do_if_all_else\syst_helpers_do_if_all_undefined_else} \aliased\let\doifalldefinedelse \doifelsealldefined \aliased\let\doifallundefinedelse\doifelseallundefined %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. \permanent\protected\def\doifelse#1#2% {\iftok{#1}{#2}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \permanent\protected\def\doif#1#2% {\iftok{#1}{#2}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \permanent\protected\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. \permanent\protected\def\doifelseempty#1% {\def\m_syst_string_one{#1}% \ifempty\m_syst_string_one \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \aliased\let\doifemptyelse\doifelseempty \permanent\protected\def\doifempty#1% {\def\m_syst_string_one{#1}% \ifempty\m_syst_string_one \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \permanent\protected\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: %protected\def\doifelseinset#1#2{\clf_doifelseinset{#1}{#2}} %protected\def\doifinset #1#2{\clf_doifinset {#1}{#2}} %protected\def\doifnotinset #1#2{\clf_doifnotinset {#1}{#2}} % % \let\firstinset \clf_firstinset % These don't accept spaces after commas: % % \protected\def\doifelseinset#1#2% % {\ifhasxtoks{,#1,}{,#2,}% % \expandafter\firstoftwoarguments % \else % \expandafter\secondoftwoarguments % \fi} % \protected\def\doifinset#1#2% % {\ifhasxtoks{,#1,}{,#2,}% % \expandafter\firstofoneargument % \else % \expandafter\gobbleoneargument % \fi} % \protected\def\doifnotinset#1#2% % {\ifhasxtoks{,#1,}{,#2,}% % \expandafter\gobbleoneargument % \else % \expandafter\firstofoneargument % \fi} % But these do: \immutable\edef\a!comma{\expandtoken \ignorecatcode \commaasciicode} \immutable\edef\a!space{\expandtoken \ignorecatcode \spaceasciicode} \normalexpanded { \permanent \protected \def \noexpand \doifelseinset#1#2% {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}% \noexpand\expandafter\noexpand\firstoftwoarguments \noexpand\else \noexpand\expandafter\noexpand\secondoftwoarguments \noexpand\fi} \permanent \protected \def \noexpand \doifinset#1#2% {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}% \noexpand\expandafter\noexpand\firstofoneargument \noexpand\else \noexpand\expandafter\noexpand\gobbleoneargument \noexpand\fi} \permanent \protected \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. \aliased\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} \permanent\protected\def\doifelsecommon#1#2{\clf_doifelsecommon{#1}{#2}} % todo: define in lua \permanent\protected\def\doifcommon #1#2{\clf_doifcommon {#1}{#2}} % todo: define in lua \permanent\protected\def\doifnotcommon #1#2{\clf_doifnotcommon {#1}{#2}} % todo: define in lua \aliased\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. \mutable\let\commalistcommand\empty \protected\def\syst_helpers_process_comma_item#+,% {\ifarguments \expandafter\syst_helpers_process_comma_item_gobble \orelse\ifparameter#1\or \commalistcommand{#1}% \expandafter\syst_helpers_process_comma_item_next \else \expandafter\syst_helpers_process_comma_item_gobble \fi} % \def\syst_helpers_process_comma_item_next % {\expandafterspaces\syst_helpers_process_comma_item} % \protected\def\processcommalist[#1]#2% % {\pushmacro\commalistcommand % \def\commalistcommand{#2}% % \expandafterspaces\syst_helpers_process_comma_item#1,\ignorearguments\ignorearguments\ignorearguments % \popmacro\commalistcommand} % \protected\def\processcommacommand[#1]#2% % {\pushmacro\commalistcommand % \def\commalistcommand{#2}% % \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_comma_item#1,}\ignorearguments\ignorearguments\ignorearguments % \popmacro\commalistcommand} % \permanent\protected\def\processcommalist[#*#+]#2% % {\pushmacro\commalistcommand % \def\commalistcommand{#2}% % \expandafterspaces\syst_helpers_process_comma_item#1,\ignorearguments\ignorearguments\ignorearguments % \popmacro\commalistcommand} % % \permanent\protected\def\processcommacommand[#*#+]#2% % {\pushmacro\commalistcommand % \def\commalistcommand{#2}% % \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_comma_item#1,}\ignorearguments\ignorearguments\ignorearguments % \popmacro\commalistcommand} \tolerant\protected\def\syst_helpers_process_comma_item#*#1,% {\ifarguments\or \commalistcommand{#1}% \expandafter\syst_helpers_process_comma_item_next \fi} \def\syst_helpers_process_comma_item_next {\expandafterspaces\syst_helpers_process_comma_item} \permanent\protected\def\processcommalist[#1]#2% {\pushmacro\commalistcommand \def\commalistcommand{#2}% \syst_helpers_process_comma_item#1\ignorearguments\ignorearguments\ignorearguments \popmacro\commalistcommand} \permanent\protected\def\processcommacommand[#1]#2% {\pushmacro\commalistcommand \def\commalistcommand{#2}% \normalexpanded{\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{} \permanent\protected\def\quitcommalist {\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b} \permanent\protected\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 \let\syst_helpers_do_process_comma_list_with_parameters\gobbleoneargument \permanent\protected\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 \mutable\let\currentcommalistitem\empty \permanent\protected\def\startprocesscommalist[#1]#2\stopprocesscommalist {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}% \processcommalist[#1]\syst_helpers_comma_list_step} \permanent\protected\def\startprocesscommacommand[#1]#2\stopprocesscommacommand {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}% \normalexpanded{\processcommalist[#1]}\syst_helpers_comma_list_step} \permanent\protected\lettonothing\stopprocesscommalist \permanent\protected\lettonothing\stopprocesscommacommand %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. \mutable\let\commalistelement\empty \let\m_syst_string_one\empty \let\m_syst_string_two\empty \let\syst_helpers_do_compare_process_action\relax \protected\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} \protected\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} \permanent\protected\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\syst_helpers_do_process_action[} \protected\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} \permanent\protected\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]} \let\syst_helpers_do_process_action \gobbleoneargument \let\syst_helpers_do_do_process_action\gobbleoneargument \tolerant\protected\def\syst_helpers_process_first_action_in_set_indeed[#1]#*[#2]% {\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[#2]\syst_helpers_do_do_process_action}% \normalexpanded{\processcommalist[#1]}\syst_helpers_do_process_action} \protected\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} \let\syst_process_action_in_set_all\relax \tolerant\protected\def\syst_helpers_process_all_actions_in_set_indeed[#1]#*[#2]% {\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[#2]\syst_process_action_in_set_one}% \processcommacommand[#1]\syst_process_action_in_set \globalpopmacro\syst_process_action_in_set_all} \permanent\protected\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: % \protected\def\processnextcommalist#1[#2#3]% % {\pushmacro\commalistcommand % \def\commalistcommand{#1}% % \expandafterspaces\syst_helpers_process_comma_item#2#3\ignorearguments\ignorearguments\ignorearguments % \popmacro\commalistcommand} \permanent\protected\def\processnextcommalist#1[#2#3]% watch out: mkiv has two extra initial arguments {\pushmacro\commalistcommand \def\commalistcommand{#1}% \expandafterspaces\syst_helpers_process_comma_item#2#3\ignorearguments\ignorearguments\ignorearguments \popmacro\commalistcommand} %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. \mutable\let\firstcharacter \empty \mutable\let\remainingcharacters\empty \permanent\protected\def\getfirstcharacter #1{\clf_getfirstcharacter{#1}} \permanent\protected\def\doifelsefirstchar #1#2{\clf_doifelsefirstchar{#1}{#2}} \permanent\protected\def\thefirstcharacter #1{\clf_thefirstcharacter{#1}} \permanent\protected\def\theremainingcharacters#1{\clf_theremainingcharacters{#1}} \aliased\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 % % \protected\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 % % \protected\def\syst_helpers_do_if_in_string_else#1% ##2 can be {abc} % {\normalexpanded{\protected\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}\s!e_o_t_token\ignorearguments\ignorearguments} % % \protected\def\syst_helpers_do_if_in_string_else_delimited#1% ##2 can be {abc} % {\normalexpanded{\protected\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,}\s!e_o_t_token\ignorearguments\ignorearguments} % % \protected\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} % % \protected\def\syst_helpers_do_if_in_string#1% ##2 can be {abc} % {\normalexpanded{\protected\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}\s!e_o_t_token\ignorearguments\ignorearguments} % % \protected\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} % % \protected\def\syst_helpers_do_if_not_in_string#1% ##2 can be {abc} % {\normalexpanded{\protected\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}\s!e_o_t_token\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} \permanent\protected\def\doifelseinstring#1#2% {\ifhasxtoks{#1}{#2}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \permanent\protected\def\doifinstring#1#2% {\ifhasxtoks{#1}{#2}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \permanent\protected\def\doifnotinstring#1#2% {\ifhasxtoks{#1}{#2}% \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \aliased\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: \aliased\let\doifelseincsname\doifelseinstring \aliased\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). \permanent\def\doifelsenumber#1% {\ifchknum#1\or \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \permanent\def\doifnumber#1% {\ifchknum#1\or \expandafter\firstoftwoarguments \else \expandafter\gobbleoneargument \fi} \permanent\def\doifnotnumber#1% {\ifchknum#1\or \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \aliased\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 \permanent\def\percentdimen#1#2% dimen percentage (with %) {\dimexpr\clf_percentageof{#2}\dimexpr#1\relax} \permanent\protected\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? \let\syst_helpers_do_make_raw_comma_list\gobbleoneargument \permanent\protected\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}} % beware: in mkiv { } were lost so it was not compatible with the non raw \aliased\let\rawprocesscommalist \processcommalist % can go \aliased\let\rawprocesscommacommand\processcommacommand % can go %D Here is one without nesting .. still needed? Only used for modes (no nesting %D there). \mutable\let\fastcommalistcommand\relax \protected\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} \protected\def\syst_helpers_process_fast_comma_item_next {\expandafterspaces\syst_helpers_process_fast_comma_item} \permanent\protected\def\fastprocesscommalist[#1]#2% {\let\fastcommalistcommand#2% \expandafterspaces\syst_helpers_process_fast_comma_item#1\ignorearguments\ignorearguments\ignorearguments} \permanent\protected\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} \permanent\protected\def\rawdoifelseinset#1% {\edef\m_syst_sub_string{#1}% expand #1 here \ifempty\m_syst_sub_string \expandafter\thirdofthreearguments \else \expandafter\syst_helpers_do_if_else_in_set \fi} \aliased\let\rawdoifinsetelse\rawdoifelseinset \def\syst_helpers_do_if_in_set#1% {\ifhasxtoks{,\m_syst_sub_string,}{,#1,}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \permanent\protected\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: % probably never used % % \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} % % \permanent\protected\def\xrawprocessaction[#1]#*[#2]% % {\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#2\ignorearguments\ignorearguments\ignorearguments % \ifrelax\m_syst_helpers_process_action % \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{\defcsname #1#2\endcsname} % takes #3 \def\dosetevalue #1#2{\edefcsname #1#2\endcsname} % takes #3 \def\dosetgvalue #1#2{\global\edefcsname#1#2\endcsname} % takes #3 \def\doresetvalue #1#2{\letcsname #1#2\endcsname\empty} \def\doignorevalue#1#2#3{\letcsname #1#2\endcsname\empty} \def\docopyvalue #1#2#3{\defcsname #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. \protected\def\showassignerror#1#2% {\writestatus{setup}{missing or ungrouped '=' after '#1' in line #2}} \permanent\protected\def\doassignempty[#1][#2=#3]% kind of obsolete {\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. % \mutable\let\currentvalue\empty % only mkii \permanent\protected\def\getparameters {\dogetparameters\dosetvalue} \permanent\protected\def\geteparameters {\dogetparameters\dosetevalue} \permanent\protected\def\getgparameters {\dogetparameters\dosetgvalue} \permanent\protected\def\getxparameters {\dogetparameters\dosetxvalue} \permanent\protected\def\forgetparameters{\dogetparameters\doignorevalue} \aliased\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} \permanent\protected\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 \let\syst_helpers_get_empty_parameters\gobbleoneargument \permanent\protected\def\getemptyparameters[#1]#*[#2]% {\def\syst_helpers_get_empty_parameters##1{\doassignempty[#1][##1]}% \processcommalist[#2]\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. \aliased\let\doassign \getparameters % obsolete, these will go \aliased\let\doeassign \geteparameters % obsolete, these will go \aliased\let\undoassign\forgetparameters % obsolete, these will go %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. \mutable\let\currentassignmentlistcommand\gobbletwoarguments \mutable\let\currentassignmentlistkey \empty \mutable\let\currentassignmentlistvalue \empty \let\syst_helpers_process_assign_entry\gobbleoneargument \permanent\protected\def\processassignmentlist[#1]#2% #2 == \command{key}{value] {\def\syst_helpers_process_assign_entry##1{#2}% {##2}{##3} % namespace is ignored \dogetparameters\syst_helpers_process_assign_entry[][#1]} \permanent\protected\def\processassignmentcommand[#1]% {\normalexpanded{\processassignmentlist[#1]}} \permanent\protected\def\startprocessassignmentlist[#1]#2\stopprocessassignmentlist {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}% \processassignmentlist[#1]\currentassignmentlistcommand} \permanent\protected\lettonothing\stopprocessassignmentlist \permanent\protected\def\startprocessassignmentcommand[#1]#2\stopprocessassignmentcommand {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}% \normalexpanded{\processassignmentlist[#1]}\currentassignmentlistcommand} \permanent\protected\lettonothing\stopprocessassignmentcommand %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. \let\syst_helpers_copy_parameter\relax \permanent\protected\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 % % \protected\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 \mutable\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} \permanent\protected\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} \permanent\protected\def\getcommacommandsize[#1]% {\normalexpanded{\getcommalistsize[#1]}} \permanent\def\getcommalistcount[#1]% {\beginlocalcontrol \getcommalistsize[#1]% \endlocalcontrol \the\commalistcounter} \permanent\def\getcommacommandcount[#1]% {\beginlocalcontrol \getcommacommandsize[#1]% \endlocalcontrol \the\commalistcounter} %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} \permanent\protected\def\getfromcommalist[#1]#*[#2]% {\let\commalistelement\empty \commalistcounter#2\relax \expandafterspaces\syst_helpers_get_from_comma_list#1\ignorearguments\ignorearguments} \permanent\protected\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} \permanent\protected\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 \protected\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. \newcount\nofarguments \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: % % \protected\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 % % \protected\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} % % % % nicer is: % % \def\syst_helpers_double_empty_one_yes[#+]% % {\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 % % \protected\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[#+]% % {\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[#+]% % {\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: % % \protected\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[#+]% % {\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[#+]% % {\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[#+]% % {\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: % % \protected\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[#+]% % {\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[#+]% % {\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[#+]% % {\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[#+]% % {\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: % % \protected\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[#+]% % {\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[#+]% % {\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[#+]% % {\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[#+]% % {\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[#+]% % {\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: % % \protected\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[#+]% % {\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[#+]% % {\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[#+]% % {\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[#+]% % {\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[#+]% % {\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[#+]% % {\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[]} %tolerant\def\syst_single_empty#1\relax[#+]% \tolerant\def\syst_single_empty#1\relax[#2]% {\ifarguments \or\firstargumentfalse \or\firstargumenttrue \fi #1[#2]} %tolerant\def\syst_double_empty#1\relax[#2]#*[#3]% \tolerant\def\syst_double_empty#1\relax[#+]#*[#+]% {\ifarguments \or\firstargumentfalse\secondargumentfalse \or\firstargumenttrue \secondargumentfalse \or\firstargumenttrue \secondargumenttrue \fi #1[#2][#3]} %tolerant\def\syst_triple_empty#1\relax[#2]#*[#3]#*[#4]% \tolerant\def\syst_triple_empty#1\relax[#+]#*[#+]#*[#+]% {\ifarguments \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fi #1[#2][#3][#4]} %tolerant\def\syst_quadruple_empty#1\relax[#2]#*[#3]#*[#4]#*[#5]% \tolerant\def\syst_quadruple_empty#1\relax[#+]#*[#+]#*[#+]#*[#+]% {\ifarguments \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fi #1[#2][#3][#4][#5]} %\tolerant\def\syst_quintuple_empty#1\relax[#2]#*[#3]#*[#4]#*[#5]#*[#6]% \tolerant\def\syst_quintuple_empty#1\relax[#+]#*[#+]#*[#+]#*[#+]#*[#+]% {\ifarguments \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \fi #1[#2][#3][#4][#5][#6]} %tolerant\def\syst_sixtuple_empty#1\relax[#2]#*[#3]#*[#4]#*[#5]#*[#6]#*[#7]% \tolerant\def\syst_sixtuple_empty#1\relax[#+]#*[#+]#*[#+]#*[#+]#*[#+]#*[#+]% {\ifarguments \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse\sixthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse\sixthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumenttrue \fi #1[#2][#3][#4][#5][#6][#7]} %tolerant\def\syst_seventuple_empty#1\relax[#2]#*[#3]#*[#4]#*[#5]#*[#6]#*[#7]#*[#8]% \tolerant\def\syst_seventuple_empty#1\relax[#+]#*[#+]#*[#+]#*[#+]#*[#+]#*[#+]#*[#+]% {\ifarguments \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse\sixthargumentfalse\seventhargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumentfalse\seventhargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumenttrue \seventhargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumenttrue \seventhargumenttrue \fi #1[#2][#3][#4][#5][#6][#7][#8]} \permanent\protected\def\dosingleempty #1{\syst_single_empty #1\relax} \permanent\protected\def\dodoubleempty #1{\syst_double_empty #1\relax} \permanent\protected\def\dotripleempty #1{\syst_triple_empty #1\relax} \permanent\protected\def\doquadrupleempty #1{\syst_quadruple_empty #1\relax} \permanent\protected\def\doquintupleempty #1{\syst_quintuple_empty #1\relax} \permanent\protected\def\dosixtupleempty #1{\syst_sixtuple_empty #1\relax} \permanent\protected\def\doseventupleempty#1{\syst_seventuple_empty#1\relax} %D Aliases: \aliased\let\dosingleargument \dosingleempty \aliased\let\dodoubleargument \dodoubleempty \aliased\let\dotripleargument \dotripleempty \aliased\let\doquadrupleargument \doquadrupleempty \aliased\let\doquintupleargument \doquintupleempty \aliased\let\dosixtupleargument \dosixtupleempty \aliased\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}. \aliased\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. \permanent\protected\def\complexorsimple#1% {\doifelsenextoptional {\firstargumenttrue \csname\s!complex\csstring#1\endcsname} {\firstargumentfalse\csname\s!simple \csstring#1\endcsname}} \permanent\protected\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. \protected\def\syst_helpers_complex_or_simple#1#2% {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#2}} \protected\def\syst_helpers_complex_or_simple_empty#1% {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#1[]}} \permanent\protected\def\definecomplexorsimple#1% {\protected\edef#1{\syst_helpers_complex_or_simple \expandafter\noexpand\csname\s!complex\csstring#1\endcsname \expandafter\noexpand\csname\s!simple \csstring#1\endcsname}} \permanent\protected\def\definecomplexorsimpleempty#1% {\protected\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 \permanent\protected\def \permitspacesbetweengroups{\settrue \c_syst_helpers_permit_spaces_between_groups} \permanent\protected\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. % \protected\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{}} % % \protected\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} % % % % nicer is: % % \def\syst_helpers_group_double_empty_one_yes#+% % {\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{}} % % \protected\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#+% % {\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#+% % {\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{}} % % \protected\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#+% % {\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#+% % {\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#+% % {\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{}} % % \protected\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#+% % {\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#+% % {\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#+% % {\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#+% % {\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{}} \tolerant\def\syst_single_group_empty#1\relax#_% {\ifarguments \or\firstargumentfalse \or\firstargumenttrue \fi #1#2} \tolerant\def\syst_double_group_empty#1\relax#_#*#_% {\ifarguments \or\firstargumentfalse\secondargumentfalse \or\firstargumenttrue \secondargumentfalse \or\firstargumenttrue \secondargumenttrue \fi #1#2#3} \tolerant\def\syst_triple_group_empty#1\relax#_#*#_#*#_% {\ifarguments \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fi #1#2#3#4} \tolerant\def\syst_quadruple_group_empty#1\relax#_#*#_#*#_#*#_% {\ifarguments \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fi #1#2#3#4#5} \tolerant\def\syst_quintuple_group_empty#1\relax#_#*#_#*#_#*#_#*#_% {\ifarguments \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \fi #1#2#3#4#5#6} \permanent\protected\def\dosinglegroupempty #1{\syst_single_group_empty #1\relax} \permanent\protected\def\dodoublegroupempty #1{\syst_double_group_empty #1\relax} \permanent\protected\def\dotriplegroupempty #1{\syst_triple_group_empty #1\relax} \permanent\protected\def\doquadruplegroupempty #1{\syst_quadruple_group_empty #1\relax} \permanent\protected\def\doquintuplegroupempty #1{\syst_quintuple_group_empty #1\relax} %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. \permanent\def\firstofoneargument #1{#1} \permanent\def\firstoftwoarguments #1#-{#1} \permanent\def\secondoftwoarguments #-#1{#1} \permanent\def\firstofthreearguments #1#-#-{#1} \permanent\def\secondofthreearguments #-#1#-{#1} \permanent\def\thirdofthreearguments #-#-#1{#1} \permanent\def\firstoffourarguments #1#-#-#-{#1} \permanent\def\secondoffourarguments #-#1#-#-{#1} \permanent\def\thirdoffourarguments #-#-#1#-{#1} \permanent\def\fourthoffourarguments #-#-#-#1{#1} \permanent\def\firstoffivearguments #1#-#-#-#-{#1} \permanent\def\secondoffivearguments #-#1#-#-#-{#1} \permanent\def\thirdoffivearguments #-#-#1#-#-{#1} \permanent\def\fourthoffivearguments #-#-#-#1#-{#1} \permanent\def\fifthoffivearguments #-#-#-#-#1{#1} \permanent\def\firstofsixarguments #1#-#-#-#-#-{#1} \permanent\def\secondofsixarguments#-#1#-#-#-#-{#1} \permanent\def\thirdofsixarguments #-#-#1#-#-#-{#1} \permanent\def\fourthofsixarguments#-#-#-#1#-#-{#1} \permanent\def\fifthofsixarguments #-#-#-#-#1#-{#1} \permanent\def\sixthofsixarguments #-#-#-#-#-#1{#1} \permanent\protected\def\firstofoneunexpanded #1{#1} \permanent\protected\def\firstoftwounexpanded #1#-{#1} \permanent\protected\def\secondoftwounexpanded #-#1{#1} \permanent\protected\def\firstofthreeunexpanded #1#-#-{#1} \permanent\protected\def\secondofthreeunexpanded#-#1#-{#1} \permanent\protected\def\thirdofthreeunexpanded #-#-#1{#1} %D \macros %D {globalletempty,letempty, %D letvalueempty,letgvalueempty, %D letvaluerelax,letgvaluerelax} %D %D Trivial: \permanent\protected\def\letempty #1{\let #1\empty} \permanent\protected\def\globalletempty#1{\glet#1\empty} \permanent\protected\def\letvalueempty #1{\letcsname #1\endcsname\empty} \permanent\protected\def\letgvalueempty#1{\gletcsname#1\endcsname\empty} \permanent\protected\def\letvaluerelax #1{\letcsname #1\endcsname\relax} \permanent\protected\def\letgvalurelax #1{\gletcsname#1\endcsname\relax} \permanent\protected\def\relaxvalueifundefined#1% {\ifcsname#1\endcsname \else \aliased\letcsname#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. \permanent\protected\def\wait {\begingroup \read16 to \wait \endgroup} % %D \macros % %D {writestring,writeline,writestatus} % %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 % % \protected\def\writestring{\immediate\write\statuswrite} % \protected\def\writeline {\writestring{}} % % \fi % % \protected\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: % \immediatemessage % {} mandate / defined at the lua end %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 \defcsname\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} \permanent\protected\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}. \permanent\protected\def\resetglobal {\enforced\let\redoglobal\relax \enforced\let\dodoglobal\relax} \resetglobal \permanent\protected\def\doglobal {\ifrelax\redoglobal \enforced\let\redoglobal\global \enforced\let\dodoglobal\syst_helpers_dodo_global \fi} \def\syst_helpers_dodo_global {\resetglobal\global} \let\syst_helpers_redo_global\relax \permanent\protected\def\saveglobal {\let\syst_helpers_dodo_global\dodoglobal \let\syst_helpers_redo_global\redoglobal} \permanent\protected\def\restoreglobal {\enforced\let\redoglobal\syst_helpers_redo_global \enforced\let\dodoglobal\syst_helpers_dodo_global} %D Whatever (will be overloaded): \protected\def\define#1% {\ifdefined#1% \message{[\noexpand#1is already defined]}% \protected\expandafter\def\expandafter\gobbleddefinition \else \protected\expandafter\def \fi#1} \protected\def\redefine#1% {\ifdefined#1% \message{[\noexpand#1is redefined]}% \fi \protected\def#1} % \permanent\protected\def\definemacro#1% % {\ifdefined#1% % \message{[\noexpand#1is already defined]}% % \protected\expandafter\def\expandafter\gobbleddefinition % \else % \protected\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] %D When okay the parameter hack will also go into \MKIV. \integerdef\c_syst_parameter_catcode\parametercatcode \bgroup \obeylines \permanent\protected\gdef\starttexdefinition% {\integerdef\c_syst_parameter_catcode\catcode\hashasciicode% \catcode\hashasciicode\parametercatcode% \bgroup% \obeylines% \syst_helpers_start_tex_definition} \gdef\syst_helpers_start_tex_definition#1 {%catcodetable\ctxcatcodes% can be adapted \catcode\endoflineasciicode\ignorecatcode% \clf_texdefinition_one{#1}} \permanent\protected\glettonothing\stoptexdefinition \permanent\gdef\dostarttexdefinition#1\stoptexdefinition% {\egroup% \clf_texdefinition_two{#1}% \catcode\hashasciicode\c_syst_parameter_catcode} \egroup % \protected\def\texdefinition#1{\csname\ifcsname#1\endcsname#1\else donothing\fi\endcsname} % todo: a nop cs: char 0 or some corenamespace \permanent\protected\def\texdefinition#1{\begincsname#1\endcsname} %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. \immutable\def\zerocountervalue{0} \permanent\protected\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\ifrelax#1\else#1\fi\fi+\plusone \relax}} \def\syst_helpers_do_decrement#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifrelax#1\else#1\fi\fi+\minusone\relax}} \def\syst_helpers_do_do_do_increment#1,#2){\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifrelax#1\else#1\fi\fi+#2\relax}} \def\syst_helpers_do_do_do_decrement#1,#2){\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifrelax#1\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} \permanent\protected\def\increment{\doifelsenextcharcs(\syst_helpers_do_do_increment\syst_helpers_do_increment} \permanent\protected\def\decrement{\doifelsenextcharcs(\syst_helpers_do_do_decrement\syst_helpers_do_decrement} \permanent\protected\def\fastincrement#1{\dodoglobal\edef#1{\the\numexpr#1+\plusone \relax}} \permanent\protected\def\fastdecrement#1{\dodoglobal\edef#1{\the\numexpr#1+\minusone\relax}} \permanent\protected\def\incrementvalue#1{\expandafter\increment\csname#1\endcsname} \permanent\protected\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\d_syst_maximum_signal % step is about 0.00025pt \permanent\protected\def\newsignal#1% {\ifdefined#1\else \advance\d_syst_maximum_signal2\scaledpoint % to be save in rounding \immutable\dimensiondef#1\d_syst_maximum_signal \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 \aliased\let\checkedstrippedcsname\csstring %D \macros %D {savenormalmeaning} %D %D We will use this one in: \permanent\protected\def\savenormalmeaning#1% {\ifcsname normal\csstring#1\endcsname \else \aliased\letcsname 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 \mutable\def\recursedepth{\the\outerrecurse} \mutable\def\recurselevel{0} \mutable\let\recurseaction\relax \mutable\let\recursestring\empty %% \protected\def\syst_helpers_stepwise_recurse#1#2#3% from to step %% {\ifnum#1>#2\relax %% \expandafter\gobblefourarguments %% \else %% \def\recurselevel{#1}% %% \doubleexpandafter\syst_helpers_stepwise_recurse_yes %% \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}} %% %% \protected\def\syst_helpers_stepwise_recurse_yes %% {\syst_helpers_recurse_content %% \syst_helpers_stepwise_recurse} %% %% \protected\def\syst_helpers_stepwise_reverse#1#2#3% from to step %% {\ifnum#1<#2\relax %% \expandafter\gobblefourarguments %% \else %% \def\recurselevel{#1}% %% \innerrecurse#1\relax %% \advance\innerrecurse#3\relax %% \doubleexpandafter\syst_helpers_stepwise_reverse_yes %% \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}} %% %% \protected\def\syst_helpers_stepwise_reverse_yes %% {\syst_helpers_recurse_content %% \syst_helpers_stepwise_reverse} %% %% \permanent\def\doexpandedrecurse#1#2% user macro (also was \doxprecurse) %% {\ifnum#1>\zerocount %% #2\expandafter\doexpandedrecurse\expandafter{\the\numexpr#1-\plusone\relax}{#2}% %% \fi} %D The next one might replace the above and provides the current step in \type {#1} %D like \type {\dorecurse} do, but it comes with a tiny performance hit. %% \installsystemnamespace{expandedrecurse} %% %% \mutable\let\m_expanded_recursed\gobbleoneargument %% %% \permanent\def\doexpandedrecursed#1#2% this might replace: \doexpandedrecurse %% {\beginlocalcontrol %% \localpushmacro\m_expanded_recursed %% \def\m_expanded_recursed##1{#2}% %% \endlocalcontrol %% \syst_do_expanded_recursed{1}{#1}{#2}% no \plusone ! %% \beginlocalcontrol %% \localpopmacro\m_expanded_recursed %% \endlocalcontrol} %% %% \def\syst_do_expanded_recursed#1#2#3% %% {\ifnum#1>#2\norelax %% \expandafter\gobblethreearguments %% \else %% \m_expanded_recursed{#1}\doubleexpandafter\syst_do_expanded_recursed %% \fi %% {\the\numexpr#1+\plusone\relax}{#2}{#3}} %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: %% \permanent\protected\def\dorecurse#1% %% {\ifcase#1\relax %% \expandafter\gobbletwoarguments %% \or %% \expandafter\syst_helpers_recurse_y %% \else %% \expandafter\syst_helpers_recurse_x %% \fi{#1}} %% %% \protected\def\syst_helpers_recurse_indeed#1#2% from to %% % {\ifnum#1>#2 % %% {\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}} %% %% \protected\def\syst_helpers_recurse_indeed_yes %% {\syst_helpers_recurse_content %% \syst_helpers_recurse_indeed} %% %% \protected\def\syst_helpers_recurse_indeed_nop#0#0#0% %% {} %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 \let\syst_helpers_with\gobbleoneargument \permanent\protected\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}. \aliased\let\endofloop\donothing % maybe \syst_helpers_loop_end \permanent\protected\def\doloop#1% {\global\advance\outerrecurse \plusone \globalpushmacro\recurseaction \globalpushmacro\recurselevel \protected\gdef\recurseaction##1##2{#1}% \enforced\let\endofloop\syst_helpers_loop \syst_helpers_loop1}% no \plusone else \recurselevel wrong \protected\def\syst_helpers_loop#1% {\def\recurselevel{#1}% \expandafter\syst_helpers_loop_yes\expandafter{\the\numexpr\recurselevel+\plusone\relax}} %% \protected\def\syst_helpers_loop_yes %% {\syst_helpers_recurse_content %% \endofloop} \protected\def\syst_helpers_loop_yes {\normalexpanded{\recurseaction{\recurselevel}{\the\outerrecurse}}% \endofloop} \protected\def\syst_helpers_loop_nop#0% {\enforced\let\endofloop\syst_helpers_loop \globalpopmacro\recurselevel \globalpopmacro\recurseaction \global\advance\outerrecurse\minusone} \permanent\protected\def\exitloop % \exitloop quits at end {\enforced\let\endofloop\syst_helpers_loop_nop} \permanent\protected\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}}} %% %% \protected\def\syst_helpers_recurse_x#1#2% %% {\global\advance\outerrecurse\plusone %% \globalpushmacro\recurseaction %% \globalpushmacro\recurselevel %% \protected\gdef\recurseaction##1##2{#2}% %% \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}% %% \globalpopmacro\recurselevel %% \globalpopmacro\recurseaction %% \global\advance\outerrecurse\minusone} %% %% \protected\def\syst_helpers_recurse_y#1#2% %% {\global\advance\outerrecurse\plusone %% \globalpushmacro\recurseaction %% \globalpushmacro\recurselevel %% \let\recurselevel\!!plusone %% \protected\gdef\recurseaction##1##2{#2}% %% \syst_helpers_recurse_content %% \globalpopmacro\recurselevel %% \globalpopmacro\recurseaction %% \global\advance\outerrecurse\minusone} %% %% \permanent\protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 ... todo: remove unused helpers %% {\global\advance\outerrecurse \plusone %% \globalpushmacro\recurseaction %% \globalpushmacro\recurselevel %% \protected\gdef\recurseaction##1##2{#4}% %% \normalexpanded{\ifcmpnum#3\zerocount %% \ifnum#1<#2\relax\relax % so we catch \number\numexpr xx without \relax's %% \doubleexpandafter\gobbletwoarguments %% \else %% \tripleexpandafter\syst_helpers_stepwise_reverse %% \fi %% \or %% \doubleexpandafter\gobbletwoarguments %% \orelse\ifnum#2<#1\relax\relax % so we catch \number\numexpr xx without \relax's %% \doubleexpandafter\gobbletwoarguments %% \else %% \doubleexpandafter\syst_helpers_stepwise_recurse %% \fi\normalexpanded{{\number#1}{\number#2}{\number#3}}}% %% \globalpopmacro\recurselevel %% \globalpopmacro\recurseaction %% \global\advance\outerrecurse\minusone} \newcount\fastloopindex \newcount\fastloopfinal \let\m_syst_helpers_fast_loop_cs\relax \permanent\protected\def\dofastloopcs#1% {\fastloopfinal#1\relax \ifcase\fastloopfinal \expandafter\gobbleoneargument \else \expandafter\syst_helpers_fast_loop_cs \fi} \protected\def\syst_helpers_fast_loop_cs#1% {\let\m_syst_helpers_fast_loop_cs#1% \fastloopindex\plusone \syst_helpers_fast_loop_cs_step} \protected\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} %D Here are the more modern implementations: \permanent\protected\def\installmacrostack#1% {\ifdefined#1\else\mutable\let#1\empty\fi \protected\gdefcsname push_macro_\csstring#1\endcsname{\localpushmacro#1}% \protected\gdefcsname pop_macro_\csstring#1\endcsname{\localpopmacro #1}} \permanent\protected\def\installglobalmacrostack#1% {\ifdefined#1\else\mutable\glet#1\empty\fi \protected\gdefcsname push_macro_\csstring#1\endcsname{\globalpushmacro#1}% \protected\gdefcsname pop_macro_\csstring#1\endcsname{\globalpopmacro #1}} % \showmacrostack can be used to see if there are different entries \installglobalmacrostack \recurseaction \installglobalmacrostack \recurselevel \installglobalmacrostack \recursedepth \permanent\protected\def\dostepwiserecurse#1#2#3#4% {\push_macro_recurseaction \push_macro_recurselevel \push_macro_recursedepth \protected\gdef\recurseaction##1##2{#4}% \localcontrolledloop=#1=#2=#3% {\edef\recurselevel{\the\currentloopiterator}% \edef\recursedepth{\the\currentloopnesting}% \normalexpanded{\recurseaction{\recurselevel}{\recursedepth}}}% \pop_macro_recursedepth \pop_macro_recurselevel \pop_macro_recurseaction} \permanent\protected\def\dorecurse#1#2% {\push_macro_recurseaction \push_macro_recurselevel \push_macro_recursedepth \protected\gdef\recurseaction##1##2{#2}% \localcontrolledloop=\plusone=#1=\plusone {\edef\recurselevel{\the\currentloopiterator}% \edef\recursedepth{\the\currentloopnesting}% \normalexpanded{\recurseaction{\recurselevel}{\recursedepth}}}% \pop_macro_recursedepth \pop_macro_recurselevel \pop_macro_recurseaction} \permanent\def\doexpandedrecurse#1#2% user macro (also was \doxprecurse) {\expandedloop\plusone#1\plusone{#2}} \mutable\let\m_expanded_recursed\gobbleoneargument \installmacrostack\m_expanded_recursed \permanent\def\doexpandedrecursed#1#2% this might replace: \doexpandedrecurse {\beginlocalcontrol \push_macro_m_expanded_recursed \def\m_expanded_recursed##1{#2}% \endlocalcontrol \expandedloop\plusone#1\plusone{\expandafter\m_expanded_recursed\expandafter{\the\currentloopiterator}}% \beginlocalcontrol \pop_macro_m_expanded_recursed \endlocalcontrol} % Helper: \permanent\protected\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 \permanent\protected\def\doloopoverlist#1#2% {\global\advance\outerrecurse\plusone \globalpushmacro\recurseaction \globalpushmacro\recursestring \protected\gdef\recurseaction##1{\edef\recursestring{##1}#2}% \processcommacommand[#1]\recurseaction \globalpopmacro\recursestring \globalpopmacro\recurseaction \global\advance\outerrecurse\minusone} %D This is some \LMTX\ experiment: %D %D Think of: %D %D \starttyping %D \doloopovermatch {(\letterpercent w+) *(\letterpercent w*)} {aa bb cc dd} { %D [ %D \doloopovermatch{(\letterpercent w)(\letterpercent w)} {#1} {(##1 ##2)} %D \doloopovermatch{(\letterpercent w)(\letterpercent w)} {#2} {(##1 ##2)} %D ] %D } %D %D and: %D %D \stoptyping %D %D \starttyping %D \def\MyMacro#1{(#1)} \ctxluamatch \MyMacro {(.)} {abcd} %D \stoptyping \aliased\let\matchloopcommand\relax \permanent\protected\def\doloopovermatch#1#2#3% {\pushmacro\matchloopcommand \enforced\def\matchloopcommand##1##2##3##4##5##6##7##8##9{#3}% \ctxluamatch\matchloopcommand{#1}{#2}% \popmacro\matchloopcommand} \permanent\def\doloopovermatched#1#2#3% {\beginlocalcontrol \pushmacro\matchloopcommand \enforced\def\matchloopcommand##1##2##3##4##5##6##7##8##9{#3}% \endlocalcontrol \the\ctxluamatch\matchloopcommand{#1}{#2}% \beginlocalcontrol \popmacro\matchloopcommand \endlocalcontrol} %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} \permanent\protected\def\newevery#1#2% {\ifx#1\everypar\else\newtoks#1\fi% we test for redefinition elsewhere \ifrelax#2\orelse\ifdefined#2\else \expandafter\newtoks\csname\??extraevery\csstring#1\endcsname \frozen\protected\edef#2{\syst_helpers_every#1\csname\??extraevery\csstring#1\endcsname}% \fi} \protected\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 \permanent\protected\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. \permanent\protected\def\convertvalue#1\to {\expandafter\convertcommand\csname#1\endcsname\to} \permanent\protected\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 \permanent\protected\def\doifelseassignment#1% {\ifhastok={#1}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \permanent\protected\def\doifelseassignmentcs#1#2#3% {\ifhastok={#1}% \expandafter#2% \else \expandafter#3% \fi} \aliased\let\doifassignmentelse \doifelseassignment \aliased\let\doifassignmentelsecs\doifelseassignmentcs \newif\ifassignment \permanent\protected\def\docheckassignment#1% {\ifhastok={#1}% \assignmenttrue \else \assignmentfalse \fi} \permanent\protected\def\validassignment #1{\ifhastok={#1}} % can become: {\ifhastok=} as we enforce {} \permanent\protected\def\novalidassignment#1{\ifnum\ifhastok={#1}\zerocount\else\plusone\fi=\plusone} % or use unless %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). \permanent\protected\def\convertargument#1\to#2{\dodoglobal\edef#2{\detokenize{#1}}} \permanent\protected\def\convertcommand #1\to#2{\dodoglobal\edef#2{\expandafter\detokenize\expandafter{#1}}} % hm, only second is also ok \permanent\protected\def\defconvertedargument #1#2{\edef#1{\detokenize{#2}}} \permanent\protected\def\defconvertedcommand #1#2{\edef#1{\detokenize\expandafter{#2}}} \permanent\protected\def\edefconvertedargument#1#2{\edef#1{#2}% \edef#1{\detokenize\expandafter{#1}}} \permanent\protected\def\gdefconvertedargument#1#2{\xdef#1{\detokenize{#2}}} \permanent\protected\def\gdefconvertedcommand #1#2{\xdef#1{\detokenize\expandafter{#2}}} \permanent\protected\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: \permanent\protected\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. \permanent\protected\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} \aliased\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} \permanent\protected\def\doifelsesamestring{\syst_helpers_if_samestring_else\firstoftwoarguments\secondoftwoarguments} \permanent\protected\def\doifsamestring {\syst_helpers_if_samestring_else\firstofoneargument \gobbleoneargument } \permanent\protected\def\doifnotsamestring {\syst_helpers_if_samestring_else\gobbleoneargument \firstofoneargument } \aliased\let\doifsamestringelse\doifelsesamestring %D These are obsolete in MTX: %D %D \starttyping %D \ConvertToConstant #1#2#3 %D \CheckConstantAfter #1#2 %D \ConvertConstantAfter #1#2#3 %D \stoptyping %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. \permanent\protected\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 \protected\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 \protected\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 \protected\def\syst_helpers_grab#1#2% {\def\syst_helpers_grab_indeed##1#1{#2{##1}}\syst_helpers_grab_indeed} \permanent\protected\def\grabuntil#1% {\expandafter\syst_helpers_grab\expandafter{\csname#1\endcsname}} %D The next command build on this mechanism: %D %D \starttyping %D \processbetween{string}\command %D \stoptyping %D %D Here: %D %D \starttyping %D \processbetween{hello}\message %D \starthello Hello again!\stophello %D \stoptyping %D %D leads to: \type{\message{Hello again!}}. The command %D %D \starttyping %D \gobbleuntil{sequence} %D \stoptyping %D %D is related to these commands. This one simply throws away %D everything preceding \type{\command}. \let\syst_helpers_gobble_indeed\relax \permanent\protected\def\processbetween#1#2% {\setvalue{\s!start#1}{\grabuntil{\s!stop#1}{#2}}} % \protected\def\gobbleuntil#1% % {\def\syst_helpers_gobble_indeed##1#1{}\syst_helpers_gobble_indeed} % % \protected\def\gobbleuntilrelax#1\relax % {} \permanent\protected\def\gobbleuntil#1% {\def\syst_helpers_gobble_indeed##-#1{}\syst_helpers_gobble_indeed} \permanent\protected\def\gobbleuntilandfinalize#1% {\def\syst_helpers_gobble_indeed##-#1{#1}\syst_helpers_gobble_indeed} \permanent\protected\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 \permanent\protected\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 \protected\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 {\beginsimplegroup \aftergroup\m_syst_helpers_handle_group_a \aftergroup\egroup \m_syst_helpers_handle_group_b} \protected\def\syst_helpers_handle_group_normal {\beginsimplegroup \afterassignment\m_syst_helpers_handle_group_normal_before \let\next=} \def\m_syst_helpers_handle_group_normal_before {\beginsimplegroup \m_syst_helpers_handle_group_b \bgroup \aftergroup\m_syst_helpers_handle_group_a \aftergroup\egroup \aftergroup\egroup} \protected\def\syst_helpers_handle_group_simple% no inner group (so no kerning interference) {\beginsimplegroup \afterassignment\m_syst_helpers_handle_group_simple_before \let\next=} \def\m_syst_helpers_handle_group_simple_before {\beginsimplegroup \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} \protected\def\syst_helpers_handle_group_pickup% no inner group (so no kerning interference) {\beginsimplegroup \afterassignment\m_syst_helpers_handle_group_pickup_before \let\next=} \def\m_syst_helpers_handle_group_pickup_before {\beginsimplegroup \aftergroup\m_syst_helpers_handle_group_a \aftergroup\egroup \aftergroup\m_syst_helpers_handle_group_p \m_syst_helpers_handle_group_b} \protected\def\syst_helpers_handle_group_nop_x {\ifnum\currentgrouptype=\semisimplegroupcode \begingroup \aftergroup\endgroup \else \beginsimplegroup \aftergroup\egroup \fi \m_syst_helpers_handle_group_b} \protected\def\syst_helpers_handle_group_normal_x {\beginsimplegroup \afterassignment\m_syst_helpers_handle_group_normal_before_x \let\next=} \def\m_syst_helpers_handle_group_normal_before_x {\beginsimplegroup \expandafter\m_syst_helpers_handle_group_b \ifmmode\beginsimplegroup\else\bgroup\fi % is this save enough? also the other ones? \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: \permanent\protected\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} \permanent\protected\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} \permanent\protected\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} \permanent\protected\def\simplegroupedcommandcs#1#2% {\let\m_syst_helpers_handle_group_b#1% \let\m_syst_helpers_handle_group_a#2% \futureexpandis\bgroup\syst_helpers_handle_group_simple\syst_helpers_handle_group_nop} \permanent\protected\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} \permanent\protected\def\pickupgroupedcommandcs#1#2#3% {\let\m_syst_helpers_handle_group_b#1% \let\m_syst_helpers_handle_group_a#2% \let\m_syst_helpers_handle_group_p#3% \futureexpandis\bgroup\syst_helpers_handle_group_pickup\syst_helpers_handle_group_nop} \permanent\protected\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} \permanent\protected\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 For math we use this: \permanent\protected\def\mathgroupedcommandcs#1% {\let\m_syst_helpers_handle_group_b#1% \futureexpandis\bgroup\syst_helpers_handle_math_group_normal\syst_helpers_handle_math_group_nop} \protected\def\syst_helpers_handle_math_group_normal#1% {\beginmathgroup \m_syst_helpers_handle_group_b #1% \endmathgroup} \protected\def\syst_helpers_handle_math_group_nop {\m_syst_helpers_handle_group_b} % %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. These are obsolete: % % \protected\def\showdefinederror#1#2% % {\writestatus\m!system{#1 #2 replaces a macro, use CAPITALS!}} % % \protected\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 % \permanent\protected\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} % \permanent\protected\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} \permanent\protected\def\dowithpar#1#2% {\begingroup\aftergrouped{#1\dontleavehmode\wrapuppar{#2}}\expandafter\endgroup\ignorepars} \permanent\protected\def\dogotopar#1% {\begingroup\aftergrouped{#1}\expandafter\endgroup\ignorepars} \aliased\let\dogotoparcs\dogotopar \permanent\protected\def\dogotoparstart {\ignorepars} %D This is old and kind of obsolete: \newtoks\BeforePar \newtoks\AfterPar \permanent\protected\def\GetPar {\expanded {\dowithpar {\the\BeforePar \BeforePar\emptytoks} {\the\AfterPar \BeforePar\emptytoks \AfterPar\emptytoks}}} \permanent\protected\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 \permanent\protected\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 \permanent\protected\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. \permanent\protected\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. \aliased\let\doifelsestringinstring\doifelseinstring \aliased\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 \permanent\protected\def\appendtoks#1\to#2% {\ifrelax\dodoglobal \expandafter\toksapp \else \resetglobal \expandafter\gtoksapp \fi#2{#1}} \permanent\protected\def\prependtoks#1\to#2% {\ifrelax\dodoglobal \expandafter\tokspre \else \resetglobal \expandafter\gtokspre \fi#2{#1}} \permanent\protected\def\appendtoksonce#1\to#2% {\ifhasxtoks{#1}#2\else \appendtoks#1\to#2% \fi} \permanent\protected\def\prependtoksonce#1\to#2% {\ifhasxtoks{#1}{#2}\m_syst_helpers_scratch\else \prependtoks#1\to#2% \fi} %D The test macro: \permanent\protected\def\doifelseintoks#1#2% #1 en #2 zijn toks {\ifhasxtoks#1#2% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \aliased\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 \permanent\protected\lettonothing\stopcollect \permanent\protected\lettonothing\stopexpandedcollect \permanent\protected\def\startcollect #1\stopcollect {\toksapp \collectingtoks{#1}} \permanent\protected\def\startexpandedcollect#1\stopexpandedcollect{\etoksapp\collectingtoks{#1}} \permanent\protected\def\startcollecting{\collectingtoks\emptytoks} \permanent\protected\def\stopcollecting {\the\collectingtoks} \permanent\protected\def\collect {\toksapp \collectingtoks} \permanent\protected\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]} \let\syst_helpers_remove_toks\relax \permanent\protected\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: \permanent\protected\def\appendetoks#1\to#2% {\ifrelax\dodoglobal \expandafter\etoksapp \else \resetglobal \expandafter\xtoksapp \fi#2{#1}} \permanent\protected\def\prependetoks#1\to#2% {\ifrelax\dodoglobal \expandafter\etokspre \else \resetglobal \expandafter\xtokspre \fi#2{#1}} %D Hm. \permanent\protected\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} \aliased\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 \permanent\protected\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} \permanent\protected\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. \permanent\protected\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} \permanent\protected\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} \aliased\let\beforetestandsplitstring\beforesplitstring \permanent\protected\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} \permanent\protected\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, splitatcomma, splitatasterisk, splitatcolon, splitatcolons} \protected\def\syst_helpers_splitatperiod #1.#2.#-^^^^0004#3#4{\def #3{#1}\def #4{#2}} \protected\def\syst_helpers_splitatcomma #1,#2,#-^^^^0004#3#4{\def #3{#1}\def #4{#2}} \protected\def\syst_helpers_splitatasterisk #1*#2*#-^^^^0004#3#4{\def #3{#1}\def #4{#2}} \protected\def\syst_helpers_splitatcolon #1:#2:#-^^^^0004#3#4{\def #3{#1}\def #4{#2}} \protected\def\syst_helpers_splitatcolons #1::#2::#-^^^^0004#3#4{\edef#3{#1}\edef#4{#2}} \permanent\protected\def\splitatperiod #1{\normalexpanded{\syst_helpers_splitatperiod #1}..^^^^0004} \permanent\protected\def\splitatcomma #1{\normalexpanded{\syst_helpers_splitatcomma #1},,^^^^0004} % not at ", " \permanent\protected\def\splitatasterisk#1{\normalexpanded{\syst_helpers_splitatasterisk#1}**^^^^0004} \permanent\protected\def\splitatcolon #1{\normalexpanded{\syst_helpers_splitatcolon #1}::^^^^0004} \permanent\protected\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: \permanent\protected\def\removesubstring#1\from#2\to#3% {\splitstring#2\at#1\to\m_syst_string_one\and\m_syst_string_two \dodoglobal\def#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: \permanent\protected\def\appendtocommalist#1#2% {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}} \permanent\protected\def\prependtocommalist#1#2% {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}} \permanent\protected\def\addtocommalist#1#2% {item} \cs {\rawdoifelseinset{#1}#2\resetglobal {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}}} \permanent\protected\def\pretocommalist#1#2% {item} \cs {\rawdoifelseinset{#1}#2\resetglobal {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}}} \permanent\protected\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} \aliased\let\robustdoifinsetelse\robustdoifelseinset \permanent\protected\def\robustaddtocommalist#1#2% {item} \cs {\robustdoifelseinset{#1}#2\resetglobal {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}}} \permanent\protected\def\robustpretocommalist#1#2% {item} \cs {\robustdoifelseinset{#1}#2\resetglobal {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}}} \permanent\protected\def\xsplitstring#1#2% \cs {str} {\def\syst_helpers_split_string##1,#2,##2,#2,##-\\% {\edef\m_syst_string_one{\syst_cleanedup_commalist_b##1\empty\empty\relax}% \edef\m_syst_string_two{\syst_cleanedup_commalist_a##2,,\relax}}% \expandafter\syst_helpers_split_string\expandafter,#1,,#2,,#2,\\} \def\syst_cleanedup_commalist_b#1#2\relax{\if#1,\else#1\fi#2} \def\syst_cleanedup_commalist_a#1,,#-\relax{#1} \permanent\protected\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} \permanent\protected\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 \mutable\let\newcommalistelement\empty \def\syst_helpers_replace_in_comma_list_step#1% we can use #+ here too {\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} \permanent\protected\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} \permanent\protected\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. % \permanent\def\withoutpt#1{\thewithoutunit\dimexpr#1} \aliased\let\withoutpt\toscaled %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. % \permanent\def\PtToCm#1{\thewithoutunit\dimexpr0.0351459804\dimexpr#1\relax\relax cm} \permanent\def\PtToCm#1{\toscaled\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} \permanent\def\dimensiontocount#1#2{#2\numexpr\dimexpr#1\relax/\maxcard\relax} \permanent\def\numberofpoints #1{\the\numexpr\dimexpr#1\relax/\maxcard\relax} %D \macros %D {swapdimens,swapskips,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 \newskip \s_syst_helpers_swapped \newcount\c_syst_helpers_swapped \let \m_syst_helpers_swapped\relax % \protected\def\swapdimens#1#2{\d_syst_helpers_swapped#1#1#2#2\d_syst_helpers_swapped} % \protected\def\swapskips #1#2{\s_syst_helpers_swapped#1#1#2#2\s_syst_helpers_swapped} % \protected\def\swapcounts#1#2{\c_syst_helpers_swapped#1#1#2#2\c_syst_helpers_swapped} % \protected\def\swapmacros#1#2{\let\m_syst_helpers_swapped#1\let#1#2\let#2\m_syst_helpers_swapped} \aliased\let\swapdimens\swapcsvalues \aliased\let\swapskips \swapcsvalues \aliased\let\swapcounts\swapcsvalues \aliased\let\swapmacros\swapcsvalues % \protected\def\globalswapdimens#1#2{\d_syst_helpers_swapped#1\global#1#2\global#2\d_syst_helpers_swapped} % \protected\def\globalswapskips #1#2{\s_syst_helpers_swapped#1\global#1#2\global#2\s_syst_helpers_swapped} % \protected\def\globalswapcounts#1#2{\c_syst_helpers_swapped#1\global#1#2\global#2\c_syst_helpers_swapped} % \protected\def\globalswapmacros#1#2{\let\m_syst_helpers_swapped#1\glet#1#2\glet#2\m_syst_helpers_swapped} \permanent\protected\def\globalswapdimens{\global\swapcsvalues} \permanent\protected\def\globalswapskips {\global\swapcsvalues} \permanent\protected\def\globalswapcounts{\global\swapcsvalues} \permanent\protected\def\globalswapmacros{\global\swapcsvalues} %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 \permanent\let\pushmacro\localpushmacro \permanent\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 \permanent\protected\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} \permanent\def\availablehsize {\dimexpr \hsize-\leftskip-\rightskip \ifnum\hangafter<\zerocount \ifdim\hangindent>\zeropoint-\else+\fi\hangindent \fi \relax} \permanent\def\distributedhsize#1#2#3% {\dimexpr(#1-\numexpr#3-1\relax\dimexpr#2\relax)/#3\relax} \permanent\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. \permanent\protected\def\doifvalue#1#2% {\iftok{\csname#1\endcsname}{#2}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \permanent\protected\def\doifnotvalue#1#2% {\iftok{\csname#1\endcsname}{#2}% \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \permanent\protected\def\doifelsevalue#1#2% {\iftok{\csname#1\endcsname}{#2}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \permanent\protected\def\doifnothing#1% {\iftok{#1}\emptytoks \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \permanent\protected\def\doifsomething#1% {\iftok{#1}\emptytoks \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \permanent\protected\def\doifelsenothing#1% {\iftok{#1}\emptytoks \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \permanent\protected\def\doifelsesomething#1% {\iftok{#1}\emptytoks \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} \permanent\protected\def\doifvaluenothing#1% {\iftok{\csname#1\endcsname}\emptytoks \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \permanent\protected\def\doifvaluesomething#1% {\iftok{\csname#1\endcsname}\emptytoks \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \permanent\protected\def\doifelsevaluenothing#1% {\iftok{\csname#1\endcsname}\emptytoks \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \aliased\let\doifvalueelse \doifelsevalue \aliased\let\doifnothingelse \doifelsenothing \aliased\let\doifsomethingelse \doifelsesomething \aliased\let\doifvaluenothingelse\doifelsevaluenothing %D \macros %D {doifemptyelsevalue, doifemptyvalue, doifnotemptyvalue} %D %D Also handy: \permanent\def\doifelseemptyvalue#1% {\expandafter\ifempty\csname#1\endcsname \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \aliased\let\doifemptyvalueelse\doifelseemptyvalue \permanent\def\doifemptyvalue#1% {\expandafter\ifempty\csname#1\endcsname \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \permanent\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. \let\syst_helpers_do_common_check_all\gobbleoneargument \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} \permanent\protected\def\doifelseallcommon{\syst_helpers_do_if_all_common_else\firstoftwoarguments\secondoftwoarguments} \permanent\protected\def\doifallcommon {\syst_helpers_do_if_all_common_else\firstofoneargument \gobbleoneargument } \permanent\protected\def\doifnotallcommon {\syst_helpers_do_if_all_common_else\gobbleoneargument \firstofoneargument } \aliased\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: \protected\def\syst_helpers_do_IF#1#2% {\uppercase{\iftok{#1}{#2}}% \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \protected\def\syst_helpers_do_IF_NOT#1#2% {\uppercase{\iftok{#1}{#2}}% \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \protected\def\syst_helpers_do_IF_ELSE#1#2% {\uppercase{\iftok{#1}{#2}}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \protected\def\syst_helpers_do_IF_INSTRING_ELSE#1#2% {\uppercase{\doifelseinstring{#1}{#2}}} \permanent\protected\def\DOIF #1#2{\normalexpanded{\syst_helpers_do_IF {#1}{#2}}}% will become obsolete \permanent\protected\def\DOIFNOT #1#2{\normalexpanded{\syst_helpers_do_IF_NOT {#1}{#2}}}% will become obsolete \permanent\protected\def\DOIFELSE #1#2{\normalexpanded{\syst_helpers_do_IF_ELSE {#1}{#2}}}% will become obsolete \permanent\protected\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 \protected\def\definesomething% %D {\dodoubleargumentwithset\dodefinesomething} %D \stoptyping %D %D Which accepts calls like: %D %D \starttyping %D \definesomething[alfa,beta,...][variable=...,...] %D \stoptyping \let\syst_helpers_with_set_step\relax % maybe push pop \permanent\tolerant\protected\def\dodoubleemptywithset#1#*[#2]#*[#3]% {\doifsomething{#2}% {\def\syst_helpers_with_set_step##1{#1[##1][#3]}% \processcommalist[#2]\syst_helpers_with_set_step}} \permanent\tolerant\protected\def\dotripleemptywithset#1#*[#2]#*[#3]#*[#4]% {\doifsomething{#2}% {\def\syst_helpers_with_set_step##1{#1[##1][#3][#4]}% \processcommalist[#2]\syst_helpers_with_set_step}} \aliased\let\dodoubleargumentwithset\dodoubleemptywithset \aliased\let\dotripleargumentwithset\dotripleemptywithset %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} are expanded. This %D 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\syst_helpers_strip_character \relax \let\m_syst_helpers_strip_character\empty \permanent\protected\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} \permanent\protected\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...}. \permanent\protected\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. \permanent\def\executeifdefined#1% #2 / never change this one again {\ifcsname#1\endcsname \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} \permanent\edef\doifelsesomespace#1% {\noexpand\ifhastok\space{#1}% \noexpand\expandafter\noexpand\firstoftwoarguments \noexpand\else \noexpand\expandafter\noexpand\secondoftwoarguments \noexpand\fi} \aliased\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% % {\def\syst_helpers_process_separated_list_step##1##2#2% % {\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 % #3{##1}% % \orelse\if]##2% % \let\syst_helpers_process_separated_list_step\relax % \else % #3{##1##2}% % \fi % \syst_helpers_process_separated_list_step}% % \expandafter\syst_helpers_process_separated_list_step\gobbleoneargument#1#2]#2} % \def\syst_helpers_process_separated_list#1]#*[#2]#3% % {\def\syst_helpers_process_separated_list_step##1##2#2% % {\def\m_syst_string_one{##2}% suggested by VZ % \if]##1% % \orelse\ifx\blankspace\m_syst_string_one % #3{##1}% % \expandafter\syst_helpers_process_separated_list_step % \orelse\if]##2% % \else % #3{##1##2}% % \expandafter\syst_helpers_process_separated_list_step % \fi % }% % \expandafter\syst_helpers_process_separated_list_step\gobbleoneargument#1#2]#2} % \permanent\protected\def\processseparatedlist[% % {\syst_helpers_process_separated_list\relax} \permanent\protected\def\processseparatedlist[#+]#*[#2]#3% {\tolerant\def\syst_helpers_process_separated_list_step##1#2% {\ifarguments\or #3{##1}% \expandafter\syst_helpers_process_separated_list_step \fi}% \syst_helpers_process_separated_list_step#1\ignorearguments\ignorearguments} %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 \permanent\protected\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. % \protected\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} \let\syst_helpers_process_assign_list_assign\gobbleoneoptional \let\syst_helpers_process_assign_list_step \gobbleoneargument \permanent\protected\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 \permanent\protected\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} \permanent\protected\def\untexargument{\untexsomething\convertargument} \permanent\protected\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 \aliased\let\tobigpoints \clf_tobigpoints % todo: permanent at lua end \aliased\let\towholebigpoints\clf_towholebigpoints % todo: permanent at lua end \permanent\protected\def\PointsToBigPoints #1#2{\edef#2{\tobigpoints #1}} % can be avoided \permanent\protected\def\PointsToWholeBigPoints #1#2{\edef#2{\towholebigpoints#1}} % can be avoided \permanent\protected\def\ScaledPointsToBigPoints #1#2{\edef#2{\tobigpoints #1\scaledpoint}} % obsolete \permanent\protected\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 % \permanent\protected\def\PointsToReal#1#2{\edef#2{\thewithoutunit\dimexpr#1}} \permanent\protected\def\PointsToReal#1#2{\edef#2{\toscaled#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\orelse\ifmmode\else$ $\fi} %D \stoptyping %D %D Next, Taco came with a better alternative (using mathsurround): %D %D \starttyping %D \def\dontleavehmode %D {\ifhmode\orelse\ifmmode\else %D {\mathsurround\zeropoint\everymath\emptytoks$ $}% %D \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 \protected\def\dontleavehmode %D {\ifhmode\orelse\ifmmode\else %D \setbox\b_syst_helpers_dlh\hbox{\mathsurround\zeropoint\everymath\emptytoks$ $}\unhbox\b_syst_helpers_dlh %D \fi} %D \stoptyping %D %D But, as we run a recent version of \TEX, we can use the new primitive: \aliased\let\dontleavehmode\quitvmode %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: \permanent\def\utfupper#1{\clf_upper{#1}} % expandable \permanent\def\utflower#1{\clf_lower{#1}} % expandable \permanent\protected\def\uppercasestring#1\to#2{\dodoglobal\edef#2{\clf_upper{#1}}} \permanent\protected\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} \permanent\protected\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} \permanent\protected\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. \let\syst_helpers_split_off_tokens\gobbleoneargument \permanent\protected\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. \mutable\let\nexthandledtoken\empty % part of public interface \let\syst_helpers_handle_tokens_command\relax \protected\def\syst_helpers_handle_tokens {\futurelet\nexthandledtoken\syst_helpers_handle_tokens_indeed} \permanent\protected\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 \permanent\protected\def\settrialtypesetting {\the\everysettrialtypesetting } % obeys grouping so \permanent\protected\def\resettrialtypesetting{\the\everyresettrialtypesetting} % this one is seldom needed \aliased\let\iftrialtypesetting\iffalse % so we have no \trialtypesettingtrue|false in mkiv ! \appendtoks \enforced\aliased\let\iftrialtypesetting\iftrue \to \everysettrialtypesetting \appendtoks \enforced\aliased\let\iftrialtypesetting\iffalse \to \everyresettrialtypesetting %D \macros %D {twodigitrounding} %D %D The next macro rounds a real number to two digits. They are probably no longer needed %D but we keep them around for a while. \permanent\def\integerrounding #1{\clf_rounded\zerocount\numexpr#1\relax} \permanent\def\onedigitrounding #1{\clf_rounded\plusone \numexpr#1\relax} \permanent\def\twodigitrounding #1{\clf_rounded\plustwo \numexpr#1\relax} \permanent\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 \protected\def\starthans% %D {\processcontent{stophans}\test{\message{\test}\wait}} %D \stoptyping % \starttabulate[|||] % \NC \type{#} \NC # \NC \NR % \stoptabulate % % \def\test#1% % {\starttabulate[|||] % \NC \type{#1} \NC #1 \NC \NR % \stoptabulate} % % \test{!} %% \permanent\protected\def\processcontent#1% %% {\begingroup\expandafter\syst_helpers_process_content\csname#1\endcsname} \protected\def\syst_helpers_process_content#1#2#3% {\protected\def\syst_helpers_process_content##1#1% {\endgroup\def#2{##1}#3}% \syst_helpers_process_content} \permanent\protected\def\processcontent#1% {\begingroup \catcode\hashasciicode\othercatcode \expandafter\syst_helpers_process_content\csname#1\endcsname} %D \macros %D {dogobblesingleempty, dogobbledoubleempty} %D %D These two macros savely grab and dispose two arguments. \permanent\tolerant\protected\def\dogobblesingleempty [#-]{} \permanent\tolerant\protected\def\dogobbledoubleempty[#-]#*[#-]{} \aliased\let\gobblesingleempty\dogobblesingleempty % also used \aliased\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{\strutdp} %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. \permanent\protected\def\setdimensionwithunit#1#2#3% number unit dimension / nice trick {\afterassignment\gobblefourarguments#1=#2#3pt\relax\empty\empty\empty\empty} \permanent\protected\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. \permanent\protected\def\doifelsesometoks#1% {\iftok#1\emptytoks \expandafter\secondoftwoarguments \else \expandafter\firstoftwoarguments \fi} \permanent\protected\def\doifsometoks#1% {\iftok#1\emptytoks \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \permanent\protected\def\doifemptytoks#1% {\iftok#1\emptytoks \expandafter\firstofoneargument \else \expandafter\gobbleoneargument \fi} \aliased\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} \permanent\protected\def\strictdoifelsenextoptional#1#2% {\def\m_syst_action_yes{#1}% \def\m_syst_action_nop{#2}% \futurelet\nexttoken\syst_helpers_strict_inspect_next_character} \aliased\let\strictdoifnextoptionalelse\strictdoifelsenextoptional %D \macros %D {gobblespacetokens} %D %D This macro needs a speed-up! %\def\gobblespacetokens % {\doifnextcharelse\empty\donothing\donothing} % no {}\do\do ! \permanent\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. \aliased\let\verbatimstring\detokenize %D These are needed in ordinal number conversions: \permanent\def\lastdigit#1% {\expandafter\thelastdigit\number#1\relax} \permanent\def\thelastdigit#1#2% {\ifrelax#2#1\else\expandafter\thelastdigit\expandafter#2\fi} \permanent\def\lasttwodigits#1% {\expandafter\thelasttwodigits\expandafter0\number#1\relax} \permanent\def\thelasttwodigits#1#2#3% 0 dig ... \relax {\ifrelax#3#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 \mutable\let\serializedcommalist\empty \def\syst_helpers_serialize_comma_list_step#1% {\edef\serializedcommalist{\serializedcommalist#1}} \permanent\protected\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}. \permanent\def\purenumber#1{\expandafter\firstofoneargument\expandafter{\number#1}} %D \macros %D {filterfromvalue, filterfromnext} %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 % can be sped up with ignored arguments \def\syst_filter_from_next_yes#1% {\advance\scratchcounterone\plusone \ifnum\scratchcounterone=\scratchcountertwo \scratchtoks{#1}% \fi \ifnum\scratchcounterone=\scratchcounterthree \expandafter\syst_filter_from_next_nop \else \expandafter\syst_filter_from_next_yes \fi} \def\syst_filter_from_next_nop {\expandafter\endgroup \expandafter\endlocalcontrol \the\scratchtoks} \permanent\def\filterfromnext#1#2% max n {..}{..}{..}{..} {\beginlocalcontrol \begingroup \scratchcounterone \zerocount \scratchcountertwo #2% \scratchcounterthree #1% \syst_filter_from_next_yes} \permanent\def\filterfromvalue#1#2#3% max n {..}{..}{..}{..} {\beginlocalcontrol \begingroup \scratchcounterone \zerocount \scratchcountertwo #3% \scratchcounterthree #2% \expandafter\expandafter\expandafter\syst_filter_from_next_yes\csname#1\endcsname} %D \macros %D {definemeasure} %D %D \starttyping %D \definemeasure[mywidth][\dimexpr(\textwidth-1cm)] %D %D ... \measure{mywidth} ... %D \stoptyping \installsystemnamespace{measure} \permanent\tolerant\protected\def\definemeasure[#1]#*[#2]{\defcsname \??measure#1\endcsname{#2}} \permanent\tolerant\protected\def\freezemeasure[#1]#*[#2]{\edefcsname\??measure#1\endcsname{\the\dimexpr#2}} \permanent\protected\def\setmeasure #1#2{\defcsname \??measure#1\endcsname{#2}} % quick way \permanent\protected\def\setgmeasure#1#2{\gdefcsname\??measure#1\endcsname{#2}} % quick way \permanent\protected\def\setemeasure#1#2{\edefcsname\??measure#1\endcsname{\the\dimexpr#2}} % quick way \permanent\protected\def\setxmeasure#1#2{\xdefcsname\??measure#1\endcsname{\the\dimexpr#2}} % quick way \permanent\def\measure {\the\measured} \permanent\def\measured#1{\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} \permanent\tolerant\protected\def\definequantity[#1]#*[#2]{\defcsname \??quantity#1\endcsname{#2}} \permanent\tolerant\protected\def\freezequantity[#1]#*[#2]{\edefcsname\??quantity#1\endcsname{\the\numexpr#2}} \permanent\protected\def\setquantity #1#2{\defcsname \??quantity#1\endcsname{#2}} % quick way \permanent\protected\def\setgquantity#1#2{\gdefcsname\??quantity#1\endcsname{#2}} % quick way \permanent\protected\def\setequantity#1#2{\edefcsname\??quantity#1\endcsname{\the\numexpr#2}} % quick way \permanent\protected\def\setxquantity#1#2{\xdefcsname\??quantity#1\endcsname{\the\numexpr#2}} % quick way \permanent\def\quantity {\the\quantitied} \permanent\def\quantitied #1{\numexpr\ifcsname\??quantity#1\endcsname\lastnamedcs\else\zeropoint\fi\relax} \permanent\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 \permanent\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. \permanent\def\doifelsedimension#1% {\ifchkdim#1\or \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \aliased\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 \aliased\let\doifelsedimenstring\doifelsedimension \aliased\let\doifdimenstringelse\doifelsedimenstring %D \macros %D {comparedimension,comparedimensioneps} %D %D We no longer use the \MKIV\ dirty trick. These are obsolete anyway. \newdimen \roundingeps \roundingeps=10sp \newconstant\compresult \permanent\protected\def\comparedimension#1#2% {\compresult \ifcmpdim#1#2 % space wil be skipped \zerocount \or \plusone \else \plustwo \fi} \permanent\protected\def\comparedimensioneps#1#2% todo: use eps feature {\compresult \ifabsdim\dimexpr#1-#2\relax<\roundingeps \zerocount \orelse\ifdim#1<#2 % space wil be skipped \plusone \else \plustwo \fi} % pretty ugly but fast % \copycsname xxx\endcsname\csname ..\endcsname \permanent\protected\def\copycsname{\expandafter\expandafter\expandafter\let\expandafter\expandafter\csname} % \letcscsname \crap \csname ..\endcsname % \letcsnamecs \csname ..\endcsname\crap % \letcsnamecsname\csname ..\endcsname\csname ..\endcsname \permanent\protected\def\letcscsname {\expandafter\let\expandafter} \permanent\protected\def\letcsnamecs {\expandafter\let} \permanent\protected\def\letcsnamecsname{\expandafter\expandafter\expandafter\let\expandafter\expandafter} % another one, add an item to a commalist \permanent\protected\def\addvalue#1#2% cs item {\ifcsname#1\endcsname\else\letcsname#1\endcsname\empty\fi \normalexpanded{\addtocommalist{#2}\expandafter\noexpand\csname#1\endcsname}} %D Are these ever used? Anyway, these variants are somewhat more efficient than %D the \MKIV variants for larger strings. % A variant: % % \permanent\def\unspaced#1% % {\localcontrolled{\begingroup\catcode\spaceasciicode\ignorecatcode}% % \tokenized{#1}% % \localcontrolled{\endgroup}} % % but we can also do this: \permanent\def\unspaced#1% {\tokenized \s!catcodetable \ctdcatcodes {#1}} \permanent\protected\def\unspaceargument#1\to#2% {\edef#2{\tokenized \s!catcodetable \ctdcatcodes {#1}}} \permanent\protected\def\unspaceafter#1#2% {\expandafter#1\expandafter{\tokenized \s!catcodetable \ctdcatcodes {#2}}} \permanent\protected\def\doifelsehasspace#1% {\expandafter\ifhastok\space{#1}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \aliased\let\doifhasspaceelse\doifelsehasspace % this will replace loadfile once and alike !!! todo \installsystemnamespace{flag} \permanent\protected\def\setflag #1{\dodoglobal\letcsname\??flag#1\endcsname\zerocount} \permanent\protected\def\resetflag#1{\dodoglobal\letcsname\??flag#1\endcsname\plusone} \permanent\def\flag#1{\csname\??flag#1\endcsname} \permanent\def\doifelseflagged#1% {\expandafter\ifrelax\csname\??flag#1\endcsname \expandafter\secondoftwoarguments \orelse\ifcase\csname\??flag#1\endcsname \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \aliased\let\doifflaggedelse\doifelseflagged \permanent\def\doifnotflagged#1% {\expandafter\ifrelax\csname\??flag#1\endcsname \expandafter\firstofoneargument \orelse\ifcase\csname\??flag#1\endcsname \expandafter\gobbleoneargument \else \expandafter\firstofoneargument \fi} \permanent\protected\def\inheritparameter[#1]#2[#3]#4[#5]% tag tokey fromkey {\defcsname#1#3\expandafter\endcsname\expandafter{\csname#1#5\endcsname}} \def\syst_helpers_if_non_zero_positive_else#1#2\end % #3#4% {\ifrelax#1% \ifcase\privatescratchcounter \endgroup \doubleexpandafter\secondoftwoarguments \else \endgroup \doubleexpandafter\firstoftwoarguments \fi \else \endgroup \expandafter\secondoftwoarguments \fi} % used ? \permanent\def\doifelsenonzeropositive#1% {\begingroup\afterassignment\syst_helpers_if_non_zero_positive_else\privatescratchcounter=0#1\relax\empty\end} \aliased\let\doifnonzeropositiveelse\doifelsenonzeropositive %D Some old|-|time favourites: \permanent\protected\def\dosetrawvalue #1#2#3{\defcsname #1#2\endcsname{#3}} \permanent\protected\def\dosetrawevalue#1#2#3{\edefcsname #1#2\endcsname{#3}} \permanent\protected\def\dosetrawgvalue#1#2#3{\global\defcsname #1#2\endcsname{#3}} \permanent\protected\def\dosetrawxvalue#1#2#3{\global\edefcsname#1#2\endcsname{#3}} \permanent\protected\def\getrawparameters {\dogetparameters\dosetrawvalue } \permanent\protected\def\getraweparameters {\dogetparameters\dosetrawevalue} \permanent\protected\def\getrawgparameters {\dogetparameters\dosetrawgvalue} \permanent\protected\def\getrawxparameters {\dogetparameters\dosetrawxvalue} \permanent\protected\def\globalgetrawparameters{\dogetparameters\dosetrawgvalue} % obsolete %D Sort of obsolete: \newcount\c_syst_helpers_mod \permanent\protected\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} \permanent\protected\def\dosetdivision#1#2#3% {#3#1\divide#3 #2\relax} \permanent\protected\def\DoMod#1by#2to#3{\dosetmodulo {#1}{#2}{#3}} \permanent\protected\def\DoDiv#1by#2to#3{\dosetdivision{#1}{#2}{#3}} %D This is obsolete, just use \type {\unprotect} and \type {\protect} instead. % \def\syst_helpers_unprotected#1\par % {#1\protect} % % \permanent\protected\def\unprotected % {\unprotect % \syst_helpers_unprotected} \aliased\let\resettimer \clf_resettimer % todo: at lua end \aliased\let\elapsedtime \clf_elapsedtime % todo: at lua end \aliased\let\elapsedseconds\elapsedtime \aliased\let\elapsedsteps\!!zerocount \permanent\protected\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 \let\syst_helpers_test_feature_yes\relax \let\syst_helpers_test_feature_nop\relax \permanent\def\currentfeaturetest{\number\c_syst_helpers_test_feature_n} \permanent\protected\def\testfeature#1#2% {\c_syst_helpers_test_feature_m#1\relax \enforced\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} \permanent\protected\def\retestfeature % timer support is new per 10/5/2005 {\bgroup \ifcase\interactionmode\enforced\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} \permanent\protected\def\showtimer#1% {\writestatus{runtime}{\elapsedseconds\space s / #1}} \permanent\protected\def\testfeatureonce#1#2% {\begingroup \enforced\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 \permanent\protected\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 %D %D Do we still need this? If not it will go away: \permanent\def\negated#1{\if-#1\else-#1\fi} % does only work in macros or text % \def\gobbleassigndimen#1\\{} % % \permanent\def\assigndimen#1#2% % {\afterassignment\gobbleassigndimen#1=#2\zeropoint\\} %D Maybe ... toksapp should deal with this ... \permanent\protected\def\appendvalue #1#2% {\defcsname#1\expandafter\expandafter\expandafter\endcsname\expandafter\expandafter\expandafter {\begincsname#1\endcsname#2}} \permanent\protected\def\prependvalue #1#2% {\edefcsname#1\endcsname {\normalunexpanded{#2}\expandafter\expandafter\expandafter\empty\begincsname#1\endcsname}} \permanent\protected\def\appendgvalue #1#2{\global\appendvalue} \permanent\protected\def\prependgvalue#1#2{\global\prependvalue} %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}}}% \permanent\protected\def\processranges[#1]#2% #1= n:m,p,q:r {\def\m_helpers_range_action{#2}% \processcommacommand[#1]\syst_helpers_with_range} \permanent\protected\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 \permanent\protected\def\ignoreimplicitspaces {\doifelsenextchar\relax\relax\relax} % %D \macros % %D {processwords} % %D % %D Not that sophisticated but sometimes users (like in metafun). % % %D This will be overloaded. % % \def\syst_helpers_process_word#1 #2\s!e_o_t_token % {\doifsomething{#1}{\processword{#1} \syst_helpers_process_word#2 \s!e_o_t_token}} % % \def\processwords#1% % {\syst_helpers_process_word#1 \s!e_o_t_token}% 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 \permanent\protected\def\startnointerference % not even grouped ! {\setbox\b_syst_helpers_no_interference\vbox \bgroup} \permanent\protected\def\stopnointerference {\egroup \setbox\b_syst_helpers_no_interference\emptybox} %D A variant for \type {\executeifdefined}: \permanent\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) \protected\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. \permanent\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}). This will be replaced. % %D % %D \startbuffer % %D [\docheckedpair{}] % %D [\docheckedpair{a}] % %D [\docheckedpair{a,b}] % %D [\docheckedpair{a,b,c}] % %D \stopbuffer % %D % %D \typebuffer \startlines \getbuffer \stoplines % % \permanent\def\docheckedpair#1% % {\syst_helpers_checked_pair#1,,\s!e_o_t_token} % % \def\syst_helpers_checked_pair#1,#2,#-\s!e_o_t_token % {#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). \permanent\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} \permanent\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} \permanent\def\constantdimen#1% {\ifdim#1=\zeropoint \zeropoint \else \the#1\relax \fi} \permanent\def\constantdimenargument#1% {\ifdim#1=\zeropoint \zeropoint \else {\the#1}% \fi} \permanent\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: \permanent\def\getsubstring#1#2#3{\clf_getsubstring{#3}{#1}{#2}} %D Other dimensions than pt (used in mb-mp) \permanent\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}} % \protected\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 \permanent\def\ntimes#1#2{\clf_ntimes{#1}\numexpr#2\relax} % 0.33 %D Experiment (sometimes looks nicer in code): \permanent\protected\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} \permanent\protected\def\emptyargumentcondition#1% {\edef\m_syst_string_one{#1}% \ifempty\m_syst_string_one} %D New (also serves as an example): %D %D \starttyping %D \commandflags\defineframed %D \stoptyping \permanent\def\commandflags#1% {\beginlocalcontrol\begingroup\scratchtoks\emptytoks\donefalse \ifflags#1= \frozenflagcode\etoksapp\scratchtoks{\ifdone \space\fi frozen}\donetrue\fi \ifflags#1=\permanentflagcode\etoksapp\scratchtoks{\ifdone \space\fi permanent}\donetrue\fi \ifflags#1=\immutableflagcode\etoksapp\scratchtoks{\ifdone \space\fi immutable}\donetrue\fi \ifflags#1=\primitiveflagcode\etoksapp\scratchtoks{\ifdone \space\fi primitive}\donetrue\fi \ifflags#1= \mutableflagcode\etoksapp\scratchtoks{\ifdone \space\fi mutable}\donetrue\fi \ifflags#1=\noalignedflagcode\etoksapp\scratchtoks{\ifdone \space\fi noaligned}\donetrue\fi \ifflags#1= \instanceflagcode\etoksapp\scratchtoks{\ifdone \space\fi instance}\donetrue\fi %\ifflags#1= \reservedflagcode\etoksapp\scratchtoks{\ifdone \space\fi reserved}\donetrue\fi \ifflags#1= \tolerantflagcode\etoksapp\scratchtoks{\ifdone \space\fi tolerant}\donetrue\fi \ifflags#1=\protectedflagcode\etoksapp\scratchtoks{\ifdone \space\fi protected}\donetrue\fi \expandafter\endgroup\expandafter\endlocalcontrol\the\scratchtoks} %D \macros %D {resetmacros} %D %D The next macro can be used to reset a macro: %D %D \starttyping %D \resetmacros[startfoo,\stopfoo] %D \stoptyping \permanent\protected\def\syst_reset_macro#1% {\overloaded\letcsname\csstring#1\endcsname\undefined} % so only frozen (instances( \permanent\protected\def\resetmacros[#1]% {\processcommalist[#1]\syst_reset_macro} %D These demos are for hvdm who needs ways to manipulate arguments but %D in a fully expandable way (some explanation is given in the low level %D expansion manual). %D %D \startbuffer %D \edef\xxx{[\wipetokens {123}{abc123abc123abc123abc123abc}]}1 : \meaningless\xxx\par %D \edef\xxx{[\wipetokens {123}{abc}]} 2 : \meaningless\xxx\par %D \edef\xxx{[\wipetokens {123}{123}]} 3 : \meaningless\xxx\par %D \edef\xxx{[\wipetokens {123}{123123}]} 4 : \meaningless\xxx\par %D \edef\xxx{[\wipetokens {123}{}]} 5 : \meaningless\xxx\par %D \edef\xxx{[\wipetokens {}{123}]} 6 : \meaningless\xxx\par %D \edef\xxx{[\wipetokens {\relax}{1\relax2\relax3}]} 7 : \meaningless\xxx\par %D \edef\xxx{[\wipetokens {1}{1{2}3}]} 8 : \meaningless\xxx\par %D \edef\xxx{[\wipedtokens{{1}}{{1}23}]} 9 : \meaningless\xxx\par %D \edef\xxx{[\wipedtokens{{1}}{123}]} 0 : \meaningless\xxx\par %D \stopbuffer %D %D \typebuffer \startpacked \getbuffer \stoppacked \def\syst_helpers_wipe_tokens_nop#0^^04{}% \permanent\def\wipetokens#1#2% {\beginlocalcontrol \tolerant\def\syst_helpers_wipe_tokens_yes##1#1##2^^04% {##1% \ifparameter##2\or \expandafter\syst_helpers_wipe_tokens_yes \else \expandafter\syst_helpers_wipe_tokens_nop \fi ##2^^04}% \endlocalcontrol \syst_helpers_wipe_tokens_yes#2#1^^04} \permanent\def\wipedtokens#1#2% {\tokenized{\normalexpanded{\noexpand\wipetokens{\detokenize{#1}}{\detokenize{#2}}}}} %D \startbuffer %D \def\abc{abc} %D \semiprotected \def\xyz{xyz} %D \edef\pqr{\expandtoken\notcatcodes`p% %D \expandtoken\notcatcodes`q% %D \expandtoken\notcatcodes`r} %D %D 1: \ifcondition\similartokens{abc} {def}YES\else NOP\fi (NOP) \quad %D 2: \ifcondition\similartokens{abc}{\abc}YES\else NOP\fi (YES) %D %D 3: \ifcondition\similartokens{xyz} {pqr}YES\else NOP\fi (NOP) \quad %D 4: \ifcondition\similartokens{xyz}{\xyz}YES\else NOP\fi (YES) %D %D 5: \ifcondition\similartokens{pqr} {pqr}YES\else NOP\fi (YES) \quad %D 6: \ifcondition\similartokens{pqr}{\pqr}YES\else NOP\fi (YES) %D \stopbuffer %D %D \typebuffer \startpacked \getbuffer \stoppacked \permanent\protected\def\similartokens#1#2% %{\normalexpanded{\noexpand\iftok{\noexpand\detokenize{#1}}{\noexpand\detokenize{#2}}}} {\semiexpanded{\noexpand\iftok{\noexpand\detokenize{#1}}{\noexpand\detokenize{#2}}}} \permanent\protected\def\doifelsesimilartokens#1#2% {\ifcondition\similartokens{#1}{#2}% \expandafter\firstoftwoarguments \else \expandafter\secondoftwoarguments \fi} \protect \endinput