diff options
Diffstat (limited to 'tex/context/base/mkxl/syst-aux.mkxl')
-rw-r--r-- | tex/context/base/mkxl/syst-aux.mkxl | 6704 |
1 files changed, 6704 insertions, 0 deletions
diff --git a/tex/context/base/mkxl/syst-aux.mkxl b/tex/context/base/mkxl/syst-aux.mkxl new file mode 100644 index 000000000..8fc9d6e02 --- /dev/null +++ b/tex/context/base/mkxl/syst-aux.mkxl @@ -0,0 +1,6704 @@ +%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 \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. + +\aliased \let\startlmtxmode\relax +\aliased \let\stoplmtxmode \relax +\permanent\def\startmkivmode#-\stopmkivmode{} +\permanent\let\stopmkivmode \relax + +%D As we don't have namespace definers yet, we use a special one. Later we will +%D do a better job. + +\ifdefined\c_syst_helpers_n_of_namespaces + + % lets plug in a better error message + +\else + + \newcount\c_syst_helpers_n_of_namespaces \c_syst_helpers_n_of_namespaces\pluseight % 1-8 reserved for catcodes + + \def\v_interfaces_prefix_template_system{\number \c_syst_helpers_n_of_namespaces>>} + %def\v_interfaces_prefix_template_system{\characters\c_syst_helpers_n_of_namespaces>>} % no \characters yet + +\fi + +\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 + \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: + +\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 + +\ifdefined\data \else \let\data \relax \fi % dep checker + +%D \macros +%D {s!,c!,e!,p!,v!,@@,??} +%D +%D To save memory, we use constants (sometimes called variables). Redefining these +%D constants can have disastrous results. Of course, expanding them costs time, so +%D that's the price to pay. More are defined elsewhere. Of course we later pay a +%D price when they need to be expanded. + +% \def\v!prefix! {v!} +% \def\c!prefix! {c!} +% \def\s!prefix! {s!} +% +% \def\s!next {next} +% \def\s!default {default} +% \def\s!dummy {dummy} +% \def\s!unknown {unknown} +% +% \def\s!do {do} +% \def\s!dodo {dodo} +% +% \def\s!complex {complex} +% \def\s!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} + +\aliased\let\stopexpanded\relax + +%D Recent \TEX\ engines have a primitive \type {\expanded} and we will use that when +%D possible. After all, we can make not expandable macros now. The name clash is an +%D unfortunate fact and in retrospect I should nto have agreed to a primitive having +%D that same name. + +% We cannot use the next variant as first we need to adapt \type {##}'s in callers: +% +% \def\expanded#1% +% {\normalexpanded{\noexpand#1}} +% +% \def\startexpanded#1\stopexpanded +% {\normalexpanded{#1}} + +%D \macros +%D {gobbleoneargument,gobble...arguments} +%D +%D The next set of macros just do nothing, except that they get rid of a number of +%D arguments. These zero references prevent intermediate storage. In principle that +%D is more efficient but it probably won't be noticed. It's anyway cheaper on memory +%D access. We use versions that don't even store the arguments. + +%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: + +\aliased\let\donothing\empty + +\let\m_syst_string_one \empty +\let\m_syst_string_two \empty +\let\m_syst_string_three\empty +\let\m_syst_string_four \empty + +\let\m_syst_action_yes \empty +\let\m_syst_action_nop \empty + +%D \macros +%D {doifnextcharelse} +%D +%D This macro has a long history. Among the things we had to deal with was ignoring +%D blank spaces but in the meantime we can use some \LUATEX\ trickery. Older versions +%D use more code and can be find in the \MKIV\ and \MKII\ files. + +% \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: + +\def\syst_helpers_do_if_fast_optional_check_else + {\ifx\nexttoken\syst_helpers_next_optional_character_token + \expandafter\m_syst_action_yes + \else + \expandafter\m_syst_action_nop + \fi} + +\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\ifx\csname NameA\endcsname\relax ... \else ... \fi +%D +%D \ifundefined\NameB ... \else ... \fi +%D \stoptyping +%D +%D Suppression of errors while constructing a control sequence is one of the (few) +%D things that I remember being discussed in the perspective of \ETEX\ but it was +%D rejected because it was not considered useful. Well, in \LUATEX\ we can surpress +%D it and that is what we do in \MKIV. We're probably the only macro package that +%D needs it. That kind of configuration happens elsewhere. These macros are (mostly +%D for historic reasons) fully expandable. + +\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} % or use \undefinevalue to match \setvalue + +\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}. + +\permanent\protected\def\doifelsealldefined#1% + {\begingroup + \donetrue % we could use a reserved one and avoid the group + \processcommalist[#1]\syst_helpers_do_if_all_defined_else + \ifdone + \endgroup\expandafter\firstoftwoarguments + \else + \endgroup\expandafter\secondoftwoarguments + \fi} + +\aliased\let\doifalldefinedelse\doifelsealldefined + +\def\syst_helpers_do_if_all_defined_else#1% + {\ifcsname#1\endcsname\else + \donefalse + \expandafter\quitcommalist % added + \fi} + +%D \macros +%D {doif,doifelse,doifnot} +%D +%D Programming in \TEX\ differs from programming in procedural languages like +%D \MODULA. This means that one --- well, let me speek for myself --- tries to do +%D the things in the well known way. Therefore the next set of \type {ifthenelse} +%D commands were between the first ones we needed. A few years later, the opposite +%D became true: when programming in \MODULA, I sometimes miss handy things like +%D grouping, runtime redefinition, expansion etc. While \MODULA\ taught me to +%D structure, \TEX\ taught me to think recursive. +%D +%D \starttyping +%D \doif {string1} {string2} {...} +%D \doifnot {string1} {string2} {...} +%D \doifelse {string1} {string2} {then ...}{else ...} +%D \stoptyping +%D +%D Again, we use some of the new primitives in \LUAMETATEX. Using straightforward +%D \type {\edef}'s and \type {\ifx} comparison works as well, but this saves tokens +%D and, more important, tracing clutter. + +\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 + +\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} + +\aliased\let\stopprocesscommalist \relax +\aliased\let\stopprocesscommacommand\relax + +%D \macros +%D {processaction, +%D processfirstactioninset, +%D processallactionsinset} +%D +%D \CONTEXT\ makes extensive use of a sort of case or switch command. Depending of +%D the presence of one or more provided items, some actions is taken. These macros +%D can be nested without problems. +%D +%D \starttyping +%D \processaction [x] [x=>a,y=>b,z=>c] +%D \processfirstactioninset [x,y,z] [x=>a,y=>b,z=>c] +%D \processallactionsinset [x,y,z] [x=>a,y=>b,z=>c] +%D \stoptyping +%D +%D We can supply both a \type {default} action and an action to be undertaken when +%D an \type {unknown} value is met: +%D +%D \starttyping +%D \processallactionsinset +%D [x,y,z] +%D [ a=>\a, +%D b=>\b, +%D c=>\c, +%D default=>\default, +%D unknown=>\unknown{... \commalistelement ...}] +%D \stoptyping +%D +%D When \type {#1} is empty, this macro scans list \type {#2} for the keyword \type +%D {default} and executed the related action if present. When \type {#1} is non +%D empty and not in the list, the action related to \type {unknown} is executed. +%D Both keywords must be at the end of list \type{#2}. Afterwards, the actually +%D found keyword is available in \type {\commalistelement}. An advanced example of +%D the use of this macro can be found in \PPCHTEX, where we completely rely on \TEX\ +%D for interpreting user supplied keywords like \type {SB}, \type {SB1..6}, \type +%D {SB125} etc. +%D +%D In the meantime we follow a different approach, often somewhat more heavy on the +%D number of control sequences used, but that is no lomger a real issue. The code +%D has been simplified and nwo uses the macro stack mechanism. If needed I can make +%D this more hip and a bit faster now but \unknown\ it's seldom used nowadays as we +%D have better ways now. + +\mutable\let\commalistelement\empty + +\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]} + +\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} + +\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]% + {\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}\e_o_t\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,}\e_o_t\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}\e_o_t\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}\e_o_t\ignorearguments\ignorearguments} +% +% \def\syst_helpers_do_if_in_string_else_yes#0\ignorearguments\ignorearguments#2#0{#2} +% \def\syst_helpers_do_if_in_string_else_nop#0\ignorearguments#0#3{#3} +% \def\syst_helpers_do_if_in_string_yes #0\ignorearguments\ignorearguments#2{#2} +% \def\syst_helpers_do_if_in_string_nop #0\ignorearguments#0{} +% \def\syst_helpers_do_if_not_in_string_yes #0\ignorearguments\ignorearguments#0{} +% \def\syst_helpers_do_if_not_in_string_nop #0\ignorearguments#2{#2} + +\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? + +\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? + +\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: + +\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} + +\protected\def\xrawprocessaction[#1]#2[#3]% + {\edef\m_syst_string_one{#1}% + \ifempty\m_syst_string_one + \let\m_syst_string_one\s!default + \fi + \let\m_syst_helpers_process_action\relax + \let\m_syst_helpers_process_action_unknown\relax + \syst_helpers_raw_process_action#3\ignorearguments\ignorearguments\ignorearguments + \ifx\m_syst_helpers_process_action\relax + \m_syst_helpers_process_action_unknown + \else + \m_syst_helpers_process_action + \fi} + +%D When we process the list \type {a,b,c,d,e}, the raw routine takes over 30\% less +%D time, when we feed $20+$ character strings we gain about 20\%. Alternatives which +%D use \type {\futurelet} perform worse. Part of the speedup is due to the \type +%D {\let} and \type {\expandafter} in the test. +%D +%D \macros +%D {dosetvalue,dosetevalue,dosetgvalue,docopyvalue,doresetvalue, +%D dogetvalue} +%D +%D When we are going to do assignments, we have to take multi||linguality into account. +%D For the moment we keep things simple and single||lingual. +%D +%D \starttyping +%D \dosetvalue {label} {variable} {value} +%D \dosetevalue {label} {variable} {value} +%D \dosetgvalue {label} {variable} {value} +%D \docopyvalue {to label} {from label} {variable} +%D \doresetvalue {label} {variable} +%D \stoptyping +%D +%D These macros are in fact auxiliary ones and are not meant for use outside the +%D assignment macros. + +\def\dosetvalue #1#2{\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. + +\let\currentvalue\empty + +\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 + +\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 + +\permanent\protected\def\processassignmentlist[#1]#2% #2 == \command{key}{value] + {\def\syst_helpers_process_assignment_entry##1{#2}% {##2}{##3} % namespace is ignored + \dogetparameters\syst_helpers_process_assignment_entry[][#1]} + +\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} + +\aliased\let\stopprocessassignmentlist\relax + +\permanent\protected\def\startprocessassignmentcommand[#1]#2\stopprocessassignmentcommand + {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}% + \normalexpanded{\processassignmentlist[#1]}\currentassignmentlistcommand} + +\aliased\let\stopprocessassignmentcommand\relax + +%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. + +\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]}} + +%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 + \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. + +\protected\def\wait + {\begingroup + \read16 to \wait + \endgroup} + +%D \macros +%D {writestring,writeline, +%D writestatus,statuswidth,normalwritestatus} +%D +%D Maybe one didn't notice, but we've already introduced a macro for showing +%D messages. In the multi||lingual modules, we will also introduce a mechanism for +%D message passing. For the moment we stick to the core macros: +%D +%D \starttyping +%D \writestring {string} +%D \writeline +%D \writestatus {category} {message} +%D \stoptyping +%D +%D Messages are formatted. One can provide the maximum with of the identification +%D string with the macro \type {\statuswidth}. + +\setnewconstant\statuswidth 15 +%setnewconstant\statuswrite 128 % \pluscxxviii + +\ifdefined\writestring \else + + \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: + +\let\immediatemessage\clf_immediatemessage % {} mandate / todo permanent at 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 + {\ifx\redoglobal\relax + \enforced\let\redoglobal\global + \enforced\let\dodoglobal\syst_helpers_dodo_global + \fi} + +\def\syst_helpers_dodo_global + {\resetglobal\global} + +\def\saveglobal + {\let\syst_helpers_dodo_global\dodoglobal + \let\syst_helpers_redo_global\redoglobal} + +\def\restoreglobal + {\enforced\let\redoglobal\syst_helpers_redo_global + \enforced\let\dodoglobal\syst_helpers_dodo_global} + +%D Whatever (will be overtoaded): + +\protected\def\define#1% + {\ifdefined#1% + \message{[\noexpand#1is already defined]}% + \protected\expandafter\def\expandafter\gobbleddefinition + \else + \protected\expandafter\def + \fi#1} + +\permanent\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] + +\bgroup \obeylines + +\permanent\protected\gdef\starttexdefinition% + {\bgroup% + \obeylines% + \syst_helpers_start_tex_definition} + +\gdef\syst_helpers_start_tex_definition#1 + {\catcode\endoflineasciicode\ignorecatcode% + \clf_texdefinition_one{#1}} + +\aliased\glet\stoptexdefinition\relax + +\permanent\gdef\dostarttexdefinition#1\stoptexdefinition% + {\egroup% + \clf_texdefinition_two{#1}} + +\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. + +\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\ifx#1\relax\else#1\fi\fi+\plusone \relax}} +\def\syst_helpers_do_decrement#1{\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+\minusone\relax}} + +\def\syst_helpers_do_do_do_increment#1,#2){\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi+#2\relax}} +\def\syst_helpers_do_do_do_decrement#1,#2){\dodoglobal\edef#1{\the\numexpr\ifdefined#1\ifx#1\relax\else#1\fi\fi-#2\relax}} + +\def\syst_helpers_do_do_increment(#1% + {\def\m_syst_action_yes{\syst_helpers_do_do_do_increment#1}% + \def\m_syst_action_nop{\syst_helpers_do_do_do_increment#1,\plusone}% + \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop} + +\def\syst_helpers_do_do_decrement(#1% + {\def\m_syst_action_yes{\syst_helpers_do_do_do_decrement#1}% + \def\m_syst_action_nop{\syst_helpers_do_do_do_decrement#1,\plusone}% + \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop} + +\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 + \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. + +% left overs: too much \protected here + +\newcount\outerrecurse +\newcount\innerrecurse + +\mutable\def\recursedepth{\the\outerrecurse} +\mutable\def\recurselevel{0} + +\mutable\let\recurseaction\relax +\mutable\let\recursestring\empty + +% \let\syst_helpers_stepwise_next\relax + +% \protected\def\syst_helpers_stepwise_recurse#1#2#3% from to step +% {\ifnum#1>#2\relax +% \expandafter\syst_helpers_stepwise_recurse_nop +% \else +% \def\recurselevel{#1}% +% \doubleexpandafter\syst_helpers_stepwise_recurse_yes\expandafter +% \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}} + +\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\expandafter +% \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}} + \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\syst_helpers_stepwise_recurse_nop + \expandafter\gobblefourarguments + \else + \def\recurselevel{#1}% + \innerrecurse#1\relax + \advance\innerrecurse#3\relax +% \doubleexpandafter\syst_helpers_stepwise_reverse_yes\expandafter +% \fi\expandafter{\the\innerrecurse}{#2}{#3}} + \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} + +% \protected\def\syst_helpers_stepwise_exit +% {\syst_helpers_stepwise_recurse_nop\relax} + +\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 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 + +\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_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} + +% \protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 +% {\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 +% \syst_helpers_stepwise_exit +% \else +% \syst_helpers_stepwise_reverse +% \fi +% \or +% \syst_helpers_stepwise_exit +% \or +% \ifnum#2<#1\relax\relax % so we catch \number\numexpr xx without \relax's +% \syst_helpers_stepwise_exit +% \else +% \syst_helpers_stepwise_recurse +% \fi +% \fi{\number#1}{\number#2}{\number#3}}% +% \globalpopmacro\recurselevel +% \globalpopmacro\recurseaction +% \global\advance\outerrecurse\minusone} + +\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} + +% \protected\def\syst_helpers_stepwise_recurse_nop#0#0#0#0% +% {} + +\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} + +% 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 \domatch {(\letterpercent w+) *(\letterpercent w*)} {aa bb cc dd} { +%D [ +%D \domatch{(\letterpercent w)(\letterpercent w)} {#1} {(##1 ##2)} +%D \domatch{(\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 + \ifx#2\relax\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\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 + {\bgroup + \aftergroup\m_syst_helpers_handle_group_a + \aftergroup\egroup + \m_syst_helpers_handle_group_b} + +\protected\def\syst_helpers_handle_group_normal + {\bgroup + \afterassignment\m_syst_helpers_handle_group_normal_before + \let\next=} + +\def\m_syst_helpers_handle_group_normal_before + {\bgroup + \m_syst_helpers_handle_group_b + \bgroup + \aftergroup\m_syst_helpers_handle_group_a + \aftergroup\egroup + \aftergroup\egroup} + +\protected\def\syst_helpers_handle_group_simple% no inner group (so no kerning interference) + {\bgroup + \afterassignment\m_syst_helpers_handle_group_simple_before + \let\next=} + +\def\m_syst_helpers_handle_group_simple_before + {\bgroup + \aftergroup\m_syst_helpers_handle_group_simple_after + \m_syst_helpers_handle_group_b} + +\def\m_syst_helpers_handle_group_simple_after + {\m_syst_helpers_handle_group_a + \egroup}% + +\protected\def\syst_helpers_handle_group_pickup% no inner group (so no kerning interference) + {\bgroup + \afterassignment\m_syst_helpers_handle_group_pickup_before + \let\next=} + +\def\m_syst_helpers_handle_group_pickup_before + {\bgroup + \aftergroup\m_syst_helpers_handle_group_a + \aftergroup\egroup + \aftergroup\m_syst_helpers_handle_group_p + \m_syst_helpers_handle_group_b} + +\protected\def\syst_helpers_handle_group_nop_x + {\ifnum\currentgrouptype=\semisimplegroupcode + \begingroup + \aftergroup\endgroup + \else + \bgroup + \aftergroup\egroup + \fi + \m_syst_helpers_handle_group_b} + +\protected\def\syst_helpers_handle_group_normal_x + {\bgroup + \afterassignment\m_syst_helpers_handle_group_normal_before_x + \let\next=} + +\def\m_syst_helpers_handle_group_normal_before_x + {\bgroup + \m_syst_helpers_handle_group_b + \bgroup + \aftergroup\egroup + \aftergroup\egroup} + +%D I considered it a nuisance that +%D +%D \starttyping +%D \color[green] +%D {as grass} +%D \stoptyping +%D +%D was not interpreted as one would expect. This is due to the fact that \type +%D {\futurelet} obeys blank spaces, and a line||ending token is treated as a blank +%D space. So the final implementation became: + +\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\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\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 \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} + +\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% + {\ifx\dodoglobal\relax + \expandafter\toksapp + \else + \resetglobal + \expandafter\gtoksapp + \fi#2{#1}} + +\permanent\protected\def\prependtoks#1\to#2% + {\ifx\dodoglobal\relax + \expandafter\tokspre + \else + \resetglobal + \expandafter\gtokspre + \fi#2{#1}} + +% \def\syst_helpers_append_toks_indeed +% {\ifx\dodoglobal\relax +% \expandafter\toksapp +% \else +% \resetglobal +% \expandafter\gtoksapp +% \fi\m_syst_helpers_scratch\t_syst_helpers_scratch} +% +% \def\syst_helpers_prepend_toks_indeed +% {\ifx\dodoglobal\relax +% \expandafter\tokspre +% \else +% \resetglobal +% \expandafter\gtokspre +% \fi\m_syst_helpers_scratch\t_syst_helpers_scratch} +% +% \protected\def\appendtoksonce#1\to#2% +% {\let\m_syst_helpers_scratch#2% +% \t_syst_helpers_scratch{#1}% +% \ifhasxtoks\t_syst_helpers_scratch\m_syst_helpers_scratch\else +% \syst_helpers_append_toks_indeed +% \fi} +% +% \protected\def\prependtoksonce#1\to#2% +% {\let\m_syst_helpers_scratch#2% +% \t_syst_helpers_scratch{#1}% +% \ifhasxtoks\t_syst_helpers_scratch\m_syst_helpers_scratch\else +% \syst_helpers_prepend_toks_indeed +% \fi} + +\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 + +\aliased\let\stopcollect \relax +\aliased\let\stopexpandedcollect\relax + +\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]} + +\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% + {\ifx\dodoglobal\relax + \expandafter\etoksapp + \else + \resetglobal + \expandafter\xtoksapp + \fi#2{#1}} + +\permanent\protected\def\prependetoks#1\to#2% + {\ifx\dodoglobal\relax + \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\to\m_syst_string_one\and\m_syst_string_two + \dodoglobal#3{\m_syst_string_one\m_syst_string_two}} + +%D \macros +%D {appendtocommalist,prependtocommalist, +%D addtocommalist,removefromcommalist} +%D +%D When working with comma separated lists, one sooner or later want the tools to +%D append or remove items from such a list. When we add an item, we first check if +%D it's already there. This means that every item in the list is unique. +%D +%D \starttyping +%D \addtocommalist {alfa} \name +%D \addtocommalist {beta} \name +%D \addtocommalist {gamma} \name +%D \removefromcommalist {beta} \name +%D \stoptyping +%D +%D These commands can be prefixed with \type {\doglobal}. The implementation of the +%D second command is more complecated, because we have to take leading spaces into +%D account. Keep in mind that users may provide lists with spaces after the commas. +%D When one item is left, we also have to get rid of trailing spaces. +%D +%D \starttyping +%D \def\words{alfa, beta, gamma, delta} +%D \def\words{alfa,beta,gamma,delta} +%D \stoptyping +%D +%D Removing an item takes more time than adding one. A fast appending alternative, +%D without any testing, is also provided: +%D +%D \starttyping +%D \appendtocommalist {something} \name +%D \prependtocommalist {something} \name +%D \stoptyping +%D +%D This can be implemented as follows: +%D +%D \starttyping +%D \def\appendtocommalist#1#2% +%D {\ifx#2\empty +%D \dodoglobal\edef#2{#1}% +%D \else % no test on empty +%D \dodoglobal\edef#2{#2,#1}% +%D \fi} +%D +%D \def\prependtocommalist#1#2% +%D {\ifx#2\empty +%D \dodoglobal\edef#2{#1}% +%D \else % no test on empty +%D \dodoglobal\edef#2{#1,#2}% +%D \fi} +%D \stoptyping +%D +%D The faster alternatives are: + +\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,##3\\% + {\def\syst_helpers_split_string##1,#2,##2,#2,##-\\% + {\edef\m_syst_string_one{\bcleanedupcommalist##1\empty\empty\relax}% + \edef\m_syst_string_two{\acleanedupcommalist##2,,\relax}}% + \expandafter\syst_helpers_split_string\expandafter,#1,,#2,,#2,\\} + +\def\bcleanedupcommalist#1#2#3\relax{\if#1,\else#1\fi\if#2,\else#2\fi#3} +\def\bcleanedupcommalist#1#2\relax{\if#1,\else#1\fi#2} +%def\acleanedupcommalist#1,,#2\relax{#1} +\def\acleanedupcommalist#1,,#-\relax{#1} + +\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} % best use the primitive directly + +%D The capitals are needed because \type {p} and \type {t} have catcode~12, while +%D macronames only permit tokens with the catcode~11. As a result we cannot use the +%D \type {.group} primitives. Those who want to know more about this kind of +%D manipulations, we advice to study the \TEX book in detail. Because this macro +%D does not do any assignment, we can use it in the following way too. + +% \def\PtToCm#1% +% {\withoutpt\the\dimexpr0.0351459804\dimexpr#1\relax\relax cm} + +\permanent\def\PtToCm#1{\thewithoutunit\dimexpr0.0351459804\dimexpr#1\relax\relax cm} + +%D We also support: +%D +%D \starttyping +%D \numberofpoints {dimension} +%D \dimensiontocount {dimension} {\count} +%D \stoptyping +%D +%D Both macros return a rounded number. + +% \dimensiontocount{10.49pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.49pt} +% \dimensiontocount{10.51pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.51pt} + +\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. + +\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\firstofonearguments\gobbleoneargument } +\permanent\protected\def\doifnotallcommon {\syst_helpers_do_if_all_common_else\gobbleoneargument \firstofonearguments } + +\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 +%% \let\m_syst_helpers_with_set_command\empty +%% +%% \def\syst_helpers_with_set_double[#1][#2]% +%% {\doifsomething{#1}% +%% {\def\syst_helpers_with_set_step##1{\m_syst_helpers_with_set_command[##1][#2]}% +%% \processcommalist[#1]\syst_helpers_with_set_step}} +%% +%% \def\syst_helpers_with_set_triple[#1][#2][#3]% +%% {\doifsomething{#1}% +%% {\def\syst_helpers_with_set_step##1{\m_syst_helpers_with_set_command[##1][#2][#3]}% +%% \processcommalist[#1]\syst_helpers_with_set_step}} +%% +%% \def\dodoubleemptywithset #1{\let\m_syst_helpers_with_set_command#1\dodoubleempty \syst_helpers_with_set_double} % \command +%% \def\dodoubleargumentwithset#1{\let\m_syst_helpers_with_set_command#1\dodoubleargument\syst_helpers_with_set_double} % \command +%% +%% \def\dotripleemptywithset #1{\let\m_syst_helpers_with_set_command#1\dotripleempty \syst_helpers_with_set_triple} % \command +%% \def\dotripleargumentwithset#1{\let\m_syst_helpers_with_set_command#1\dotripleargument\syst_helpers_with_set_triple} % \command + +\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[#1]\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[#1]\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} +%D are expanded. This command is a special case of: +%D +%D \starttyping +%D \stripcharacter\char\from\one\to\two +%D \stoptyping +%D +%D As we can see below, spaces following a control sequence are to enclosed in \type +%D {{}}. + +\let\m_syst_helpers_strip_character\empty + +\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} + +\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 + +% \protected\def\PointsToReal#1#2% +% {\edef#2{\withoutpt\the\dimexpr#1}} + +\permanent\protected\def\PointsToReal#1#2% + {\edef#2{\thewithoutunit\dimexpr#1}} + +%D \macros +%D {dontleavehmode} +%D +%D Sometimes when we enter a paragraph with some command, the first token gets the +%D whole first line. We can prevent this by saying: +%D +%D \starttyping +%D \dontleavehmode +%D \stoptyping +%D +%D This command is used in for instance the language module \type {lang-ini}. The +%D first version was: +%D +%D \starttyping +%D \def\dontleavehmode{\ifhmode\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\else \ifmmode\else +%D {\mathsurround\zeropoint\everymath\emptytoks$ $}% +%D \fi \fi} +%D \stoptyping +%D +%D And finaly we got the following alternative, one that avoids interfering grouping +%D at the cost of a box. +%D +%D \starttyping +%D \newbox\b_syst_helpers_dlh +%D +%D \protected\def\dontleavehmode +%D {\ifhmode\else \ifmmode\else +%D \setbox\b_syst_helpers_dlh\hbox{\mathsurround\zeropoint\everymath\emptytoks$ $}\unhbox\b_syst_helpers_dlh +%D \fi \fi} +%D \stoptyping +%D +%D But, as we run a recent version of \TEX, we can use the new primitive: + +\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. + +\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. + +\protected\def\syst_helpers_handle_tokens % \nexthandledtoken is part of interface + {\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 + +\let\iftrialtypesetting\iffalse % so we have no \trialtypesettingtrue|false in mkiv ! + +\appendtoks \let\iftrialtypesetting\iftrue \to \everysettrialtypesetting +\appendtoks \let\iftrialtypesetting\iffalse \to \everyresettrialtypesetting + +%D \macros +%D {twodigitrounding} +%D +%D When using \type {\special}s or \type {\pdfliteral}s, it sometimes makes sense to +%D limit the precission. The next macro rounds a real number to two digits. It takes +%D one argument and only works in \ETEX. + +\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 + +\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} + +%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{\dp\strutbox} +%D \stoptyping +%D +%D As an alternative for the next macro we can use a global assignment inside a box. +%D The \type {\empty}'s permits gobbling while preventing spurious \type {\relax}'s. + +\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% + {\ifx#2\relax#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 + {\ifx#3\relax#1#2\else\expandafter\thelasttwodigits\expandafter#2\expandafter#3\fi} + +%D \macros +%D {serializecommalist} +%D +%D Concatenate commalists: + +\let\syst_helpers_serialize_comma_list_step\relax + +\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} +%D +%D \starttyping +%D \setvalue{xx}{{A}{B}{C}} +%D +%D \filterfromvalue{xx}{3}{3} +%D \filterfromvalue{xx}{3}{2} +%D \filterfromvalue{xx}{3}{1} +%D \stoptyping +%D +%D An alternative is to store 'max' in the list, say: +%D +%D \starttyping +%D \setvalue{xx}{3{A}{B}{C}} +%D +%D \filterfromvalues{3}{xx}{3} +%D \filterfromvalues{3}{xx}{2} +%D \filterfromvalues{3}{xx}{1} +%D \stoptyping +%D +%D I'll implement this when I'm in \quotation {writing dirty macros mood}. + +\permanent\def\dofilterfromstr#1#2% max n % no need to be fast + {\expandafter \expandafter \expandafter \csstring + \ifcase#1\or \ifcase#2\or + \firstofoneargument \else + \gobbleoneargument \fi + \or \ifcase#2\or + \firstoftwoarguments \or + \secondoftwoarguments \else + \gobbletwoarguments \fi + \or \ifcase#2\or + \firstofthreearguments \or + \secondofthreearguments \or + \thirdofthreearguments \else + \gobblethreearguments \fi + \or \ifcase#2\or + \firstoffourarguments \or + \secondoffourarguments \or + \thirdoffourarguments \or + \fourthoffourarguments \else + \gobblefourarguments \fi + \or \ifcase#2\or + \firstoffivearguments \or + \secondoffivearguments \or + \thirdoffivearguments \or + \fourthoffivearguments \or + \fifthoffivearguments \else + \gobblefivearguments \fi + \fi} + +\permanent\def\filterfromvalue#1#2#3% value max n + {\expandafter\doubleexpandafter\csname % we use the fact that an + \expandafter\ifx\csname#1\endcsname\relax % undefined cs has become \relax + \csstring\gobbleoneargument % which we then gobble here + \else + \dofilterfromstr{#2}{#3}% + \fi + \endcsname\csname#1\endcsname} + +\permanent\def\filterfromnext#1#2% max n {..}{..}{..}{..} + {\csname\dofilterfromstr{#1}{#2}\endcsname} + +%D \macros +%D {definemeasure} +%D +%D \starttyping +%D \definemeasure[mywidth][\dimexpr(\textwidth-1cm)] +%D +%D ... \measure{mywidth} ... +%D \stoptyping + +\installsystemnamespace{measure} + +\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\def\comparedimension#1#2% + {\compresult + \ifdim#1<#2% + \zerocount + \orelse\ifdim#1<#2% + \plusone + \else + \plustwo + \fi} + +\permanent\def\comparedimensioneps#1#2% todo: use eps feature + {\compresult + \ifdim\dimexpr#1-#2\relax<\roudingeps + \zerocount + \orelse\ifdim\dimexpr#2-#1\relax<\roudingeps + \zerocount + \orelse\ifdim#1<#2% + \plusone + \else + \plustwo + \fi} + +% pretty ugly but fast + +% \copycsname xxx\endcsname\csname ..\endcsname + +\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\ifx\csname\??flag#1\endcsname\relax + \expandafter\secondoftwoarguments + \orelse\ifcase\csname\??flag#1\endcsname + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\aliased\let\doifflaggedelse\doifelseflagged + +\permanent\def\doifnotflagged#1% + {\expandafter\ifx\csname\??flag#1\endcsname\relax + \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% + {\ifx#1\relax + \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 + +% here ? + +\protected\def\dosetrawvalue #1#2#3{\defcsname #1#2\endcsname{#3}} +\protected\def\dosetrawevalue#1#2#3{\edefcsname #1#2\endcsname{#3}} +\protected\def\dosetrawgvalue#1#2#3{\global\defcsname #1#2\endcsname{#3}} +\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}} + +\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 + +\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 + +\def\currentfeaturetest{\number\c_syst_helpers_test_feature_n} + +\permanent\protected\def\testfeature#1#2% + {\c_syst_helpers_test_feature_m#1\relax + \xdef\elapsedsteps{\number\c_syst_helpers_test_feature_m}% + \def\syst_helpers_test_feature_yes + {\advance\c_syst_helpers_test_feature_n\plusone + \ifnum\c_syst_helpers_test_feature_n>\c_syst_helpers_test_feature_m\else + #2\expandafter\syst_helpers_test_feature_yes + \fi}% + \def\syst_helpers_test_feature_nop + {\advance\c_syst_helpers_test_feature_n\plusone + \ifnum\c_syst_helpers_test_feature_n>\c_syst_helpers_test_feature_m\else + \expandafter\syst_helpers_test_feature_nop + \fi}% + \retestfeature} + +\permanent\protected\def\retestfeature % timer support is new per 10/5/2005 + {\bgroup + \ifcase\interactionmode\let\wait\relax\fi + \clf_resettimer + \c_syst_helpers_test_feature_n\zerocount + \syst_helpers_test_feature_nop + \clf_benchmarktimer + \writestatus\m!system + {starting feature test: % + \number\c_syst_helpers_test_feature_m\space steps}% + \c_syst_helpers_test_feature_n\zerocount + \syst_helpers_test_feature_yes + \writestatus\m!system + {feature test done: % + \number\c_syst_helpers_test_feature_m\space steps, % + \clf_elapsedtime\space seconds, % + \clf_elapsedsteptime\elapsedsteps\space\space per step}% + \egroup} + +\permanent\protected\def\showtimer#1% + {\writestatus{runtime}{\elapsedseconds\space s / #1}} + +\permanent\protected\def\testfeatureonce#1#2% + {\begingroup + \let\wait\relax + \testfeature{#1}{#2}% + \endgroup} + +%D \macros +%D {freezedimenmacro} +%D +%D This macro is use as: +%D +%D \starttyping +%D \freezedimenmacro\leftmargindistance +%D \stoptyping + +\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\_e_o_w_ + {\doifsomething{#1}{\processword{#1} \syst_helpers_process_word#2 \_e_o_w_}} + +\def\processwords#1% + {\syst_helpers_process_word#1 \_e_o_w_}% no \unskip + +\let\processword\relax + +%D \macros +%D {startnointerference} +%D +%D \starttyping +%D \startnointerference +%D all kind of code +%D \stopnointerference +%D \stoptyping + +\newbox\b_syst_helpers_no_interference + +\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}): + +%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,,\_o_e_p_} + +% \def\syst_helpers_checked_pair#1,#2,#0\_o_e_p_ +% {#1,#2} + +\def\syst_helpers_checked_pair#1,#2,#-\_o_e_p_ + {#1,#2} + +%D Here are some nasty helpers. They can be used to fill often expanded token +%D lists efficiently (see tabulate for an example). + +\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} + +\protect \endinput |