1%D \module 2%D [ file=syst-aux, % merge of syst-gen cum suis 3%D version=1996.03.20, 4%D title=\CONTEXT\ System Macros, 5%D subtitle=General, 6%D author=Hans Hagen, 7%D date=\currentdate, 8%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] 9%C 10%C This module is part of the \CONTEXT\ macro||package and is 11%C therefore copyrighted by \PRAGMA. See mreadme.pdf for 12%C details. 13 14%D In the process of being adapted to \LMTX\ flags so a bit of a mess now. Also 15%D our playground. 16 17\registerctxluafile{syst-aux}{autosuffix} 18\registerctxluafile{syst-mac}{autosuffix} 19 20%D This file is a follow up in \type {syst-aux.mkii} and \type {syst-aux.mkiv} where 21%D you can find some more pure \TEX\ or \LUATEX\ variants. There are quite some 22%D helpers here and many no longer are used but we keep them around because they 23%D have always been there. However, the implementation in \LMTX\ can be somewhat 24%D different that in \MKIV, because it's also a kind of a playground for new 25%D features. Of course nostalgia also plays a role. 26%D 27%D For the original code we refer to \type {syst-aux.mkii} and its follow up \type 28%D {syst-aux.mkiv}. These also have historic versions. As usual, the upgrade went 29%D through stages. First I wrote variants usign the new \type {\ignorearguments} 30%D feature, then I decided to implement a few more conditionals and new fancy code 31%D could be ditched before it was even published. I kept some as examples. 32%D 33%D Of course I couldn't do this without Wolfgang looking over my shoulder. Changing 34%D core code is kind of tricky. 35%D 36%D There are quite some helpers here that are effectively obsolete (read: there is no 37%D need for them). Examples are \type {setflag} and \type {\uppercasestring} and their 38%D relatives. We keep them around as illustration and possible usage in old styles. 39 40\unprotect 41 42%D These are for local helpers, the names are historic: 43 44\mutable\let\docommand \relax 45\mutable\let\dodocommand\relax 46 47%D \macros 48%D {unexpanded} 49%D 50%D Because we use this module only in \MKIV, we have removed the old protection 51%D code. 52%D 53%D \starttyping 54%D \protected\def\somecommand{... ... ...} 55%D \stoptyping 56%D 57%D This overloads the \ETEX\ primitive but as we already had an \MKII\ solution we 58%D keep the same name for a similar mechanism. So, effectively we have two ways to 59%D protect a macro. 60%D 61%D In \LMTX\ we try to avoid this and use \type {\protected} instead, which is 62%D possible because over time we got rid of using the \CONTEXT\ macro with that 63%D name. 64 65\pushoverloadmode 66 67\aliased\let\unexpanded\normalprotected 68 69\popoverloadmode 70 71%D We're definitely in \LMTX\ mode here. 72 73\permanent\protected\lettonothing\startlmtxmode 74\permanent\protected\lettonothing\stoplmtxmode 75\permanent \def\startmkivmode#-\stopmkivmode{} 76\permanent\protected\lettonothing\stopmkivmode 77 78%D As we don't have namespace definers yet, we use a special one. Later we will 79%D do a better job. 80 81\ifdefined\c_syst_helpers_n_of_namespaces 82 83 % lets plug in a better error message 84 85\else 86 87 \newinteger\c_syst_helpers_n_of_namespaces \c_syst_helpers_n_of_namespaces\pluseight % 1-8 reserved for catcodes 88 89 \def\v_interfaces_prefix_template_system{\the \c_syst_helpers_n_of_namespaces>>} 90 %def\v_interfaces_prefix_template_system{\characters\c_syst_helpers_n_of_namespaces>>} % no \characters yet 91 92\fi 93 94\permanent\protected\def\installsystemnamespace#1% maybe move this to syst-ini 95 {\ifcsname ??#1\endcsname 96 \writestatus\m!system{duplicate system namespace '#1'}\wait 97 \else 98 \global\advanceby\c_syst_helpers_n_of_namespaces\plusone 99 \immutable\edefcsname ??#1\endcsname{\v_interfaces_prefix_template_system}% 100 \fi} 101 102%D \macros 103%D {normalspace} 104%D 105%D There is already \type{\space} but just to be sure we also provide this one: 106 107\permanent\def\normalspace{ } 108 109\newif\if!!donea \newif\if!!doneb \newif\if!!donec % soon obsolete in lmtx 110\newif\if!!doned \newif\if!!donee \newif\if!!donef % soon obsolete in lmtx 111 112\immutable\def\!!zerocount {0} % alongside \zerocount 113\immutable\def\!!minusone {-1} % ... 114\immutable\def\!!plusone {1} % ... 115\immutable\def\!!plustwo {2} % ... 116\immutable\def\!!plusthree {3} % ... 117\immutable\def\!!plusfour {4} % ... 118\immutable\def\!!plusfive {5} % ... 119\immutable\def\!!plussix {6} % ... 120\immutable\def\!!plusseven {7} % ... 121\immutable\def\!!pluseight {8} % ... 122\immutable\def\!!plusnine {9} % alongside \plusnine 123 124\setnewconstant \uprotationangle 0 125\setnewconstant\rightrotationangle 90 126\setnewconstant \downrotationangle 180 127\setnewconstant \leftrotationangle 270 128 129%D \macros 130%D {s!,c!,e!,p!,v!,@@,??} 131%D 132%D To save memory, we use constants (sometimes called variables). Redefining these 133%D constants can have disastrous results. Of course, expanding them costs time, so 134%D that's the price to pay. More are defined elsewhere. Of course we later pay a 135%D price when they need to be expanded. 136 137% \def\v!prefix! {v!} 138% \def\c!prefix! {c!} 139% \def\s!prefix! {s!} 140% 141% \def\s!next {next} 142% \def\s!default {default} 143% \def\s!dummy {dummy} 144% \def\s!unknown {unknown} 145% 146% \def\s!do {do} 147% \def\s!dodo {dodo} 148% 149% \def\s!complex {complex} 150% \def\s!simple {simple} 151% 152% \def\s!start{start} 153% \def\s!stop {stop} 154 155% \immutable\def\s!empty {empty} 156 157%D Sometimes we pass macros as arguments to commands that don't expand them before 158%D interpretation. Such commands can be enclosed with \type {\expanded}, like: 159%D 160%D \starttyping 161%D \expanded{\setupsomething[\alfa]} 162%D \stoptyping 163%D 164%D Such situations occur for instance when \type {\alfa} is a commalist or when data 165%D stored in macros is fed to index of list commands. If needed, one should use 166%D \type {\noexpand} inside the argument. Later on we will meet some more clever 167%D alternatives to this command. Beware, only the simple one has \type {\noexpand} 168%D before its argument. 169 170\lettonothing\m_syst_helpers_expanded 171 172\pushoverloadmode 173 174\permanent\protected\def\expanded#1% 175 {\xdef\m_syst_helpers_expanded{\noexpand#1}\m_syst_helpers_expanded} 176 177\popoverloadmode 178 179\permanent\protected\def\startexpanded#1\stopexpanded 180 {\xdef\m_syst_helpers_expanded{#1}\m_syst_helpers_expanded} 181 182\permanent\protected\lettonothing\stopexpanded 183 184%D Recent \TEX\ engines have a primitive \type {\expanded} and we will use that when 185%D possible. After all, we can make not expandable macros now. The name clash is an 186%D unfortunate fact and in retrospect I should nto have agreed to a primitive having 187%D that same name. 188 189% We cannot use the next variant as first we need to adapt \type {##}'s in callers: 190% 191% \def\expanded#1% 192% {\normalexpanded{\noexpand#1}} 193% 194% \def\startexpanded#1\stopexpanded 195% {\normalexpanded{#1}} 196 197%D \macros 198%D {gobbleoneargument,gobble...arguments} 199%D 200%D The next set of macros just do nothing, except that they get rid of a number of 201%D arguments. These zero references prevent intermediate storage. In principle that 202%D is more efficient but it probably won't be noticed. It's anyway cheaper on memory 203%D access. We use versions that don't even store the arguments. 204 205%permanent\def\gobbleoneargument #-{} 206\permanent\def\gobbletwoarguments #-#-{} 207\permanent\def\gobblethreearguments #-#-#-{} 208\permanent\def\gobblefourarguments #-#-#-#-{} 209\permanent\def\gobblefivearguments #-#-#-#-{} 210\permanent\def\gobblesixarguments #-#-#-#-{} 211\permanent\def\gobblesevenarguments #-#-#-#-#-{} 212\permanent\def\gobbleeightarguments #-#-#-#-#-#-{} 213\permanent\def\gobbleninearguments #-#-#-#-#-#-#-{} 214 215\permanent\tolerant\def\gobbleoneoptional [#S#-]{} 216\permanent\tolerant\def\gobbletwooptionals [#S#-]#*[#S#-]{} 217\permanent\tolerant\def\gobblethreeoptionals[#S#-]#*[#S#-]#*[#S#-]{} 218\permanent\tolerant\def\gobblefouroptionals [#S#-]#*[#S#-]#*[#S#-]#*[#S#-]{} 219\permanent\tolerant\def\gobblefiveoptionals [#S#-]#*[#S#-]#*[#S#-]#*[#S#-]#*[#S#-]{} 220 221%D Reserved macros for tests: 222 223%D A \type \type {\let} is more efficient than a \type {\def} so we often let something 224%D to relax or do nothing. However, we then loose the name in tracing. For that we now 225%D have \type {\lettonothing \foo} which is efficient but also keeps the name. Of course 226%D this is no solution for commands that take arguments but at least it helps tracing a 227%D bit. We keep of course \type {\donothing}. 228 229%D Maybe even nicer is a let that aliases but keeps the name. 230 231 % \lettonothing\donothing 232 \immutable\def\donothing {} % better in tracing 233%untraced\immutable\def\untraceddonothing{} 234 235\lettonothing\m_syst_string_one 236\lettonothing\m_syst_string_two 237\lettonothing\m_syst_string_three 238\lettonothing\m_syst_string_four 239 240\lettonothing\m_syst_action_yes 241\lettonothing\m_syst_action_nop 242 243%D \macros 244%D {doifnextcharelse} 245%D 246%D This macro has a long history. Among the things we had to deal with was ignoring 247%D blank spaces but in the meantime we can use some \LUATEX\ trickery. Older versions 248%D use more code and can be find in the \MKIV\ and \MKII\ files. 249 250% \mutable\let\next \relax 251% \mutable\let\nextnext \relax % kind of obsolete 252% \mutable\let\nextnextnext \relax % kind of obsolete 253% \mutable\let\nexttoken \relax 254 255\permanent\protected\def\doifelsenextchar#1#2#3% #1 should not be {} ! 256 {\def\m_syst_action_yes{#2}% 257 \def\m_syst_action_nop{#3}% 258 \futureexpandis#1\m_syst_action_yes\m_syst_action_nop} 259 260\permanent\protected\def\doifelsenextcharcs % #1#2#3% #1 should not be {} ! 261 {\futureexpandis} 262 263\aliased\let\doifnextcharelse \doifelsenextchar 264\aliased\let\doifnextcharcselse\doifelsenextcharcs 265 266%D Because we will mostly use this macro for testing if the next character is \type 267%D {[}, we also make a slightly faster variant as it is not uncommon to have tens of 268%D thousands of calls to this test in a run. Of course it also is more convenient to 269%D read a trace then. Here we use a lookahead primitive that ignores (blank) spaces 270%D which makes for less tracing clutter than the original definition. It's also 271%D faster bit that will probably go unnoticed. Of course, hard||core \TEX ies whose 272%D reputations depends on understanding obscure macro definitions will love the more 273%D low level variants. 274 275\permanent\protected\def\doifelsenextoptional#1#2% 276 {\def\m_syst_action_yes{#1}% 277 \def\m_syst_action_nop{#2}% 278 \futureexpandis[\m_syst_action_yes\m_syst_action_nop} 279 280\permanent\protected\def\doifelsenextoptionalcs 281 {\futureexpandis[} 282 283\aliased\let\doifnextoptionalelse \doifelsenextoptional 284\aliased\let\doifnextoptionalcselse\doifelsenextoptionalcs 285 286\permanent\protected\def\doifelsenextbgroup#1#2% 287 {\def\m_syst_action_yes{#1}% 288 \def\m_syst_action_nop{#2}% 289 \futureexpandis\bgroup\m_syst_action_yes\m_syst_action_nop} 290 291\permanent\protected\def\doifelsenextbgroupcs % #1#2 292 {\futureexpandis\bgroup} 293 294\aliased\let\doifnextbgroupelse \doifelsenextbgroup 295\aliased\let\doifnextbgroupcselse\doifelsenextbgroupcs 296 297\permanent\protected\def\doifelsenextparenthesis#1#2% 298 {\def\m_syst_action_yes{#1}% 299 \def\m_syst_action_nop{#2}% 300 \futureexpandis(\m_syst_action_yes\m_syst_action_nop} 301 302\aliased\let\doifnextparenthesiselse\doifelsenextparenthesis 303 304%D The next one is handy in predictable situations: 305 306\permanent\protected\def\doifelsefastoptionalcheck#1#2% 307 {\def\m_syst_action_yes{#1}% 308 \def\m_syst_action_nop{#2}% 309 \futureexpandis[\m_syst_action_yes\m_syst_action_nop} 310 311\permanent\protected\def\doifelsefastoptionalcheckcs 312 {\futureexpandis[} 313 314\aliased\let\doiffastoptionalcheckelse \doifelsefastoptionalcheck 315\aliased\let\doiffastoptionalcheckcselse\doifelsefastoptionalcheckcs 316 317%D Here's one for skipping spaces and pars, handy for: 318%D 319%D \starttyping 320%D \hbox 321%D 322%D {a few lines later} 323%D \stoptyping 324%D 325%D like: 326%D 327%D \starttyping 328%D \def\somecommand{\dowithnextbox{\box\nextbox}\ruledhbox} 329%D 330%D \assumelongusagecs\somecommand 331%D 332%D \bgroup 333%D oeps 334%D \egroup 335%D \stoptyping 336%D 337%D The original kind of clumsy but working version is now replaced by a simple 338%D macro. And it can be even less code in \LUAMETATEX: 339 340\aliased\let\assumelongusagecs\expandafterpars % so we can replace it 341 342%D It's used to skip over empty lines in some constructs that we like to set up 343%D spacy. We already permit par tokens (and equivalents) in math and some other 344%D places visa engine features. And, \type {\long} stuff has been dropped for a long 345%D time already (it is still optional in \lUATEX). But the \quote {long} in the 346%D name kind of stuck. 347 348%D \macros 349%D {blankspace} 350%D 351%D Here is how this works. The \type {\let} primitive first picks up the to be let 352%D name. Then it scans for an optional equal and when that is found it will skip the 353%D next space, which is why we need an extra one to achieve our goal. Such a \type 354%D {\blankspace} has the meaning \typ {blank space}. A typical \TEX ie definition: 355 356\normalexpanded{\permanent\let\noexpand\blankspace=\space\space} 357 358%D this is the same as \type {\relaxedspace} in \type {syst-ini.mkxl} but we had 359%D it (name) from the start of \CONTEXT\ so we keep it; it is also a name used in 360%D logging. 361 362%D \macros 363%D {setvalue,setgvalue,setevalue,setxvalue, 364%D letvalue,letgvalue,getvalue,resetvalue, 365%D undefinevalue,ignorevalue} 366%D 367%D \TEX's primitive \type {\csname} can be used to construct all kind of commands 368%D that cannot be defined with \type {\def} and \type {\let}. Every macro programmer 369%D sooner or later wants macros like these. 370%D 371%D \starttyping 372%D \setvalue {name}{...} = \def\name{...} 373%D \setgvalue {name}{...} = \gdef\name{...} 374%D \setevalue {name}{...} = \edef\name{...} 375%D \setxvalue {name}{...} = \xdef\name{...} 376%D \letvalue {name}=\... = \let\name=\... 377%D \letgvalue {name}=\... = \glet\name=\... 378%D \getvalue {name} = \name 379%D \resetvalue {name} = \def\name{} 380%D \stoptyping 381%D 382%D As we will see, \CONTEXT\ uses these commands many times, which is mainly due to 383%D its object oriented and parameter driven character. 384 385\permanent\def\setvalue #1{\defcsname #1\endcsname} 386\permanent\def\setgvalue #1{\gdefcsname#1\endcsname} 387\permanent\def\setevalue #1{\edefcsname#1\endcsname} 388\permanent\def\setxvalue #1{\xdefcsname#1\endcsname} 389\permanent\def\getvalue #1{\begincsname#1\endcsname} 390\permanent\def\letvalue #1{\letcsname #1\endcsname} 391\permanent\def\letgvalue #1{\gletcsname#1\endcsname} 392\permanent\def\resetvalue #1{\letcsname#1\endcsname\empty} 393\permanent\def\undefinevalue#1{\letcsname#1\endcsname\undefined} 394\permanent\def\ignorevalue#1#2{\letcsname#1\endcsname\empty} 395 396\permanent\def\setuvalue #1{\protected\defcsname #1\endcsname} 397\permanent\def\setuevalue #1{\protected\edefcsname#1\endcsname} 398\permanent\def\setugvalue #1{\protected\gdefcsname#1\endcsname} 399\permanent\def\setuxvalue #1{\protected\xdefcsname#1\endcsname} 400 401\permanent\protected\def\getuvalue#1{\begincsname#1\endcsname} 402 403%D \macros 404%D {globallet,glet} 405%D 406%D In \CONTEXT\ of May 2000 using \type {\globallet} instead of the two tokens will 407%D save us some $300\times4=1200$ bytes of format file on a 32~bit system. Not that 408%D it matters much today. But nowadays we can just alias to a primitive: 409 410\aliased\let\globallet\glet 411 412%D \macros 413%D {doifundefined,doifdefined, 414%D doifundefinedelse,doifdefinedelse, 415%D doifalldefinedelse} 416%D 417%D The standard way of testing if a macro is defined is comparing its meaning with 418%D another undefined one, usually \type {\undefined}. To garantee correct working of 419%D the next set of macros, \type {\undefined} may never be defined! 420%D 421%D \starttyping 422%D \doifundefined {string} {...} 423%D \doifdefined {string} {...} 424%D \doifundefinedelse {string} {then ...} {else ...} 425%D \doifdefinedelse {string} {then ...} {else ...} 426%D \doifalldefinedelse {commalist} {then ...} {else ...} 427%D \stoptyping 428%D 429%D Every macroname that \TEX\ builds gets an entry in the hash table, which is of 430%D limited size. It is expected that \ETEX\ will offer a less memory||consuming 431%D alternative. 432%D 433%D Although it will probably never be a big problem, it is good to be aware of the 434%D difference between testing on a macro name to be build by using \type {\csname} and 435%D \type {\endcsname} and testing the \type {\name} directly. 436%D 437%D \starttyping 438%D \expandafter\ifrelax\csname NameA\endcsname ... \else ... \fi 439%D 440%D \ifundefined\NameB ... \else ... \fi 441%D \stoptyping 442%D 443%D Suppression of errors while constructing a control sequence is one of the (few) 444%D things that I remember being discussed in the perspective of \ETEX\ but it was 445%D rejected because it was not considered useful. Well, in \LUATEX\ we can surpress 446%D it and that is what we do in \MKIV. We're probably the only macro package that 447%D needs it. That kind of configuration happens elsewhere. These macros are (mostly 448%D for historic reasons) fully expandable. 449 450\permanent\def\doifelseundefined#1% 451 {\ifcsname#1\endcsname 452 \expandafter\secondoftwoarguments\else\expandafter\firstoftwoarguments 453 \fi} 454 455\permanent\def\doifelsedefined#1% 456 {\ifcsname#1\endcsname 457 \expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments 458 \fi} 459 460\permanent\def\doifundefined#1% 461 {\ifcsname#1\endcsname 462 \expandafter\gobbleoneargument\else\expandafter\firstofoneargument 463 \fi} 464 465\permanent\def\doifdefined#1% 466 {\ifcsname#1\endcsname 467 \expandafter\firstofoneargument\else\expandafter\gobbleoneargument 468 \fi} 469 470\aliased\let\doifundefinedelse\doifelseundefined 471\aliased\let\doifdefinedelse \doifelsedefined 472 473%D \macros 474%D {letbeundefined} 475%D 476%D Testing for being undefined comes down to testing on \type {\relax} when we use 477%D \type {\csname}, but when using \type {\ifx}, we test on being \type 478%D {\undefined}! In \ETEX\ we have \type {\ifcsname} and that way of testing on 479%D existance is not the same as the one described here. Therefore we introduce: 480 481\permanent\protected\def\letbeundefined#1% 482 {\letcsname#1\endcsname\undefined} 483 484\permanent\protected\def\localundefine#1% conditional 485 {\ifcsname#1\endcsname\letcsname#1\endcsname\undefined\fi} 486 487\permanent\protected\def\globalundefine#1% conditional 488 {\ifcsname#1\endcsname\gletcsname#1\endcsname\undefined\fi} 489 490%D Beware, being \type {\undefined} in \ETEX\ means that the macro {\em is} defined! 491%D 492%D When we were developing the scientific units module, we encountered different 493%D behavior in text and math mode, which was due to this grouping subtilities. We 494%D therefore decided to use \type {\begingroup} instead of \type {\bgroup}. 495%D 496%D \stoptyping 497%D \doifelsealldefined {foo,ofo} {YES}{NOP} 498%D \doifelseallundefined{foo,ofo} {YES}{NOP} 499%D \doifelsealldefined {relax,ofo}{YES}{NOP} 500%D \doifelseallundefined{foo,relax}{YES}{NOP} 501%D \stoptyping 502 503\def\syst_helpers_do_if_all_defined_else#1% 504 {\ifcsname#1\endcsname\else 505 \donefalse 506 \expandafter\quitcommalist % added 507 \fi} 508 509\def\syst_helpers_do_if_all_undefined_else#1% 510 {\ifcsname#1\endcsname 511 \donefalse 512 \expandafter\quitcommalist 513 \fi} 514 515\permanent\protected\def\syst_helpers_do_if_all_else#1#2% 516 {\begingroup 517 \donetrue % we could use a reserved one and avoid the group 518 \processcommalist[#2]#1% 519 \ifdone 520 \endgroup\expandafter\firstoftwoarguments 521 \else 522 \endgroup\expandafter\secondoftwoarguments 523 \fi} 524 525\permanent\protected\def\doifelsealldefined {\syst_helpers_do_if_all_else\syst_helpers_do_if_all_defined_else} 526\permanent\protected\def\doifelseallundefined{\syst_helpers_do_if_all_else\syst_helpers_do_if_all_undefined_else} 527 528\aliased\let\doifalldefinedelse \doifelsealldefined 529\aliased\let\doifallundefinedelse\doifelseallundefined 530 531%D \macros 532%D {doif,doifelse,doifnot} 533%D 534%D Programming in \TEX\ differs from programming in procedural languages like 535%D \MODULA. This means that one --- well, let me speek for myself --- tries to do 536%D the things in the well known way. Therefore the next set of \type {ifthenelse} 537%D commands were between the first ones we needed. A few years later, the opposite 538%D became true: when programming in \MODULA, I sometimes miss handy things like 539%D grouping, runtime redefinition, expansion etc. While \MODULA\ taught me to 540%D structure, \TEX\ taught me to think recursive. 541%D 542%D \starttyping 543%D \doif {string1} {string2} {...} 544%D \doifnot {string1} {string2} {...} 545%D \doifelse {string1} {string2} {then ...}{else ...} 546%D \stoptyping 547%D 548%D Again, we use some of the new primitives in \LUAMETATEX. Using straightforward 549%D \type {\edef}'s and \type {\ifx} comparison works as well, but this saves tokens 550%D and, more important, tracing clutter. 551 552\permanent\protected\def\doifelse#1#2% 553 {\iftok{#1}{#2}% 554 \expandafter\firstoftwoarguments 555 \else 556 \expandafter\secondoftwoarguments 557 \fi} 558 559\permanent\protected\def\doif#1#2% 560 {\iftok{#1}{#2}% 561 \expandafter\firstofoneargument 562 \else 563 \expandafter\gobbleoneargument 564 \fi} 565 566\permanent\protected\def\doifnot#1#2% 567 {\iftok{#1}{#2}% 568 \expandafter\gobbleoneargument 569 \else 570 \expandafter\firstofoneargument 571 \fi} 572 573%D \macros 574%D {doifempty,doifemptyelse,doifnotempty} 575%D 576%D We complete our set of conditionals with: 577%D 578%D \starttyping 579%D \doifempty {string} {...} 580%D \doifnotempty {string} {...} 581%D \doifemptyelse {string} {then ...} {else ...} 582%D \stoptyping 583%D 584%D This time, the string is not expanded, but we use the dedicated empty checker 585%D here. 586 587\permanent\protected\def\doifelseempty#1% 588 {\def\m_syst_string_one{#1}% 589 \ifempty\m_syst_string_one 590 \expandafter\firstoftwoarguments 591 \else 592 \expandafter\secondoftwoarguments 593 \fi} 594 595% or (test this): 596% 597% \permanent\protected\def\processallactionsinset[#1]% 598% {\ifempty{#1}% 599% \expandafter\processaction 600% \else 601% \expandafter\syst_helpers_process_all_actions_in_set_indeed 602% \fi[#1]} 603 604\aliased\let\doifemptyelse\doifelseempty 605 606\permanent\protected\def\doifempty#1% 607 {\def\m_syst_string_one{#1}% 608 \ifempty\m_syst_string_one 609 \expandafter\firstofoneargument 610 \else 611 \expandafter\gobbleoneargument 612 \fi} 613 614\permanent\protected\def\doifnotempty#1% 615 {\def\m_syst_string_one{#1}% 616 \ifempty\m_syst_string_one 617 \expandafter\gobbleoneargument 618 \else 619 \expandafter\firstofoneargument 620 \fi} 621 622%D \macros 623%D {doifinset,doifnotinset,doifinsetelse} 624%D 625%D We can check if a string is present in a comma separated set of strings. 626%D Depending on the result, some action is taken. 627%D 628%D \starttyping 629%D \doifinset {string} {string,...} {...} 630%D \doifnotinset {string} {string,...} {...} 631%D \doifinsetelse {string} {string,...} {then ...} {else ...} 632%D \stoptyping 633 634% !0nop=\doifinsetelse{ccc}{,}{yes}{nop} 635% !0nop=\doifinsetelse{ccc}{,,}{yes}{nop} 636% !0nop=\doifinsetelse{ccc}{,,,}{yes}{nop} 637 638% !1nop=\doifinsetelse{}{}{yes}{nop} 639% !2yes=\doifinsetelse{aaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop} 640% !3nop=\doifinsetelse{aaa}{bbb}{yes}{nop} 641% !4yes=\doifinsetelse{aaa}{aaa}{yes}{nop} 642% !5nop=\doifinsetelse{aaaa}{bbb,ccc,ddd,aaa,eee}{yes}{nop} 643% !6nop=\doifinsetelse{}{}{yes}{nop} 644% !7nop=\doifinsetelse{}{aaa}{yes}{nop} 645% !8nop=\doifinsetelse{aaa}{}{yes}{nop} 646 647% !1=\doifinset{}{}{yes} 648% !2yes=\doifinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes} 649% !3=\doifinset{aaa}{bbb}{yes} 650% !4yes=\doifinset{aaa}{aaa}{yes} 651% !5=\doifinset{}{}{yes} 652% !6=\doifinset{aaa}{}{yes} 653 654% !1yes=\doifnotinset{}{}{yes} 655% !2=\doifnotinset{aaa}{bbb,ccc,ddd,aaa,eee}{yes} 656% !3yes=\doifnotinset{aaa}{bbb}{yes} 657% !4=\doifnotinset{aaa}{aaa}{yes} 658% !5yes=\doifnotinset{}{}{yes} 659% !6yes=\doifnotinset{aaa}{}{yes} 660 661%D These are normally only used for keywords, i.e.\ strings so we can delegate 662%D the work to \LUA: 663 664%protected\def\doifelseinset#1#2{\clf_doifelseinset{#1}{#2}} 665%protected\def\doifinset #1#2{\clf_doifinset {#1}{#2}} 666%protected\def\doifnotinset #1#2{\clf_doifnotinset {#1}{#2}} 667% % \let\firstinset \clf_firstinset 668 669% These don't accept spaces after commas: 670% 671% \protected\def\doifelseinset#1#2% 672% {\ifhasxtoks{,#1,}{,#2,}% 673% \expandafter\firstoftwoarguments 674% \else 675% \expandafter\secondoftwoarguments 676% \fi} 677 678% \protected\def\doifinset#1#2% 679% {\ifhasxtoks{,#1,}{,#2,}% 680% \expandafter\firstofoneargument 681% \else 682% \expandafter\gobbleoneargument 683% \fi} 684 685% \protected\def\doifnotinset#1#2% 686% {\ifhasxtoks{,#1,}{,#2,}% 687% \expandafter\gobbleoneargument 688% \else 689% \expandafter\firstofoneargument 690% \fi} 691 692% But these do: 693 694\immutable\edef\a!comma{\expandtoken \ignorecatcode \commaasciicode} 695\immutable\edef\a!space{\expandtoken \ignorecatcode \spaceasciicode} 696 697\normalexpanded { 698 699 \permanent \protected \def \noexpand \doifelseinset#1#2% 700 {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}% 701 \noexpand\expandafter\noexpand\firstoftwoarguments 702 \noexpand\else 703 \noexpand\expandafter\noexpand\secondoftwoarguments 704 \noexpand\fi} 705 706 \permanent \protected \def \noexpand \doifinset#1#2% 707 {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}% 708 \noexpand\expandafter\noexpand\firstofoneargument 709 \noexpand\else 710 \noexpand\expandafter\noexpand\gobbleoneargument 711 \noexpand\fi} 712 713 \permanent \protected \def \noexpand \doifnotinset#1#2% 714 {\noexpand\ifhasxtoks{,\a!space#1,}{,#2,}% 715 \noexpand\expandafter\noexpand\gobbleoneargument 716 \noexpand\else 717 \noexpand\expandafter\noexpand\firstofoneargument 718 \noexpand\fi} 719 720} 721 722%D Done. 723 724\aliased\let\doifinsetelse\doifelseinset 725 726%D \macros 727%D {doifcommon,doifnotcommon,doifcommonelse} 728%D 729%D Probably the most time consuming tests are those that test for overlap in sets 730%D of strings. 731%D 732%D \starttyping 733%D \doifcommon {string,...} {string,...} {...} 734%D \doifnotcommon {string,...} {string,...} {...} 735%D \doifcommonelse {string,...} {string,...} {then ...} {else ...} 736%D \stoptyping 737 738% !1yes=\doifcommonelse{aaa,bbb,ccc}{aaa,bbb,ccc}{yes}{nop} 739% !2nop=\doifcommonelse{aaa,bbb,ccc}{ddd,eee,fff}{yes}{nop} 740% !3nop=\doifcommonelse{aaa}{ddd,eee,fff}{yes}{nop} 741% !4yes=\doifcommonelse{aaa}{aaa}{yes}{nop} 742% !5nop=\doifcommonelse{bbb}{aaa}{yes}{nop} 743% !6nop=\doifcommonelse{}{aaa,bbb,ccc}{yes}{nop} 744% !7nop=\doifcommonelse{aaa,bbb,ccc}{}{yes}{nop} 745% !8nop=\doifcommonelse{}{}{yes}{nop} 746 747% !9nop=\doifcommonelse{,,}{,,}{yes}{nop} 748% !9yes=\doifcommonelse{,a,}{,a,}{yes}{nop} 749% !9yes=\doifcommonelse{,,a,}{,a,}{yes}{nop} 750% !9yes=\doifcommonelse{,a,}{,,a,}{yes}{nop} 751% !9yes=\doifcommonelse{,a,}{,,,a,}{yes}{nop} 752% !9yes=\doifcommonelse{,,a,}{,,,a,}{yes}{nop} 753 754% \permanent\protected\def\doifelsecommon#1#2{\clf_doifelsecommon{#1}{#2}} % todo: define in lua 755% \permanent\protected\def\doifcommon #1#2{\clf_doifcommon {#1}{#2}} % todo: define in lua 756% \permanent\protected\def\doifnotcommon #1#2{\clf_doifnotcommon {#1}{#2}} % todo: define in lua 757 758\permanent\protected\def\doifelsecommon#1#2{{#1}{#2}\expandafter\firstoftwoarguments\else\expandafter\secondoftwoarguments\fi} 759\permanent\protected\def\doifcommon #1#2{{#1}{#2}\expandafter\firstofoneargument \else\expandafter\gobbleoneargument \fi} 760\permanent\protected\def\doifnotcommon #1#2{{#1}{#2}\expandafter\gobbleoneargument \else\expandafter\firstofoneargument \fi} 761 762\aliased\let\doifcommonelse\doifelsecommon 763 764%D \macros 765%D {processcommalist,processcommacommand,quitcommalist, 766%D processcommalistwithparameters} 767%D 768%D We've already seen some macros that take care of comma separated lists. Such 769%D list can be processed with 770%D 771%D \starttyping 772%D \processcommalist[string,string,...]\commando 773%D \stoptyping 774%D 775%D The user supplied command \type{\commando} receives one argument: the string. 776%D This command permits nesting and spaces after commas are skipped. Empty sets 777%D are no problem. 778%D 779%D \startbuffer 780%D \def\dosomething#1{(#1)} 781%D 782%D 1: \processcommalist [\hbox{$a,b,c,d,e,f$}] \dosomething \par 783%D 2: \processcommalist [{a,b,c,d,e,f}] \dosomething \par 784%D 3: \processcommalist [{a,b,c},d,e,f] \dosomething \par 785%D 4: \processcommalist [a,b,{c,d,e},f] \dosomething \par 786%D 5: \processcommalist [a{b,c},d,e,f] \dosomething \par 787%D 6: \processcommalist [{a,b}c,d,e,f] \dosomething \par 788%D 7: \processcommalist [] \dosomething \par 789%D 8: \processcommalist [{[}] \dosomething \par 790%D \stopbuffer 791%D 792%D \typebuffer 793%D 794%D Or expanded: 795%D 796%D \blank \getbuffer \blank 797%D 798%D The original definitions can be found elsewhere and need a bit more code. In case of 799%D issues, consult the \MKIV\ variant or previous \LMTX\ variants in the repository. 800%D 801%D When a list is saved in a macro, we can use a construction like: 802%D 803%D \starttyping 804%D \expandafter\processcommalist\expandafter[\list]\command 805%D \stoptyping 806%D 807%D Such solutions suit most situations, but we wanted a bit more. 808%D 809%D \starttyping 810%D \processcommacommand[string,\stringset,string]\commando 811%D \stoptyping 812%D 813%D where \type{\stringset} is a predefined set, like: 814%D 815%D \starttyping 816%D \def\first{aap,noot,mies} 817%D \def\second{laatste} 818%D 819%D \processcommacommand[\first]\message 820%D \processcommacommand[\first,second,third]\message 821%D \processcommacommand[\first,between,\second]\message 822%D \stoptyping 823%D 824%D Commands that are part of the list are expanded, so the use of this macro has its 825%D limits. We use a new \LUAMETATEX\ feature that intercepts invalid macro arguments 826%D by quitting when \type {\ignorearguments} is seen. That's why we act on the 827%D arguments state. Again it permits leaner and meaner macro definitions with a bit 828%D less clutter in tracing. 829 830\mutable\lettonothing\commalistcommand 831 832% \protected\def\syst_helpers_process_comma_item#+,% 833% {\ifarguments 834% \expandafter\syst_helpers_process_comma_item_gobble 835% \orelse\ifparameter#1\or 836% \commalistcommand{#1}% 837% \expandafter\syst_helpers_process_comma_item_next 838% \else 839% \expandafter\syst_helpers_process_comma_item_gobble 840% \fi} 841 842% \def\syst_helpers_process_comma_item_next 843% {\expandafterspaces\syst_helpers_process_comma_item} 844 845% \protected\def\processcommalist[#1]#2% 846% {\pushmacro\commalistcommand 847% \def\commalistcommand{#2}% 848% \expandafterspaces\syst_helpers_process_comma_item#1,\ignorearguments\ignorearguments\ignorearguments 849% \popmacro\commalistcommand} 850 851% \protected\def\processcommacommand[#1]#2% 852% {\pushmacro\commalistcommand 853% \def\commalistcommand{#2}% 854% \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_comma_item#1,}\ignorearguments\ignorearguments\ignorearguments 855% \popmacro\commalistcommand} 856 857% \permanent\protected\def\processcommalist[#*#+]#2% 858% {\pushmacro\commalistcommand 859% \def\commalistcommand{#2}% 860% \expandafterspaces\syst_helpers_process_comma_item#1,\ignorearguments\ignorearguments\ignorearguments 861% \popmacro\commalistcommand} 862% 863% \permanent\protected\def\processcommacommand[#*#+]#2% 864% {\pushmacro\commalistcommand 865% \def\commalistcommand{#2}% 866% \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_comma_item#1,}\ignorearguments\ignorearguments\ignorearguments 867% \popmacro\commalistcommand} 868 869\tolerant\protected\def\syst_helpers_process_comma_item#*#1,% 870 {\ifarguments\or 871 \commalistcommand{#1}% 872 \expandafter\syst_helpers_process_comma_item_next 873 \fi} 874 875\def\syst_helpers_process_comma_item_next 876 {\expandafterspaces\syst_helpers_process_comma_item} 877 878\permanent\protected\def\processcommalist[#1]#2% 879 {\pushmacro\commalistcommand 880 \def\commalistcommand{#2}% 881 \syst_helpers_process_comma_item#1\ignorearguments\ignorearguments\ignorearguments 882 \popmacro\commalistcommand} 883 884\permanent\protected\def\processcommacommand[#1]#2% 885 {\pushmacro\commalistcommand 886 \def\commalistcommand{#2}% 887 \normalexpanded{\syst_helpers_process_comma_item#1}\ignorearguments\ignorearguments\ignorearguments 888 \popmacro\commalistcommand} 889 890% \let\syst_helpers_process_comma_item_next_a \syst_helpers_process_comma_item_next 891% \def\syst_helpers_process_comma_item_next_b#-\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_a} 892% \def\syst_helpers_process_comma_item_next_c#-\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b} 893% \def\syst_helpers_process_comma_item_gobble#-\ignorearguments{} 894 895\let\syst_helpers_process_comma_item_next_a \syst_helpers_process_comma_item_next 896\def\syst_helpers_process_comma_item_next_b#-\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_a} 897\def\syst_helpers_process_comma_item_next_c#-\ignorearguments{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b} 898\def\syst_helpers_process_comma_item_gobble#-\ignorearguments{} 899 900\permanent\protected\def\quitcommalist {\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_b} 901\permanent\protected\def\quitprevcommalist{\let\syst_helpers_process_comma_item_next\syst_helpers_process_comma_item_next_c} 902 903%D \startbuffer 904%D \def\foo#1{(#1)} 905%D <\processcommalist[a,b,c,d]\foo> 906%D 907%D \def\foo#1{(#1)} 908%D \def\oof#1{<\processcommalist[#1]\foo>} 909%D <\processcommalist[{a,b},{c,d}]\oof> 910%D 911%D \def\foo#1{(#1)\quitcommalist} 912%D <\processcommalist[a,b,c,d]\foo> 913%D 914%D \def\foo#1{(#1)} 915%D \def\oof#1{<\processcommalist[#1]\foo\quitcommalist>} 916%D <\processcommalist[{a,b},{c,d}]\oof> 917%D 918%D \def\foo#1{(#1)\quitcommalist} 919%D \def\oof#1{<\processcommalist[#1]\foo>} 920%D <\processcommalist[{a,b},{c,d},{e,f}]\oof> 921%D 922%D \def\foo#1{(#1)\quitprevcommalist} 923%D \def\oof#1{<\processcommalist[#1]\foo>} 924%D <\processcommalist[{a,b},{c,d},{e,f}]\oof> 925%D \stopbuffer 926%D 927%D \typebuffer \blank \getbuffer \blank 928 929%D The argument to \type{\command} is not delimited. Because we often use \type {[]} 930%D as delimiters, we also have: 931%D 932%D \starttyping 933%D \processcommalistwithparameters[string,string,...]\command 934%D \stoptyping 935%D 936%D where \type{\command} looks like: 937%D 938%D \starttyping 939%D \def\command[#1]{... #1 ...} 940%D \stoptyping 941 942\let\syst_helpers_do_process_comma_list_with_parameters\gobbleoneargument 943 944\permanent\protected\def\processcommalistwithparameters[#1]#2% 945 {\def\syst_helpers_do_process_comma_list_with_parameters##1{#2[##1]}% 946 \processcommalist[#1]\syst_helpers_do_process_comma_list_with_parameters} 947 948%D \macros 949%D {startprocesscommalist,startprocesscommacommand} 950%D 951%D Two more: 952 953\let\syst_helpers_comma_list_step\relax 954 955\mutable\lettonothing\currentcommalistitem 956 957\permanent\protected\def\startprocesscommalist[#1]#2\stopprocesscommalist 958 {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}% 959 \processcommalist[#1]\syst_helpers_comma_list_step} 960 961\permanent\protected\def\startprocesscommacommand[#1]#2\stopprocesscommacommand 962 {\def\syst_helpers_comma_list_step##1{\def\currentcommalistitem{##1}#2}% 963 \normalexpanded{\processcommalist[#1]}\syst_helpers_comma_list_step} 964 965\permanent\protected\lettonothing\stopprocesscommalist 966\permanent\protected\lettonothing\stopprocesscommacommand 967 968%D \macros 969%D {processaction, 970%D processfirstactioninset, 971%D processallactionsinset} 972%D 973%D \CONTEXT\ makes extensive use of a sort of case or switch command. Depending of 974%D the presence of one or more provided items, some actions is taken. These macros 975%D can be nested without problems. 976%D 977%D \starttyping 978%D \processaction [x] [x=>a,y=>b,z=>c] 979%D \processfirstactioninset [x,y,z] [x=>a,y=>b,z=>c] 980%D \processallactionsinset [x,y,z] [x=>a,y=>b,z=>c] 981%D \stoptyping 982%D 983%D We can supply both a \type {default} action and an action to be undertaken when 984%D an \type {unknown} value is met: 985%D 986%D \starttyping 987%D \processallactionsinset 988%D [x,y,z] 989%D [ a=>\a, 990%D b=>\b, 991%D c=>\c, 992%D default=>\default, 993%D unknown=>\unknown{... \commalistelement ...}] 994%D \stoptyping 995%D 996%D When \type {#1} is empty, this macro scans list \type {#2} for the keyword \type 997%D {default} and executed the related action if present. When \type {#1} is non 998%D empty and not in the list, the action related to \type {unknown} is executed. 999%D Both keywords must be at the end of list \type{#2}. Afterwards, the actually 1000%D found keyword is available in \type {\commalistelement}. An advanced example of 1001%D the use of this macro can be found in \PPCHTEX, where we completely rely on \TEX\ 1002%D for interpreting user supplied keywords like \type {SB}, \type {SB1..6}, \type 1003%D {SB125} etc. 1004%D 1005%D In the meantime we follow a different approach, often somewhat more heavy on the 1006%D number of control sequences used, but that is no lomger a real issue. The code 1007%D has been simplified and nwo uses the macro stack mechanism. If needed I can make 1008%D this more hip and a bit faster now but \unknown\ it's seldom used nowadays as we 1009%D have better ways now. 1010 1011\mutable\lettonothing\commalistelement 1012 1013\lettonothing\m_syst_string_one 1014\lettonothing\m_syst_string_two 1015 1016\let\syst_helpers_do_compare_process_action\relax 1017 1018\protected\def\syst_helpers_do_compare_process_action_a[#1=>#2][#3]% 1019 {\edef\m_syst_string_two{#1}% 1020 \ifx\m_syst_string_two\s!default 1021 \lettonothing\commalistelement 1022 #2% 1023 \fi} 1024 1025\protected\def\syst_helpers_do_compare_process_action_b[#1=>#2][#3]% 1026 {\edef\m_syst_string_two{#1}% 1027 \ifx\m_syst_string_one\m_syst_string_two 1028 \def\commalistelement{#3}% 1029 #2% 1030 \expandafter\quitcommalist 1031 \orelse\ifx\m_syst_string_two\s!unknown 1032 \def\commalistelement{#3}% beware of loops 1033 #2% 1034 \fi} 1035 1036\permanent\protected\def\processaction[#1]#2[% 1037 {\edef\m_syst_string_one{#1}% 1038 \ifempty\m_syst_string_one 1039 \let\syst_helpers_do_compare_process_action\syst_helpers_do_compare_process_action_a 1040 \else 1041 \let\syst_helpers_do_compare_process_action\syst_helpers_do_compare_process_action_b 1042 \fi 1043 \edef\syst_helpers_do_process_action##1{\syst_helpers_do_compare_process_action[##1][#1]}% expands #1 1044 \processnextcommalist\syst_helpers_do_process_action[} 1045 1046\protected\def\syst_helpers_do_compare_process_action_c[#1=>#2][#3]% 1047 {\edef\m_syst_string_one{#1}% 1048 \edef\m_syst_string_two{#3}% 1049 \ifx\m_syst_string_one\m_syst_string_two 1050 \def\commalistelement{#3}% 1051 #2% 1052 \expandafter\quitprevcommalist 1053 \else 1054 \edef\m_syst_string_one{#1}% 1055 \ifx\m_syst_string_one\s!unknown 1056 \def\commalistelement{#3}% 1057 #2% 1058 \fi 1059 \fi} 1060 1061\permanent\protected\def\processfirstactioninset[#1]% 1062 {\edef\m_syst_string_one{#1}% 1063 \ifempty\m_syst_string_one 1064 \expandafter\processaction 1065 \else 1066 \expandafter\syst_helpers_process_first_action_in_set_indeed 1067 \fi 1068 [#1]} 1069 1070\let\syst_helpers_do_process_action \gobbleoneargument 1071\let\syst_helpers_do_do_process_action\gobbleoneargument 1072 1073\tolerant\protected\def\syst_helpers_process_first_action_in_set_indeed[#1]#*[#2]% 1074 {\def\syst_helpers_do_process_action##1% 1075 {\def\syst_helpers_do_do_process_action####1{\syst_helpers_do_compare_process_action_c[####1][##1]}% 1076 \processcommalist[#2]\syst_helpers_do_do_process_action}% 1077 \normalexpanded{\processcommalist[#1]}\syst_helpers_do_process_action} 1078 1079\protected\def\syst_helpers_do_compare_process_action_d[#1=>#2][#3]% 1080 {\edef\m_syst_string_one{#1}% 1081 \edef\m_syst_string_two{#3}% 1082 \ifx\m_syst_string_one\m_syst_string_two 1083 \def\commalistelement{#3}% 1084 #2% 1085 \expandafter\quitcommalist 1086 \else 1087 \edef\m_syst_string_one{#1}% 1088 \ifx\m_syst_string_one\s!unknown 1089 \def\commalistelement{#3}% 1090 #2% 1091 \fi 1092 \fi} 1093 1094\let\syst_process_action_in_set_all\relax 1095 1096\tolerant\protected\def\syst_helpers_process_all_actions_in_set_indeed[#1]#*[#2]% 1097 {\globalpushmacro\syst_process_action_in_set_all 1098 \def\syst_process_action_in_set##1% 1099 {\def\syst_process_action_in_set_one####1{\syst_helpers_do_compare_process_action_d[####1][##1]}% 1100 \processcommalist[#2]\syst_process_action_in_set_one}% 1101 \processcommacommand[#1]\syst_process_action_in_set 1102 \globalpopmacro\syst_process_action_in_set_all} 1103 1104\permanent\protected\def\processallactionsinset[#1]% 1105 {\edef\m_syst_string_one{#1}% 1106 \ifempty\m_syst_string_one 1107 \expandafter\processaction 1108 \else 1109 \expandafter\syst_helpers_process_all_actions_in_set_indeed 1110 \fi[#1]} 1111 1112%D These macros use: 1113 1114% \protected\def\processnextcommalist#1[#2#3]% 1115% {\pushmacro\commalistcommand 1116% \def\commalistcommand{#1}% 1117% \expandafterspaces\syst_helpers_process_comma_item#2#3\ignorearguments\ignorearguments\ignorearguments 1118% \popmacro\commalistcommand} 1119 1120\permanent\protected\def\processnextcommalist#1[#2#3]% watch out: mkiv has two extra initial arguments 1121 {\pushmacro\commalistcommand 1122 \def\commalistcommand{#1}% 1123 \expandafterspaces\syst_helpers_process_comma_item#2#3\ignorearguments\ignorearguments\ignorearguments 1124 \popmacro\commalistcommand} 1125 1126%D \macros 1127%D {getfirstcharacter, firstcharacter, remainingcharacters, doiffirstcharacter} 1128%D 1129%D Sometimes the action to be undertaken depends on the next character. This macro 1130%D get this character and puts it in \type {\firstcharacter}. 1131%D 1132%D \starttyping 1133%D \getfirstcharacter {string} 1134%D \stoptyping 1135%D 1136%D A two step expansion is used to prevent problems with complicated arguments, for 1137%D instance arguments that consist of two or more expandable tokens. 1138 1139\mutable\lettonothing\firstcharacter 1140\mutable\lettonothing\remainingcharacters 1141 1142\permanent\protected\def\getfirstcharacter #1{{#1}} 1143\permanent\protected\def\doifelsefirstchar #1#2{{#1}{#2}} 1144\permanent\protected\def\thefirstcharacter #1{{#1}} 1145\permanent\protected\def\theremainingcharacters#1{{#1}} 1146 1147\aliased\let\doiffirstcharelse\doifelsefirstchar 1148 1149%D \macros 1150%D {doifinstringelse, doifincsnameelse} 1151%D 1152%D We can check for the presence of a substring in a given sequence of characters. 1153%D 1154%D \starttyping 1155%D \doifinstringelse {substring} {string} {then ...} {else ...} 1156%D \stoptyping 1157%D 1158%D \startbuffer 1159%D \doifinstringelse{abc}{foo bar abc}{Y}{N}=Y\par 1160%D \doifinstringelse{abc}{foo bar cab}{Y}{N}=N\par 1161%D \doifinstring {abc}{foo bar abc}{Y}=Y\par 1162%D \doifinstring {abc}{foo bar cab}{Y}\par 1163%D \doifnotinstring {abc}{foo bar abc}{Y}\par 1164%D \doifnotinstring {abc}{foo bar cab}{Y}=Y\par 1165%D \doifinstringelse{}{foo bar abc}{Y}{N}=N\par 1166%D \doifinstring {}{foo bar abc}{N}\par 1167%D \doifnotinstring {}{foo bar abc}{Y}=Y\par 1168%D \doifinstringelse{x}{}{Y}{N}=N\par 1169%D \doifinstring {x}{}{N}\par 1170%D \doifnotinstring {x}{}{Y}=Y\par 1171%D \doifinstringelse{}{}{Y}{N}=N\par 1172%D \doifinstring {}{}{N}\par 1173%D \doifnotinstring {}{}{Y}=Y\par 1174%D \stopbuffer 1175%D 1176%D \typebuffer \blank \getbuffer \blank 1177 1178%D I keep the following as example code: 1179 1180% \let\syst_helpers_do_do_if_in_string_else\relax 1181% \let\syst_helpers_do_do_if_in_string \relax 1182% \let\syst_helpers_do_do_if_not_in_string \relax 1183% 1184% \lettonothing\m_syst_sub_string 1185% 1186% \protected\def\doifelseinstring#1% 1187% {\edef\m_syst_sub_string{#1}% expand #1 here 1188% \ifempty\m_syst_sub_string 1189% \expandafter\thirdofthreearguments 1190% \else 1191% \expandafter\syst_helpers_do_if_in_string_else 1192% \fi} 1193% 1194% \let\doifinstringelse\doifelseinstring 1195% 1196% \protected\def\syst_helpers_do_if_in_string_else#1% ##2 can be {abc} 1197% {\normalexpanded{\protected\def\syst_helpers_do_do_if_in_string_else##1\m_syst_sub_string##2}% 1198% {\ifarguments 1199% \or 1200% \expandafter\syst_helpers_do_if_in_string_else_nop 1201% \or 1202% \expandafter\syst_helpers_do_if_in_string_else_yes 1203% \fi}% 1204% \normalexpanded{\syst_helpers_do_do_if_in_string_else#1}\s!e_o_t_token\ignorearguments\ignorearguments} 1205% 1206% \protected\def\syst_helpers_do_if_in_string_else_delimited#1% ##2 can be {abc} 1207% {\normalexpanded{\protected\def\syst_helpers_do_do_if_in_string_else##1,\m_syst_sub_string,##2}% 1208% {\ifarguments 1209% \or 1210% \expandafter\syst_helpers_do_if_in_string_else_nop 1211% \or 1212% \expandafter\syst_helpers_do_if_in_string_else_yes 1213% \fi}% 1214% \normalexpanded{\syst_helpers_do_do_if_in_string_else,#1,}\s!e_o_t_token\ignorearguments\ignorearguments} 1215% 1216% \protected\def\doifinstring#1% 1217% {\edef\m_syst_sub_string{#1}% expand #1 here 1218% \ifempty\m_syst_sub_string 1219% \expandafter\gobbletwoarguments 1220% \else 1221% \expandafter\syst_helpers_do_if_in_string 1222% \fi} 1223% 1224% \protected\def\syst_helpers_do_if_in_string#1% ##2 can be {abc} 1225% {\normalexpanded{\protected\def\syst_helpers_do_do_if_in_string##1\m_syst_sub_string##2}% 1226% {\ifarguments 1227% \or 1228% \expandafter\syst_helpers_do_if_in_string_nop 1229% \or 1230% \expandafter\syst_helpers_do_if_in_string_yes 1231% \fi}% 1232% \normalexpanded{\syst_helpers_do_do_if_in_string#1}\s!e_o_t_token\ignorearguments\ignorearguments} 1233% 1234% \protected\def\doifnotinstring#1% 1235% {\edef\m_syst_sub_string{#1}% expand #1 here 1236% \ifempty\m_syst_sub_string 1237% \expandafter\secondoftwoarguments 1238% \else 1239% \expandafter\syst_helpers_do_if_not_in_string 1240% \fi} 1241% 1242% \protected\def\syst_helpers_do_if_not_in_string#1% ##2 can be {abc} 1243% {\normalexpanded{\protected\def\syst_helpers_do_do_if_not_in_string##1\m_syst_sub_string##2}% 1244% {\ifarguments 1245% \or 1246% \expandafter\syst_helpers_do_if_not_in_string_nop 1247% \or 1248% \expandafter\syst_helpers_do_if_not_in_string_yes 1249% \fi}% 1250% \normalexpanded{\syst_helpers_do_do_if_not_in_string#1}\s!e_o_t_token\ignorearguments\ignorearguments} 1251% 1252% \def\syst_helpers_do_if_in_string_else_yes#0\ignorearguments\ignorearguments#2#0{#2} % of #- and #1 1253% \def\syst_helpers_do_if_in_string_else_nop#0\ignorearguments#0#3{#3} 1254% \def\syst_helpers_do_if_in_string_yes #0\ignorearguments\ignorearguments#2{#2} 1255% \def\syst_helpers_do_if_in_string_nop #0\ignorearguments#0{} 1256% \def\syst_helpers_do_if_not_in_string_yes #0\ignorearguments\ignorearguments#0{} 1257% \def\syst_helpers_do_if_not_in_string_nop #0\ignorearguments#2{#2} 1258 1259\permanent\protected\def\doifelseinstring#1#2% 1260 {\ifhasxtoks{#1}{#2}% 1261 \expandafter\firstoftwoarguments 1262 \else 1263 \expandafter\secondoftwoarguments 1264 \fi} 1265 1266\permanent\protected\def\doifinstring#1#2% 1267 {\ifhasxtoks{#1}{#2}% 1268 \expandafter\firstofoneargument 1269 \else 1270 \expandafter\gobbleoneargument 1271 \fi} 1272 1273\permanent\protected\def\doifnotinstring#1#2% 1274 {\ifhasxtoks{#1}{#2}% 1275 \expandafter\gobbleoneargument 1276 \else 1277 \expandafter\firstofoneargument 1278 \fi} 1279 1280\aliased\let\doifinstringelse\doifelseinstring 1281 1282%D The next one one of those variants that we used when speed was more of an issue 1283%D that today. Now we just expand the lot. We just use an alias now: 1284 1285\aliased\let\doifelseincsname\doifelseinstring 1286\aliased\let\doifincsnameelse\doifinstringelse 1287 1288%D \macros 1289%D {doifnumberelse,doifnumber,doifnotnumber} 1290%D 1291%D The next macro executes a command depending of the outcome of a test on numerals. 1292%D We now use a \LUATEX\ feature that permits a more robust checking, but you might 1293%D want to take a look at the originals. It's typically one of these new features 1294%D that probably only \CONTEXT\ will use, which is probably true for more such 1295%D features that no one ever asked for (but they are pretty generic in nature 1296%D anyway). 1297 1298\permanent\def\doifelsenumber#1% 1299 {\ifchknum#1\or 1300 \expandafter\firstoftwoarguments 1301 \else 1302 \expandafter\secondoftwoarguments 1303 \fi} 1304 1305\permanent\def\doifnumber#1% 1306 {\ifchknum#1\or 1307 \expandafter\firstoftwoarguments 1308 \else 1309 \expandafter\gobbleoneargument 1310 \fi} 1311 1312\permanent\def\doifnotnumber#1% 1313 {\ifchknum#1\or 1314 \expandafter\gobbleoneargument 1315 \else 1316 \expandafter\firstofoneargument 1317 \fi} 1318 1319\aliased\let\doifnumberelse\doifelsenumber 1320 1321%D \macros 1322%D {setpercentdimen} 1323%D 1324%D \starttyping 1325%D \scratchdimen=100pt \setpercentdimen\scratchdimen{10\letterpercent} 1326%D \scratchdimen=100pt \setpercentdimen\scratchdimen{5pt} 1327%D \scratchdimen \percentdimen \hsize {10\letterpercent} 1328%D \stoptyping 1329 1330% todo: use the push back dimen trickery 1331 1332\permanent\def\percentdimen#1#2% dimen percentage (with %) 1333 {\dimexpr{#2}{#1}\relax} 1334 1335\permanent\protected\def\setpercentdimen#1#2% dimen percentage (with %) 1336 {#1={#2}{#1}} 1337 1338%D \macros 1339%D {makerawcommalist, 1340%D rawdoinsetelse, 1341%D rawprocesscommalist, 1342%D rawprocessaction} 1343%D 1344%D Some of the commands mentioned earlier are effective but slow. When one is 1345%D desperately in need of faster alternatives and when the conditions are 1346%D predictable safe, the \type {\raw} alternatives come into focus. A major drawback 1347%D is that they do not take \type {\c!constants} into account, simply because no 1348%D expansion is done. This is no problem with \type {\rawprocesscommalist}, because 1349%D this macro does not compare anything. Expandable macros are permitted as search 1350%D string. 1351%D 1352%D \starttyping 1353%D \makerawcommalist[string,string,...]\stringlist 1354%D \rawdoifelseinset{string}{string,...}{...}{...} 1355%D \rawprocesscommalist[string,string,...]\commando 1356%D \rawprocessaction[x][a=>\a,b=>\b,c=>\c] 1357%D \stoptyping 1358%D 1359%D Spaces embedded in the list, for instance after commas, spoil the search process. 1360%D The gain in speed depends on the length of the argument (the longer the argument, 1361%D the less we gain). The question is: do we still need these raw variants? 1362 1363\let\syst_helpers_do_make_raw_comma_list\gobbleoneargument 1364 1365\permanent\protected\def\makerawcommalist[#1]#2% use \processnext ... here 1366 {\scratchtoks\emptytoks 1367 \def\syst_helpers_do_make_raw_comma_list##1{\ifempty\scratchtoks\scratchtoks{##1}\else\toksapp\scratchtoks{,##1}\fi}% 1368 \processcommalist[#1]\syst_helpers_do_make_raw_comma_list 1369 \edef#2{\the\scratchtoks}} 1370 1371% beware: in mkiv { } were lost so it was not compatible with the non raw 1372 1373\aliased\let\rawprocesscommalist \processcommalist % can go 1374\aliased\let\rawprocesscommacommand\processcommacommand % can go 1375 1376%D Here is one without nesting .. still needed? Only used for modes (no nesting 1377%D there). 1378 1379\mutable\let\fastcommalistcommand\relax 1380 1381\protected\def\syst_helpers_process_fast_comma_item#1,% 1382 {\ifarguments 1383 \expandafter\syst_helpers_process_comma_item_gobble 1384 \or 1385 \fastcommalistcommand{#1}% 1386 \expandafter\syst_helpers_process_fast_comma_item_next 1387 \fi} 1388 1389\protected\def\syst_helpers_process_fast_comma_item_next 1390 {\expandafterspaces\syst_helpers_process_fast_comma_item} 1391 1392\permanent\protected\def\fastprocesscommalist[#1]#2% 1393 {\let\fastcommalistcommand#2% 1394 \expandafterspaces\syst_helpers_process_fast_comma_item#1\ignorearguments\ignorearguments\ignorearguments} 1395 1396\permanent\protected\def\fastprocesscommacommand[#1]#2% 1397 {\let\fastcommalistcommand#2% 1398 \normalexpanded{\noexpand\expandafterspaces\syst_helpers_process_fast_comma_item#1}\ignorearguments\ignorearguments\ignorearguments} 1399 1400% \def\rawdoifelseinset#1#2{\doifinstringelse{,#1,}{,#2,}} 1401% \def\rawdoifinset #1#2{\doifinstring {,#1,}{,#2,}} 1402 1403\def\syst_helpers_do_if_else_in_set#1% 1404 {\ifhasxtoks{,\m_syst_sub_string,}{,#1,}% 1405 \expandafter\firstoftwoarguments 1406 \else 1407 \expandafter\secondoftwoarguments 1408 \fi} 1409 1410\permanent\protected\def\rawdoifelseinset#1% 1411 {\edef\m_syst_sub_string{#1}% expand #1 here 1412 \ifempty\m_syst_sub_string 1413 \expandafter\thirdofthreearguments 1414 \else 1415 \expandafter\syst_helpers_do_if_else_in_set 1416 \fi} 1417 1418\aliased\let\rawdoifinsetelse\rawdoifelseinset 1419 1420\def\syst_helpers_do_if_in_set#1% 1421 {\ifhasxtoks{,\m_syst_sub_string,}{,#1,}% 1422 \expandafter\firstofoneargument 1423 \else 1424 \expandafter\gobbleoneargument 1425 \fi} 1426 1427\permanent\protected\def\rawdoifinset#1% or just alias this one 1428 {\edef\m_syst_sub_string{#1}% expand #1 here 1429 \ifx\m_syst_sub_string\m_syst_two_commas 1430 \expandafter\gobbletwoarguments 1431 \else 1432 \expandafter\syst_helpers_do_if_in_set 1433 \fi} 1434 1435%D Some more raw material: 1436 1437% probably never used 1438% 1439% \def\syst_helpers_raw_process_action#1=>#2,% 1440% {\ifarguments 1441% %\expandafter\syst_helpers_raw_process_action_gobble 1442% \or 1443% \expandafter\syst_helpers_raw_process_action_gobble 1444% \or 1445% \edef\m_syst_string_two{#1}% 1446% \ifx\m_syst_string_one\m_syst_string_two 1447% \def\m_syst_helpers_process_action{#2}% 1448% \expandafter\expandafter\expandafter\syst_helpers_raw_process_action_gobble 1449% \else 1450% \ifx\s!unknown\m_syst_string_two 1451% \def\m_syst_helpers_process_action_unknown{#2}% 1452% \fi 1453% \expandafter\expandafter\expandafter\syst_helpers_raw_process_action_next 1454% \fi 1455% \fi} 1456% 1457% \def\syst_helpers_raw_process_action_gobble#-\ignorearguments 1458% {} 1459% 1460% \def\syst_helpers_raw_process_action_next 1461% {\expandafterspaces\syst_helpers_raw_process_action} 1462% 1463% \permanent\protected\def\xrawprocessaction[#1]#*[#2]% 1464% {\edef\m_syst_string_one{#1}% 1465% \ifempty\m_syst_string_one 1466% \let\m_syst_string_one\s!default 1467% \fi 1468% \let\m_syst_helpers_process_action\relax 1469% \let\m_syst_helpers_process_action_unknown\relax 1470% \syst_helpers_raw_process_action#2\ignorearguments\ignorearguments\ignorearguments 1471% \ifrelax\m_syst_helpers_process_action 1472% \m_syst_helpers_process_action_unknown 1473% \else 1474% \m_syst_helpers_process_action 1475% \fi} 1476 1477%D When we process the list \type {a,b,c,d,e}, the raw routine takes over 30\% less 1478%D time, when we feed $20+$ character strings we gain about 20\%. Alternatives which 1479%D use \type {\futurelet} perform worse. Part of the speedup is due to the \type 1480%D {\let} and \type {\expandafter} in the test. 1481%D 1482%D \macros 1483%D {dosetvalue,dosetevalue,dosetgvalue,docopyvalue,doresetvalue, 1484%D dogetvalue} 1485%D 1486%D When we are going to do assignments, we have to take multi||linguality into account. 1487%D For the moment we keep things simple and single||lingual. 1488%D 1489%D \starttyping 1490%D \dosetvalue {label} {variable} {value} 1491%D \dosetevalue {label} {variable} {value} 1492%D \dosetgvalue {label} {variable} {value} 1493%D \docopyvalue {to label} {from label} {variable} 1494%D \doresetvalue {label} {variable} 1495%D \stoptyping 1496%D 1497%D These macros are in fact auxiliary ones and are not meant for use outside the 1498%D assignment macros. 1499 1500\def\dosetvalue #1#2{\defcsname #1#2\endcsname} % takes #3 1501\def\dosetevalue #1#2{\edefcsname #1#2\endcsname} % takes #3 1502\def\dosetgvalue #1#2{\global\edefcsname#1#2\endcsname} % takes #3 1503\def\doresetvalue #1#2{\letcsname #1#2\endcsname\empty} 1504\def\doignorevalue#1#2#3{\letcsname #1#2\endcsname\empty} 1505\def\docopyvalue #1#2#3{\defcsname #1#3\endcsname{\csname#2#3\endcsname}} 1506 1507%D \macros 1508%D {doassign,undoassign,doassignempty} 1509%D 1510%D Assignments are the backbone of \CONTEXT. Abhorred by the concept of style file 1511%D hacking, we took a considerable effort in building a parameterized system. 1512%D Unfortunately there is a price to pay in terms of speed. Compared to other 1513%D packages and taking the functionality of \CONTEXT\ into account, the total size 1514%D of the format file is still very acceptable. Now how are these assignments done. 1515%D 1516%D Assignments can be realized with: 1517%D 1518%D \starttyping 1519%D \doassign[label][variable=value] 1520%D \undoassign[label][variable=value] 1521%D \stoptyping 1522%D 1523%D and: 1524%D 1525%D \starttyping 1526%D \doassignempty[label][variable=value] 1527%D \stoptyping 1528%D 1529%D Assignments like \type{\doassign} are compatible with: 1530%D 1531%D \starttyping 1532%D \def\labelvariable{value} 1533%D \stoptyping 1534%D 1535%D We do check for the presence of an \type{=} and loudly complain of it's missed. We 1536%D will redefine this macro later on, when a more advanced message mechanism is 1537%D implemented. 1538 1539\protected\def\showassignerror#1#2% 1540 {\writestatus{setup}{missing or ungrouped '=' after '#1' in line #2}} 1541 1542\permanent\protected\def\doassignempty[#1][#2=#3]% kind of obsolete 1543 {\ifcsname#1#2\endcsname\else\dosetvalue{#1}{#2}{#3}\fi} 1544 1545%D \macros 1546%D {getparameters,geteparameters,getgparameters, 1547%D forgetparameters} 1548%D 1549%D Using the assignment commands directly is not our ideal of user friendly interfacing, 1550%D so we take some further steps. 1551%D 1552%D \starttyping 1553%D \getparameters [label] [...=...,...=...] 1554%D \forgetparameters [label] [...=...,...=...] 1555%D \stoptyping 1556%D 1557%D Again, the label identifies the category a variable belongs to. The second argument 1558%D can be a comma separated list of assignments. 1559%D 1560%D \starttyping 1561%D \getparameters 1562%D [demo] 1563%D [alfa=1, 1564%D beta=2] 1565%D \stoptyping 1566%D 1567%D is equivalent to 1568%D 1569%D \starttyping 1570%D \def\demoalfa{1} 1571%D \def\demobeta{2} 1572%D \stoptyping 1573%D 1574%D 1575%D In the pre||multi||lingual stadium \CONTEXT\ took the next approach. With 1576%D 1577%D \starttyping 1578%D \def\??demo {@@demo} 1579%D \def\!!alfa {alfa} 1580%D \def\!!beta {beta} 1581%D \stoptyping 1582%D 1583%D calling 1584%D 1585%D \starttyping 1586%D \getparameters 1587%D [\??demo] 1588%D [\!!alfa=1, 1589%D \!!beta=2] 1590%D \stoptyping 1591%D 1592%D lead to: 1593%D 1594%D \starttyping 1595%D \def\@@demoalfa{1} 1596%D \def\@@demobeta{2} 1597%D \stoptyping 1598%D 1599%D Because we want to be able to distinguish the \type{!!} pre||tagged user supplied 1600%D variables from internal counterparts, we will introduce a slightly different tag 1601%D in the multi||lingual modules. There we will use \type{c!} or \type{v!}, 1602%D depending on the context. 1603%D 1604%D By calling \type{doassign} directly, we save ourselves some argument passing 1605%D and gain some speed. Whatever optimizations we do, this command will always be 1606%D one of the bigger bottlenecks. The alternative \type{\geteparameters} --- it's 1607%D funny to see that this alternative saw the light so lately --- can be used to do 1608%D expanded assigments. 1609 1610% \mutable\lettonothing\currentvalue % only mkii 1611 1612% \permanent\protected\def\getparameters {\dogetparameters\dosetvalue} 1613% \permanent\protected\def\geteparameters {\dogetparameters\dosetevalue} 1614% \permanent\protected\def\getgparameters {\dogetparameters\dosetgvalue} 1615% \permanent\protected\def\getxparameters {\dogetparameters\dosetxvalue} 1616% \permanent\protected\def\forgetparameters{\dogetparameters\doignorevalue} 1617% 1618% \aliased\let\getexpandedparameters\geteparameters 1619% 1620% \def\syst_helpers_grab_parameter_error#1% 1621% {\showassignerror{#1}{\the\inputlineno\space(\m_syst_parameter_n)}} 1622% 1623% \def\syst_helpers_grab_parameter#1,% 1624% {\ifarguments 1625% % done 1626% \else 1627% \syst_helpers_grab_parameter_okay#1,\ignorearguments 1628% \expandafter\syst_helpers_grab_parameter_next 1629% \fi} 1630% 1631% \def\syst_helpers_grab_parameter_okay#1=#2,% 1632% {\ifarguments 1633% \or 1634% \syst_helpers_grab_parameter_error{#1}% 1635% \or 1636% \m_syst_parameter_s\m_syst_parameter_n{#1}{#2}% 1637% \fi} 1638% 1639% \def\syst_helpers_grab_parameter_next 1640% {\expandafterspaces\syst_helpers_grab_parameter} 1641% 1642% \permanent\protected\def\dogetparameters#1[#2]#*[#3]% 1643% {\def\m_syst_parameter_n{#2}% 1644% \let\m_syst_parameter_s#1% 1645% \expandafterspaces\syst_helpers_grab_parameter#3\ignorearguments\ignorearguments} 1646 1647% Doesn't work with single entry empty values [foo=] 1648% 1649% \tolerant\def\syst_get_parameters_n#*#1=#S#2,#M,% 1650% {\ifarguments \expandafter\gobbleoneargument 1651% \or \mult_interfaces_get_parameters_error_indeed\m_syst_parameter{#1}% 1652% \orelse\ifparameter#2\or \defcsname\m_syst_parameter#1\endcsname{#2}% 1653% \else \letcsname\m_syst_parameter#1\endcsname\empty 1654% \fi \syst_get_parameters_n} 1655% 1656% so we do this instead (and also silently recover). 1657 1658\tolerant\def\syst_get_parameters_n#*#1=#S#2,#M,% 1659 {\unless\ifparameter#1\or\expandafter\gobbleoneargument 1660 \orelse\ifparameter#2\or\defcsname\m_syst_parameter#1\endcsname{#2}% 1661 \else \letcsname\m_syst_parameter#1\endcsname\empty 1662 \fi \syst_get_parameters_n} 1663\tolerant\def\syst_get_parameters_e#*#1=#S#2,#M,% 1664 {\unless\ifparameter#1\or\expandafter\gobbleoneargument 1665 \orelse\ifparameter#2\or\edefcsname\m_syst_parameter#1\endcsname{#2}% 1666 \else \letcsname\m_syst_parameter#1\endcsname\empty 1667 \fi \syst_get_parameters_e} 1668\tolerant\def\syst_get_parameters_g#*#1=#S#2,#M,% 1669 {\unless\ifparameter#1\or\expandafter\gobbleoneargument 1670 \orelse\ifparameter#2\or\gdefcsname\m_syst_parameter#1\endcsname{#2}% 1671 \else \letcsname\m_syst_parameter#1\endcsname\empty 1672 \fi \syst_get_parameters_g} 1673\tolerant\def\syst_get_parameters_x#*#1=#S#2,#M,% 1674 {\unless\ifparameter#1\or\expandafter\gobbleoneargument 1675 \orelse\ifparameter#2\or\xdefcsname\m_syst_parameter#1\endcsname{#2}% 1676 \else \letcsname\m_syst_parameter#1\endcsname\empty 1677 \fi \syst_get_parameters_x} 1678\tolerant\def\syst_get_parameters_f#*#1=#S#2,#M,% 1679 {\unless\ifparameter#1\or\expandafter\gobbleoneargument 1680 \else \letcsname\m_syst_parameter#1\endcsname\empty 1681 \fi \syst_get_parameters_f} 1682 1683\permanent\protected\def\getparameters [#1]#*[#S#2]{\cdef\m_syst_parameter{#1}\syst_get_parameters_n#2\ignorearguments\ignorearguments} 1684\permanent\protected\def\geteparameters [#1]#*[#S#2]{\cdef\m_syst_parameter{#1}\syst_get_parameters_e#2\ignorearguments\ignorearguments} 1685\permanent\protected\def\getgparameters [#1]#*[#S#2]{\cdef\m_syst_parameter{#1}\syst_get_parameters_g#2\ignorearguments\ignorearguments} 1686\permanent\protected\def\getxparameters [#1]#*[#S#2]{\cdef\m_syst_parameter{#1}\syst_get_parameters_x#2\ignorearguments\ignorearguments} 1687\permanent\protected\def\forgetparameters[#1]#*[#S#2]{\cdef\m_syst_parameter{#1}\syst_get_parameters_f#2\ignorearguments\ignorearguments} 1688 1689\aliased\let\getexpandedparameters\geteparameters 1690 1691%D \macros 1692%D {getemptyparameters} 1693%D 1694%D Sometimes we explicitly want variables to default to an empty string, so we 1695%D welcome: 1696%D 1697%D \starttyping 1698%D \getemptyparameters [label] [...=...,...=...] 1699%D \stoptyping 1700 1701% \let\syst_helpers_get_empty_parameters\gobbleoneargument 1702% 1703% \permanent\protected\def\getemptyparameters[#1]#*[#2]% 1704% {\def\syst_helpers_get_empty_parameters##1{\doassignempty[#1][##1]}% 1705% \processcommalist[#2]\syst_helpers_get_empty_parameters} 1706 1707\let\syst_helpers_copy_parameter\relax 1708 1709\permanent\protected\def\getemptyparameters[#1]#*[#2]% 1710 {\def\syst_helpers_copy_parameter##1{\letcsname#1##1\endcsname\empty}% 1711 \processcommalist[#2]\syst_helpers_get_empty_parameters} 1712 1713%D We now just alias these as there is no need for a speedup. These have not been used 1714%D for a long time in core code. 1715 1716\aliased\let\doassign \getparameters % obsolete, these will go 1717\aliased\let\doeassign \geteparameters % obsolete, these will go 1718\aliased\let\undoassign\forgetparameters % obsolete, these will go 1719 1720%D \macros 1721%D {processassignmentlist,processassignmentcommand, 1722%D startprocessassignmentlist,startprocessassignmentcommand} 1723%D 1724%D For Wolfgang: 1725%D 1726%D \starttyping 1727%D \def\showpair#1#2{key:#1, value:#2\par} 1728%D \processassignmentlist[a=1,b=2]\showpair 1729%D \stoptyping 1730%D 1731%D We can optimize this one if needed but it's not a core macro so hardly worth the 1732%D trouble and tokens. 1733 1734\mutable\let\currentassignmentlistcommand\gobbletwoarguments 1735 1736\mutable\lettonothing\currentassignmentlistkey 1737\mutable\lettonothing\currentassignmentlistvalue 1738 1739\let\syst_helpers_process_assign_entry\gobbleoneargument 1740 1741% \permanent\protected\def\processassignmentlist[#1]#2% #2 == \command{key}{value] 1742% {\def\syst_helpers_process_assign_entry##1{#2}% {##2}{##3} % namespace is ignored 1743% \dogetparameters\syst_helpers_process_assign_entry[][#1]} 1744 1745\tolerant\def\syst_helpers_process_assign_value#*#1=#S#2,#M,% 1746 {\ifarguments 1747 \expandafter\gobbleoneargument 1748 \or 1749 \mult_interfaces_get_parameters_error_indeed\empty{#1}% 1750 \else 1751 \syst_helpers_process_assign_entry{#1}{#2}% 1752 \fi 1753 \syst_helpers_process_assign_value} 1754 1755\permanent\protected\def\processassignmentlist[#S#1]#2% #2 == \command{key}{value] 1756 {\def\syst_helpers_process_assign_entry{#2}% 1757 \syst_helpers_process_assign_value#1\ignorearguments\ignorearguments} 1758 1759\permanent\protected\def\processassignmentcommand[#1]% 1760 {\normalexpanded{\processassignmentlist[#1]}} 1761 1762\permanent\protected\def\startprocessassignmentlist[#1]#2\stopprocessassignmentlist 1763 {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}% 1764 \processassignmentlist[#1]\currentassignmentlistcommand} 1765 1766\permanent\protected\lettonothing\stopprocessassignmentlist 1767 1768\permanent\protected\def\startprocessassignmentcommand[#1]#2\stopprocessassignmentcommand 1769 {\def\currentassignmentlistcommand##1##2{\def\currentassignmentlistkey{##1}\def\currentassignmentlistvalue{##2}#2}% 1770 \normalexpanded{\processassignmentlist[#1]}\currentassignmentlistcommand} 1771 1772\permanent\protected\lettonothing\stopprocessassignmentcommand 1773 1774%D \macros 1775%D {currentvalue} 1776%D 1777%D Just in case a \type{\getparameter} argument itself ends up inside a \type 1778%D {\write} or other expandable location, our new macro needs a default value. 1779%D 1780%D \starttyping 1781%D \getparameters[xxx][aaa=bbb]\par 1782%D \getparameters[xxx][=bbb]\par 1783%D \getparameters[xxx][aaa=]\par 1784%D \getparameters[xxx][=]\par 1785%D \getparameters[xxx][aaa]\par 1786%D \stoptyping 1787 1788%D \macros 1789%D {copyparameters} 1790%D 1791%D Some \CONTEXT\ commands take their default setups from others. All commands that 1792%D are able to provide backgounds or rules around some content, for instance default 1793%D to the standard command for ruled boxes. Is situations like this we can use: 1794%D 1795%D \starttyping 1796%D \copyparameters [to-label] [from-label] [name1,name2,...] 1797%D \stoptyping 1798%D 1799%D For instance 1800%D 1801%D \starttyping 1802%D \copyparameters 1803%D [internal][external] 1804%D [alfa,beta] 1805%D \stoptyping 1806%D 1807%D Leads to: 1808%D 1809%D \starttyping 1810%D \def\internalalfa {\externalalfa} 1811%D \def\internalbeta {\externalbeta} 1812%D \stoptyping 1813%D 1814%D By using \type {\docopyvalue} we've prepared this command for use in a 1815%D multi||lingual environment. 1816 1817% \let\syst_helpers_copy_parameter\relax 1818% 1819% \permanent\protected\def\copyparameters[#1]#*[#2]#*[#3]% 1820% {\iftok{#1}{#2}\else 1821% \def\syst_helpers_copy_parameter{\docopyvalue{#1}{#2}}% ##1 1822% \processcommalist[#3]\syst_helpers_copy_parameter 1823% \fi} 1824 1825\let\syst_helpers_copy_parameter\relax 1826 1827\permanent\protected\def\copyparameters[#1]#*[#2]#*[#3]% 1828 {\iftok{#1}{#2}\else 1829 \def\syst_helpers_copy_parameter##1{\defcsname#1##1\endcsname{\begincsname#2##1\endcsname}}% 1830 \processcommalist[#3]\syst_helpers_copy_parameter 1831 \fi} 1832 1833% %D \macros 1834% %D {ifparameters,checkparameters} 1835% %D 1836% %D A slightly different one is \type {\checkparameters}, which also checks on the 1837% %D presence of a~\type {=}. 1838% %D 1839% %D The boolean \type {\ifparameters} can be used afterwards. Combining both in one 1840% %D \type {\if}||macro would lead to problems with nested \type {\if}'s. 1841% %D 1842% %D \starttyping 1843% %D \checkparameters[argument] 1844% %D \stoptyping 1845 1846% \newif\ifparameters 1847 1848% \protected\def\checkparameters[#1]% 1849% {\ifhastok={#1}\parameterstrue\else\parametersfalse\fi} 1850 1851%D \macros 1852%D {getfromcommalist,getfromcommacommand, 1853%D commalistelement, 1854%D getcommalistsize,getcommacommandsize} 1855%D 1856%D It's possible to get an element from a commalist or a command representing 1857%D a commalist. 1858%D 1859%D \starttyping 1860%D \getfromcommalist [string] [n] 1861%D \getfromcommacommand [string,\strings,string,...] [n] 1862%D \stoptyping 1863%D 1864%D The difference betwee the two of them is the same as the difference between 1865%D \type {\processcomma...}. The found string is stored in \type 1866%D {\commalistelement}. 1867%D 1868%D We can calculate the size of a comma separated list by using: 1869%D 1870%D \starttyping 1871%D \getcommalistsize [string,string,...] 1872%D \getcommacommandsize [string,\strings,string,...] 1873%D \stoptyping 1874%D 1875%D Afterwards, the length is available in the macro \type {\commalistsize} 1876%D (not a \COUNTER). 1877 1878\newinteger\commalistcounter 1879 1880\mutable\def\commalistsize{0} 1881 1882\def\syst_helpers_get_comma_list_size#0,% no #- as we need to count 1883 {\ifarguments\or 1884 \advanceby\commalistcounter\plusone 1885 \expandafter\syst_helpers_get_comma_list_size 1886 \fi} 1887 1888\permanent\protected\def\getcommalistsize[% 1889 {\futureexpand]\syst_helpers_get_comma_list_size_nop\syst_helpers_get_comma_list_size_yes} 1890 1891\def\syst_helpers_get_comma_list_size_yes#+]% 1892 {\commalistcounter\zerocount 1893 \syst_helpers_get_comma_list_size #1,\ignorearguments\ignorearguments 1894 \edef\commalistsize{\the\commalistcounter}} 1895 1896\def\syst_helpers_get_comma_list_size_nop]% 1897 {\commalistcounter\zerocount 1898 \let\commalistsize\!!zerocount} 1899 1900\permanent\protected\def\getcommacommandsize[#1]% 1901 {\normalexpanded{\getcommalistsize[#1]}} 1902 1903\permanent\def\getcommalistcount[#1]% 1904 {\beginlocalcontrol 1905 \getcommalistsize[#1]% 1906 \endlocalcontrol 1907 \the\commalistcounter} 1908 1909\permanent\def\getcommacommandcount[#1]% 1910 {\beginlocalcontrol 1911 \getcommacommandsize[#1]% 1912 \endlocalcontrol 1913 \the\commalistcounter} 1914 1915%D Filters: 1916 1917% \def\syst_helpers_gobble_comma_list#0\ignorearguments{} 1918 1919\def\syst_helpers_gobble_comma_list#-\ignorearguments{} 1920 1921\def\syst_helpers_get_from_comma_list#1,% 1922 {\ifarguments \or 1923 \advanceby\commalistcounter \minusone 1924 \ifcase\commalistcounter 1925 \def\commalistelement{#1}% 1926 \expandafter\expandafter\expandafter\syst_helpers_gobble_comma_list 1927 \else 1928 \expandafter\expandafter\expandafter\syst_helpers_get_from_comma_list_next 1929 \fi 1930 \fi} 1931 1932\def\syst_helpers_get_from_comma_list_next 1933 {\expandafterspaces\syst_helpers_get_from_comma_list} 1934 1935\permanent\protected\def\getfromcommalist[#1]#*[#2]% 1936 {\lettonothing\commalistelement 1937 \commalistcounter#2\relax 1938 \expandafterspaces\syst_helpers_get_from_comma_list#1\ignorearguments\ignorearguments} 1939 1940\permanent\protected\def\getfromcommacommand[#1]% 1941 {\normalexpanded{\getfromcommalist[#1]}} 1942 1943%D Watertight (and efficient) solutions are hard to find, due to the handling of 1944%D braces during parameters passing and scanning. Nevertheless: 1945%D 1946%D \startbuffer 1947%D \def\dosomething#1{(#1=\commalistsize) } 1948%D 1949%D \getcommalistsize [\hbox{$a,b,c,d,e,f$}] \dosomething 1 1950%D \getcommalistsize [{a,b,c,d,e,f}] \dosomething 1 1951%D \getcommalistsize [{a,b,c},d,e,f] \dosomething 4 1952%D \getcommalistsize [a,b,{c,d,e},f] \dosomething 4 1953%D \getcommalistsize [a{b,c},d,e,f] \dosomething 4 1954%D \getcommalistsize [{a,b}c,d,e,f] \dosomething 4 1955%D \getcommalistsize [] \dosomething 0 1956%D \getcommalistsize [{[}] \dosomething 1 1957%D \stopbuffer 1958%D 1959%D \typebuffer 1960%D 1961%D reports: 1962%D 1963%D \getbuffer 1964 1965%D \macros 1966%D {dogetcommalistelement,dogetcommacommandelement} 1967%D 1968%D For low level (fast) purposes, we can also use the next alternative, which can 1969%D handle 9~elements at most. 1970%D 1971%D \starttyping 1972%D \dogetcommalistelement1\from a,b,c\to\commalistelement 1973%D \stoptyping 1974 1975\def\syst_helpers_get_comma_list_element#1,#2,#3,#4,#5,#6,#7,#8,#9,% 1976 {\ifcase\scratchcounter\or#1\or#2\or#3\or#4\or#5\or#6\or#7\or#8\or#9\fi 1977 \syst_helpers_gobble_comma_list} 1978 1979\permanent\protected\def\dogetcommacommandelement#1\from#2\to#3% 1980 {\scratchcounter#1\relax 1981 \edef#3{\normalexpanded{\syst_helpers_get_comma_list_element#2\ignorearguments\ignorearguments}}} 1982 1983%D \macros 1984%D {dosingleargument,dodoubleargument,dotripleargument, 1985%D doquadrupleargument,doquintupleargument,dosixtupleargument, 1986%D doseventupleargument} 1987%D 1988%D When working with delimited arguments, spaces and lineendings can interfere. The 1989%D next set of macros uses \TEX' internal scanner for grabbing everything between 1990%D arguments. Forgive me the funny names. 1991%D 1992%D \starttyping 1993%D \dosingleargument \command = \command[#1] 1994%D \dodoubleargument \command = \command[#1][#2] 1995%D \dotripleargument \command = \command[#1][#2][#3] 1996%D \doquadrupleargument \command = \command[#1][#2][#3][#4] 1997%D \doquintupleargument \command = \command[#1][#2][#3][#4][#5] 1998%D \dosixtupleargument \command = \command[#1][#2][#3][#4][#5][#6] 1999%D \doseventupleargument\command = \command[#1][#2][#3][#4][#5][#6][#7] 2000%D \stoptyping 2001%D 2002%D These macros can be used in the following way: 2003%D 2004%D \starttyping 2005%D \def\dosetupsomething[#1][#2]% 2006%D {... #1 ... #2 ...} 2007%D 2008%D \protected\def\setupsomething 2009%D {\dodoubleargument\dosetupsomething} 2010%D \stoptyping 2011%D 2012%D The implementation can be surprisingly simple and needs no further explanation, 2013%D like: 2014%D 2015%D \starttyping 2016%D \def\dosingleargument#1[#2]% 2017%D {#1[#2]} 2018%D \def\dotripleargument#1[#2]#3[#4]#5[#6]% 2019%D {#1[#2][#4][#6]} 2020%D \def\doquintupleargument#1% 2021%D {\def\dodoquintupleargument[##1]##2[##3]##4[##5]##6[##7]##8[##9]% 2022%D {#1[##1][##3][##5][##7][##9]}% 2023%D \dodoquintupleargument} 2024%D \stoptyping 2025%D 2026%D Because \TEX\ accepts 9~arguments at most, we have to use two||step solution when 2027%D getting five or more arguments. 2028%D 2029%D When developing more and more of the real \CONTEXT, we started using some 2030%D alternatives that provided empty arguments (in fact optional ones) whenever the 2031%D user failed to supply them. Because this more complicated macros enable us to do 2032%D some checking, we reimplemented the non||empty ones. 2033 2034%D \macros 2035%D {iffirstagument,ifsecondargument,ifthirdargument, 2036%D iffourthargument,iffifthargument,ifsixthargument, 2037%D ifseventhargument} 2038%D 2039%D We use some signals for telling the calling macros if all wanted arguments are 2040%D indeed supplied by the user. 2041 2042\newinteger\nofarguments 2043 2044\newif\iffirstargument 2045\newif\ifsecondargument 2046\newif\ifthirdargument 2047\newif\iffourthargument 2048\newif\iffifthargument 2049\newif\ifsixthargument 2050\newif\ifseventhargument 2051 2052%D \macros 2053%D {dosingleempty,dodoubleempty,dotripleempty, 2054%D doquadrupleempty,doquintupleempty,dosixtupeempty, 2055%D doseventupleempty} 2056%D 2057%D The empty argument supplying macros mentioned before, look like: 2058%D 2059%D \starttyping 2060%D \dosingleempty \command 2061%D \dodoubleempty \command 2062%D \dotripleempty \command 2063%D \doquadrupleempty \command 2064%D \doquintupleempty \command 2065%D \dosixtuple_empty \command 2066%D \doseventupleempty\command 2067%D \stoptyping 2068%D 2069%D So \type{\dodoubleempty} leads to: 2070%D 2071%D \starttyping 2072%D \command[#1][#2] 2073%D \command[#1][] 2074%D \command[][] 2075%D \stoptyping 2076%D 2077%D Depending of the generousity of the user. Afterwards one can use the \type 2078%D {\if...argument} boolean. For novice: watch the stepwise doubling of \type {#}'s. 2079 2080%D NB : experimental versions in cont-exp.mkiv 2081 2082%D Common: 2083 2084\newtoks\t_syst_aux 2085 2086% \def\syst_helpers_single_empty_one_yes {\firstargumenttrue \the\t_syst_aux} 2087% \def\syst_helpers_double_empty_two_yes {\secondargumenttrue \the\t_syst_aux} 2088% \def\syst_helpers_triple_empty_three_yes {\thirdargumenttrue \the\t_syst_aux} 2089% \def\syst_helpers_quadruple_empty_four_yes {\fourthargumenttrue \the\t_syst_aux} 2090% \def\syst_helpers_quintuple_empty_five_yes {\fifthargumenttrue \the\t_syst_aux} 2091% \def\syst_helpers_sixtuple_empty_six_yes {\sixthargumenttrue \the\t_syst_aux} 2092% \def\syst_helpers_seventuple_empty_seven_yes{\seventhargumenttrue\the\t_syst_aux} 2093% 2094% %D Single: 2095% 2096% \protected\def\dosingleempty#1% 2097% {\t_syst_aux{#1}% 2098% \futureexpand[\syst_helpers_single_empty_one_yes\syst_helpers_single_empty_one_nop} 2099% 2100% \def\syst_helpers_single_empty_one_nop 2101% {\firstargumentfalse 2102% \the\t_syst_aux[]} 2103% 2104% %D Double 2105% 2106% \protected\def\dodoubleempty#1% 2107% {\t_syst_aux{#1}% 2108% \futureexpand[\syst_helpers_double_empty_one_yes\syst_helpers_double_empty_one_nop} 2109% 2110% % \def\syst_helpers_double_empty_one_yes[#1]% 2111% % {\firstargumenttrue 2112% % \toksapp\t_syst_aux{[{#1}]}% 2113% % \futureexpand[\syst_helpers_double_empty_two_yes\syst_helpers_double_empty_two_nop} 2114% % 2115% % nicer is: 2116% 2117% \def\syst_helpers_double_empty_one_yes[#+]% 2118% {\firstargumenttrue 2119% \toksapp\t_syst_aux{[#1]}% 2120% \futureexpand[\syst_helpers_double_empty_two_yes\syst_helpers_double_empty_two_nop} 2121% 2122% \def\syst_helpers_double_empty_one_nop 2123% {\firstargumentfalse 2124% \secondargumentfalse 2125% \the\t_syst_aux[][]} 2126% 2127% \def\syst_helpers_double_empty_two_nop 2128% {\secondargumentfalse 2129% \the\t_syst_aux[]} 2130% 2131% % Triple 2132% 2133% \protected\def\dotripleempty#1% 2134% {\t_syst_aux{#1}% 2135% \futureexpand[\syst_helpers_triple_empty_one_yes\syst_helpers_triple_empty_one_nop} 2136% 2137% \def\syst_helpers_triple_empty_one_yes[#+]% 2138% {\firstargumenttrue 2139% \toksapp\t_syst_aux{[#1]}% 2140% \futureexpand[\syst_helpers_triple_empty_two_yes\syst_helpers_triple_empty_two_nop} 2141% 2142% \def\syst_helpers_triple_empty_two_yes[#+]% 2143% {\secondargumenttrue 2144% \toksapp\t_syst_aux{[#1]}% 2145% \futureexpand[\syst_helpers_triple_empty_three_yes\syst_helpers_triple_empty_three_nop} 2146% 2147% \def\syst_helpers_triple_empty_one_nop 2148% {\firstargumentfalse 2149% \secondargumentfalse 2150% \thirdargumentfalse 2151% \the\t_syst_aux[][][]} 2152% 2153% \def\syst_helpers_triple_empty_two_nop 2154% {\secondargumentfalse 2155% \thirdargumentfalse 2156% \the\t_syst_aux[][]} 2157% 2158% \def\syst_helpers_triple_empty_three_nop 2159% {\thirdargumentfalse 2160% \the\t_syst_aux[]} 2161% 2162% %D Quadruple: 2163% 2164% \protected\def\doquadrupleempty#1% 2165% {\t_syst_aux{#1}% 2166% \futureexpand[\syst_helpers_quadruple_empty_one_yes\syst_helpers_quadruple_empty_one_nop} 2167% 2168% \def\syst_helpers_quadruple_empty_one_yes[#+]% 2169% {\firstargumenttrue 2170% \toksapp\t_syst_aux{[#1]}% 2171% \futureexpand[\syst_helpers_quadruple_empty_two_yes\syst_helpers_quadruple_empty_two_nop} 2172% 2173% \def\syst_helpers_quadruple_empty_two_yes[#+]% 2174% {\secondargumenttrue 2175% \toksapp\t_syst_aux{[#1]}% 2176% \futureexpand[\syst_helpers_quadruple_empty_three_yes\syst_helpers_quadruple_empty_three_nop} 2177% 2178% \def\syst_helpers_quadruple_empty_three_yes[#+]% 2179% {\thirdargumenttrue 2180% \toksapp\t_syst_aux{[#1]}% 2181% \futureexpand[\syst_helpers_quadruple_empty_four_yes\syst_helpers_quadruple_empty_four_nop} 2182% 2183% \def\syst_helpers_quadruple_empty_one_nop 2184% {\firstargumentfalse 2185% \secondargumentfalse 2186% \thirdargumentfalse 2187% \fourthargumentfalse 2188% \the\t_syst_aux[][][][]} 2189% 2190% \def\syst_helpers_quadruple_empty_two_nop 2191% {\secondargumentfalse 2192% \thirdargumentfalse 2193% \fourthargumentfalse 2194% \the\t_syst_aux[][][]} 2195% 2196% \def\syst_helpers_quadruple_empty_three_nop 2197% {\thirdargumentfalse 2198% \fourthargumentfalse 2199% \the\t_syst_aux[][]} 2200% 2201% \def\syst_helpers_quadruple_empty_four_nop 2202% {\fourthargumentfalse 2203% \the\t_syst_aux[]} 2204% 2205% %D Quintuple: 2206% 2207% \protected\def\doquintupleempty#1% 2208% {\t_syst_aux{#1}% 2209% \futureexpand[\syst_helpers_quintuple_empty_one_yes\syst_helpers_quintuple_empty_one_nop} 2210% 2211% \def\syst_helpers_quintuple_empty_one_yes[#+]% 2212% {\firstargumenttrue 2213% \toksapp\t_syst_aux{[#1]}% 2214% \futureexpand[\syst_helpers_quintuple_empty_two_yes\syst_helpers_quintuple_empty_two_nop} 2215% 2216% \def\syst_helpers_quintuple_empty_two_yes[#+]% 2217% {\secondargumenttrue 2218% \toksapp\t_syst_aux{[#1]}% 2219% \futureexpand[\syst_helpers_quintuple_empty_three_yes\syst_helpers_quintuple_empty_three_nop} 2220% 2221% \def\syst_helpers_quintuple_empty_three_yes[#+]% 2222% {\thirdargumenttrue 2223% \toksapp\t_syst_aux{[#1]}% 2224% \futureexpand[\syst_helpers_quintuple_empty_four_yes\syst_helpers_quintuple_empty_four_nop} 2225% 2226% \def\syst_helpers_quintuple_empty_four_yes[#+]% 2227% {\fourthargumenttrue 2228% \toksapp\t_syst_aux{[#1]}% 2229% \futureexpand[\syst_helpers_quintuple_empty_five_yes\syst_helpers_quintuple_empty_five_nop} 2230% 2231% \def\syst_helpers_quintuple_empty_one_nop 2232% {\firstargumentfalse 2233% \secondargumentfalse 2234% \thirdargumentfalse 2235% \fourthargumentfalse 2236% \fifthargumentfalse 2237% \the\t_syst_aux[][][][][]} 2238% 2239% \def\syst_helpers_quintuple_empty_two_nop 2240% {\secondargumentfalse 2241% \thirdargumentfalse 2242% \fourthargumentfalse 2243% \fifthargumentfalse 2244% \the\t_syst_aux[][][][]} 2245% 2246% \def\syst_helpers_quintuple_empty_three_nop 2247% {\thirdargumentfalse 2248% \fourthargumentfalse 2249% \fifthargumentfalse 2250% \the\t_syst_aux[][][]} 2251% 2252% \def\syst_helpers_quintuple_empty_four_nop 2253% {\fourthargumentfalse 2254% \fifthargumentfalse 2255% \the\t_syst_aux[][]} 2256% 2257% \def\syst_helpers_quintuple_empty_five_nop 2258% {\fifthargumentfalse 2259% \the\t_syst_aux[]} 2260% 2261% %D Sixtuple: 2262% 2263% \protected\def\dosixtupleempty#1% 2264% {\t_syst_aux{#1}% 2265% \futureexpand[\syst_helpers_sixtuple_empty_one_yes\syst_helpers_sixtuple_empty_one_nop} 2266% 2267% \def\syst_helpers_sixtuple_empty_one_yes[#+]% 2268% {\firstargumenttrue 2269% \toksapp\t_syst_aux{[#1]}% 2270% \futureexpand[\syst_helpers_sixtuple_empty_two_yes\syst_helpers_sixtuple_empty_two_nop} 2271% 2272% \def\syst_helpers_sixtuple_empty_two_yes[#+]% 2273% {\secondargumenttrue 2274% \toksapp\t_syst_aux{[#1]}% 2275% \futureexpand[\syst_helpers_sixtuple_empty_three_yes\syst_helpers_sixtuple_empty_three_nop} 2276% 2277% \def\syst_helpers_sixtuple_empty_three_yes[#+]% 2278% {\thirdargumenttrue 2279% \toksapp\t_syst_aux{[#1]}% 2280% \futureexpand[\syst_helpers_sixtuple_empty_four_yes\syst_helpers_sixtuple_empty_four_nop} 2281% 2282% \def\syst_helpers_sixtuple_empty_four_yes[#+]% 2283% {\fourthargumenttrue 2284% \toksapp\t_syst_aux{[#1]}% 2285% \futureexpand[\syst_helpers_sixtuple_empty_five_yes\syst_helpers_sixtuple_empty_five_nop} 2286% 2287% \def\syst_helpers_sixtuple_empty_five_yes[#+]% 2288% {\fifthargumenttrue 2289% \toksapp\t_syst_aux{[#1]}% 2290% \futureexpand[\syst_helpers_sixtuple_empty_six_yes\syst_helpers_sixtuple_empty_six_nop} 2291% 2292% \def\syst_helpers_sixtuple_empty_one_nop 2293% {\firstargumentfalse 2294% \secondargumentfalse 2295% \thirdargumentfalse 2296% \fourthargumentfalse 2297% \fifthargumentfalse 2298% \sixthargumentfalse 2299% \the\t_syst_aux[][][][][][]} 2300% 2301% \def\syst_helpers_sixtuple_empty_two_nop 2302% {\secondargumentfalse 2303% \thirdargumentfalse 2304% \fourthargumentfalse 2305% \fifthargumentfalse 2306% \sixthargumentfalse 2307% \the\t_syst_aux[][][][][]} 2308% 2309% \def\syst_helpers_sixtuple_empty_three_nop 2310% {\thirdargumentfalse 2311% \fourthargumentfalse 2312% \fifthargumentfalse 2313% \sixthargumentfalse 2314% \the\t_syst_aux[][][][]} 2315% 2316% \def\syst_helpers_sixtuple_empty_four_nop 2317% {\fourthargumentfalse 2318% \fifthargumentfalse 2319% \sixthargumentfalse 2320% \the\t_syst_aux[][][]} 2321% 2322% \def\syst_helpers_sixtuple_empty_five_nop 2323% {\fifthargumentfalse 2324% \sixthargumentfalse 2325% \the\t_syst_aux[][]} 2326% 2327% \def\syst_helpers_sixtuple_empty_six_nop 2328% {\sixthargumentfalse 2329% \the\t_syst_aux[]} 2330% 2331% %D Seventuple: 2332% 2333% \protected\def\doseventupleempty#1% 2334% {\t_syst_aux{#1}% 2335% \futureexpand[\syst_helpers_seventuple_empty_one_yes\syst_helpers_seventuple_empty_one_nop} 2336% 2337% \def\syst_helpers_seventuple_empty_one_yes[#+]% 2338% {\firstargumenttrue 2339% \toksapp\t_syst_aux{[#1]}% 2340% \futureexpand[\syst_helpers_seventuple_empty_two_yes\syst_helpers_seventuple_empty_two_nop} 2341% 2342% \def\syst_helpers_seventuple_empty_two_yes[#+]% 2343% {\secondargumenttrue 2344% \toksapp\t_syst_aux{[#1]}% 2345% \futureexpand[\syst_helpers_seventuple_empty_three_yes\syst_helpers_seventuple_empty_three_nop} 2346% 2347% \def\syst_helpers_seventuple_empty_three_yes[#+]% 2348% {\thirdargumenttrue 2349% \toksapp\t_syst_aux{[#1]}% 2350% \futureexpand[\syst_helpers_seventuple_empty_four_yes\syst_helpers_seventuple_empty_four_nop} 2351% 2352% \def\syst_helpers_seventuple_empty_four_yes[#+]% 2353% {\fourthargumenttrue 2354% \toksapp\t_syst_aux{[#1]}% 2355% \futureexpand[\syst_helpers_seventuple_empty_five_yes\syst_helpers_seventuple_empty_five_nop} 2356% 2357% \def\syst_helpers_seventuple_empty_five_yes[#+]% 2358% {\fifthargumenttrue 2359% \toksapp\t_syst_aux{[#1]}% 2360% \futureexpand[\syst_helpers_seventuple_empty_six_yes\syst_helpers_seventuple_empty_six_nop} 2361% 2362% \def\syst_helpers_seventuple_empty_six_yes[#+]% 2363% {\sixthargumenttrue 2364% \toksapp\t_syst_aux{[#1]}% 2365% \futureexpand[\syst_helpers_seventuple_empty_seven_yes\syst_helpers_seventuple_empty_seven_nop} 2366% 2367% \def\syst_helpers_seventuple_empty_one_nop 2368% {\firstargumentfalse 2369% \secondargumentfalse 2370% \thirdargumentfalse 2371% \fourthargumentfalse 2372% \fifthargumentfalse 2373% \sixthargumentfalse 2374% \seventhargumentfalse 2375% \the\t_syst_aux[][][][][][][]} 2376% 2377% \def\syst_helpers_seventuple_empty_two_nop 2378% {\secondargumentfalse 2379% \thirdargumentfalse 2380% \fourthargumentfalse 2381% \fifthargumentfalse 2382% \sixthargumentfalse 2383% \seventhargumentfalse 2384% \the\t_syst_aux[][][][][][]} 2385% 2386% \def\syst_helpers_seventuple_empty_three_nop 2387% {\thirdargumentfalse 2388% \fourthargumentfalse 2389% \fifthargumentfalse 2390% \sixthargumentfalse 2391% \seventhargumentfalse 2392% \the\t_syst_aux[][][][][]} 2393% 2394% \def\syst_helpers_seventuple_empty_four_nop 2395% {\fourthargumentfalse 2396% \fifthargumentfalse 2397% \sixthargumentfalse 2398% \seventhargumentfalse 2399% \the\t_syst_aux[][][][]} 2400% 2401% \def\syst_helpers_seventuple_empty_five_nop 2402% {\fifthargumentfalse 2403% \sixthargumentfalse 2404% \seventhargumentfalse 2405% \the\t_syst_aux[][][]} 2406% 2407% \def\syst_helpers_seventuple_empty_six_nop 2408% {\sixthargumentfalse 2409% \seventhargumentfalse 2410% \the\t_syst_aux[][]} 2411% 2412% \def\syst_helpers_seventuple_empty_seven_nop 2413% {\seventhargumentfalse 2414% \the\t_syst_aux[]} 2415 2416\tolerant\def\syst_single_empty#1\relax[#S#2]% 2417 {\ifarguments 2418 \or\firstargumentfalse 2419 \or\firstargumenttrue 2420 \fi 2421 #1[#2]} 2422 2423\tolerant\def\syst_double_empty#1\relax[#S#+]#*[#S#+]% 2424 {\ifarguments 2425 \or\firstargumentfalse\secondargumentfalse 2426 \or\firstargumenttrue \secondargumentfalse 2427 \or\firstargumenttrue \secondargumenttrue 2428 \fi 2429 #1[#2][#3]} 2430 2431\tolerant\def\syst_triple_empty#1\relax[#S#+]#*[#S#+]#*[#S#+]% 2432 {\ifarguments 2433 \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse 2434 \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse 2435 \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse 2436 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue 2437 \fi 2438 #1[#2][#3][#4]} 2439 2440\tolerant\def\syst_quadruple_empty#1\relax[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]% 2441 {\ifarguments 2442 \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse 2443 \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse 2444 \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse 2445 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse 2446 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue 2447 \fi 2448 #1[#2][#3][#4][#5]} 2449 2450\tolerant\def\syst_quintuple_empty#1\relax[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]% 2451 {\ifarguments 2452 \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse 2453 \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse 2454 \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse 2455 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse 2456 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse 2457 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue 2458 \fi 2459 #1[#2][#3][#4][#5][#6]} 2460 2461\tolerant\def\syst_sixtuple_empty#1\relax[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]% 2462 {\ifarguments 2463 \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse 2464 \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse 2465 \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse 2466 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse\sixthargumentfalse 2467 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse\sixthargumentfalse 2468 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumentfalse 2469 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumenttrue 2470 \fi 2471 #1[#2][#3][#4][#5][#6][#7]} 2472 2473\tolerant\def\syst_seventuple_empty#1\relax[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]#*[#S#+]% 2474 {\ifarguments 2475 \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse 2476 \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse 2477 \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse 2478 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse\sixthargumentfalse\seventhargumentfalse 2479 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse\sixthargumentfalse\seventhargumentfalse 2480 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumentfalse\seventhargumentfalse 2481 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumenttrue \seventhargumentfalse 2482 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue \sixthargumenttrue \seventhargumenttrue 2483 \fi 2484 #1[#2][#3][#4][#5][#6][#7][#8]} 2485 2486\permanent\protected\def\dosingleempty #1{\syst_single_empty #1\relax} 2487\permanent\protected\def\dodoubleempty #1{\syst_double_empty #1\relax} 2488\permanent\protected\def\dotripleempty #1{\syst_triple_empty #1\relax} 2489\permanent\protected\def\doquadrupleempty #1{\syst_quadruple_empty #1\relax} 2490\permanent\protected\def\doquintupleempty #1{\syst_quintuple_empty #1\relax} 2491\permanent\protected\def\dosixtupleempty #1{\syst_sixtuple_empty #1\relax} 2492\permanent\protected\def\doseventupleempty#1{\syst_seventuple_empty#1\relax} 2493 2494%D Aliases: 2495 2496\aliased\let\dosingleargument \dosingleempty 2497\aliased\let\dodoubleargument \dodoubleempty 2498\aliased\let\dotripleargument \dotripleempty 2499\aliased\let\doquadrupleargument \doquadrupleempty 2500\aliased\let\doquintupleargument \doquintupleempty 2501\aliased\let\dosixtupleargument \dosixtupleempty 2502\aliased\let\doseventupleargument\doseventupleempty 2503 2504%D \macros 2505%D {strippedcsname} 2506%D 2507%D The next macro can be very useful when using \type{\csname} like in: 2508%D 2509%D \starttyping 2510%D \csname if\strippedcsname\something\endcsname 2511%D \stoptyping 2512%D 2513%D This expands to \type{\ifsomething}. 2514 2515\aliased\let\strippedcsname\csstring 2516 2517%D \macros 2518%D {complexorsimple,complexorsimpleempty} 2519%D 2520%D Setups can be optional. A command expecting a setup is prefixed by \type 2521%D {\complex}, a command without one gets the prefix \type {\simple}. Commands like 2522%D this can be defined by: 2523%D 2524%D \starttyping 2525%D \complexorsimple\command 2526%D \stoptyping 2527%D 2528%D When \type{\command} is followed by a \type{[setup]}, then 2529%D 2530%D \starttyping 2531%D \complexcommand [setup] 2532%D \stoptyping 2533%D 2534%D executes, else we get 2535%D 2536%D \starttyping 2537%D \simplecommand 2538%D \stoptyping 2539%D 2540%D An alternative for \type{\complexorsimple} is: 2541%D 2542%D \starttyping 2543%D \complexorsimpleempty {command} 2544%D \stoptyping 2545%D 2546%D Depending on the presence of \type{[setup]}, this one leads to one of: 2547%D 2548%D \starttyping 2549%D \complexcommando [setup] 2550%D \complexcommando [] 2551%D \stoptyping 2552%D 2553%D Many \CONTEXT\ commands started as complex or simple ones, but changed into more 2554%D versatile (more object oriented) ones using the \type {\get..argument} commands. 2555 2556\permanent\protected\def\complexorsimple#1% 2557 {\doifelsenextoptional 2558 {\firstargumenttrue \csname\s!complex\csstring#1\endcsname} 2559 {\firstargumentfalse\csname\s!simple \csstring#1\endcsname}} 2560 2561\permanent\protected\def\complexorsimpleempty#1% 2562 {\doifelsenextoptional 2563 {\firstargumenttrue \csname\s!complex\csstring#1\endcsname} 2564 {\firstargumentfalse\csname\s!complex\csstring#1\endcsname[]}} 2565 2566%D \macros 2567%D {definecomplexorsimple,definecomplexorsimpleempty} 2568%D 2569%D The previous commands are used that often that we found it worthwile to offer two 2570%D more alternatives. Watch the build in protection. 2571 2572\protected\def\syst_helpers_complex_or_simple#1#2% 2573 {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#2}} 2574 2575\protected\def\syst_helpers_complex_or_simple_empty#1% 2576 {\doifelsenextoptional{\firstargumenttrue#1}{\firstargumentfalse#1[]}} 2577 2578\permanent\protected\def\definecomplexorsimple#1% 2579 {\protected\edef#1{\syst_helpers_complex_or_simple 2580 \expandafter\noexpand\csname\s!complex\csstring#1\endcsname 2581 \expandafter\noexpand\csname\s!simple \csstring#1\endcsname}} 2582 2583\permanent\protected\def\definecomplexorsimpleempty#1% 2584 {\protected\edef#1{\syst_helpers_complex_or_simple_empty 2585 \expandafter\noexpand\csname\s!complex\csstring#1\endcsname}} 2586 2587%D These commands are called as: 2588%D 2589%D \starttyping 2590%D \definecomplexorsimple\command 2591%D \stoptyping 2592%D 2593%D Of course, we must have available 2594%D 2595%D \starttyping 2596%D \def\complexcommand[#1]{...} 2597%D \def\simplecommand {...} 2598%D \stoptyping 2599%D 2600%D Using this construction saves a few string now and then. 2601 2602%D \macros 2603%D {dosinglegroupempty,dodoublegroupempty,dotriplegroupempty, 2604%D doquadruplegroupempty, doquintuplegroupempty} 2605%D 2606%D We've already seen some commands that take care of 2607%D optional arguments between \type{[]}. The next two commands 2608%D handle the ones with \type{{}}. They are called as: 2609%D 2610%D \starttyping 2611%D \dosinglegroupempty \ineedONEargument 2612%D \dodoublegroupempty \ineedTWOarguments 2613%D \dotriplegroupempty \ineedTHREEarguments 2614%D \doquadruplegroupempty \ineedFOURarguments 2615%D \doquintuplegroupempty \ineedFIVEarguments 2616%D \stoptyping 2617%D 2618%D We can add additional definitions later when we have defined \type {\appendtoks}. 2619 2620\newconditional\c_syst_helpers_permit_spaces_between_groups 2621 2622\permanent\protected\def \permitspacesbetweengroups{\c_syst_helpers_permit_spaces_between_groups\conditionaltrue} 2623\permanent\protected\def\dontpermitspacesbetweengroups{\c_syst_helpers_permit_spaces_between_groups\conditionalfalse} 2624 2625\dontpermitspacesbetweengroups 2626 2627%D We can avoid the nasty if handling in \type {syst-gen} by splitting the lot in 2628%D pieces so that we have no nested \type {\nextarguments} potentially being an 2629%D \type {conditional} token. Okay, these macros are not called that often but it 2630%D saves crap when tracing. 2631 2632% \protected\def\dosinglegroupempty#1% 2633% {\t_syst_aux{#1}% 2634% \futureexpand\bgroup\syst_helpers_single_empty_one_yes\syst_helpers_single_group_empty_one_nop} 2635% 2636% \def\syst_helpers_single_group_empty_one_nop 2637% {\firstargumentfalse 2638% \the\t_syst_aux{}} 2639% 2640% \protected\def\dodoublegroupempty#1% 2641% {\t_syst_aux{#1}% 2642% \futureexpand\bgroup\syst_helpers_group_double_empty_one_yes\syst_helpers_group_double_empty_one_nop} 2643% 2644% % \def\syst_helpers_group_double_empty_one_yes#1% 2645% % {\firstargumenttrue 2646% % \toksapp\t_syst_aux{{#1}}% 2647% % \futureexpand\bgroup\syst_helpers_double_empty_two_yes\syst_helpers_group_double_empty_two_nop} 2648% % 2649% % nicer is: 2650% 2651% \def\syst_helpers_group_double_empty_one_yes#+% 2652% {\firstargumenttrue 2653% \toksapp\t_syst_aux{#1}% 2654% \futureexpand\bgroup\syst_helpers_double_empty_two_yes\syst_helpers_group_double_empty_two_nop} 2655% 2656% \def\syst_helpers_group_double_empty_one_nop 2657% {\firstargumentfalse 2658% \secondargumentfalse 2659% \the\t_syst_aux{}{}} 2660% 2661% \def\syst_helpers_group_double_empty_two_nop 2662% {\secondargumentfalse 2663% \the\t_syst_aux{}} 2664% 2665% \protected\def\dotriplegroupempty#1% 2666% {\t_syst_aux{#1}% 2667% \futureexpand\bgroup\syst_helpers_group_triple_empty_one_yes\syst_helpers_group_triple_empty_one_nop} 2668% 2669% \def\syst_helpers_group_triple_empty_one_yes#+% 2670% {\firstargumenttrue 2671% \toksapp\t_syst_aux{#1}% 2672% \futureexpand\bgroup\syst_helpers_group_triple_empty_two_yes\syst_helpers_group_triple_empty_two_nop} 2673% 2674% \def\syst_helpers_group_triple_empty_two_yes#+% 2675% {\secondargumenttrue 2676% \toksapp\t_syst_aux{#1}% 2677% \futureexpand\bgroup\syst_helpers_triple_empty_three_yes\syst_helpers_group_triple_empty_three_nop} 2678% 2679% \def\syst_helpers_group_triple_empty_one_nop 2680% {\firstargumentfalse 2681% \secondargumentfalse 2682% \thirdargumentfalse 2683% \the\t_syst_aux{}{}{}} 2684% 2685% \def\syst_helpers_group_triple_empty_two_nop 2686% {\secondargumentfalse 2687% \thirdargumentfalse 2688% \the\t_syst_aux{}{}} 2689% 2690% \def\syst_helpers_group_triple_empty_three_nop 2691% {\thirdargumentfalse 2692% \the\t_syst_aux{}} 2693% 2694% \protected\def\doquadruplegroupempty#1% 2695% {\t_syst_aux{#1}% 2696% \futureexpand\bgroup\syst_helpers_group_quadruple_empty_one_yes\syst_helpers_group_quadruple_empty_one_nop} 2697% 2698% \def\syst_helpers_group_quadruple_empty_one_yes#+% 2699% {\firstargumenttrue 2700% \toksapp\t_syst_aux{#1}% 2701% \futureexpand\bgroup\syst_helpers_group_quadruple_empty_two_yes\syst_helpers_group_quadruple_empty_two_nop} 2702% 2703% \def\syst_helpers_group_quadruple_empty_two_yes#+% 2704% {\secondargumenttrue 2705% \toksapp\t_syst_aux{#1}% 2706% \futureexpand\bgroup\syst_helpers_group_quadruple_empty_three_yes\syst_helpers_group_quadruple_empty_three_nop} 2707% 2708% \def\syst_helpers_group_quadruple_empty_three_yes#+% 2709% {\thirdargumenttrue 2710% \toksapp\t_syst_aux{#1}% 2711% \futureexpand\bgroup\syst_helpers_quadruple_empty_four_yes\syst_helpers_group_quadruple_empty_four_nop} 2712% 2713% \def\syst_helpers_group_quadruple_empty_one_nop 2714% {\firstargumentfalse 2715% \secondargumentfalse 2716% \thirdargumentfalse 2717% \fourthargumentfalse 2718% \the\t_syst_aux{}{}{}{}} 2719% 2720% \def\syst_helpers_group_quadruple_empty_two_nop 2721% {\secondargumentfalse 2722% \thirdargumentfalse 2723% \fourthargumentfalse 2724% \the\t_syst_aux{}{}{}} 2725% 2726% \def\syst_helpers_group_quadruple_empty_three_nop 2727% {\thirdargumentfalse 2728% \fourthargumentfalse 2729% \the\t_syst_aux{}{}} 2730% 2731% \def\syst_helpers_group_quadruple_empty_four_nop 2732% {\fourthargumentfalse 2733% \the\t_syst_aux{}} 2734% 2735% \protected\def\doquintuplegroupempty#1% 2736% {\t_syst_aux{#1}% 2737% \futureexpand\bgroup\syst_helpers_group_quintuple_empty_one_yes\syst_helpers_group_quintuple_empty_one_nop} 2738% 2739% \def\syst_helpers_group_quintuple_empty_one_yes#+% 2740% {\firstargumenttrue 2741% \toksapp\t_syst_aux{#1}% 2742% \futureexpand\bgroup\syst_helpers_group_quintuple_empty_two_yes\syst_helpers_group_quintuple_empty_two_nop} 2743% 2744% \def\syst_helpers_group_quintuple_empty_two_yes#+% 2745% {\secondargumenttrue 2746% \toksapp\t_syst_aux{#1}% 2747% \futureexpand\bgroup\syst_helpers_group_quintuple_empty_three_yes\syst_helpers_group_quintuple_empty_three_nop} 2748% 2749% \def\syst_helpers_group_quintuple_empty_three_yes#+% 2750% {\thirdargumenttrue 2751% \toksapp\t_syst_aux{#1}% 2752% \futureexpand\bgroup\syst_helpers_group_quintuple_empty_four_yes\syst_helpers_group_quintuple_empty_four_nop} 2753% 2754% \def\syst_helpers_group_quintuple_empty_four_yes#+% 2755% {\fourthargumenttrue 2756% \toksapp\t_syst_aux{#1}% 2757% \futureexpand\bgroup\syst_helpers_quintuple_empty_five_yes\syst_helpers_group_quintuple_empty_five_nop} 2758% 2759% \def\syst_helpers_group_quintuple_empty_one_nop 2760% {\firstargumentfalse 2761% \secondargumentfalse 2762% \thirdargumentfalse 2763% \fourthargumentfalse 2764% \fifthargumentfalse 2765% \the\t_syst_aux{}{}{}{}{}} 2766% 2767% \def\syst_helpers_group_quintuple_empty_two_nop 2768% {\secondargumentfalse 2769% \thirdargumentfalse 2770% \fourthargumentfalse 2771% \fifthargumentfalse 2772% \the\t_syst_aux{}{}{}{}} 2773% 2774% \def\syst_helpers_group_quintuple_empty_three_nop 2775% {\thirdargumentfalse 2776% \fourthargumentfalse 2777% \fifthargumentfalse 2778% \the\t_syst_aux{}{}{}} 2779% 2780% \def\syst_helpers_group_quintuple_empty_four_nop 2781% {\fourthargumentfalse 2782% \fifthargumentfalse 2783% \the\t_syst_aux{}{}} 2784% 2785% \def\syst_helpers_group_quintuple_empty_five_nop 2786% {\fifthargumentfalse 2787% \the\t_syst_aux{}} 2788 2789\tolerant\def\syst_single_group_empty#1\relax#_% 2790 {\ifarguments 2791 \or\firstargumentfalse 2792 \or\firstargumenttrue 2793 \fi 2794 #1#2} 2795 2796\tolerant\def\syst_double_group_empty#1\relax#_#*#_% 2797 {\ifarguments 2798 \or\firstargumentfalse\secondargumentfalse 2799 \or\firstargumenttrue \secondargumentfalse 2800 \or\firstargumenttrue \secondargumenttrue 2801 \fi 2802 #1#2#3} 2803 2804\tolerant\def\syst_triple_group_empty#1\relax#_#*#_#*#_% 2805 {\ifarguments 2806 \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse 2807 \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse 2808 \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse 2809 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue 2810 \fi 2811 #1#2#3#4} 2812 2813\tolerant\def\syst_quadruple_group_empty#1\relax#_#*#_#*#_#*#_% 2814 {\ifarguments 2815 \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse 2816 \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse 2817 \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse 2818 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse 2819 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue 2820 \fi 2821 #1#2#3#4#5} 2822 2823\tolerant\def\syst_quintuple_group_empty#1\relax#_#*#_#*#_#*#_#*#_% 2824 {\ifarguments 2825 \or\firstargumentfalse\secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse 2826 \or\firstargumenttrue \secondargumentfalse\thirdargumentfalse\fourthargumentfalse\fifthargumentfalse 2827 \or\firstargumenttrue \secondargumenttrue \thirdargumentfalse\fourthargumentfalse\fifthargumentfalse 2828 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumentfalse\fifthargumentfalse 2829 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumentfalse 2830 \or\firstargumenttrue \secondargumenttrue \thirdargumenttrue \fourthargumenttrue \fifthargumenttrue 2831 \fi 2832 #1#2#3#4#5#6} 2833 2834\permanent\protected\def\dosinglegroupempty #1{\syst_single_group_empty #1\relax} 2835\permanent\protected\def\dodoublegroupempty #1{\syst_double_group_empty #1\relax} 2836\permanent\protected\def\dotriplegroupempty #1{\syst_triple_group_empty #1\relax} 2837\permanent\protected\def\doquadruplegroupempty #1{\syst_quadruple_group_empty #1\relax} 2838\permanent\protected\def\doquintuplegroupempty #1{\syst_quintuple_group_empty #1\relax} 2839 2840%D These macros can explictly take care of spaces, which means that the next 2841%D definition and calls are valid: 2842%D 2843%D \starttyping 2844%D \def\test#1#2#3{[#1#2#3]} 2845%D 2846%D \dotriplegroupempty\test {a}{b}{c} 2847%D \dotriplegroupempty\test {a}{b} 2848%D \dotriplegroupempty\test {a} 2849%D \dotriplegroupempty\test 2850%D \dotriplegroupempty\test {a} {b} {c} 2851%D \dotriplegroupempty\test {a} {b} 2852%D \dotriplegroupempty\test 2853%D {a} 2854%D {b} 2855%D \stoptyping 2856%D 2857%D And alike. 2858 2859%D \macros 2860%D {firstofoneargument, firstoftwoarguments, firstofthreearguments 2861%D secondoftwoarguments, secondofthreearguments, 2862%D thirdofthreearguments} 2863%D 2864%D The next six macros (dedicated to Taco) can conveniently used to select 2865%D arguments. Their names explain their functionality. 2866 2867\permanent\def\firstofoneargument #1{#1} 2868 2869\permanent\def\firstoftwoarguments #1#-{#1} 2870\permanent\def\secondoftwoarguments #-#1{#1} 2871 2872\permanent\def\firstofthreearguments #1#-#-{#1} 2873\permanent\def\secondofthreearguments #-#1#-{#1} 2874\permanent\def\thirdofthreearguments #-#-#1{#1} 2875 2876\permanent\def\firstoffourarguments #1#-#-#-{#1} 2877\permanent\def\secondoffourarguments #-#1#-#-{#1} 2878\permanent\def\thirdoffourarguments #-#-#1#-{#1} 2879\permanent\def\fourthoffourarguments #-#-#-#1{#1} 2880 2881\permanent\def\firstoffivearguments #1#-#-#-#-{#1} 2882\permanent\def\secondoffivearguments #-#1#-#-#-{#1} 2883\permanent\def\thirdoffivearguments #-#-#1#-#-{#1} 2884\permanent\def\fourthoffivearguments #-#-#-#1#-{#1} 2885\permanent\def\fifthoffivearguments #-#-#-#-#1{#1} 2886 2887\permanent\def\firstofsixarguments #1#-#-#-#-#-{#1} 2888\permanent\def\secondofsixarguments#-#1#-#-#-#-{#1} 2889\permanent\def\thirdofsixarguments #-#-#1#-#-#-{#1} 2890\permanent\def\fourthofsixarguments#-#-#-#1#-#-{#1} 2891\permanent\def\fifthofsixarguments #-#-#-#-#1#-{#1} 2892\permanent\def\sixthofsixarguments #-#-#-#-#-#1{#1} 2893 2894\permanent\protected\def\firstofoneunexpanded #1{#1} 2895 2896\permanent\protected\def\firstoftwounexpanded #1#-{#1} 2897\permanent\protected\def\secondoftwounexpanded #-#1{#1} 2898 2899\permanent\protected\def\firstofthreeunexpanded #1#-#-{#1} 2900\permanent\protected\def\secondofthreeunexpanded#-#1#-{#1} 2901\permanent\protected\def\thirdofthreeunexpanded #-#-#1{#1} 2902 2903%D \macros 2904%D {globalletempty,letempty, 2905%D letvalueempty,letgvalueempty, 2906%D letvaluerelax,letgvaluerelax} 2907%D 2908%D Trivial: 2909 2910\permanent\protected\def\letempty #1{\lettonothing #1} 2911\permanent\protected\def\globalletempty#1{\glettonothing#1} 2912 2913\permanent\protected\def\letvalueempty #1{\letcsname #1\endcsname\empty} 2914\permanent\protected\def\letgvalueempty#1{\gletcsname#1\endcsname\empty} 2915\permanent\protected\def\letvaluerelax #1{\letcsname #1\endcsname\relax} 2916\permanent\protected\def\letgvaluerelax#1{\gletcsname#1\endcsname\relax} 2917 2918\permanent\protected\def\relaxvalueifundefined#1% 2919 {\ifcsname#1\endcsname \else 2920 \aliased\letcsname#1\endcsname\relax 2921 \fi} 2922 2923%D \macros 2924%D {wait} 2925%D 2926%D The next macro hardly needs explanation. Because no nesting is to be expected, we 2927%D can reuse \type {\wait} within \type {\wait} itself. 2928 2929\permanent\protected\def\wait 2930 {\begingroup 2931 \read16 to \wait 2932 \endgroup} 2933 2934% %D \macros 2935% %D {writestring,writeline,writestatus} 2936% %D 2937% %D Maybe one didn't notice, but we've already introduced a macro for showing 2938% %D messages. In the multi||lingual modules, we will also introduce a mechanism for 2939% %D message passing. For the moment we stick to the core macros: 2940% %D 2941% %D \starttyping 2942% %D \writestring {string} 2943% %D \writeline 2944% %D \writestatus {category} {message} 2945% %D \stoptyping 2946% %D 2947% %D Messages are formatted. One can provide the maximum with of the identification 2948% %D string with the macro \type {\statuswidth}. 2949% 2950% \setnewconstant\statuswidth 15 2951% \setnewconstant\statuswrite 128 % \pluscxxviii 2952% 2953% \ifdefined\writestring \else 2954% 2955% \protected\def\writestring{\immediate\write\statuswrite} 2956% \protected\def\writeline {\writestring{}} 2957% 2958% \fi 2959% 2960% \protected\def\normalwritestatus#1#2% 2961% {\writestring{\expandafter\syst_helpers_split_status_yes\expandafter\statuswidth#1% 2962% \space\space\space\space\space\space\space 2963% \space\space\space\space\space\space\space 2964% \space\space\space\space\space\space\end 2965% \space:\space#2}} 2966% 2967% \def\syst_helpers_split_status_yes#1#2% 2968% {\ifcase#1 \expandafter\syst_helpers_split_status_nop\fi#2% 2969% \expandafter\syst_helpers_split_status_yes\expandafter{\the\numexpr#1+\minusone\relax}} 2970% 2971% \def\syst_helpers_split_status_nop#1\end 2972% {} 2973 2974%D \macros 2975%D {immediatemessage} 2976%D 2977%D A fully expandable message: 2978 2979% \immediatemessage % {} mandate / defined at the lua end 2980 2981%D \macros 2982%D {rawgetparameters} 2983%D 2984%D A raw and dirty alternative for \type {\getparameters}; no checking is done! 2985 2986\def\syst_helpers_grab_raw_parameter#1=#2,% 2987 {\ifarguments\or\or 2988 \defcsname\m_syst_parameter_n#1\endcsname{#2}% 2989 \expandafter\syst_helpers_grab_raw_parameter_next 2990 \fi} 2991 2992\def\syst_helpers_grab_raw_parameter_next 2993 {\expandafterspaces\syst_helpers_grab_raw_parameter} 2994 2995\permanent\protected\def\rawgetparameters#1[#2]#*[#3]% 2996 {\def\m_syst_parameter_n{#2}% 2997 %\expandafterspaces\syst_helpers_grab_raw_parameter#3\ignorearguments\ignorearguments} 2998 \expandafter\expandafterspaces\expandafter\syst_helpers_grab_raw_parameter#3\ignorearguments\ignorearguments} 2999 3000%D \macros 3001%D {doglobal, 3002%D redoglobal,dodoglobal,resetglobal} 3003%D 3004%D The two macros \type {\redoglobal} and \type{\dodoglobal} are used in this and 3005%D some other modules to enforce a user specified \type {\doglobal} action. The last 3006%D and often only global assignment in a macro is done with \type {\dodoglobal}, but 3007%D all preceding ones with \type {\redoglobal}. When using only alternatives, one 3008%D can reset this mechanism with \type {\resetglobal}. 3009 3010\permanent\protected\def\resetglobal 3011 {\enforced\let\redoglobal\relax 3012 \enforced\let\dodoglobal\relax} 3013 3014\resetglobal 3015 3016\permanent\protected\def\doglobal 3017 {\ifrelax\redoglobal 3018 \enforced\let\redoglobal\global 3019 \enforced\let\dodoglobal\syst_helpers_dodo_global 3020 \fi} 3021 3022\def\syst_helpers_dodo_global 3023 {\resetglobal\global} 3024 3025\let\syst_helpers_redo_global\relax 3026 3027\permanent\protected\def\saveglobal 3028 {\let\syst_helpers_dodo_global\dodoglobal 3029 \let\syst_helpers_redo_global\redoglobal} 3030 3031\permanent\protected\def\restoreglobal 3032 {\enforced\let\redoglobal\syst_helpers_redo_global 3033 \enforced\let\dodoglobal\syst_helpers_dodo_global} 3034 3035% Maybe: 3036% 3037% \installmacrostack\globaldefs 3038% 3039% \protected\def\start_global 3040% {\push_macro_globaldefs 3041% \globaldefs\plusone} 3042% 3043% \protected\def\stop_global 3044% {\pop_macro_globaldefs} 3045 3046%D Whatever (will be overloaded): 3047 3048\protected\def\define#1% 3049 {\ifdefined#1% 3050 \message{[\noexpand#1is already defined]}% 3051 \protected\expandafter\def\expandafter\gobbleddefinition 3052 \else 3053 \protected\expandafter\def 3054 \fi#1} 3055 3056\protected\def\redefine#1% 3057 {\ifdefined#1% 3058 \message{[\noexpand#1is redefined]}% 3059 \fi 3060 \protected\def#1} 3061 3062% \permanent\protected\def\definemacro#1% 3063% {\ifdefined#1% 3064% \message{[\noexpand#1is already defined]}% 3065% \protected\expandafter\def\expandafter\gobbleddefinition 3066% \else 3067% \protected\expandafter\def 3068% \fi#1} 3069 3070% \define\hans{hans} 3071% \redefine\hans{hans} 3072% \define\hans#1[]#2#3{hans} 3073 3074%D The next variant fits nicely in the setups syntax: 3075%D 3076%D \starttyping 3077%D \starttexdefinition bagger [#1] #2 3078%D oeps 3079%D #1 3080%D oeps 3081%D \stoptexdefinition 3082%D 3083%D \bagger [a] {b} 3084%D \stoptyping 3085 3086% \starttexdefinition test 3087% oeps 3088% \stoptexdefinition 3089% 3090% [\test] 3091 3092%D When okay the parameter hack will also go into \MKIV. 3093 3094\integerdef\c_syst_parameter_catcode\parametercatcode 3095 3096% \catcode\endoflineasciicode\activecatcode\letcharcode\endoflineasciicode\par 3097 3098\bgroup 3099 3100\catcode\endoflineasciicode\activecatcode% 3101\letcharcode\endoflineasciicode\par% 3102 3103\permanent\protected\gdef\starttexdefinition% 3104 {\integerdef\c_syst_parameter_catcode\catcode\hashasciicode% 3105 \catcode\hashasciicode\parametercatcode% 3106 \bgroup% 3107 \catcode\endoflineasciicode\activecatcode% 3108 \letcharcode\endoflineasciicode\par% 3109 \syst_helpers_start_tex_definition} 3110 3111\gdef\syst_helpers_start_tex_definition#1 3112 {%catcodetable\ctxcatcodes% can be adapted 3113 \catcode\endoflineasciicode\ignorecatcode% 3114 {#1}} 3115 3116\permanent\protected\glettonothing\stoptexdefinition 3117 3118\permanent\gdef\dostarttexdefinition#1\stoptexdefinition% 3119 {\egroup% 3120 {#1}% 3121 \catcode\hashasciicode\c_syst_parameter_catcode} 3122 3123\egroup 3124 3125% \protected\def\texdefinition#1{\csname\ifcsname#1\endcsname#1\else donothing\fi\endcsname} % todo: a nop cs: char 0 or some corenamespace 3126 3127\permanent\protected\def\texdefinition#1{\begincsname#1\endcsname} 3128 3129%D \macros 3130%D {newcounter, 3131%D increment,decrement} 3132%D 3133%D Unfortunately the number of \COUNTERS\ in \TEX\ is limited, but fortunately we 3134%D can store numbers in a macro. We can increment such pseudo \COUNTERS\ with \type 3135%D {\increment}. 3136%D 3137%D \starttyping 3138%D \increment(\counter,20) 3139%D \increment(\counter,-4) 3140%D \increment(\counter) 3141%D \increment\counter 3142%D \stoptyping 3143%D 3144%D After this sequence of commands, the value of \type {\counter} is 20, 16, 17 3145%D and~18. Of course there is also the complementary command \type {\decrement}. 3146%D 3147%D Global assignments are possible too, using \type{\doglobal}: 3148%D 3149%D \starttyping 3150%D \doglobal\increment\counter 3151%D \stoptyping 3152%D 3153%D When \type {\counter} is undefined, it's value is initialized at~0. It is 3154%D nevertheless better to define a \COUNTER\ explicitly. One reason could be that 3155%D the \COUNTER\ can be part of a test with \type {\ifnum} and this conditional does 3156%D not accept undefined macro's. The \COUNTER\ in our example can for instance be 3157%D defined with: 3158%D 3159%D \starttyping 3160%D \newcounter\counter 3161%D \stoptyping 3162%D 3163%D The command \type {\newcounter} must not be confused with \type {\newcount}! Of 3164%D course this mechanism is much slower than using \TEX's \COUNTERS\ directly. In 3165%D practice \COUNTERS\ (and therefore our pseudo counters too) are seldom the 3166%D bottleneck in the processing of a text. Apart from some other incompatilities we 3167%D want to mention a pitfal when using \type {\ifnum}. 3168%D 3169%D \starttyping 3170%D \ifnum\normalcounter=\pseudocounter \doif \else \doelse \fi 3171%D \ifnum\pseudocounter=\normalcounter \doif \else \doelse \fi 3172%D \stoptyping 3173%D 3174%D In the first test, \TEX\ continues it's search for the second number after 3175%D reading \type {\pseudocounter}, while in the second test, it stops reading after 3176%D having encountered a real one. Tests like the first one therefore can give 3177%D unexpected results, for instance execution of \type {\doif} even if both numbers 3178%D are unequal. 3179 3180\immutable\def\zerocountervalue{0} 3181 3182\permanent\protected\def\newcounter#1% 3183 {\dodoglobal\let#1\zerocountervalue} 3184 3185%D Nowadays we don't mind a few more tokens if we can gain a bit of speed. 3186 3187\def\syst_helpers_do_increment#1{\dodoglobal\edef#1{\tointeger{\ifdefined#1\ifrelax#1\else#1\fi\fi+\plusone }}} 3188\def\syst_helpers_do_decrement#1{\dodoglobal\edef#1{\tointeger{\ifdefined#1\ifrelax#1\else#1\fi\fi+\minusone}}} 3189 3190\def\syst_helpers_do_do_do_increment#1,#2){\dodoglobal\edef#1{\tointeger{\ifdefined#1\ifrelax#1\else#1\fi\fi+#2}}} 3191\def\syst_helpers_do_do_do_decrement#1,#2){\dodoglobal\edef#1{\tointeger{\ifdefined#1\ifrelax#1\else#1\fi\fi-#2}}} 3192 3193\def\syst_helpers_do_do_increment(#1% 3194 {\def\m_syst_action_yes{\syst_helpers_do_do_do_increment#1}% 3195 \def\m_syst_action_nop{\syst_helpers_do_do_do_increment#1,\plusone}% 3196 \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop} 3197 3198\def\syst_helpers_do_do_decrement(#1% 3199 {\def\m_syst_action_yes{\syst_helpers_do_do_do_decrement#1}% 3200 \def\m_syst_action_nop{\syst_helpers_do_do_do_decrement#1,\plusone}% 3201 \doifelsenextcharcs,\m_syst_action_yes\m_syst_action_nop} 3202 3203\permanent\protected\def\increment{\doifelsenextcharcs(\syst_helpers_do_do_increment\syst_helpers_do_increment} 3204\permanent\protected\def\decrement{\doifelsenextcharcs(\syst_helpers_do_do_decrement\syst_helpers_do_decrement} 3205 3206\permanent\protected\def\fastincrement#1{\dodoglobal\edef#1{\tointeger{#1+\plusone }}} 3207\permanent\protected\def\fastdecrement#1{\dodoglobal\edef#1{\tointeger{#1+\minusone}}} 3208 3209\permanent\protected\def\incrementvalue#1{\expandafter\increment\csname#1\endcsname} 3210\permanent\protected\def\decrementvalue#1{\expandafter\decrement\csname#1\endcsname} 3211 3212%D \macros 3213%D {newsignal} 3214%D 3215%D When writing advanced macros, we cannot do without signaling. A signal is a small 3216%D (invisible) kern or penalty that signals the next macro that something just 3217%D happened. This macro can take any action depending on the previous signal. 3218%D Signals must be unique and the next macro takes care of that. 3219%D 3220%D \starttyping 3221%D \newsignal\somesignal 3222%D \stoptyping 3223%D 3224%D Signals old dimensions and can be used in skips, kerns and tests like \type 3225%D {\ifdim}. 3226 3227\newdimension\d_syst_maximum_signal % step is about 0.00025pt 3228 3229\permanent\protected\def\newsignal#1% 3230 {\ifdefined#1\else 3231 \advanceby\d_syst_maximum_signal2\scaledpoint % to be save in rounding 3232 \immutable\dimensiondef#1\d_syst_maximum_signal 3233 \fi} 3234 3235%D \macros 3236%D {strippedcsname} 3237%D 3238%D The next macro can be very useful when using \type {\csname} like in: 3239%D 3240%D \starttyping 3241%D \csname if\strippedcsname\something\endcsname 3242%D \stoptyping 3243 3244\aliased\let\checkedstrippedcsname\csstring 3245 3246%D \macros 3247%D {savenormalmeaning} 3248%D 3249%D We will use this one in: 3250 3251\permanent\protected\def\savenormalmeaning#1% 3252 {\ifcsname normal\csstring#1\endcsname \else 3253 \aliased\letcsname normal\csstring#1\endcsname#1% 3254 \fi} 3255 3256%D \macros 3257%D {dorecurse,recurselevel,recursedepth, 3258%D dostepwiserecurse} 3259%D 3260%D \TEX\ does not offer us powerfull for||loop mechanisms. On the other hand its 3261%D recursion engine is quite unique. We therefore identify the for||looping macros 3262%D by this method. The most simple alternative is the one that only needs a number. 3263%D 3264%D \starttyping 3265%D \dorecurse {n} {whatever we want} 3266%D \stoptyping 3267%D 3268%D This macro can be nested without problems and therefore be used in situations 3269%D where \PLAIN\ \TEX's \type {\loop} macro ungracefully fails. The current value of 3270%D the counter is available in \type {\recurselevel}, before as well as after the 3271%D \typ {whatever we wat} stuff. 3272%D 3273%D \starttyping 3274%D \dorecurse % inner loop 3275%D {10} 3276%D {\recurselevel: % outer value 3277%D \dorecurse % inner loop 3278%D {\recurselevel} % outer value 3279%D {\recurselevel} % inner value 3280%D \dorecurse % inner loop 3281%D {\recurselevel} % outer value 3282%D {\recurselevel} % inner value 3283%D \endgraf} 3284%D \stoptyping 3285%D 3286%D In this example the first, second and fourth \type {\recurselevel} concern the 3287%D outer loop, while the third and fifth one concern the inner loop. The depth of 3288%D the nesting is available for inspection in \type {\recursedepth}. 3289%D 3290%D Both \type {\recurselevel} and \type {\recursedepth} are macros. The real 3291%D \COUNTERS\ are hidden from the user because we don't want any interference. 3292%D 3293%D We now use the macro stack which is somewhat leaner and meaner and a little 3294%D faster too. 3295 3296\newinteger\outerrecurse 3297\newinteger\innerrecurse 3298 3299% \mutable\def\recursedepth{\the\outerrecurse} 3300% \mutable\def\recurselevel{0} 3301 3302\mutable\untraced\def\recursedepth{\the\outerrecurse} 3303\mutable\untraced\def\recurselevel{0} 3304 3305\mutable\let\recurseaction\relax 3306 3307\mutable\lettonothing\recursestring 3308 3309%% \protected\def\syst_helpers_stepwise_recurse#1#2#3% from to step 3310%% {\ifnum#1>#2\relax 3311%% \expandafter\gobblefourarguments 3312%% \else 3313%% \def\recurselevel{#1}% 3314%% \doubleexpandafter\syst_helpers_stepwise_recurse_yes 3315%% \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}} 3316%% 3317%% \protected\def\syst_helpers_stepwise_recurse_yes 3318%% {\syst_helpers_recurse_content 3319%% \syst_helpers_stepwise_recurse} 3320%% 3321%% \protected\def\syst_helpers_stepwise_reverse#1#2#3% from to step 3322%% {\ifnum#1<#2\relax 3323%% \expandafter\gobblefourarguments 3324%% \else 3325%% \def\recurselevel{#1}% 3326%% \innerrecurse#1\relax 3327%% \advanceby\innerrecurse#3\relax 3328%% \doubleexpandafter\syst_helpers_stepwise_reverse_yes 3329%% \fi\expandafter{\the\numexpr\recurselevel+#3\relax}{#2}{#3}} 3330%% 3331%% \protected\def\syst_helpers_stepwise_reverse_yes 3332%% {\syst_helpers_recurse_content 3333%% \syst_helpers_stepwise_reverse} 3334%% 3335%% \permanent\def\doexpandedrecurse#1#2% user macro (also was \doxprecurse) 3336%% {\ifnum#1>\zerocount 3337%% #2\expandafter\doexpandedrecurse\expandafter{\the\numexpr#1-\plusone\relax}{#2}% 3338%% \fi} 3339 3340%D The next one might replace the above and provides the current step in \type {#1} 3341%D like \type {\dorecurse} do, but it comes with a tiny performance hit. 3342 3343%% \installsystemnamespace{expandedrecurse} 3344%% 3345%% \mutable\let\m_expanded_recursed\gobbleoneargument 3346%% 3347%% \permanent\def\doexpandedrecursed#1#2% this might replace: \doexpandedrecurse 3348%% {\beginlocalcontrol 3349%% \localpushmacro\m_expanded_recursed 3350%% \def\m_expanded_recursed##1{#2}% 3351%% \endlocalcontrol 3352%% \syst_do_expanded_recursed{1}{#1}{#2}% no \plusone ! 3353%% \beginlocalcontrol 3354%% \localpopmacro\m_expanded_recursed 3355%% \endlocalcontrol} 3356%% 3357%% \def\syst_do_expanded_recursed#1#2#3% 3358%% {\ifnum#1>#2\norelax 3359%% \expandafter\gobblethreearguments 3360%% \else 3361%% \m_expanded_recursed{#1}\doubleexpandafter\syst_do_expanded_recursed 3362%% \fi 3363%% {\the\numexpr#1+\plusone\relax}{#2}{#3}} 3364 3365%D As we can see here, the simple command \type{\dorecurse} is a special case of the 3366%D more general: 3367%D 3368%D \starttyping 3369%D \dostepwiserecurse {from} {to} {step} {action} 3370%D \stoptyping 3371%D 3372%D This commands accepts positive and negative steps. Illegal values are handles as 3373%D good as possible and the macro accepts numbers and \COUNTERS. 3374%D 3375%D \starttyping 3376%D \dostepwiserecurse {1} {10} {2} {...} 3377%D \dostepwiserecurse {10} {1} {-2} {...} 3378%D \stoptyping 3379%D 3380%D Because the simple case (n=1) is used often, we implement it more efficiently: 3381 3382%% \permanent\protected\def\dorecurse#1% 3383%% {\ifcase#1\relax 3384%% \expandafter\gobbletwoarguments 3385%% \or 3386%% \expandafter\syst_helpers_recurse_y 3387%% \else 3388%% \expandafter\syst_helpers_recurse_x 3389%% \fi{#1}} 3390%% 3391%% \protected\def\syst_helpers_recurse_indeed#1#2% from to 3392%% % {\ifnum#1>#2 % 3393%% {\ifnum#1>#2\relax 3394%% \expandafter\syst_helpers_recurse_indeed_nop 3395%% \else 3396%% \def\recurselevel{#1}% 3397%% \innerrecurse#1\advanceby\innerrecurse\plusone 3398%% \doubleexpandafter\syst_helpers_recurse_indeed_yes 3399%% \fi\expandafter{\the\innerrecurse}{#2}} 3400%% 3401%% \protected\def\syst_helpers_recurse_indeed_yes 3402%% {\syst_helpers_recurse_content 3403%% \syst_helpers_recurse_indeed} 3404%% 3405%% \protected\def\syst_helpers_recurse_indeed_nop#0#0#0% 3406%% {} 3407 3408%D \macros 3409%D {dowith} 3410%D 3411%D Here's a loop over whatever is in a list: 3412%D 3413%D \starttyping 3414%D \dowith{a,b,c}{[#1]} 3415%D \stoptyping 3416 3417\let\syst_helpers_with\gobbleoneargument 3418 3419\permanent\protected\def\dowith#1#2% 3420 {\def\syst_helpers_with##1{#2}% 3421 \normalexpanded{\processcommalist[#1]}\syst_helpers_with} 3422 3423%D \macros 3424%D {doloop,exitloop} 3425%D 3426%D Sometimes loops are not determined by counters, but by (a combinations of) 3427%D conditions. We therefore implement a straightforward loop, which can only be left 3428%D when we explictly exit it. Nesting is supported. First we present a more 3429%D extensive alternative. 3430%D 3431%D \starttyping 3432%D \doloop 3433%D {Some kind of typesetting punishment \par 3434%D \ifnum\pageno>100 \exitloop \fi} 3435%D \stoptyping 3436%D 3437%D When needed, one can call for \type {\looplevel} and \type {\loopdepth}. 3438 3439% \aliased\let\endofloop\donothing % maybe \syst_helpers_loop_end 3440% 3441% \permanent\protected\def\doloop#1% 3442% {\global\advanceby\outerrecurse \plusone 3443% \push_macro_recurseaction 3444% \push_macro_recurselevel 3445% \protected\gdef\recurseaction##1##2{#1}% 3446% \enforced\let\endofloop\syst_helpers_loop 3447% \syst_helpers_loop1}% no \plusone else \recurselevel wrong 3448% 3449% \protected\def\syst_helpers_loop#1% 3450% {\def\recurselevel{#1}% 3451% \expandafter\syst_helpers_loop_yes\expandafter{\the\numexpr\recurselevel+\plusone\relax}} 3452% 3453% %% \protected\def\syst_helpers_loop_yes 3454% %% {\syst_helpers_recurse_content 3455% %% \endofloop} 3456% 3457% \protected\def\syst_helpers_loop_yes 3458% {\normalexpanded{\recurseaction{\recurselevel}{\the\outerrecurse}}% 3459% \endofloop} 3460% 3461% \protected\def\syst_helpers_loop_nop#-% 3462% {\enforced\let\endofloop\syst_helpers_loop 3463% \pop_macro_recurselevel 3464% \pop_macro_recurseaction 3465% \global\advanceby\outerrecurse\minusone} 3466% 3467% \permanent\protected\def\exitloop % \exitloop quits at end 3468% {\enforced\let\endofloop\syst_helpers_loop_nop} 3469% 3470% \permanent\protected\def\exitloopnow#-\endofloop % \exitloopnow quits directly 3471% {\syst_helpers_loop_nop} 3472 3473\permanent\protected\def\doloop#1% 3474 {\push_macro_recurseaction 3475 \push_macro_recurselevel 3476 \push_macro_recursedepth 3477 \protected\gdef\recurseaction##1##2{#1}% 3478 \untraced\edef\recurselevel{\noexpand\the\nestedloopiterator\the\currentloopnesting\space}% 3479 \untraced\edef\recursedepth{\tointeger{\currentloopnesting+\plusone}}% 3480 \localcontrolledendless 3481 {%\edef\recurselevel{\the\currentloopiterator}% 3482 %\edef\recursedepth{\the\currentloopnesting}% 3483 \normalexpanded{\recurseaction{\the\currentloopiterator}{\the\currentloopnesting}}}% 3484 \pop_macro_recursedepth 3485 \pop_macro_recurselevel 3486 \pop_macro_recurseaction} 3487 3488\aliased\let\exitloop \quitloop 3489\aliased\let\exitloopnow\quitloopnow 3490 3491%D The loop is executed at least once, so beware of situations like: 3492%D 3493%D \starttyping 3494%D \doloop {\exitloop some commands} 3495%D \stoptyping 3496%D 3497%D It's just a matter of putting the text into the \type {\if} statement that should 3498%D be there anyway, like in: 3499%D 3500%D \starttyping 3501%D \doloop {\ifwhatever \exitloop \else some commands\fi} 3502%D \stoptyping 3503%D 3504%D You can also quit a loop immediately, by using \type 3505%D {\exitloopnow} instead. Beware, this is more sensitive 3506%D for conditional errors. 3507 3508%D Krzysztof Leszczynski suggested to provide access to the level by means of a 3509%D \type {#1}. I decided to pass the more frequently used level as \type {#1} and 3510%D the less favoured depth as \type {#2}. The intended usage is: 3511%D 3512%D \starttyping 3513%D \dorecurse{3}{\definesymbol[test-#1][xx-#1]} 3514%D 3515%D \def\test{\dorecurse{3}{\definesymbol[test-##1][xx-##1]}} \test 3516%D 3517%D \symbol[test-1]\quad\symbol[test-2]\quad\symbol[test-3] 3518%D \stoptyping 3519%D 3520%D Since the hashed arguments are expanded, we don't need tricky expansion here. 3521%D 3522%D \starttyping 3523%D \dorecurse{3}{\expanded{\definesymbol[test-\recurselevel][xx-\recurselevel]}} 3524%D \stoptyping 3525 3526%% \def\syst_helpers_recurse_content 3527%% {\normalexpanded{\recurseaction{\recurselevel}{\the\outerrecurse}}} 3528%% 3529%% \protected\def\syst_helpers_recurse_x#1#2% 3530%% {\global\advanceby\outerrecurse\plusone 3531%% \globalpushmacro\recurseaction 3532%% \globalpushmacro\recurselevel 3533%% \protected\gdef\recurseaction##1##2{#2}% 3534%% \expandafter\syst_helpers_recurse_indeed\expandafter1\expandafter{\number#1}% 3535%% \globalpopmacro\recurselevel 3536%% \globalpopmacro\recurseaction 3537%% \global\advanceby\outerrecurse\minusone} 3538%% 3539%% \protected\def\syst_helpers_recurse_y#1#2% 3540%% {\global\advanceby\outerrecurse\plusone 3541%% \globalpushmacro\recurseaction 3542%% \globalpushmacro\recurselevel 3543%% \let\recurselevel\!!plusone 3544%% \protected\gdef\recurseaction##1##2{#2}% 3545%% \syst_helpers_recurse_content 3546%% \globalpopmacro\recurselevel 3547%% \globalpopmacro\recurseaction 3548%% \global\advanceby\outerrecurse\minusone} 3549%% 3550%% \permanent\protected\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 ... todo: remove unused helpers 3551%% {\global\advanceby\outerrecurse \plusone 3552%% \globalpushmacro\recurseaction 3553%% \globalpushmacro\recurselevel 3554%% \protected\gdef\recurseaction##1##2{#4}% 3555%% \normalexpanded{\ifcmpnum#3\zerocount 3556%% \ifnum#1<#2\relax\relax % so we catch \the\numexpr xx without \relax's 3557%% \doubleexpandafter\gobbletwoarguments 3558%% \else 3559%% \tripleexpandafter\syst_helpers_stepwise_reverse 3560%% \fi 3561%% \or 3562%% \doubleexpandafter\gobbletwoarguments 3563%% \orelse\ifnum#2<#1\relax\relax % so we catch \the\numexpr xx without \relax's 3564%% \doubleexpandafter\gobbletwoarguments 3565%% \else 3566%% \doubleexpandafter\syst_helpers_stepwise_recurse 3567%% \fi\normalexpanded{{\number#1}{\number#2}{\number#3}}}% 3568%% \globalpopmacro\recurselevel 3569%% \globalpopmacro\recurseaction 3570%% \global\advanceby\outerrecurse\minusone} 3571 3572\newinteger\fastloopindex 3573\newinteger\fastloopfinal 3574 3575\let\m_syst_helpers_fast_loop_cs\relax 3576 3577\permanent\protected\def\dofastloopcs#1% kind of obsolete 3578 {\fastloopfinal{#1}% 3579 \ifcase\fastloopfinal 3580 \expandafter\gobbleoneargument 3581 \else 3582 \expandafter\syst_helpers_fast_loop_cs 3583 \fi} 3584 3585\protected\def\syst_helpers_fast_loop_cs#1% 3586 {\let\m_syst_helpers_fast_loop_cs#1% 3587 \fastloopindex\plusone 3588 \syst_helpers_fast_loop_cs_step} 3589 3590\protected\def\syst_helpers_fast_loop_cs_step 3591 {\ifnum\fastloopindex>\fastloopfinal 3592 \let\m_syst_helpers_fast_loop_cs\relax 3593 \else 3594 \m_syst_helpers_fast_loop_cs 3595 \advanceby\fastloopindex\plusone 3596 \expandafter\syst_helpers_fast_loop_cs_step 3597 \fi} 3598 3599%D Here are the more modern implementations: 3600 3601\permanent\protected\def\installmacrostack#1% 3602 {\ifdefined#1\else\mutable\lettonothing#1\fi 3603 \protected\gdefcsname push_macro_\csstring#1\endcsname{\localpushmacro#1}% 3604 \protected\gdefcsname pop_macro_\csstring#1\endcsname{\localpopmacro #1}} 3605 3606\permanent\protected\def\installglobalmacrostack#1% 3607 {\ifdefined#1\else\mutable\glettonothing#1\fi 3608 \protected\gdefcsname push_macro_\csstring#1\endcsname{\globalpushmacro#1}% 3609 \protected\gdefcsname pop_macro_\csstring#1\endcsname{\globalpopmacro #1}} 3610 3611\installglobalmacrostack \recurseaction 3612\installglobalmacrostack \recurselevel 3613\installglobalmacrostack \recursedepth 3614\installglobalmacrostack \recursestring 3615 3616% \showmacrostack can be used to see if there are different entries 3617 3618\permanent\protected\def\dostepwiserecurse#1#2#3#4% 3619 {\push_macro_recurseaction 3620 \push_macro_recurselevel 3621 \push_macro_recursedepth 3622 \protected\gdef\recurseaction##1##2{#4}% 3623% \untraced\edef\recurselevel{\noexpand\the\nestedloopiterator\the\currentloopnesting\space}% 3624% \untraced\edef\recursedepth{\the\numexpr\currentloopnesting+\plusone}% 3625% \localcontrolledloop=#1=#2=#3% 3626\untraced\edef\recurselevel{\noexpand\tointeger\nestedloopiterator{\the\currentloopnesting}}% 3627\untraced\edef\recursedepth{\tointeger{\currentloopnesting+\plusone}}% 3628 \localcontrolledloop{#1}{#2}{#3}% 3629 {%\edef\recurselevel{\the\currentloopiterator}% 3630 %\edef\recursedepth{\the\currentloopnesting}% 3631 \normalexpanded{\recurseaction{\the\currentloopiterator}{\the\currentloopnesting}}}% 3632 \pop_macro_recursedepth 3633 \pop_macro_recurselevel 3634 \pop_macro_recurseaction} 3635 3636\permanent\protected\def\dorecurse#1#2% 3637 {\push_macro_recurseaction 3638 \push_macro_recurselevel 3639 \push_macro_recursedepth 3640 \protected\gdef\recurseaction##1##2{#2}% 3641% \untraced\edef\recurselevel{\noexpand\the\nestedloopiterator\the\currentloopnesting\space}% 3642% \untraced\edef\recursedepth{\the\numexpr\currentloopnesting+\plusone}% 3643% \localcontrolledrepeat=#1% 3644 \untraced\edef\recurselevel{\noexpand\tointeger\nestedloopiterator{\the\currentloopnesting}}% 3645 \untraced\edef\recursedepth{\tointeger{\currentloopnesting+\plusone}}% 3646 \localcontrolledrepeat{#1}% 3647 {%\edef\recurselevel{\the\currentloopiterator}% 3648 %\edef\recursedepth{\the\currentloopnesting}% 3649 \normalexpanded{\recurseaction{\the\currentloopiterator}{\the\currentloopnesting}}}% 3650 \pop_macro_recursedepth 3651 \pop_macro_recurselevel 3652 \pop_macro_recurseaction} 3653 3654\permanent\def\doexpandedrecurse#1#2% user macro (also was \doxprecurse) 3655 {\expandedloop\plusone#1\plusone{#2}} 3656 3657\mutable\let\m_expanded_recursed\gobbleoneargument 3658 3659\installmacrostack\m_expanded_recursed 3660 3661\permanent\def\doexpandedrecursed#1#2% this might replace: \doexpandedrecurse 3662 {\beginlocalcontrol 3663 \push_macro_m_expanded_recursed 3664 \def\m_expanded_recursed##1{#2}% 3665 \endlocalcontrol 3666 \expandedrepeat{#1}{\expandafter\m_expanded_recursed\expandafter{\the\currentloopiterator}}% 3667 \beginlocalcontrol 3668 \pop_macro_m_expanded_recursed 3669 \endlocalcontrol} 3670 3671% Helper: 3672 3673% \permanent\protected\def\resetrecurselevel{\let\recurselevel\!!zerocount} 3674 3675\permanent\protected\def\resetrecurselevel{\untraced\let\recurselevel\!!zerocount} 3676 3677% \appendtoks \resetrecurselevel \to \everydump 3678 3679%D \macros 3680%D {doloopoverlist} 3681%D 3682%D \starttyping 3683%D \doloopoverlist {red,green,blue} { 3684%D \setuppalet[\recursestring] 3685%D \doloopoverlist {light,normal,dark} { 3686%D \blackrule[color=\recursestring,width=20cm,height=2cm,depth=0cm]\par 3687%D } 3688%D } 3689%D \stoptyping 3690%D 3691%D or: 3692%D 3693%D \starttyping 3694%D \doloopoverlist {red,green,blue} { 3695%D \setuppalet[#1] 3696%D \doloopoverlist {light,normal,dark} { 3697%D \blackrule[color=##1,width=20cm,height=2cm,depth=0cm]\par 3698%D } 3699%D } 3700%D \stoptyping 3701 3702\permanent\protected\def\doloopoverlist#1#2% 3703 {\global\advanceby\outerrecurse\plusone 3704% \globalpushmacro\recurseaction 3705% \globalpushmacro\recursestring 3706 \push_macro_recurseaction 3707 \push_macro_recursestring 3708 \protected\gdef\recurseaction##1{\edef\recursestring{##1}#2}% 3709 \processcommacommand[#1]\recurseaction 3710% \globalpopmacro\recursestring 3711% \globalpopmacro\recurseaction 3712 \pop_macro_recursestring 3713 \pop_macro_recurseaction 3714 \global\advanceby\outerrecurse\minusone} 3715 3716%D This is some \LMTX\ experiment: 3717%D 3718%D Think of: 3719%D 3720%D \starttyping 3721%D \doloopovermatch {(\letterpercent w+) *(\letterpercent w*)} {aa bb cc dd} { 3722%D [ 3723%D \doloopovermatch{(\letterpercent w)(\letterpercent w)} {#1} {(##1 ##2)} 3724%D \doloopovermatch{(\letterpercent w)(\letterpercent w)} {#2} {(##1 ##2)} 3725%D ] 3726%D } 3727%D 3728%D and: 3729%D 3730%D \stoptyping 3731%D 3732%D \starttyping 3733%D \def\MyMacro#1{(#1)} \ctxluamatch \MyMacro {(.)} {abcd} 3734%D \stoptyping 3735 3736\aliased\let\matchloopcommand\relax 3737 3738\permanent\protected\def\doloopovermatch#1#2#3% 3739 {\pushmacro\matchloopcommand 3740 \enforced\def\matchloopcommand##1##2##3##4##5##6##7##8##9##A##B##C##D##E{#3}% 3741 \ctxluamatch\matchloopcommand{#1}{#2}% 3742 \popmacro\matchloopcommand} 3743 3744\permanent\def\doloopovermatched#1#2#3% 3745 {\beginlocalcontrol 3746 \pushmacro\matchloopcommand 3747 \enforced\def\matchloopcommand##1##2##3##4##5##6##7##8##9##A##B##C##D##E{#3}% 3748 \endlocalcontrol 3749 \the\ctxluamatch\matchloopcommand{#1}{#2}% 3750 \beginlocalcontrol 3751 \popmacro\matchloopcommand 3752 \endlocalcontrol} 3753 3754% \defineatluaend\ctxluamatch 3755% \defineatluaend\ctxluamatchfile 3756 3757%D \starttyping 3758%D \ctxluamatch 3759%D \Whatever 3760%D {(\letterpercent S+) *(\letterpercent S+) *(\letterpercent S+) *(\letterpercent S+)\letterpercent s*} 3761%D {\cldloadfile{crap.txt}} 3762%D \ctxluamatchfile 3763%D \Whatever 3764%D {(\letterpercent S+) *(\letterpercent S+) *(\letterpercent S+) *(\letterpercent S+)\letterpercent s*} 3765%D {crap.txt} 3766%D \stoptyping 3767 3768%D \macros 3769%D {newevery,everyline,EveryLine,EveryPar} 3770%D 3771%D Lets skip to something quite different. It's common use to use \type {\everypar} 3772%D for special purposes. In \CONTEXT\ we use this primitive for locating sidefloats. 3773%D This means that when user assignments to \type {\everypar} can interfere with 3774%D those of the package. We therefore introduce \type {\EveryPar}. 3775%D 3776%D The same goes for \type {\EveryLine}. Because \TEX\ offers no \type {\everyline} 3777%D primitive, we have to call for \type {\everyline} when we are working on a line 3778%D by line basis. Just by calling \type {\EveryPar{}} and \type {\EveryLine{}} we 3779%D restore the old situation. 3780 3781% \dorecurse{2}{ 3782% \expanded{\everypar{before \recurselevel\space}} 3783% \EveryPar{x } [before \recurselevel\space x] \par 3784% \EveryPar{y } [before \recurselevel\space y] \par 3785% \EveryPar{} [before \recurselevel] \par 3786% \EveryPar{x } \EveryPar{y } \EveryPar{} [before \recurselevel] \par 3787% \EveryPar{y } \everypar{before } [before] \par 3788% } 3789 3790\installsystemnamespace{extraevery} 3791 3792\permanent\protected\def\newevery#1#2% 3793 {\ifx#1\everypar\else\newtoks#1\fi% we test for redefinition elsewhere 3794 \ifrelax#2\orelse\ifdefined#2\else 3795 \expandafter\newtoks\csname\??extraevery\csstring#1\endcsname 3796 \frozen\protected\edef#2{\syst_helpers_every#1\csname\??extraevery\csstring#1\endcsname}% 3797 \fi} 3798 3799\protected\def\syst_helpers_every#1#2% 3800 {\removetoks\the#2\from#1% 3801 \appendtoks\the#2\to #1% 3802 #2} 3803 3804%D This one permits definitions like: 3805 3806\newevery \everypar \EveryPar % we get a warning which is ok 3807\newevery \everyline \EveryLine 3808 3809%D and how about: 3810 3811\newtoks \neverypar 3812 3813\permanent\protected\def\forgeteverypar 3814 {\everypar{\expand\neverypar}} 3815 3816%D Which we're going to use indeed! When the second argument equals \type {\relax}, 3817%D the first token list is created unless it is already defined. 3818%D 3819%D Technically spoken we could have used the method we are going to present in the 3820%D visual debugger. First we save the primitive \type{\everypar}: 3821%D 3822%D \starttyping 3823%D \let\normaleverypar=\everypar 3824%D \stoptyping 3825%D 3826%D Next we allocate a \TOKENLIST\ named \type{\everypar}, which means that 3827%D \type{\everypar} is no longer a primitive but something like \type{\toks44}. 3828%D 3829%D \starttyping 3830%D \newtoks\everypar 3831%D \stoptyping 3832%D 3833%D Because \TEX\ now executes \type{\normaleverypar} instead of \type{\everypar}, we 3834%D are ready to assign some tokens to this internally known and used \TOKENLIST. 3835%D 3836%D \starttyping 3837%D \normaleverypar={all the things the system wants to do \the\everypar} 3838%D \stoptyping 3839%D 3840%D Where the user can provide his own tokens to be expanded every time he expects 3841%D them to expand. 3842%D 3843%D \starttyping 3844%D \everypar={something the user wants to do} 3845%D \stoptyping 3846%D 3847%D We don't use this method because it undoubtly leads to confusing situations, 3848%D especially when other packages are used, but it's this kind of tricks that make 3849%D \TEX\ so powerful. 3850 3851%D \macros 3852%D {convertargument,convertcommand,convertvalue} 3853%D 3854%D Some persistent experimenting led us to the next macro. This macro converts a 3855%D parameter or an expanded macro to it's textual meaning. 3856%D 3857%D \starttyping 3858%D \convertargument ... \to \command 3859%D \stoptyping 3860%D 3861%D For example, 3862%D 3863%D \starttyping 3864%D \convertargument{one \two \three{four}}\to\ascii 3865%D \stoptyping 3866%D 3867%D The resulting macro \type{\ascii} can be written to a file or the terminal 3868%D without problems. In \CONTEXT\ we use this macro for generating registers and 3869%D tables of contents. 3870%D 3871%D The second conversion alternative accepts a command: 3872%D 3873%D \starttyping 3874%D \convertcommand\command\to\ascii 3875%D \stoptyping 3876%D 3877%D Both commands accept the prefix \type{\doglobal} for global assignments. 3878 3879\permanent\protected\def\convertvalue#1\to 3880 {\expandafter\convertcommand\csname#1\endcsname\to} 3881 3882\permanent\protected\def\defconvertedvalue#1#2% less sensitive for \to 3883 {\expandafter\defconvertedcommand\expandafter#1\csname#2\endcsname} 3884 3885%D \macros 3886%D {doifassignmentelse} 3887%D 3888%D A lot of \CONTEXT\ commands take optional arguments, for instance: 3889%D 3890%D \starttyping 3891%D \dothisorthat[alfa,beta] 3892%D \dothisorthat[first=foo,second=bar] 3893%D \dothisorthat[alfa,beta][first=foo,second=bar] 3894%D \stoptyping 3895%D 3896%D Although a combined solution is possible, we prefer a seperation. The next 3897%D command takes care of propper handling of such multi||faced commands. 3898%D 3899%D \starttyping 3900%D \doifassignmentelse {...} {then ...} {else ...} 3901%D \stoptyping 3902%D 3903%D So 3904%D \startbuffer 3905%D \doifelseassignment{a=b}{Y}{N} 3906%D \doifelseassignment{a+b}{Y}{N} 3907%D 3908%D \ifcondition\validassignment {a=b}Y\else N\fi 3909%D \ifcondition\novalidassignment{a=b}N\else Y\fi 3910%D \ifcondition\novalidassignment {ab}Y\else N\fi 3911%D \stopbuffer 3912%D 3913%D \typebuffer gives: \blank \getbuffer \blank 3914 3915\permanent\protected\def\doifelseassignment#1% 3916 {\ifhastok={#1}% 3917 \expandafter\firstoftwoarguments 3918 \else 3919 \expandafter\secondoftwoarguments 3920 \fi} 3921 3922\permanent\protected\def\doifelseassignmentcs#1#2#3% 3923 {\ifhastok={#1}% 3924 \expandafter#2% 3925 \else 3926 \expandafter#3% 3927 \fi} 3928 3929\aliased\let\doifassignmentelse \doifelseassignment 3930\aliased\let\doifassignmentelsecs\doifelseassignmentcs 3931 3932\newif\ifassignment 3933 3934\permanent\protected\def\docheckassignment#1% 3935 {\ifhastok={#1}% 3936 \assignmenttrue 3937 \else 3938 \assignmentfalse 3939 \fi} 3940 3941\permanent\protected\def\validassignment #1{\ifhastok={#1}} % can become: {\ifhastok=} as we enforce {} 3942\permanent\protected\def\novalidassignment#1{\ifnum\ifhastok={#1}\zerocount\else\plusone\fi=\plusone} % or use unless 3943 3944%D In \ETEX\ we can use \type {\detokenize} and gain some speed, but in general far 3945%D less that 1\% for \type {\convertargument} and nil for \type {\convertcommand}. 3946%D This macro is more robust than the pure \TEX\ one, something I found out when 3947%D primitives like \type {\jobname} were fed (or something undefined). 3948 3949\permanent\protected\def\convertargument#1\to#2{\dodoglobal\edef#2{\detokenize{#1}}} 3950\permanent\protected\def\convertcommand #1\to#2{\dodoglobal\edef#2{\expandafter\detokenize\expandafter{#1}}} % hm, only second is also ok 3951 3952\permanent\protected\def\defconvertedargument #1#2{\edef#1{\detokenize{#2}}} 3953\permanent\protected\def\defconvertedcommand #1#2{\edef#1{\detokenize\expandafter{#2}}} 3954\permanent\protected\def\edefconvertedargument#1#2{\edef#1{#2}% 3955 \edef#1{\detokenize\expandafter{#1}}} 3956\permanent\protected\def\gdefconvertedargument#1#2{\xdef#1{\detokenize{#2}}} 3957\permanent\protected\def\gdefconvertedcommand #1#2{\xdef#1{\detokenize\expandafter{#2}}} 3958\permanent\protected\def\xdefconvertedargument#1#2{\xdef#1{#2}% 3959 \xdef#1{\detokenize\expandafter{#1}}} 3960 3961%D When you try to convert a primitive command, you'll find out that the \ETEX\ 3962%D method fails on for instance \type {\jobname} in the sense that it returns the 3963%D filename instead of just \type {\jobname}. So far this does not give real 3964%D problems. 3965 3966%D This is typically a macro that one comes to after reading the \TEX book 3967%D carefully. Even then, the definite solution was found after rereading the \TEX 3968%D book. The first implementation was: 3969%D 3970%D \starttyping 3971%D \def\doconvertargument#1->#2\\\\{#2} 3972%D \stoptyping 3973%D 3974%D The \type {-}, the delimiter \type {\\\\} and the the second argument are 3975%D completely redundant. 3976 3977%D \macros 3978%D {showvalue} 3979%D 3980%D Ahandy macro, for testing purposes only: 3981 3982\permanent\protected\def\showvalue#1% 3983 {\ifcsname#1\endcsname 3984 \expandafter\show\csname#1\endcsname 3985 \else 3986 \show\undefined 3987 \fi} 3988 3989%D \macros 3990%D {doifmeaningelse} 3991%D 3992%D We can use both commands in testing, but alas, not all meanings expand to 3993%D something \type {->}. This is no problem in the \ETEX\ implementation, but since 3994%D we want compatibility, we need: 3995%D 3996%D \starttyping 3997%D \doifmeaningelse {\next} {\something} {true} {false} 3998%D \stoptyping 3999%D 4000%D Watch the one level expansion of the second argument. 4001 4002% This is only used in \spac_lines_between .. pretty ugly. Never change 4003% this one! 4004 4005\permanent\protected\def\doifelsemeaning#1#2% 4006 {\edef\m_syst_string_one{\normalmeaning#1}% 4007 \def \m_syst_string_two{#2}% 4008 \edef\m_syst_string_two{\normalmeaning\m_syst_string_two}% 4009 \ifx\m_syst_string_one\m_syst_string_two 4010 \expandafter\firstoftwoarguments 4011 \else 4012 \expandafter\secondoftwoarguments 4013 \fi} 4014 4015\aliased\let\doifmeaningelse\doifelsemeaning 4016 4017%D \macros 4018%D {doifsamestringselse,doifsamestring,doifnotsamestring} 4019%D 4020%D The next comparison macro converts the arguments into expanded strings. This 4021%D command can be used to compare for instance \type {\jobname} with a name stored 4022%D in a macro. 4023%D 4024%D \starttyping 4025%D \doifelse {\jobname}{oeps}{YES}{NO} 4026%D \doifsamestringelse{\jobname}{oeps}{YES}{NO} 4027%D \stoptyping 4028 4029\def\syst_helpers_if_samestring_else#1#2#3#4% 4030 {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#3}}}% 4031 \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#4}}}% 4032 \ifx\m_syst_string_one\m_syst_string_two\expandafter#1\else\expandafter#2\fi} 4033 4034\permanent\protected\def\doifelsesamestring{\syst_helpers_if_samestring_else\firstoftwoarguments\secondoftwoarguments} 4035\permanent\protected\def\doifsamestring {\syst_helpers_if_samestring_else\firstofoneargument \gobbleoneargument } 4036\permanent\protected\def\doifnotsamestring {\syst_helpers_if_samestring_else\gobbleoneargument \firstofoneargument } 4037 4038\aliased\let\doifsamestringelse\doifelsesamestring 4039 4040%D These are obsolete in MTX: 4041%D 4042%D \starttyping 4043%D \ConvertToConstant #1#2#3 4044%D \CheckConstantAfter #1#2 4045%D \ConvertConstantAfter #1#2#3 4046%D \stoptyping 4047 4048%D \macros 4049%D {assignifempty} 4050%D 4051%D We can assign a default value to an empty macro using: 4052%D 4053%D \starttyping 4054%D \assignifempty \macros {default value} 4055%D \stoptyping 4056%D 4057%D We don't explicitly test if the macro is defined. 4058 4059\permanent\protected\def\assignifempty#1#2% 4060 {\ifempty{#1}\def#1{#2}\fi} 4061 4062%D \macros 4063%D {gobbleuntil,grabuntil,gobbleuntilrelax, 4064%D processbetween,processuntil} 4065%D 4066%D In \TEX\ gobbling usually stand for skipping arguments, so here are our gobbling 4067%D macros. 4068%D 4069%D In \CONTEXT\ we use a lot of \type {\start}||\type {\stop} like constructions. 4070%D Sometimes, the \type {\stop} is used as a hard coded delimiter like in: %D 4071%D \starttyping 4072%D \protected\def\startcommand#1\stopcommand% 4073%D {... #1 ...} 4074%D \stoptyping 4075%D 4076%D In many cases the \type {\start}||\type {\stop} pair is defined at format 4077%D generation time or during a job. This means that we cannot hardcode the \type 4078%D {\stop} criterium. Only after completely understanding \type {\csname} and \type 4079%D {\expandafter} I was able to to implement a solution, starting with: 4080%D 4081%D \starttyping 4082%D \grabuntil{stop}\command 4083%D \stoptyping 4084%D 4085%D This commands executes, after having encountered \type {\stop} the command \type 4086%D {\command}. This command receives as argument the text preceding the \type 4087%D {\stop}. This means that: 4088%D 4089%D \starttyping 4090%D \protected\def\starthello% 4091%D {\grabuntil{stophello}\message} 4092%D 4093%D \starthello Hello world!\stophello 4094%D \stoptyping 4095%D 4096%D results in: \type{\message{Hello world!}}. 4097 4098\let\syst_helpers_grab_indeed\relax 4099 4100\protected\def\syst_helpers_grab#1#2% 4101 {\def\syst_helpers_grab_indeed##1#1{#2{##1}}\syst_helpers_grab_indeed} 4102 4103\permanent\protected\def\grabuntil#1% 4104 {\expandafter\syst_helpers_grab\expandafter{\csname#1\endcsname}} 4105 4106%D The next command build on this mechanism: 4107%D 4108%D \starttyping 4109%D \processbetween{string}\command 4110%D \stoptyping 4111%D 4112%D Here: 4113%D 4114%D \starttyping 4115%D \processbetween{hello}\message 4116%D \starthello Hello again!\stophello 4117%D \stoptyping 4118%D 4119%D leads to: \type{\message{Hello again!}}. The command 4120%D 4121%D \starttyping 4122%D \gobbleuntil{sequence} 4123%D \stoptyping 4124%D 4125%D is related to these commands. This one simply throws away 4126%D everything preceding \type{\command}. 4127 4128\let\syst_helpers_gobble_indeed\relax 4129 4130\permanent\protected\def\processbetween#1#2% 4131 {\setvalue{\s!start#1}{\grabuntil{\s!stop#1}{#2}}} 4132 4133% \protected\def\gobbleuntil#1% 4134% {\def\syst_helpers_gobble_indeed##1#1{}\syst_helpers_gobble_indeed} 4135% 4136% \protected\def\gobbleuntilrelax#1\relax 4137% {} 4138 4139\permanent\protected\def\gobbleuntil#1% 4140 {\def\syst_helpers_gobble_indeed##-#1{}\syst_helpers_gobble_indeed} 4141 4142\permanent\protected\def\gobbleuntilandfinalize#1% 4143 {\def\syst_helpers_gobble_indeed##-#1{#1}\syst_helpers_gobble_indeed} 4144 4145\permanent\protected\def\gobbleuntilrelax#-\relax 4146 {} 4147 4148%D The next one simply expands the pickup up tokens. 4149%D 4150%D \starttyping 4151%D \processuntil{sequence} 4152%D \stoptyping 4153 4154\let\syst_helpers_until_indeed\relax 4155 4156\permanent\protected\def\processuntil#1% 4157 {\def\syst_helpers_until_indeed##1#1{##1}\syst_helpers_until_indeed} 4158 4159%D \macros 4160%D {groupedcommand} 4161%D 4162%D Commands often manipulate argument as in: 4163%D 4164%D \starttyping 4165%D \def\doezomaarwat#1{....#1....} 4166%D \stoptyping 4167%D 4168%D A disadvantage of this approach is that the tokens that form \type{#1} are fixed 4169%D the the moment the argument is read in. Normally this is no problem, but for 4170%D instance verbatim environments adapt the \CATCODES\ of characters and therefore 4171%D are not always happy with already fixed tokens. 4172%D 4173%D Another problem arises when the argument is grouped not by \type {{}} but by 4174%D \type {\bgroup} and \type {\egroup}. Such an argument fails, because the \type 4175%D {\bgroup} is een as the argument (which is quite normal). 4176%D 4177%D The next macro offers a solution for both unwanted situations: 4178%D 4179%D \starttyping 4180%D \groupedcommand {before} {after} 4181%D \stoptyping 4182%D 4183%D Which can be used like: 4184%D 4185%D \starttyping 4186%D \def\cite% 4187%D {\groupedcommand{\rightquote\rightquote}{\leftquote\leftquote}} 4188%D \stoptyping 4189%D 4190%D This command is equivalent to, but more 'robust' than: 4191%D 4192%D \starttyping 4193%D \def\cite#1% 4194%D {\rightquote\rightquote#1\leftquote\leftquote} 4195%D \stoptyping 4196%D 4197%D \starttyping 4198%D \def\rightword% 4199%D {\groupedcommand{\hfill\hbox}{\parfillskip\zeropoint}} 4200%D 4201%D .......... \rightword{the right way} 4202%D \stoptyping 4203%D 4204%D Here \TEX\ typesets \type {\bf the right way} unbreakable at the end of the line. 4205%D The solution mentioned before does not work here. We also handle 4206%D 4207%D \starttyping 4208%D to be \bold{bold} or not, that's the question 4209%D \stoptyping 4210%D 4211%D and 4212%D 4213%D \starttyping 4214%D to be {\bold bold} or not, that's the question 4215%D \stoptyping 4216%D 4217%D This alternative checks for a \type {\bgroup} token first. The internal 4218%D alternative does not accept the box handling mentioned before, but further 4219%D nesting works all right. The extra \type {\bgroup}||\type {\egroup} is needed to 4220%D keep \type {\m_syst_helpers_handle_group_after} both into sight and local. 4221 4222%D Todo: use more modern features like atendofgroup 4223 4224\let\m_syst_helpers_handle_group_after \relax 4225\let\m_syst_helpers_handle_group_before\relax 4226 4227\protected\def\syst_helpers_handle_group_nop 4228 {\ifnum\currentgrouptype=\semisimplegroupcode 4229 \expandafter\syst_helpers_handle_group_nop_a 4230 \else 4231 \expandafter\syst_helpers_handle_group_nop_b 4232 \fi} 4233 4234\def\syst_helpers_handle_group_nop_a 4235 {\begingroup 4236 \aftergroup\m_syst_helpers_handle_group_a 4237 \aftergroup\endgroup 4238 \m_syst_helpers_handle_group_b} 4239 4240\def\syst_helpers_handle_group_nop_b 4241 {\beginsimplegroup 4242 \aftergroup\m_syst_helpers_handle_group_a 4243 \aftergroup\egroup 4244 \m_syst_helpers_handle_group_b} 4245 4246\protected\def\syst_helpers_handle_group_normal 4247 {\beginsimplegroup 4248 \afterassignment\m_syst_helpers_handle_group_normal_before 4249 \let\next=} 4250 4251\def\m_syst_helpers_handle_group_normal_before 4252 {\beginsimplegroup 4253 \m_syst_helpers_handle_group_b 4254 \bgroup 4255 \aftergroup\m_syst_helpers_handle_group_a 4256 \aftergroup\egroup 4257 \aftergroup\egroup} 4258 4259\protected\def\syst_helpers_handle_group_simple% no inner group (so no kerning interference) 4260 {\beginsimplegroup 4261 \afterassignment\m_syst_helpers_handle_group_simple_before 4262 \let\next=} 4263 4264\def\m_syst_helpers_handle_group_simple_before 4265 {\beginsimplegroup 4266 \aftergroup\m_syst_helpers_handle_group_simple_after 4267 \m_syst_helpers_handle_group_b} 4268 4269\def\m_syst_helpers_handle_group_simple_after 4270 {\m_syst_helpers_handle_group_a 4271 \egroup} 4272 4273\protected\def\syst_helpers_handle_group_pickup% no inner group (so no kerning interference) 4274 {\beginsimplegroup 4275 \afterassignment\m_syst_helpers_handle_group_pickup_before 4276 \let\next=} 4277 4278\def\m_syst_helpers_handle_group_pickup_before 4279 {\beginsimplegroup 4280 \aftergroup\m_syst_helpers_handle_group_a 4281 \aftergroup\egroup 4282 \aftergroup\m_syst_helpers_handle_group_p 4283 \m_syst_helpers_handle_group_b} 4284 4285\protected\def\syst_helpers_handle_group_nop_x 4286 {% why this ... 4287 \ifnum\currentgrouptype=\semisimplegroupcode 4288 \begingroup 4289 \aftergroup\endgroup 4290 \else 4291 \beginsimplegroup 4292 \aftergroup\egroup 4293 \fi 4294 % ... till here 4295 \m_syst_helpers_handle_group_b} 4296 4297\protected\def\syst_helpers_handle_group_normal_x 4298 {\beginsimplegroup 4299 \afterassignment\m_syst_helpers_handle_group_normal_before_x 4300 \let\next=} 4301 4302\def\m_syst_helpers_handle_group_normal_before_x 4303 {\beginsimplegroup 4304 \expandafter\m_syst_helpers_handle_group_b 4305 \ifmmode\beginsimplegroup\else\bgroup\fi % is this save enough? also the other ones? 4306 \aftergroup\egroup 4307 \aftergroup\egroup} 4308 4309%D I considered it a nuisance that 4310%D 4311%D \starttyping 4312%D \color[green] 4313%D {as grass} 4314%D \stoptyping 4315%D 4316%D was not interpreted as one would expect. This is due to the fact that \type 4317%D {\futurelet} obeys blank spaces, and a line||ending token is treated as a blank 4318%D space. So the final implementation became: 4319 4320\permanent\protected\def\groupedcommand#1#2% 4321 {\def\m_syst_helpers_handle_group_b{#1}% 4322 \def\m_syst_helpers_handle_group_a{#2}% 4323 \futureexpandis\bgroup\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop} 4324 4325\permanent\protected\def\groupedcommandcs#1#2% 4326 {\let\m_syst_helpers_handle_group_b#1% 4327 \let\m_syst_helpers_handle_group_a#2% 4328 \futureexpandis\bgroup\syst_helpers_handle_group_normal\syst_helpers_handle_group_nop} 4329 4330\permanent\protected\def\simplegroupedcommand#1#2% 4331 {\def\m_syst_helpers_handle_group_b{#1}% 4332 \def\m_syst_helpers_handle_group_a{#2}% 4333 \futureexpandis\bgroup\syst_helpers_handle_group_simple\syst_helpers_handle_group_nop} 4334 4335\permanent\protected\def\simplegroupedcommandcs#1#2% 4336 {\let\m_syst_helpers_handle_group_b#1% 4337 \let\m_syst_helpers_handle_group_a#2% 4338 \futureexpandis\bgroup\syst_helpers_handle_group_simple\syst_helpers_handle_group_nop} 4339 4340\permanent\protected\def\pickupgroupedcommand#1#2#3% 4341 {\def\m_syst_helpers_handle_group_b{#1}% 4342 \def\m_syst_helpers_handle_group_a{#2}% 4343 \def\m_syst_helpers_handle_group_p{#3}% 4344 \futureexpandis\bgroup\syst_helpers_handle_group_pickup\syst_helpers_handle_group_nop} 4345 4346\permanent\protected\def\pickupgroupedcommandcs#1#2#3% 4347 {\let\m_syst_helpers_handle_group_b#1% 4348 \let\m_syst_helpers_handle_group_a#2% 4349 \let\m_syst_helpers_handle_group_p#3% 4350 \futureexpandis\bgroup\syst_helpers_handle_group_pickup\syst_helpers_handle_group_nop} 4351 4352\permanent\protected\def\triggergroupedcommand#1% 4353 {\def\m_syst_helpers_handle_group_b{#1}% 4354 \futureexpandis\bgroup\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x} 4355 4356\permanent\protected\def\triggergroupedcommandcs#1% 4357 {\let\m_syst_helpers_handle_group_b#1% 4358 \futureexpandis\bgroup\syst_helpers_handle_group_normal_x\syst_helpers_handle_group_nop_x} 4359 4360%D Users should be aware of the fact that grouping can interfere with ones paragraph 4361%D settings that are executed after the paragraph is closed. One should therefore 4362%D explictly close the paragraph with \type {\par}, else the settings will be 4363%D forgotten and not applied. So it's: 4364%D 4365%D \starttyping 4366%D \def\BoldRaggedCenter% 4367%D {\groupedcommand{\raggedcenter\bf}{\par}} 4368%D \stoptyping 4369%D 4370%D In \LUAMETATEX\ this is less of an issue as we can save the state (and do so in 4371%D \CONTEXT). 4372 4373%D For math we use this: 4374 4375\permanent\protected\def\mathgroupedcommandcs#1% 4376 {\let\m_syst_helpers_handle_group_b#1% 4377 \futureexpandis\bgroup\syst_helpers_handle_math_group_normal\syst_helpers_handle_math_group_nop} 4378 4379\protected\def\syst_helpers_handle_math_group_normal#1% 4380 {\beginmathgroup 4381 \m_syst_helpers_handle_group_b 4382 #1% 4383 \endmathgroup} 4384 4385\protected\def\syst_helpers_handle_math_group_nop 4386 {\m_syst_helpers_handle_group_b} 4387 4388% %D \macros 4389% %D {checkdefined} 4390% %D 4391% %D The bigger the system, the greater the change that user defined commands collide 4392% %D with those that are part of the system. The next macro gives a warning when a 4393% %D command is already defined. We considered blocking the definition, but this is 4394% %D not always what we want. 4395% %D 4396% %D \starttyping 4397% %D \checkdefined {category} {class} {command} 4398% %D \stoptyping 4399% %D 4400% %D The user is warned with the suggestion to use \type {CAPITALS}. This suggestion 4401% %D is feasible, because \CONTEXT only defines lowcased macros. These are obsolete: 4402% 4403% \protected\def\showdefinederror#1#2% 4404% {\writestatus\m!system{#1 #2 replaces a macro, use CAPITALS!}} 4405% 4406% \protected\def\checkdefined#1#2#3% 4407% {\doifdefined{#3}{\showdefinederror{#2}{#3}}} 4408 4409%D \macros 4410%D {GotoPar,GetPar} 4411%D 4412%D Typesetting a paragraph in a special way can be done by first grabbing the 4413%D contents of the paragraph and processing this contents grouped. The next macro 4414%D for instance typesets a paragraph in boldface. 4415%D 4416%D \starttyping 4417%D \def\remark#1\par% 4418%D {\bgroup\bf#1\egroup} 4419%D \stoptyping 4420%D 4421%D This macro has to be called like 4422%D 4423%D \starttyping 4424%D \remark some text ... ending with \par 4425%D \stoptyping 4426%D 4427%D Instead of \type {\par} we can of course use an empty line. When we started 4428%D typesetting with \TEX, we already had produced lots of text in plain \ASCII. In 4429%D producing such simple formatted texts, we adopted an open layout, and when 4430%D switching to \TEX, we continued this open habit. Although \TEX\ permits a cramped 4431%D and badly formatted source, it adds to confusion and sometimes introduces errors. 4432%D So we prefer: 4433%D 4434%D \starttyping 4435%D \remark 4436%D 4437%D some text ... ending with an empty line 4438%D \stoptyping 4439%D 4440%D We are going to implement a mechanism that allows such open specifications. The 4441%D definition of the macro handling \type {\remark} becomes: 4442%D 4443%D \starttyping 4444%D \def\remark% 4445%D {\BeforePar{\bgroup\bf}% 4446%D \AfterPar{\egroup}% 4447%D \GetPar} 4448%D \stoptyping 4449%D 4450%D A macro like \type {\GetPar} can be defined in several ways. The recent version, 4451%D the fourth one in a row, originally was far more complicated, but some 4452%D functionality has been moved to other macros. 4453%D 4454%D We start with the more simple but in some cases more appropriate alternative is 4455%D \type {\GotoPar}. This one leaves \type {\par} unchanged and is therefore more 4456%D robust. On the other hand, \type {\AfterPar} is not supported. 4457 4458\let\syst_helpers_par_before\relax 4459\let\syst_helpers_par_around\relax 4460 4461% \permanent\protected\def\dowithpar#1#2% 4462% {\globalpushmacro\syst_helpers_par_around 4463% \def\syst_helpers_par_around##1\par{#1##1#2\globalpopmacro\syst_helpers_par_around}% 4464% \expandafter\syst_helpers_par_around\ignorepars} 4465 4466% \permanent\protected\def\dogotopar#1% 4467% {\globalpushmacro\syst_helpers_par_before 4468% \def\syst_helpers_par_before{#1\globalpopmacro\syst_helpers_par_before}% 4469% \expandafter\syst_helpers_par_before\ignorepars} 4470 4471\permanent\protected\def\dowithpar#1#2% 4472 {\begingroup\aftergrouped{#1\dontleavehmode\wrapuppar{#2}}\expandafter\endgroup\ignorepars} 4473 4474\permanent\protected\def\dogotopar#1% 4475 {\begingroup\aftergrouped{#1}\expandafter\endgroup\ignorepars} 4476 4477\aliased\let\dogotoparcs\dogotopar 4478 4479\permanent\protected\def\dogotoparstart 4480 {\ignorepars} 4481 4482%D This is old and kind of obsolete: 4483 4484\newtoks\BeforePar 4485\newtoks\AfterPar 4486 4487\permanent\protected\def\GetPar 4488 {\normalexpanded 4489 {\dowithpar 4490 {\the\BeforePar 4491 \BeforePar\emptytoks} 4492 {\the\AfterPar 4493 \BeforePar\emptytoks 4494 \AfterPar\emptytoks}}} 4495 4496\permanent\protected\def\GotoPar 4497 {\normalexpanded 4498 {\dogotopar 4499 {\the\BeforePar 4500 \BeforePar\emptytoks}}} 4501 4502%D \macros 4503%D {dowithpargument,dowithwargument} 4504%D 4505%D The next macros are a variation on \type {\GetPar}. When macros expect an 4506%D argument, it interprets a grouped sequence of characters a one token. While this 4507%D adds to robustness and less ambiguous situations, we sometimes want to be a bit 4508%D more flexible, or at least want to be a bit more tolerant to user input. 4509%D 4510%D We start with a commands that acts on paragraphs. This 4511%D command is called as: 4512%D 4513%D \starttyping 4514%D \dowithpargument\command 4515%D \dowithpargument{\command ... } 4516%D \stoptyping 4517%D 4518%D In \CONTEXT\ we use this one to read in the titles of chapters, sections etc. The 4519%D commands responsible for these activities accept several alternative ways of 4520%D argument passing. In these examples, the \type {\par} can be omitted when an 4521%D empty line is present. 4522%D 4523%D \starttyping 4524%D \command{...} 4525%D \command ... \par 4526%D \command 4527%D {...} 4528%D \command 4529%D ... \par 4530%D \stoptyping 4531 4532\let\syst_helpers_next_par\relax 4533\let\syst_helpers_next_arg\relax 4534 4535\permanent\protected\def\dowithpargument#1% 4536 {\def\syst_helpers_next_par##1 \par{#1{##1}}% 4537 \def\syst_helpers_next_arg##1{#1{##1}}% 4538 \doifelsenextbgroup\syst_helpers_next_arg{\doifelsenextchar\par{#1{}}\syst_helpers_next_par}} 4539 4540%D The \type {p} in the previous command stands for paragraph. When we want to act 4541%D upon words we can use the \type{w} alternative. 4542%D 4543%D \starttyping 4544%D \dowithwargument\command 4545%D \dowithwargument{... \command ...} 4546%D \stoptyping 4547%D 4548%D The main difference bwteen two alternatives is in the handling of \type {\par}'s. 4549%D This time the space token acts as a delimiter. 4550%D 4551%D \starttyping 4552%D \command{...} 4553%D \command ... 4554%D \command 4555%D {...} 4556%D \command 4557%D ... 4558%D \stoptyping 4559 4560\let\syst_helpers_next_war\relax 4561\let\syst_helpers_next_arg\relax 4562 4563\permanent\protected\def\dowithwargument#1% 4564 {\def\syst_helpers_next_war##1 {#1{##1}}% 4565 \def\syst_helpers_next_arg##1{#1{##1}}% 4566 \doifelsenextbgroup\syst_helpers_next_arg\syst_helpers_next_war} 4567 4568%D \macros 4569%D {dorepeat,dorepeatwithcommand} 4570%D 4571%D When doing repetitive tasks, we stromgly advice to use \type {\dorecurse}. The 4572%D next alternative however, suits better some of the \CONTEXT\ interface commands. 4573%D 4574%D \starttyping 4575%D \dorepeat[n*\command] 4576%D \stoptyping 4577%D 4578%D The value of the used \COUNTER\ can be called within 4579%D \type{\command} by \type{\repeater}. 4580%D 4581%D A slightly different alternative is: 4582%D 4583%D \starttyping 4584%D \dorepeatwithcommand[n*{...}]\command 4585%D \stoptyping 4586%D 4587%D When we call for something like: 4588%D 4589%D \starttyping 4590%D \dorepeatwithcommand[3*{Hello}]\message 4591%D \stoptyping 4592%D 4593%D we get ourselves three \type {\message{Hello}} messages in a row. In both 4594%D commands, the \type {n*} is optional. When this specification is missing, the 4595%D command executes once. 4596 4597\permanent\protected\def\dorepeatwithcommand[#1]% 4598 {\syst_helpers_repeat_with_command#1*\empty*\relax} 4599 4600\def\syst_helpers_repeat_with_command#1*#2#3*#4\relax#5% 4601 {\ifx#2\empty\syst_helpers_repeat_with_command_again[#1]#5\else\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5\fi} 4602 4603\def\syst_helpers_repeat_with_command_indeed#1#2#3#4% 4604 {\ifx#2\empty % redundant but gives cleaner extensions 4605 #4{#1}% 4606 \orelse\ifnum#1<\zerocount 4607 %\normalexpanded{\dorecurse{\number-\number#1}}{#4{-#2#3}}% 4608 \dorecurse{-#1}{#4{-#2#3}}% 4609 \orelse\ifx#2+% 4610 \dorecurse{#1}{#4{#3}}% 4611 \else 4612 \dorecurse{#1}{#4{#2#3}}% 4613 \fi} 4614 4615\def\syst_helpers_repeat_with_command_again[#1]#2% 4616 {#2{#1}} 4617 4618%D The extension hook permits something like: 4619%D 4620%D \starttyping 4621%D \bgroup 4622%D 4623%D \catcode`\*=\superscriptcatcode 4624%D 4625%D \gdef\syst_helpers_repeat_with_command_again[#1]% 4626%D {\redodorepeatwithcommand#1*\empty*\relax} 4627%D 4628%D \gdef\redodorepeatwithcommand#1*#2#3*#4\relax#5% 4629%D {\syst_helpers_repeat_with_command_indeed{#1}{#2}{#3}#5} 4630%D 4631%D \egroup 4632%D \stoptyping 4633%D 4634%D although one may wonder if changing the catcode of \type {*} is wise. 4635 4636%D \macros 4637%D {doifstringinstringelse} 4638%D 4639%D The next macro is meant for situations where both strings are macros. This save 4640%D some unneeded expansion. But now we just alias. 4641 4642\aliased\let\doifelsestringinstring\doifelseinstring 4643\aliased\let\doifstringinstringelse\doifelseinstring 4644 4645%D \macros 4646%D {appendtoks,prependtoks,appendtoksonce,prependtoksonce, 4647%D doifintokselse,flushtoks,dotoks} 4648%D 4649%D We use tokenlists sparsely within \CONTEXT, because the comma separated lists are 4650%D more suitable for the user interface. Nevertheless we have: 4651%D 4652%D \starttyping 4653%D (\doglobal) \appendtoks ... \to\tokenlist 4654%D (\doglobal) \prependtoks ... \to\tokenlist 4655%D (\doglobal) \flushtoks\tokenlist 4656%D \dotoks\tokenlist 4657%D \stoptyping 4658%D 4659%D These macros are clones of the ones implemented in page~378 of Knuth's \TEX book. 4660 4661\newtoks\t_syst_helpers_scratch 4662 4663\lettonothing\m_syst_helpers_scratch 4664 4665\permanent\protected\def\appendtoks#1\to#2% 4666 {\ifrelax\dodoglobal 4667 \expandafter\toksapp 4668 \else 4669 \resetglobal 4670 \expandafter\gtoksapp 4671 \fi#2{#1}} 4672 4673\permanent\protected\def\prependtoks#1\to#2% 4674 {\ifrelax\dodoglobal 4675 \expandafter\tokspre 4676 \else 4677 \resetglobal 4678 \expandafter\gtokspre 4679 \fi#2{#1}} 4680 4681\permanent\protected\def\appendtoksonce#1\to#2% 4682 {\ifhasxtoks{#1}#2\else 4683 \toksapp#2{#1}% 4684 \fi} 4685 4686\permanent\protected\def\prependtoksonce#1\to#2% 4687 {\ifhasxtoks{#1}#2\else 4688 \tokspre#2{#1}% 4689 \fi} 4690 4691% Soon (check usage): 4692% 4693% \permanent\protected\def\appendtoks #1\to#2{\toksapp#2{#1}} 4694% \permanent\protected\def\prependtoks#1\to#2{\tokspre#2{#1}} 4695% 4696% \permanent\protected\def\appendtoksonce #1\to#2{\ifhasxtoks{#1}#2\else\toksapp#1{#1}\fi} 4697% \permanent\protected\def\prependtoksonce#1\to#2{\ifhasxtoks{#1}#2\else\tokspre#2{#1}\fi} 4698 4699%D The test macro: 4700 4701\permanent\protected\def\doifelseintoks#1#2% #1 en #2 zijn toks 4702 {\ifhasxtoks#1#2% 4703 \expandafter\firstoftwoarguments 4704 \else 4705 \expandafter\secondoftwoarguments 4706 \fi} 4707 4708\aliased\let\doifintokselse\doifelseintoks 4709 4710%D Moved from \type {lxml-ini.tex} to here. This one is for generators that collect 4711%D stuff piecewise, which is sometimes hard on mechanisms that grab content using 4712%D delimiters: 4713%D 4714%D \starttyping 4715%D \startcollecting 4716%D \startcollect \bTABLE \stopcollect 4717%D \startcollect \bTR \stopcollect 4718%D \startcollect \bTD \stopcollect 4719%D \startcollect foo\stopcollect 4720%D \startcollect \eTD \stopcollect 4721%D \startcollect \bTD \stopcollect 4722%D \startcollect bar\stopcollect 4723%D \startcollect \eTD \stopcollect 4724%D \startcollect \eTR \stopcollect 4725%D \startcollect \eTABLE \stopcollect 4726%D \stopcollecting 4727%D \stoptyping 4728 4729\newtoks \collectingtoks 4730 4731\permanent\protected\lettonothing\stopcollect 4732\permanent\protected\lettonothing\stopexpandedcollect 4733 4734\permanent\protected\def\startcollect #1\stopcollect {\toksapp \collectingtoks{#1}} 4735\permanent\protected\def\startexpandedcollect#1\stopexpandedcollect{\etoksapp\collectingtoks{#1}} 4736 4737\permanent\protected\def\startcollecting{\collectingtoks\emptytoks} 4738\permanent\protected\def\stopcollecting {\the\collectingtoks} 4739 4740\permanent\protected\def\collect {\toksapp \collectingtoks} 4741\permanent\protected\def\collectexpanded{\etoksapp\collectingtoks} 4742 4743%D A nice one too: 4744 4745% {\scratchtoks{abc} \removetoks b\from\scratchtoks [\the\scratchtoks]} 4746% {\scratchtoks{abc} \removetoks x\from\scratchtoks [\the\scratchtoks]} 4747% {\scratchtoks{} \removetoks x\from\scratchtoks [\the\scratchtoks]} 4748% {\scratchtoks{xaa} \removetoks x\from\scratchtoks [\the\scratchtoks]} 4749% {\scratchtoks{a\relax b} \removetoks \relax\from\scratchtoks [\showthe\scratchtoks]} 4750 4751\let\syst_helpers_remove_toks\relax 4752 4753\permanent\protected\def\removetoks#1\from#2% 4754 {\def\syst_helpers_remove_toks##1#1##2\empty\empty\empty##3^^^^0004% 4755 {\def\m_syst_string_one{##3}% 4756 \ifempty\m_syst_string_one#2{##1}\else#2{##1##2}\fi}% 4757 \expandafter\syst_helpers_remove_toks\the#2\empty\empty\empty#1\empty\empty\empty^^^^0004} 4758 4759%D Also: 4760 4761\permanent\protected\def\appendetoks#1\to#2% 4762 {\ifrelax\dodoglobal 4763 \expandafter\etoksapp 4764 \else 4765 \resetglobal 4766 \expandafter\xtoksapp 4767 \fi#2{#1}} 4768 4769\permanent\protected\def\prependetoks#1\to#2% 4770 {\ifrelax\dodoglobal 4771 \expandafter\etokspre 4772 \else 4773 \resetglobal 4774 \expandafter\xtokspre 4775 \fi#2{#1}} 4776 4777%D Hm. 4778 4779\permanent\protected\def\flushtoks#1% nb: can reassign to #1 again, hence the indirectness 4780 {\t_syst_helpers_scratch#1\relax 4781 \dodoglobal#1\emptytoks 4782 \the\t_syst_helpers_scratch\relax} 4783 4784\aliased\let\dotoks\the 4785 4786%D \macros 4787%D {beforesplitstring,aftersplitstring} 4788%D 4789%D These both commands split a string at a given point in two 4790%D parts, so \type{x.y} becomes \type{x} or \type{y}. 4791%D 4792%D \starttyping 4793%D \beforesplitstring test.tex\at.\to\filename 4794%D \aftersplitstring test.tex\at.\to\extension 4795%D \stoptyping 4796%D 4797%D The first routine looks (and is indeed) a bit simpler than the second one. The 4798%D alternative looking more or less like the first one did not always give the 4799%D results we needed. Both implementations show some insight in the manipulation of 4800%D arguments. 4801 4802\let\syst_helpers_split_string\relax 4803 4804\permanent\protected\def\beforesplitstring#1\at#2\to#3% 4805 {\def\syst_helpers_split_string##1#2##0^^^^0004% no #- as we need to count 4806 {\ifarguments 4807 \lettonothing#3% 4808 \or 4809 \lettonothing#3% 4810 \else 4811 \def#3{##1}% 4812 \fi}% 4813 \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments} 4814 4815\permanent\protected\def\aftersplitstring#1\at#2\to#3% 4816 {\def\syst_helpers_split_string##0#2##2^^^^0004% no #- as we need to count 4817 {\ifarguments 4818 \lettonothing#3% 4819 \or 4820 \def#3{#1}% 4821 \else 4822 \def#3{##2}% 4823 \fi}% 4824 \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments} 4825 4826%D \macros 4827%D {splitstring,greedysplitstring} 4828%D 4829%D A bonus macro. 4830 4831\permanent\protected\def\splitstring#1\at#2\to#3\and#4% 4832 {\def\syst_helpers_split_string##1#2##2^^^^0004% 4833 {\ifarguments 4834 \lettonothing#3* 4835 \lettonothing#4* 4836 \or 4837 \def#3{#1}% 4838 \lettonothing#4% 4839 \else 4840 \def#3{##1}% 4841 \def#4{##2}% 4842 \fi}% 4843 \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments} 4844 4845\permanent\protected\def\greedysplitstring#1\at#2\to#3\and#4% 4846 {\def\syst_helpers_split_string##1#2##2^^^^0004% 4847 {\ifarguments 4848 \lettonothing#3% 4849 \lettonothing#4% 4850 \or 4851 \def#3{#1}% 4852 \lettonothing#4% 4853 \else 4854 \def#3{##1}% 4855 \def#4{##2}% 4856 \def\syst_helpers_split_string####1#2####2^^^^0004% 4857 {\ifarguments 4858 \or 4859 \else 4860 \expandafter\def\expandafter#3\expandafter{#3####1}% 4861 \def#4{####2}% 4862 \syst_helpers_split_string####2^^^^0004\ignorearguments\ignorearguments 4863 \fi}% 4864 \syst_helpers_split_string##2^^^^0004\ignorearguments\ignorearguments 4865 \fi}% 4866 \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments} 4867 4868%D \macros 4869%D {beforetestandsplitstring, 4870%D aftertestandsplitstring, 4871%D testandsplitstring} 4872 4873\aliased\let\beforetestandsplitstring\beforesplitstring 4874 4875\permanent\protected\def\aftertestandsplitstring#1\at#2\to#3% 4876 {\def\syst_helpers_split_string##0#2##2^^^^0004% no #- as we need to count 4877 {\ifarguments 4878 \lettonothing#3% 4879 \or 4880 \lettonothing#3% 4881 \else 4882 \def#3{##2}% 4883 \fi}% 4884 \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments} 4885 4886\permanent\protected\def\testandsplitstring#1\at#2\to#3\and#4% 4887 {\def\syst_helpers_split_string##1#2##2^^^^0004% 4888 {\ifarguments 4889 \lettonothing#3% 4890 \lettonothing#4% 4891 \or 4892 \lettonothing#3% 4893 \lettonothing#4% 4894 \else 4895 \def#3{##1}% 4896 \def#4{##2}% 4897 \fi}% 4898 \expandafter\syst_helpers_split_string#1^^^^0004\ignorearguments\ignorearguments} 4899 4900%D \macros 4901%D {splitatperiod, splitatcomma, splitatasterisk, splitatcolon, splitatcolons} 4902 4903\protected\def\syst_helpers_splitatperiod #1.#2.#-^^^^0004#3#4{\def #3{#1}\def #4{#2}} 4904\protected\def\syst_helpers_splitatcomma #1,#2,#-^^^^0004#3#4{\def #3{#1}\def #4{#2}} 4905\protected\def\syst_helpers_splitatasterisk #1*#2*#-^^^^0004#3#4{\def #3{#1}\def #4{#2}} 4906\protected\def\syst_helpers_splitatcolon #1:#2:#-^^^^0004#3#4{\def #3{#1}\def #4{#2}} 4907\protected\def\syst_helpers_splitatcolons #1::#2::#-^^^^0004#3#4{\edef#3{#1}\edef#4{#2}} 4908 4909\permanent\protected\def\splitatperiod #1{\normalexpanded{\syst_helpers_splitatperiod #1}..^^^^0004} 4910\permanent\protected\def\splitatcomma #1{\normalexpanded{\syst_helpers_splitatcomma #1},,^^^^0004} % not at ", " 4911\permanent\protected\def\splitatasterisk#1{\normalexpanded{\syst_helpers_splitatasterisk#1}**^^^^0004} 4912\permanent\protected\def\splitatcolon #1{\normalexpanded{\syst_helpers_splitatcolon #1}::^^^^0004} 4913\permanent\protected\def\splitatcolons #1{\normalexpanded{\syst_helpers_splitatcolons #1}::::^^^^0004} 4914 4915%D \macros 4916%D {removesubstring} 4917%D 4918%D A first application of the two routines defined above is: 4919%D 4920%D \starttyping 4921%D \removesubstring-\from first-last\to\nothyphenated 4922%D \stoptyping 4923%D 4924%D Which in terms of \TEX\ looks like: 4925 4926\permanent\protected\def\removesubstring#1\from#2\to#3% 4927 {\splitstring#2\at#1\to\m_syst_string_one\and\m_syst_string_two 4928 \dodoglobal\def#3{\m_syst_string_one\m_syst_string_two}} 4929 4930%D \macros 4931%D {appendtocommalist,prependtocommalist, 4932%D addtocommalist,removefromcommalist} 4933%D 4934%D When working with comma separated lists, one sooner or later want the tools to 4935%D append or remove items from such a list. When we add an item, we first check if 4936%D it's already there. This means that every item in the list is unique. 4937%D 4938%D \starttyping 4939%D \addtocommalist {alfa} \name 4940%D \addtocommalist {beta} \name 4941%D \addtocommalist {gamma} \name 4942%D \removefromcommalist {beta} \name 4943%D \stoptyping 4944%D 4945%D These commands can be prefixed with \type {\doglobal}. The implementation of the 4946%D second command is more complecated, because we have to take leading spaces into 4947%D account. Keep in mind that users may provide lists with spaces after the commas. 4948%D When one item is left, we also have to get rid of trailing spaces. 4949%D 4950%D \starttyping 4951%D \def\words{alfa, beta, gamma, delta} 4952%D \def\words{alfa,beta,gamma,delta} 4953%D \stoptyping 4954%D 4955%D Removing an item takes more time than adding one. A fast appending alternative, 4956%D without any testing, is also provided: 4957%D 4958%D \starttyping 4959%D \appendtocommalist {something} \name 4960%D \prependtocommalist {something} \name 4961%D \stoptyping 4962%D 4963%D This can be implemented as follows: 4964%D 4965%D \starttyping 4966%D \def\appendtocommalist#1#2% 4967%D {\ifx#2\empty 4968%D \dodoglobal\edef#2{#1}% 4969%D \else % no test on empty 4970%D \dodoglobal\edef#2{#2,#1}% 4971%D \fi} 4972%D 4973%D \def\prependtocommalist#1#2% 4974%D {\ifx#2\empty 4975%D \dodoglobal\edef#2{#1}% 4976%D \else % no test on empty 4977%D \dodoglobal\edef#2{#1,#2}% 4978%D \fi} 4979%D \stoptyping 4980%D 4981%D The faster alternatives are: 4982 4983\permanent\protected\def\appendtocommalist#1#2% 4984 {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}} 4985 4986\permanent\protected\def\prependtocommalist#1#2% 4987 {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}} 4988 4989\permanent\protected\def\addtocommalist#1#2% {item} \cs 4990 {\rawdoifelseinset{#1}#2\resetglobal 4991 {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}}} 4992 4993\permanent\protected\def\pretocommalist#1#2% {item} \cs 4994 {\rawdoifelseinset{#1}#2\resetglobal 4995 {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}}} 4996 4997\permanent\protected\def\robustdoifelseinset#1#2% 4998 {\edef\m_syst_string_one{\detokenize\expandafter{\normalexpanded{#1}}}% 4999 \edef\m_syst_string_two{\detokenize\expandafter{\normalexpanded{#2}}}% 5000 \rawdoifelseinset\m_syst_string_one\m_syst_string_two} 5001 5002\aliased\let\robustdoifinsetelse\robustdoifelseinset 5003 5004\permanent\protected\def\robustaddtocommalist#1#2% {item} \cs 5005 {\robustdoifelseinset{#1}#2\resetglobal 5006 {\dodoglobal\edef#2{\ifempty#2\else#2,\fi#1}}} 5007 5008\permanent\protected\def\robustpretocommalist#1#2% {item} \cs 5009 {\robustdoifelseinset{#1}#2\resetglobal 5010 {\dodoglobal\edef#2{#1\ifempty#2\else,#2\fi}}} 5011 5012\permanent\protected\def\xsplitstring#1#2% \cs {str} 5013 {\def\syst_helpers_split_string##1,#2,##2,#2,##-\\% 5014 {\edef\m_syst_string_one{\syst_cleanedup_commalist_b##1\empty\empty\relax}% 5015 \edef\m_syst_string_two{\syst_cleanedup_commalist_a##2,,\relax}}% 5016 \expandafter\syst_helpers_split_string\expandafter,#1,,#2,,#2,\\} 5017 5018\def\syst_cleanedup_commalist_b#1#2\relax{\if#1,\else#1\fi#2} 5019\def\syst_cleanedup_commalist_a#1,,#-\relax{#1} 5020 5021\permanent\protected\def\removefromcommalist#1#2% to be sped up 5022 {\rawdoifelseinset{#1}#2% 5023 {\normalexpanded{\xsplitstring\noexpand#2{#1}}% 5024 \dodoglobal\edef#2% 5025 {\ifempty\m_syst_string_one 5026 \m_syst_string_two 5027 \else 5028 \m_syst_string_one\ifempty\m_syst_string_two\else,\m_syst_string_two\fi 5029 \fi}} 5030 \resetglobal} 5031 5032%D \macros 5033%D {substituteincommalist} 5034%D 5035%D Slow but seldom used, so for the moment we stick to this implementation. 5036%D 5037%D \starttyping 5038%D \substituteincommalist{old}{new}{list} 5039%D \stoptyping 5040 5041\def\syst_helpers_substitute_in_comma_list_step#1% 5042 {\edef\m_syst_string_three{#1}% 5043 \ifx\m_syst_string_one\m_syst_string_three 5044 \ifempty\m_syst_string_two \else 5045 \edef\m_syst_string_four{\ifempty\m_syst_string_four\else\m_syst_string_four,\fi\m_syst_string_two}% 5046 \fi 5047 \else 5048 \edef\m_syst_string_four{\ifempty\m_syst_string_four\else\m_syst_string_four,\fi#1}% 5049 \fi} 5050 5051\permanent\protected\def\substituteincommalist#1#2#3% old, new, list (slooow) 5052 {\edef\m_syst_string_one{#1}% 5053 \edef\m_syst_string_two{#2}% 5054 \lettonothing\m_syst_string_four 5055 \normalexpanded{\rawprocesscommacommand[#3]}\syst_helpers_substitute_in_comma_list_step 5056 \let#3\m_syst_string_four} 5057 5058%D \macros 5059%D {replaceincommalist} 5060%D 5061%D The next macro can be used to replace an indexed element in a commalist: 5062%D 5063%D \starttyping 5064%D \replaceincommalist\MyList{2} 5065%D \stoptyping 5066%D 5067%D Element~2 will be replaced by the current meaning of the macro \type 5068%D {\newcommalistelement}. The old meaning is saved in \type {\commalistelement}. 5069%D The replacement honors grouped items, like in: 5070%D 5071%D \starttyping 5072%D \def\MyList{a,b,c,d,e,f} \replaceincommalist\MyList{3} 5073%D \def\MyList{a,b,c,d,e,f} \replaceincommalist\MyList{3} 5074%D \def\MyList{a,{b,c},d,e,f} \replaceincommalist\MyList{3} 5075%D \def\MyList{a,b,c,{d,e,f}} \replaceincommalist\MyList{3} 5076%D \stoptyping 5077%D 5078%D This macro was used in the bibtex code (and is probably no longer needed). 5079 5080\newinteger\c_syst_helpers_comma_list_index 5081 5082\lettonothing\m_syst_helpers_comma_list_target 5083 5084\mutable\lettonothing\newcommalistelement 5085 5086\def\syst_helpers_replace_in_comma_list_step#1% we can use #+ here too 5087 {\ifnum\commalistcounter=\c_syst_helpers_comma_list_index\relax 5088 \ifempty\newcommalistelement\else 5089 \ifempty\m_syst_helpers_comma_list_target 5090 \let\m_syst_helpers_comma_list_target\newcommalistelement 5091 \else 5092 \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter 5093 \m_syst_helpers_comma_list_target\expandafter\expandafter\expandafter 5094 {\expandafter\m_syst_helpers_comma_list_target\expandafter,\newcommalistelement}% 5095 \fi 5096 \fi 5097 \def\commalistelement{#1}% 5098 \else 5099 \ifempty\m_syst_helpers_comma_list_target 5100 \ifx\nexttoken\bgroup % is known -) 5101 \def\m_syst_helpers_comma_list_target{{#1}}% 5102 \else 5103 \def\m_syst_helpers_comma_list_target{#1}% 5104 \fi 5105 \else 5106 \ifx\nexttoken\bgroup % is known -) 5107 \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,{#1}}% 5108 \else 5109 \expandafter\def\expandafter\m_syst_helpers_comma_list_target\expandafter{\m_syst_helpers_comma_list_target,#1}% 5110 \fi 5111 \fi 5112 \fi 5113 \advanceby\commalistcounter\plusone} 5114 5115\permanent\protected\def\replaceincommalist#1#2% #1 = commalistelement #2 = position starts at 1 5116 {\c_syst_helpers_comma_list_index#2\relax 5117 \lettonothing\m_syst_helpers_comma_list_target 5118 \lettonothing\commalistelement 5119 \commalistcounter\plusone 5120 \expandafter\processcommalist\expandafter[#1]\syst_helpers_replace_in_comma_list_step 5121 \dodoglobal\let#1\m_syst_helpers_comma_list_target} 5122 5123%D \macros 5124%D {globalprocesscommalist} 5125%D 5126%D The commalist processing commands are characterized by the fact that the way they 5127%D handle expansion as well as the fact that they can be nested. This makes them 5128%D kind of useless for handling comma lists in alignments. In these situations the 5129%D next macro can be of use. 5130 5131\lettonothing\m_syst_helpers_comma_list_command_global 5132 5133\def\syst_helpers_comma_list_command_global_step#1,% 5134 {\if]#1\else 5135 \m_syst_helpers_comma_list_command_global{#1}% 5136 \expandafter\syst_helpers_comma_list_command_global_step 5137 \fi} 5138 5139\permanent\protected\def\globalprocesscommalist[#1]#2% 5140 {\glet\m_syst_helpers_comma_list_command_global#2% 5141 \expandafter\syst_helpers_comma_list_command_global_step#1,],} 5142 5143%D \macros 5144%D {withoutpt,PtToCm, 5145%D numberofpoints,dimensiontocount} 5146%D 5147%D We can convert point into centimeters with: 5148%D 5149%D \starttyping 5150%D \PtToCm{dimension} 5151%D \stoptyping 5152 5153% {\catcode`\.=\othercatcode 5154% \catcode`\p=\othercatcode 5155% \catcode`\t=\othercatcode 5156% \gdef\WITHOUTPT#1pt{#1}} 5157% 5158% \def\withoutpt#1% 5159% {\expandafter\WITHOUTPT#1} 5160% 5161% Okay, not really an impressive extension to the engine, but better than some macro 5162% that is demonstrating deeper understanding of \TEX. It's anyway a trivial extension 5163% anyway. There are actually many examples of very advanced macros and exposure of 5164% how clever one is that could be done cheap in the engine. It's just that at the time 5165% that \TEX\ was written, it made no sense to add a lot of that. After decades we know 5166% what extras we need. 5167 5168% \permanent\def\withoutpt#1{\thewithoutunit\dimexpr#1} 5169 5170\aliased\let\withoutpt\toscaled 5171 5172%D The capitals are needed because \type {p} and \type {t} have catcode~12, while 5173%D macronames only permit tokens with the catcode~11. As a result we cannot use the 5174%D \type {.group} primitives. Those who want to know more about this kind of 5175%D manipulations, we advice to study the \TEX book in detail. Because this macro 5176%D does not do any assignment, we can use it in the following way too. 5177 5178% \permanent\def\PtToCm#1{\thewithoutunit\dimexpr0.0351459804\dimexpr#1\relax\relax cm} 5179 5180\permanent\def\PtToCm#1{\toscaled\dimexpr0.0351459804\dimexpr#1\relax\relax cm} 5181 5182%D We also support: 5183%D 5184%D \starttyping 5185%D \numberofpoints {dimension} 5186%D \dimensiontocount {dimension} {\count} 5187%D \stoptyping 5188%D 5189%D Both macros return a rounded number. 5190 5191% \dimensiontocount{10.49pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.49pt} 5192% \dimensiontocount{10.51pt}\scratchcounter \the\scratchcounter / \numberofpoints{10.51pt} 5193 5194\permanent\def\dimensiontocount#1#2{#2\numexpr\dimexpr#1\relax/\maxcard\relax} 5195\permanent\def\numberofpoints #1{\tointeger{\dimexpr#1\relax/\maxcard}} 5196 5197%D \macros 5198%D {swapdimens,swapskips,swapcounts,swapmacros, 5199%D globalswapdimens,globalswapcounts,globalswapmacros} 5200%D 5201%D Simple but effective are the next two macros. There name exactly states their 5202%D purpose. 5203 5204\newdimension\d_syst_helpers_swapped 5205\newgluespec \s_syst_helpers_swapped 5206\newinteger \c_syst_helpers_swapped 5207\let \m_syst_helpers_swapped\relax 5208 5209% \protected\def\swapdimens#1#2{\d_syst_helpers_swapped#1#1#2#2\d_syst_helpers_swapped} 5210% \protected\def\swapskips #1#2{\s_syst_helpers_swapped#1#1#2#2\s_syst_helpers_swapped} 5211% \protected\def\swapcounts#1#2{\c_syst_helpers_swapped#1#1#2#2\c_syst_helpers_swapped} 5212% \protected\def\swapmacros#1#2{\let\m_syst_helpers_swapped#1\let#1#2\let#2\m_syst_helpers_swapped} 5213 5214\aliased\let\swapdimens\swapcsvalues 5215\aliased\let\swapskips \swapcsvalues 5216\aliased\let\swapcounts\swapcsvalues 5217\aliased\let\swapmacros\swapcsvalues 5218 5219% \protected\def\globalswapdimens#1#2{\d_syst_helpers_swapped#1\global#1#2\global#2\d_syst_helpers_swapped} 5220% \protected\def\globalswapskips #1#2{\s_syst_helpers_swapped#1\global#1#2\global#2\s_syst_helpers_swapped} 5221% \protected\def\globalswapcounts#1#2{\c_syst_helpers_swapped#1\global#1#2\global#2\c_syst_helpers_swapped} 5222% \protected\def\globalswapmacros#1#2{\let\m_syst_helpers_swapped#1\glet#1#2\glet#2\m_syst_helpers_swapped} 5223 5224\permanent\protected\def\globalswapdimens{\global\swapcsvalues} 5225\permanent\protected\def\globalswapskips {\global\swapcsvalues} 5226\permanent\protected\def\globalswapcounts{\global\swapcsvalues} 5227\permanent\protected\def\globalswapmacros{\global\swapcsvalues} 5228 5229%D \macros 5230%D {pushmacro,popmacro} 5231%D 5232%D Premature and a bit of beta, we offer: 5233%D 5234%D \starttyping 5235%D \pushmacro\macro 5236%D \popmacro\macro 5237%D \stoptyping 5238 5239\permanent\let\pushmacro\localpushmacro 5240\permanent\let\popmacro \localpopmacro 5241 5242%D \macros 5243%D {setlocalhsize,distributedhsize} 5244%D 5245%D Sometimes we need to work with the \type{ \hsize} that is corrected for 5246%D indentation and left and right skips. The corrected value is available in \type 5247%D {\localhsize}, which needs to be calculated with \type {\setlocalhsize} first. %D 5248%D 5249%D \starttyping 5250%D \setlocalhsize \hbox to \localhsize{...} 5251%D \setlocalhsize[-1em] \hbox to \localhsize{...} 5252%D \setlocalhsize[.5ex] \hbox to \localhsize{...} 5253%D \stoptyping 5254%D 5255%D These examples show us that an optional can be used. The value provided is added 5256%D to \type {\localhsize}. 5257 5258\newdimension\localhsize 5259 5260\permanent\protected\def\setlocalhsize % don't change ! 5261 {\doifelsenextoptional 5262 \syst_helpers_set_local_hsize_yes 5263 \syst_helpers_set_local_hsize_nop} 5264 5265\def\syst_helpers_set_local_hsize_nop 5266 {\localhsize\availablehsize} 5267 5268\def\syst_helpers_set_local_hsize_yes[#1]% 5269 {\syst_helpers_set_local_hsize_nop 5270 \advanceby\localhsize#1\relax} 5271 5272\permanent\def\availablehsize 5273 {\dimexpr 5274 \hsize-\leftskip-\rightskip 5275 \ifnum\hangafter<\zerocount 5276 \ifdim\hangindent>\zeropoint-\else+\fi\hangindent 5277 \fi 5278 \relax} 5279 5280\permanent\def\distributedhsize#1#2#3% 5281 {\dimexpr(#1-\numexpr#3-1\relax\dimexpr#2\relax)/#3\relax} 5282 5283\permanent\def\hsizefraction#1#2% 5284 {\dimexpr#1/#2\relax} 5285 5286%D \macros 5287%D {doifvalue,doifnotvalue,doifelsevalue, 5288%D doifnothing,doifsomething,doifelsenothing, 5289%D doifvaluenothing,doifvaluesomething,doifelsevaluenothing} 5290%D 5291%D These \type {\if} commands can be used to access macros (or variables) that are 5292%D normally accessed by using \type {\getvalue}. Using these alternatives safes us 5293%D three tokens per call. Anyone familiar with the not||values ones, can derive 5294%D their meaning from the definitions. 5295 5296\permanent\protected\def\doifvalue#1#2% 5297 {\iftok{\csname#1\endcsname}{#2}% 5298 \expandafter\firstofoneargument 5299 \else 5300 \expandafter\gobbleoneargument 5301 \fi} 5302 5303\permanent\protected\def\doifnotvalue#1#2% 5304 {\iftok{\csname#1\endcsname}{#2}% 5305 \expandafter\gobbleoneargument 5306 \else 5307 \expandafter\firstofoneargument 5308 \fi} 5309 5310\permanent\protected\def\doifelsevalue#1#2% 5311 {\iftok{\csname#1\endcsname}{#2}% 5312 \expandafter\firstoftwoarguments 5313 \else 5314 \expandafter\secondoftwoarguments 5315 \fi} 5316 5317\permanent\protected\def\doifnothing#1% 5318 {\ifempty{#1}% 5319 \expandafter\firstofoneargument 5320 \else 5321 \expandafter\gobbleoneargument 5322 \fi} 5323 5324\permanent\protected\def\doifsomething#1% 5325 {\ifempty{#1}% 5326 \expandafter\gobbleoneargument 5327 \else 5328 \expandafter\firstofoneargument 5329 \fi} 5330 5331\permanent\protected\def\doifelsenothing#1% 5332 {\ifempty{#1}% 5333 \expandafter\firstoftwoarguments 5334 \else 5335 \expandafter\secondoftwoarguments 5336 \fi} 5337 5338\permanent\protected\def\doifelsesomething#1% 5339 {\ifempty{#1}% 5340 \expandafter\secondoftwoarguments 5341 \else 5342 \expandafter\firstoftwoarguments 5343 \fi} 5344 5345\permanent\protected\def\doifvaluenothing#1% 5346 {\ifempty{\csname#1\endcsname}% 5347 \expandafter\firstofoneargument 5348 \else 5349 \expandafter\gobbleoneargument 5350 \fi} 5351 5352\permanent\protected\def\doifvaluesomething#1% 5353 {\ifempty{\csname#1\endcsname}% 5354 \expandafter\gobbleoneargument 5355 \else 5356 \expandafter\firstofoneargument 5357 \fi} 5358 5359\permanent\protected\def\doifelsevaluenothing#1% 5360 {\ifempty{\csname#1\endcsname}% 5361 \expandafter\firstoftwoarguments 5362 \else 5363 \expandafter\secondoftwoarguments 5364 \fi} 5365 5366\aliased\let\doifvalueelse \doifelsevalue 5367\aliased\let\doifnothingelse \doifelsenothing 5368\aliased\let\doifsomethingelse \doifelsesomething 5369\aliased\let\doifvaluenothingelse\doifelsevaluenothing 5370 5371%D \macros 5372%D {doifemptyelsevalue, doifemptyvalue, doifnotemptyvalue} 5373%D 5374%D Also handy: 5375 5376\permanent\def\doifelseemptyvalue#1% 5377 {\expandafter\ifempty\csname#1\endcsname 5378 \expandafter\firstoftwoarguments 5379 \else 5380 \expandafter\secondoftwoarguments 5381 \fi} 5382 5383\aliased\let\doifemptyvalueelse\doifelseemptyvalue 5384 5385\permanent\def\doifemptyvalue#1% 5386 {\expandafter\ifempty\csname#1\endcsname 5387 \expandafter\firstofoneargument 5388 \else 5389 \expandafter\gobbleoneargument 5390 \fi} 5391 5392\permanent\def\doifnotemptyvalue#1% 5393 {\expandafter\ifempty\csname#1\endcsname 5394 \expandafter\gobbleoneargument 5395 \else 5396 \expandafter\firstofoneargument 5397 \fi} 5398 5399%D \macros 5400%D {doifallcommonelse} 5401%D 5402%D A complete match of two sets can be tested with \type {\doifallcommonelse}, where 5403%D the first two arguments are sets. 5404 5405\let\syst_helpers_do_common_check_all\gobbleoneargument 5406 5407\def\syst_helpers_do_if_all_common_else#1#2#3#4% slow 5408 {\def\syst_helpers_do_common_check_all##1% 5409 {\ifinset{##1}{#4}\else\donefalse\fi 5410 \ifdone\else\expandafter\quitcommalist\fi}% 5411 \donetrue 5412 \processcommalist[#3]\syst_helpers_do_common_check_all 5413 \ifdone\expandafter#1\else\expandafter#2\fi} 5414 5415\permanent\protected\def\doifelseallcommon{\syst_helpers_do_if_all_common_else\firstoftwoarguments\secondoftwoarguments} 5416\permanent\protected\def\doifallcommon {\syst_helpers_do_if_all_common_else\firstofoneargument \gobbleoneargument } 5417\permanent\protected\def\doifnotallcommon {\syst_helpers_do_if_all_common_else\gobbleoneargument \firstofoneargument } 5418 5419\aliased\let\doifallcommonelse\doifelseallcommon 5420 5421%D \macros 5422%D {DOIF,DOIFELSE,DOIFNOT} 5423%D 5424%D \TEX\ is case sensitive. When comparing arguments, this feature sometimes is less 5425%D desirable, for instance when we compare filenames. The next three alternatives 5426%D upcase their arguments before comparing them. 5427%D 5428%D \starttyping 5429%D \DOIF {string1} {string2} {...} 5430%D \DOIFNOT {string1} {string2} {...} 5431%D \DOIFELSE {string1} {string2} {then ...}{else ...} 5432%D \stoptyping 5433%D 5434%D We have to use a two||step implementation, because the 5435%D expansion has to take place outside \type{\uppercase}. 5436%D 5437%D These might end up as \LUA based helpers (i.e. consider these 5438%D obsolete: 5439 5440\protected\def\syst_helpers_do_IF#1#2% 5441 {\uppercase{\iftok{#1}{#2}}% 5442 \expandafter\firstofoneargument 5443 \else 5444 \expandafter\gobbleoneargument 5445 \fi} 5446 5447\protected\def\syst_helpers_do_IF_NOT#1#2% 5448 {\uppercase{\iftok{#1}{#2}}% 5449 \expandafter\gobbleoneargument 5450 \else 5451 \expandafter\firstofoneargument 5452 \fi} 5453 5454\protected\def\syst_helpers_do_IF_ELSE#1#2% 5455 {\uppercase{\iftok{#1}{#2}}% 5456 \expandafter\firstoftwoarguments 5457 \else 5458 \expandafter\secondoftwoarguments 5459 \fi} 5460 5461\protected\def\syst_helpers_do_IF_INSTRING_ELSE#1#2% 5462 {\uppercase{\doifelseinstring{#1}{#2}}} 5463 5464\permanent\protected\def\DOIF #1#2{\normalexpanded{\syst_helpers_do_IF {#1}{#2}}}% will become obsolete 5465\permanent\protected\def\DOIFNOT #1#2{\normalexpanded{\syst_helpers_do_IF_NOT {#1}{#2}}}% will become obsolete 5466\permanent\protected\def\DOIFELSE #1#2{\normalexpanded{\syst_helpers_do_IF_ELSE {#1}{#2}}}% will become obsolete 5467\permanent\protected\def\DOIFINSTRINGELSE #1#2{\normalexpanded{\syst_helpers_do_IF_INSTRING_ELSE{#1}{#2}}}% will become obsolete 5468 5469%D \macros 5470%D {dosingleargumentwithset, 5471%D dodoubleargumentwithset,dodoubleemptywithset, 5472%D dotripleargumentwithset,dotripleemptywithset} 5473%D 5474%D These maybe too mysterious macros enable us to handle more than one setup at once. 5475%D 5476%D \starttyping 5477%D \dosingleargumentwithset \command[#1] 5478%D \dodoubleargumentwithset \command[#1][#2] 5479%D \dotripleargumentwithset \command[#1][#2][#3] 5480%D \dodoubleemptywithset \command[#1][#2] 5481%D \dotripleemptywithset \command[#1][#2][#3] 5482%D \stoptyping 5483%D 5484%D The first macro calls \type {\command[##1]} for each string in the set~\type 5485%D {#1}. The second one calls for \typ {\command [##1][#2]} and the third, well one 5486%D may guess. These commands support constructions like: 5487%D 5488%D \starttyping 5489%D \def\dodefinesomething[#1][#2]% 5490%D {\getparameters[\??xx#1][#2]} 5491%D 5492%D \protected\def\definesomething% 5493%D {\dodoubleargumentwithset\dodefinesomething} 5494%D \stoptyping 5495%D 5496%D Which accepts calls like: 5497%D 5498%D \starttyping 5499%D \definesomething[alfa,beta,...][variable=...,...] 5500%D \stoptyping 5501 5502\let\syst_helpers_with_set_step\relax % maybe push pop 5503 5504\permanent\tolerant\protected\def\dodoubleemptywithset#1#*[#2]#*[#3]% 5505 {\ifempty{#2}\else 5506 \def\syst_helpers_with_set_step##1{#1[##1][#3]}% 5507 \processcommalist[#2]\syst_helpers_with_set_step 5508 \fi} 5509 5510\permanent\tolerant\protected\def\dotripleemptywithset#1#*[#2]#*[#3]#*[#4]% 5511 {\ifempty{#2}\else 5512 \def\syst_helpers_with_set_step##1{#1[##1][#3][#4]}% 5513 \processcommalist[#2]\syst_helpers_with_set_step 5514 \fi} 5515 5516\aliased\let\dodoubleargumentwithset\dodoubleemptywithset 5517\aliased\let\dotripleargumentwithset\dotripleemptywithset 5518 5519%D \macros 5520%D {stripcharacters,stripspaces} 5521%D 5522%D The next command was needed first when we implemented the \CONTEXT\ interactivity 5523%D macros. When we use labeled destinations, we often cannot use all the characters 5524%D we want. We therefore strip some of the troublemakers, like spaces, from the 5525%D labels before we write them to the \DVI||file, which passes them to for instance 5526%D a \POSTSCRIPT\ file. 5527%D 5528%D \starttyping 5529%D \stripspaces\from\one\to\two 5530%D \stoptyping 5531%D 5532%D Both the old string \type {\one} and the new one \type {\two} are expanded. This 5533%D command is a special case of: 5534%D 5535%D \starttyping 5536%D \stripcharacter\char\from\one\to\two 5537%D \stoptyping 5538%D 5539%D As we can see below, spaces following a control sequence are to enclosed in \type 5540%D {{}}. 5541 5542\let\syst_helpers_strip_character\relax 5543 5544\lettonothing\m_syst_helpers_strip_character 5545 5546\permanent\protected\def\stripcharacter#1\from#2\to#3% 5547 {\def\syst_helpers_strip_character##1#1##2\end 5548 {\edef\m_syst_helpers_strip_character{\m_syst_helpers_strip_character##1}% 5549 \doifnotempty{##2}{\syst_helpers_strip_character##2\end}}% 5550 \lettonothing\m_syst_helpers_strip_character 5551 \edef\m_syst_string_one{#2}% 5552 \expandafter\syst_helpers_strip_character\m_syst_string_one#1\end 5553 \dodoglobal\let#3\m_syst_helpers_strip_character} 5554 5555\permanent\protected\def\stripspaces\from#1\to#2% will become \unspacestring#1\from#2 5556 {\stripcharacter{ }\from#1\to#2} 5557 5558%D \macros 5559%D {unspacestring} 5560%D 5561%D The next macro does the same but is more compatible with other macros, like \type 5562%D {\convert...}. 5563 5564\permanent\protected\def\unspacestring#1\to#2% 5565 {\stripcharacter{ }\from#1\to#2} 5566 5567%D \macros 5568%D {executeifdefined} 5569%D 5570%D \CONTEXT\ uses one auxiliary file for all data concerning tables of contents, 5571%D references, two||pass optimizations, sorted lists etc. This file is loaded as 5572%D many times as needed. During such a pass we skip the commands thate are of no use 5573%D at that moment. Because we don't want to come into trouble with undefined 5574%D auxiliary commands, we call the macros in a way similar to \type {\getvalue}. The 5575%D next macro take care of such executions and when not defined, gobbles the 5576%D unwanted arguments. 5577%D 5578%D \starttyping 5579%D \executeifdefined{name}\gobbleoneargument 5580%D \stoptyping 5581%D 5582%D We can of course gobble more arguments using the appropriate gobbling command. 5583 5584\permanent\def\executeifdefined#1% #2 / never change this one again 5585 {\ifcsname#1\endcsname 5586 \expandafter\expandafter\expandafter\lastnamedcs\expandafter\gobbleoneargument 5587 \else 5588 \expandafter\firstofoneargument 5589 \fi} 5590 5591%D This one also has the advantage that it is fully expandable and that it can be 5592%D used after an assignment. 5593 5594%D \macros 5595%D {doifsomespaceelse} 5596%D 5597%D The next command checks a string on the presence of a space and executed a 5598%D command accordingly. 5599%D 5600%D \starttyping 5601%D \doifsomespaceelse {tekst} {then ...} {else ...} 5602%D \stoptyping 5603%D 5604%D We use this command in \CONTEXT\ for determing if an argument must be broken into 5605%D words when made interactive. Watch the use of \type {\noexpand}. 5606 5607%D Is this one still needed? 5608 5609% \def\syst_helpers_if_some_space_else#1 #2#3^^^^0004{\if\noexpand#2@} 5610% 5611% \def\doifelsesomespace#1% % #2#3% 5612% {\syst_helpers_if_some_space_else#1 @ @^^^^0004% #3\else#2\fi} 5613% \expandafter\secondoftwoarguments 5614% \else 5615% \expandafter\firstoftwoarguments 5616% \fi} 5617 5618\permanent\edef\doifelsesomespace#1% 5619 {\noexpand\ifhastok\space{#1}% 5620 \noexpand\expandafter\noexpand\firstoftwoarguments 5621 \noexpand\else 5622 \noexpand\expandafter\noexpand\secondoftwoarguments 5623 \noexpand\fi} 5624 5625\aliased\let\doifsomespaceelse\doifelsesomespace 5626 5627%D \macros 5628%D {processseparatedlist} 5629%D 5630%D Maybe a bit late, but here is a more general version of the \type 5631%D {\processcommalist} command. This time we don't handle nesting but accept 5632%D arbitrary seperators. 5633%D 5634%D \starttyping 5635%D \processseparatedlist[list][separator]\command 5636%D \stoptyping 5637%D 5638%D One can think of things like: 5639%D 5640%D \starttyping 5641%D \processseparatedlist[alfa+beta+gamma][+]\message 5642%D \stoptyping 5643%D 5644%D We want to handle all situations, like: 5645%D 5646%D \startbuffer 5647%D \processseparatedlist[{aap noot}] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} 5648%D \processseparatedlist[{aap} {noot}][ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} 5649%D \processseparatedlist[aap {noot}] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} 5650%D \processseparatedlist[aap noot] [ ]{\def\xxx} \convertcommand\xxx\to\ascii {\tttf\ascii} 5651%D \stopbuffer 5652%D 5653%D \typebuffer \getbuffer 5654%D 5655%D Therefore we smuggle a \type {\relax} in front of the argument, which we remove 5656%D afterwards. 5657 5658\let\syst_helpers_process_separated_list_step\relax 5659 5660% \def\syst_helpers_process_separated_list#1]#*[#2]#3% 5661% {\def\syst_helpers_process_separated_list_step##1##2#2% 5662% {\def\m_syst_string_one{##2}% suggested by VZ 5663% \if]##1% 5664% \let\syst_helpers_process_separated_list_step\relax 5665% \orelse\ifx\blankspace\m_syst_string_one 5666% #3{##1}% 5667% \orelse\if]##2% 5668% \let\syst_helpers_process_separated_list_step\relax 5669% \else 5670% #3{##1##2}% 5671% \fi 5672% \syst_helpers_process_separated_list_step}% 5673% \expandafter\syst_helpers_process_separated_list_step\gobbleoneargument#1#2]#2} 5674 5675% \def\syst_helpers_process_separated_list#1]#*[#2]#3% 5676% {\def\syst_helpers_process_separated_list_step##1##2#2% 5677% {\def\m_syst_string_one{##2}% suggested by VZ 5678% \if]##1% 5679% \orelse\ifx\blankspace\m_syst_string_one 5680% #3{##1}% 5681% \expandafter\syst_helpers_process_separated_list_step 5682% \orelse\if]##2% 5683% \else 5684% #3{##1##2}% 5685% \expandafter\syst_helpers_process_separated_list_step 5686% \fi 5687% }% 5688% \expandafter\syst_helpers_process_separated_list_step\gobbleoneargument#1#2]#2} 5689 5690% \permanent\protected\def\processseparatedlist[% 5691% {\syst_helpers_process_separated_list\relax} 5692 5693\permanent\protected\def\processseparatedlist[#+]#*[#2]#3% 5694 {\tolerant\def\syst_helpers_process_separated_list_step##1#2% 5695 {\ifarguments\or 5696 #3{##1}% 5697 \expandafter\syst_helpers_process_separated_list_step 5698 \fi}% 5699 \syst_helpers_process_separated_list_step#1\ignorearguments\ignorearguments} 5700 5701%D \macros 5702%D {processlist} 5703%D 5704%D An even more general list processing macro is the following one: 5705%D 5706%D \starttyping 5707%D \processlist{beginsym}{endsym}{separator}\docommand list 5708%D \stoptyping 5709%D 5710%D This one supports arbitrary open and close symbols as well as user defined 5711%D separators. 5712%D 5713%D \starttyping 5714%D \processlist(){=>}\docommand(a=>b=>c=>d) 5715%D \stoptyping 5716 5717\let\syst_helpers_process_any_list \relax 5718\let\syst_helpers_process_any_list_indeed\relax 5719\let\syst_helpers_process_any_list_step \relax 5720 5721\permanent\protected\def\processlist#1#2#3#4% no blank skipping ! 5722 {\def\syst_helpers_process_any_list_indeed##1#2% 5723 {\def\syst_helpers_process_any_list_step####1####2#3% 5724 {\ifx#2####1% 5725 \let\syst_helpers_process_any_list_step\relax 5726 \orelse\ifx#2####2% 5727 \let\syst_helpers_process_any_list_step\relax 5728 \else 5729 #4{####1####2}% 5730 \fi 5731 \syst_helpers_process_any_list_step}% 5732 \expandafter\syst_helpers_process_any_list_step\gobbleoneargument##1#3#2#3}% 5733 \def\syst_helpers_process_any_list#1% 5734 {\syst_helpers_process_any_list_indeed\relax}% 5735 \syst_helpers_process_any_list} 5736 5737%D \macros 5738%D {processassignlist} 5739%D 5740%D Is possible to combine an assignment list with one containing keywords. 5741%D Assignments are treated accordingly, keywords are treated by \type {\command}. 5742%D 5743%D \starttyping 5744%D \processassignlist[...=...,...=...,...]\commando 5745%D \stoptyping 5746%D 5747%D This command can be integrated in \type {\getparameters}, but we decided best not 5748%D to do so. 5749 5750\let\syst_helpers_process_assign_list_assign\gobbleoneoptional 5751\let\syst_helpers_process_assign_list_step \gobbleoneargument 5752 5753\permanent\protected\def\processassignlist#1[#2]#3% 5754 {\def\syst_helpers_process_assign_list_assign[##1=##-=##2]% 5755 {\iftok{##2}{\relax}#3{##1}\fi}% 5756 \def\syst_helpers_process_assign_list_step##1% 5757 {\syst_helpers_process_assign_list_assign[##1==\relax]}% 5758 \processcommalist[#2]\syst_helpers_process_assign_list_step} 5759 5760%D \macros 5761%D {untextargument 5762%D untexcommand} 5763%D 5764%D When manipulating data(bases) and for instance generating index entries, the next 5765%D three macros can be of help: 5766%D 5767%D \starttyping 5768%D \untextargument{...}\to\name 5769%D \untexcommand {...}\to\name 5770%D \stoptyping 5771%D 5772%D They remove braces and backslashes and give us something to sort. 5773 5774\lettonothing\m_syst_helpers_untexed 5775 5776\permanent\protected\def\untexsomething 5777 {\begingroup 5778 \catcode\leftbraceasciicode \ignorecatcode 5779 \catcode\rightbraceasciicode\ignorecatcode 5780 \escapechar\minusone 5781 \syst_helpers_untex_something} 5782 5783\def\syst_helpers_untex_something#1#2\to#3% 5784 {\doglobal#1#2\to\m_syst_helpers_untexed 5785 \endgroup 5786 \let#3\m_syst_helpers_untexed} 5787 5788\permanent\protected\def\untexargument{\untexsomething\convertargument} 5789\permanent\protected\def\untexcommand {\untexsomething\convertcommand} 5790 5791%D \macros 5792%D {ScaledPointsToBigPoints,ScaledPointsToWholeBigPoints} 5793%D 5794%D One characteristic of \POSTSCRIPT\ and \PDF\ is that both used big points (\TEX's 5795%D bp). The next macros convert points and scaled points into big points. The magic 5796%D factor $72/72.27$ can be found in most \TEX\ related books. 5797%D 5798%D \starttyping 5799%D \ScaledPointsToBigPoints {number} \target 5800%D \ScaledPointsToWholeBigPoints {number} \target 5801%D \stoptyping 5802 5803\aliased\let\tobigpoints % todo: permanent at lua end 5804\aliased\let\towholebigpoints % todo: permanent at lua end 5805 5806\permanent\protected\def\PointsToBigPoints #1#2{\edef#2{\tobigpoints #1}} % can be avoided 5807\permanent\protected\def\PointsToWholeBigPoints #1#2{\edef#2{\towholebigpoints#1}} % can be avoided 5808\permanent\protected\def\ScaledPointsToBigPoints #1#2{\edef#2{\tobigpoints #1\scaledpoint}} % obsolete 5809\permanent\protected\def\ScaledPointsToWholeBigPoints#1#2{\edef#2{\towholebigpoints#1\scaledpoint}} % obsolete 5810 5811%D \macros 5812%D {PointsToReal} 5813%D 5814%D Points can be stripped from their suffix by using \type {\withoutpt}. The next 5815%D macro enveloppes this macro. 5816%D 5817%D \starttyping 5818%D \PointsToReal {dimension} \target 5819%D \stoptyping 5820 5821% \permanent\protected\def\PointsToReal#1#2{\edef#2{\thewithoutunit\dimexpr#1}} 5822 5823\permanent\protected\def\PointsToReal#1#2{\edef#2{\toscaled#1}} 5824 5825%D \macros 5826%D {dontleavehmode} 5827%D 5828%D Sometimes when we enter a paragraph with some command, the first token gets the 5829%D whole first line. We can prevent this by saying: 5830%D 5831%D \starttyping 5832%D \dontleavehmode 5833%D \stoptyping 5834%D 5835%D This command is used in for instance the language module \type {lang-ini}. The 5836%D first version was: 5837%D 5838%D \starttyping 5839%D \def\dontleavehmode{\ifhmode\orelse\ifmmode\else$ $\fi} 5840%D \stoptyping 5841%D 5842%D Next, Taco came with a better alternative (using mathsurround): 5843%D 5844%D \starttyping 5845%D \def\dontleavehmode 5846%D {\ifhmode\orelse\ifmmode\else 5847%D {\mathsurround\zeropoint\everymath\emptytoks$ $}% 5848%D \fi} 5849%D \stoptyping 5850%D 5851%D And finaly we got the following alternative, one that avoids interfering grouping 5852%D at the cost of a box. 5853%D 5854%D \starttyping 5855%D \newbox\b_syst_helpers_dlh 5856%D 5857%D \protected\def\dontleavehmode 5858%D {\ifhmode\orelse\ifmmode\else 5859%D \setbox\b_syst_helpers_dlh\hbox{\mathsurround\zeropoint\everymath\emptytoks$ $}\unhbox\b_syst_helpers_dlh 5860%D \fi} 5861%D \stoptyping 5862%D 5863%D But, as we run a recent version of \TEX, we can use the new primitive: 5864 5865% \untraced\aliased\let\dontleavehmode\quitvmode % defined in syst-ini.mkxl 5866 5867%D \macros 5868%D {utfupper, utflower, uppercasestring, lowercasestring} 5869%D 5870%D The names tell what they do: 5871%D 5872%D \starttyping 5873%D \uppercasestring somestring\to\somestring 5874%D \lowercasestring somestring\to\somestring 5875%D \stoptyping 5876%D 5877%D The first argument may be a \type{\macro}. 5878%D 5879%D These macros are sort of obsolete as we never use uppercase this way. But 5880%D nevertheless we provide them: 5881 5882\permanent\def\utfupper#1{{#1}} % expandable 5883\permanent\def\utflower#1{{#1}} % expandable 5884 5885\permanent\protected\def\uppercasestring#1\to#2{\dodoglobal\edef#2{{#1}}} 5886\permanent\protected\def\lowercasestring#1\to#2{\dodoglobal\edef#2{{#1}}} 5887 5888%D \macros 5889%D {handletokens} 5890%D 5891%D With the next macro we enter a critical area of macro expansion. What we want is 5892%D a macro that looks like: 5893%D 5894%D \starttyping 5895%D \handletokens some tokens\with \somemacro 5896%D \stoptyping 5897%D 5898%D A bonus example: 5899%D 5900%D \starttyping 5901%D \hbox{\handletokens tekst en meer tekst\with\ruledhbox} 5902%D 5903%D \def\weetikveel#1{\if#1\blankspace\space\else\ruledhbox{#1}\fi} 5904%D 5905%D \hbox{\handletokens tekst en meer tekst\with\weetikveel} 5906%D \stoptyping 5907 5908%D \macros 5909%D {counttoken,counttokens} 5910%D 5911%D For the few occasions that we want to know the number of specific tokens in a 5912%D string, we can use: 5913%D 5914%D \starttyping 5915%D \counttoken token\in string\to \somecount 5916%D \counttokens string\to \somecount 5917%D \stoptyping 5918%D 5919%D This macro, that for instance is used in \type {cont-tab}, takes a real counter. 5920%D The macro can be preceded by \type {\doglobal}. 5921 5922\def\syst_helpers_count_token#1% obeys {} 5923 {\def\m_syst_string_three{#1}% 5924 \ifx\m_syst_string_two\m_syst_string_three \else 5925 \ifx\m_syst_string_one\m_syst_string_three 5926 \advanceby\privatescratchcounter\plusone 5927 \fi 5928 \expandafter\syst_helpers_count_token 5929 \fi} 5930 5931\permanent\protected\def\counttoken#1\in#2\to#3% 5932 {\privatescratchcounter\zerocount 5933 \def\m_syst_string_one{#1}% 5934 \def\m_syst_string_two{\end}% 5935 \syst_helpers_count_token#2\end 5936 \dodoglobal#3\privatescratchcounter} 5937 5938\permanent\protected\def\counttokens#1\to#2% 5939 {\privatescratchcounter\zerocount 5940 \def\syst_helpers_count_token##1{\advanceby\privatescratchcounter\plusone}% 5941 \handletokens#1\with\syst_helpers_count_token 5942 \dodoglobal#2\privatescratchcounter} 5943 5944%D \macros 5945%D {splitofftokens} 5946%D 5947%D Running this one not always gives the expected results. Consider for instance the 5948%D macro for which I originally wrote this token handler. 5949 5950\let\syst_helpers_split_off_tokens\gobbleoneargument 5951 5952\permanent\protected\def\splitofftokens#1\from#2\to#3% slow but hardly used 5953 {\ifnum#1>\zerocount 5954 \privatescratchcounter#1\relax 5955 \def\syst_helpers_split_off_tokens##1% 5956 {\ifnum\privatescratchcounter>\zerocount 5957 \advanceby\privatescratchcounter \minusone 5958 \edef#3{#3##1}% 5959 \fi}% 5960 % \lettonothing#3% #3 can be #2, so: 5961 \expandafter\let\expandafter#3\expandafter\empty 5962 \expandafter\handletokens#2\with\syst_helpers_split_off_tokens 5963 \else 5964 \edef#3{#2}% 5965 \fi} 5966 5967%D This macro can be called like: 5968%D 5969%D \startbuffer[example] 5970%D \splitofftokens10\from01234567 890123456789\to\test [\test] 5971%D \stopbuffer 5972%D 5973%D up there. The reason for this is not that logical but follows from \TEX's 5974%D However, the characters that we expect to find in \type {\test} just don't show 5975%D sometimes mysterious way of expanding. Look at this: 5976%D 5977%D \startbuffer[next] 5978%D \def\next{a} \edef\test{\next} [\test] 5979%D \let\next=b \edef\test{\test\next} [\test] 5980%D \let\next=c \edef\test{\next} [\test] 5981%D \let\next=d \edef\test{\test\next} [\test] 5982%D \let\next=e \expandafter\edef\expandafter\test\expandafter{\test\next} [\test] 5983%D \stopbuffer 5984%D 5985%D \typebuffer[next] 5986%D 5987%D Careful reading shows that inside an \type {\edef} macro's that are \type {\let} 5988%D are not expanded! 5989%D 5990%D \unprotect\getbuffer[next]\protect 5991%D 5992%D That's why we finally end up with a macro that looks ahead by using an 5993%D assignment, this time by using \type {\futurelet}, and grabbing an argument as 5994%D well. That way we can handle the sentinal, a blank space and grouped tokens. 5995 5996\mutable\lettonothing\nexthandledtoken % part of public interface 5997 5998\let\syst_helpers_handle_tokens_command\relax 5999 6000\protected\def\syst_helpers_handle_tokens 6001 {\futurelet\nexthandledtoken\syst_helpers_handle_tokens_indeed} 6002 6003\permanent\protected\def\handletokens#1\with#2% 6004 {\gdef\syst_helpers_handle_tokens_command{#2}% permits more complex #2's 6005 \syst_helpers_handle_tokens#1\end} 6006 6007\def\syst_helpers_handle_tokens_indeed 6008 {\ifx\nexthandledtoken\blankspace 6009 \expandafter\syst_helpers_handle_tokens_indeed_one 6010 \orelse\ifx\nexthandledtoken\end 6011 \expandafter\gobbletwoarguments % also gobble the \end 6012 \else 6013 \expandafter\syst_helpers_handle_tokens_indeed_two 6014 \fi *} 6015 6016\def\syst_helpers_handle_tokens_indeed_one * % 6017 {\syst_helpers_handle_tokens_command{ }\syst_helpers_handle_tokens} 6018 6019\def\syst_helpers_handle_tokens_indeed_two *#1% 6020 {\syst_helpers_handle_tokens_command{#1}\syst_helpers_handle_tokens} 6021 6022%D This macro is tested on: 6023%D 6024%D \def\xxx#1{[#1]} 6025%D 6026%D \startlines 6027%D \handletokens abc\with\xxx 6028%D \handletokens a b c\with\xxx 6029%D \handletokens a b c\with\xxx 6030%D \handletokens a{bc}d\with\xxx 6031%D \handletokens a\space bc \with\xxx 6032%D \stoplines 6033%D 6034%D And our previous example shows up as: 6035%D 6036%D \getbuffer[example] 6037 6038%D \macros 6039%D {iftrialtypesetting, ifvisible} 6040%D 6041%D The next boolean is at first sight a strange one. Sometimes one does a trial 6042%D typesetting run, for instance to determine dimensions. Some mechanisms, like 6043%D object inclusion, can fail on such trials. Temporary setting the next boolean to 6044%D true, helps a lot. The second boolena can be used to inhibit processing 6045%D completely. 6046 6047\newif\ifvisible \visibletrue 6048 6049\newtoks\everysettrialtypesetting 6050\newtoks\everyresettrialtypesetting 6051 6052\permanent\protected\def\settrialtypesetting {\expand\everysettrialtypesetting } % obeys grouping so 6053\permanent\protected\def\resettrialtypesetting{\expand\everyresettrialtypesetting} % this one is seldom needed 6054 6055\aliased\let\iftrialtypesetting\iffalse % so we have no \trialtypesettingtrue|false in mkiv ! 6056 6057\appendtoks \enforced\aliased\let\iftrialtypesetting\iftrue \to \everysettrialtypesetting 6058\appendtoks \enforced\aliased\let\iftrialtypesetting\iffalse \to \everyresettrialtypesetting 6059 6060%D \macros 6061%D {twodigitrounding} 6062%D 6063%D The next macro rounds a real number to two digits. They are probably no longer needed 6064%D but we keep them around for a while. 6065 6066\permanent\def\integerrounding #1{\zerocount{#1}} 6067\permanent\def\onedigitrounding #1{\plusone {#1}} 6068\permanent\def\twodigitrounding #1{\plustwo {#1}} 6069\permanent\def\threedigitrounding#1{\plusthree{#1}} 6070 6071%D \macros 6072%D {processcontent} 6073%D 6074%D This macro is first used in the tabulation macros. 6075%D 6076%D \starttyping 6077%D \protected\def\starthans% 6078%D {\processcontent{stophans}\test{\message{\test}\wait}} 6079%D \stoptyping 6080 6081% \starttabulate[|||] 6082% \NC \type{#} \NC # \NC \NR 6083% \stoptabulate 6084% 6085% \def\test#1% 6086% {\starttabulate[|||] 6087% \NC \type{#1} \NC #1 \NC \NR 6088% \stoptabulate} 6089% 6090% \test{!} 6091 6092%% \permanent\protected\def\processcontent#1% 6093%% {\begingroup\expandafter\syst_helpers_process_content\csname#1\endcsname} 6094 6095\protected\def\syst_helpers_process_content#1#2#3% 6096 {\protected\def\syst_helpers_process_content##1#1% 6097 {\endgroup\def#2{##1}#3}% 6098 \syst_helpers_process_content} 6099 6100\permanent\protected\def\processcontent#1% 6101 {\begingroup 6102 \catcode\hashasciicode\othercatcode 6103 \expandafter\syst_helpers_process_content\csname#1\endcsname} 6104 6105%D \macros 6106%D {dogobblesingleempty, dogobbledoubleempty} 6107%D 6108%D These two macros savely grab and dispose two arguments. We also have a few 6109%D private ones defined earlier. 6110 6111\permanent\tolerant\protected\def\dogobbledoubleempty[#S#-]#*[#S#-]{} 6112\permanent\tolerant\protected\def\dogobblesingleempty [#S#-]{} 6113 6114\aliased\let\gobblesingleempty\dogobblesingleempty % also used 6115\aliased\let\gobbledoubleempty\dogobbledoubleempty % also used 6116 6117%D \macros 6118%D {setdimensionwithunit, freezedimensionwithunit} 6119%D 6120%D The next assignments are all valid: 6121%D 6122%D \starttyping 6123%D \setdimensionwithunit\scratchdimen{10} {cm} 6124%D \setdimensionwithunit\scratchdimen{10cm}{cm} 6125%D \setdimensionwithunit\scratchdimen{10cm}{} 6126%D \freezedimensionwithunit\SomeWidth{\textwidth} 6127%D \freezedimensionwithunit\SomeDepth{\strutdp} 6128%D \stoptyping 6129%D 6130%D As an alternative for the next macro we can use a global assignment inside a box. 6131%D The \type {\empty}'s permits gobbling while preventing spurious \type {\relax}'s. 6132 6133\permanent\protected\def\setdimensionwithunit#1#2#3% number unit dimension / nice trick 6134 {\afterassignment\gobblefourarguments#1=#2#3pt\relax\empty\empty\empty\empty} 6135 6136\permanent\protected\def\freezedimensionwithunit#1#2% 6137 {\setdimensionwithunit\privatescratchdimen#1{#2}\edef#1{\the\privatescratchdimen}} 6138 6139%D \macros 6140%D {doifsometokselse, doifsometoks} 6141%D 6142%D Not that fast I guess, but here's a way to test for token registers being empty. 6143 6144\permanent\protected\def\doifelsesometoks#1% 6145 {\iftok#1\emptytoks 6146 \expandafter\secondoftwoarguments 6147 \else 6148 \expandafter\firstoftwoarguments 6149 \fi} 6150 6151\permanent\protected\def\doifsometoks#1% 6152 {\iftok#1\emptytoks 6153 \expandafter\gobbleoneargument 6154 \else 6155 \expandafter\firstofoneargument 6156 \fi} 6157 6158\permanent\protected\def\doifemptytoks#1% 6159 {\iftok#1\emptytoks 6160 \expandafter\firstofoneargument 6161 \else 6162 \expandafter\gobbleoneargument 6163 \fi} 6164 6165\aliased\let\doifsometokselse\doifelsesometoks 6166 6167%D \macros 6168%D {startstrictinspectnextcharacter} 6169%D 6170%D This one is for the bibliography module (still?): 6171 6172\let\syst_helpers_strict_inspect_next_character[ 6173 6174\def\syst_helpers_strict_inspect_next_character% no user macro ! 6175 {\ifx\nexttoken[% 6176 \expandafter\m_syst_action_yes 6177 \else 6178 \expandafter\m_syst_action_nop 6179 \fi} 6180 6181\permanent\protected\def\strictdoifelsenextoptional#1#2% 6182 {\def\m_syst_action_yes{#1}% 6183 \def\m_syst_action_nop{#2}% 6184 \futurelet\nexttoken\syst_helpers_strict_inspect_next_character} 6185 6186\aliased\let\strictdoifnextoptionalelse\strictdoifelsenextoptional 6187 6188%D \macros 6189%D {gobblespacetokens} 6190%D 6191%D This macro needs a speed-up! 6192 6193%\def\gobblespacetokens 6194% {\doifnextcharelse\empty\donothing\donothing} % no {}\do\do ! 6195 6196\permanent\def\gobblespacetokens 6197 {\afterassignment\nexttoken\let\nexttoken=} 6198 6199%D \macros 6200%D {verbatimargument} 6201%D 6202%D As the name says, this macro converts its argument to a (rather safe) string. 6203 6204\aliased\let\verbatimstring\detokenize 6205 6206%D These are needed in ordinal number conversions: 6207 6208\permanent\def\lastdigit#1% 6209 {\expandafter\thelastdigit\number#1\relax} 6210 6211\permanent\def\thelastdigit#1#2% 6212 {\ifrelax#2#1\else\expandafter\thelastdigit\expandafter#2\fi} 6213 6214\permanent\def\lasttwodigits#1% 6215 {\expandafter\thelasttwodigits\expandafter0\number#1\relax} 6216 6217\permanent\def\thelasttwodigits#1#2#3% 0 dig ... \relax 6218 {\ifrelax#3#1#2\else\expandafter\thelasttwodigits\expandafter#2\expandafter#3\fi} 6219 6220%D \macros 6221%D {serializecommalist} 6222%D 6223%D Concatenate commalists: 6224 6225\let\syst_helpers_serialize_comma_list_step\relax 6226 6227\mutable\lettonothing\serializedcommalist 6228 6229\def\syst_helpers_serialize_comma_list_step#1% 6230 {\edef\serializedcommalist{\serializedcommalist#1}} 6231 6232\permanent\protected\def\serializecommalist[#1]% 6233 {\lettonothing\serializedcommalist 6234 \processcommacommand[#1]\syst_helpers_serialize_comma_list_step} 6235 6236%D \macros 6237%D {purenumber} 6238%D 6239%D Sometimes we need control over when \TEX\ stops reading a number, especially in 6240%D full expandable macros where using \type {\relax} would lead to disasters. 6241%D 6242%D \starttyping 6243%D \ifodd\purenumber{...}\space ... \else ... \fi 6244%D \stoptyping 6245%D 6246%D Here we use a space as number delimiter in combination with a space- and 6247%D relax-less \type {\purenumber}. This macro works ok with \type {\the}, \type 6248%D {\number} as well as \ETEX's \type {\numexpr}. 6249 6250\permanent\def\purenumber#1{\expandafter\firstofoneargument\expandafter{\number#1}} 6251 6252%D \macros 6253%D {filterfromvalue, filterfromnext} 6254%D 6255%D \starttyping 6256%D \setvalue{xx}{{A}{B}{C}} 6257%D 6258%D \filterfromvalue{xx}{3}{3} 6259%D \filterfromvalue{xx}{3}{2} 6260%D \filterfromvalue{xx}{3}{1} 6261%D \stoptyping 6262 6263% can be sped up with ignored arguments 6264 6265\def\syst_filter_from_next_yes#1% 6266 {\advanceby\scratchcounterone\plusone 6267 \ifnum\scratchcounterone=\scratchcountertwo 6268 \scratchtoks{#1}% 6269 \fi 6270 \ifnum\scratchcounterone=\scratchcounterthree 6271 \expandafter\syst_filter_from_next_nop 6272 \else 6273 \expandafter\syst_filter_from_next_yes 6274 \fi} 6275 6276\def\syst_filter_from_next_nop 6277 {\expandafter\endgroup 6278 \expandafter\endlocalcontrol 6279 \the\scratchtoks} 6280 6281\permanent\def\filterfromnext#1#2% max n {..}{..}{..}{..} 6282 {\beginlocalcontrol 6283 \begingroup 6284 \scratchcounterone \zerocount 6285 \scratchcountertwo #2% 6286 \scratchcounterthree #1% 6287 \syst_filter_from_next_yes} 6288 6289\permanent\def\filterfromvalue#1#2#3% max n {..}{..}{..}{..} 6290 {\beginlocalcontrol 6291 \begingroup 6292 \scratchcounterone \zerocount 6293 \scratchcountertwo #3% 6294 \scratchcounterthree #2% 6295 \expandafter\expandafter\expandafter\syst_filter_from_next_yes\csname#1\endcsname} 6296 6297%D \macros 6298%D {definemeasure} 6299%D 6300%D \starttyping 6301%D \definemeasure[mywidth][\dimexpr(\textwidth-1cm)] 6302%D 6303%D ... \measure{mywidth} ... 6304%D \stoptyping 6305 6306\installsystemnamespace{measure} 6307 6308\permanent\tolerant\protected\def\definemeasure[#1]#*[#2]{\defcsname \??measure#1\endcsname{#2}} 6309\permanent\tolerant\protected\def\freezemeasure[#1]#*[#2]{\edefcsname\??measure#1\endcsname{\todimension{#2}}} 6310 6311\permanent\protected\def\setmeasure #1#2{\defcsname \??measure#1\endcsname{#2}} % quick way 6312\permanent\protected\def\setgmeasure#1#2{\gdefcsname\??measure#1\endcsname{#2}} % quick way 6313\permanent\protected\def\setemeasure#1#2{\edefcsname\??measure#1\endcsname{\todimension{#2}}} % quick way 6314\permanent\protected\def\setxmeasure#1#2{\xdefcsname\??measure#1\endcsname{\todimension{#2}}} % quick way 6315 6316\permanent\def\measure #1{\todimension{\ifcsname\??measure#1\endcsname\lastnamedcs\else\zeropoint\fi}} 6317\permanent\def\measured#1{\dimexpr {\ifcsname\??measure#1\endcsname\lastnamedcs\else\zeropoint\fi}} 6318 6319% #2 could be omitted, but we want to support spaces 6320% 6321% \setmeasure {x} {1cm} 6322% \setmeasure {xx} {1cm} 6323% \setmeasure {xxx}{1cm} 6324 6325%D \macros 6326%D {definequantity} 6327%D 6328%D These do the same but for numbers. 6329 6330\installsystemnamespace{quantity} 6331 6332\permanent\tolerant\protected\def\definequantity[#1]#*[#2]{\defcsname \??quantity#1\endcsname{#2}} 6333\permanent\tolerant\protected\def\freezequantity[#1]#*[#2]{\edefcsname\??quantity#1\endcsname{\tointeger{#2}}} 6334 6335\permanent\protected\def\setquantity #1#2{\defcsname \??quantity#1\endcsname{#2}} % quick way 6336\permanent\protected\def\setgquantity#1#2{\gdefcsname\??quantity#1\endcsname{#2}} % quick way 6337\permanent\protected\def\setequantity#1#2{\edefcsname\??quantity#1\endcsname{\tointeger{#2}}} % quick way 6338\permanent\protected\def\setxquantity#1#2{\xdefcsname\??quantity#1\endcsname{\tointeger{#2}}} % quick way 6339 6340\permanent\def\quantity {\the\quantitied} 6341\permanent\def\quantitied #1{\numexpr\ifcsname\??quantity#1\endcsname\lastnamedcs\else\zeropoint\fi\relax} 6342\permanent\def\directquantity#1{\tointeger{#1}} 6343 6344% let\quantified\quantitied 6345 6346%D \macros 6347%D {dividedsize} 6348%D 6349%D This one can be used inside a measure (used in m4all): 6350%D 6351%D \starttyping 6352%D \definemeasure[columnwidth][\dividebydsize\textwidth{1em}{3}] 6353%D \stoptyping 6354 6355\permanent\def\dividebydsize#1#2#3% size gap n 6356 {\dimexpr 6357 \ifnum\dimexpr#1\relax>\plusone 6358 (\dimexpr#1\relax-\numexpr#3-\plusone\relax\dimexpr#2\relax)/#3\else#1% 6359 \fi 6360 \relax} 6361 6362%D \macros 6363%D {doifdimensionelse} 6364%D 6365%D This is a dirty one: we simply append a unit and discard it when needed. We no longer 6366%D use this in core code because we just use th econditional. 6367 6368\permanent\def\doifelsedimension#1% 6369 {\ifchkdim#1\or % or \ifchkdimension 6370 \expandafter\firstoftwoarguments 6371 \else 6372 \expandafter\secondoftwoarguments 6373 \fi} 6374 6375\aliased\let\doifdimensionelse\doifelsedimension 6376 6377%D Ok, here's another one, slower but seldom used. This one scans the text. 6378%D 6379%D \starttabulate[|Tc|Tc|] 6380%D \NC pt \NC \doifdimenstringelse {pt}{yes}{no} \NC \NR 6381%D \NC 12pt \NC \doifdimenstringelse {-12pt}{yes}{no} \NC \NR 6382%D \NC 1pt \NC \doifdimenstringelse {1pt}{yes}{no} \NC \NR 6383%D \NC 12pt \NC \doifdimenstringelse {12pt}{yes}{no} \NC \NR 6384%D \NC 12.0pt \NC \doifdimenstringelse {12.0pt}{yes}{no} \NC \NR 6385%D \NC -.12pt \NC \doifdimenstringelse {-.12pt}{yes}{no} \NC \NR 6386%D \NC .12pt \NC \doifdimenstringelse {.12pt}{yes}{no} \NC \NR 6387%D \NC -12pt \NC \doifdimenstringelse {-12pt}{yes}{no} \NC \NR 6388%D \NC -12.0pt \NC \doifdimenstringelse{-12.0pt}{yes}{no} \NC \NR 6389%D \NC big \NC \doifdimenstringelse {big}{yes}{no} \NC \NR 6390%D \NC 10 \NC \doifdimenstringelse {10}{yes}{no} \NC \NR 6391%D \NC 1 \NC \doifdimenstringelse {1}{yes}{no} \NC \NR 6392%D \stoptabulate 6393 6394\aliased\let\doifelsedimenstring\doifelsedimension 6395\aliased\let\doifdimenstringelse\doifelsedimenstring 6396 6397%D \macros 6398%D {comparedimension,comparedimensioneps} 6399%D 6400%D We no longer use the \MKIV\ dirty trick. These are obsolete anyway. 6401 6402\newdimension\roundingeps \roundingeps=10sp 6403\newconstant \compresult 6404 6405\permanent\protected\def\comparedimension#1#2% 6406 {\compresult 6407 \ifcmpdim#1#2 % space wil be skipped 6408 \zerocount 6409 \or 6410 \plusone 6411 \else 6412 \plustwo 6413 \fi} 6414 6415\permanent\protected\def\comparedimensioneps#1#2% todo: use eps feature 6416 {\compresult 6417 \ifabsdim{(#1)-(#2)}<\roundingeps 6418 \zerocount 6419 \orelse\ifdim{#1}<{#2}% 6420 \plusone 6421 \else 6422 \plustwo 6423 \fi} 6424 6425% pretty ugly but fast 6426 6427% \copycsname xxx\endcsname\csname ..\endcsname 6428 6429\permanent\protected\def\copycsname{\expandafter\expandafter\expandafter\let\expandafter\expandafter\csname} 6430 6431% \letcscsname \crap \csname ..\endcsname 6432% \letcsnamecs \csname ..\endcsname\crap 6433% \letcsnamecsname\csname ..\endcsname\csname ..\endcsname 6434 6435\permanent\protected\def\letcscsname {\expandafter\let\expandafter} 6436\permanent\protected\def\letcsnamecs {\expandafter\let} 6437\permanent\protected\def\letcsnamecsname{\expandafter\expandafter\expandafter\let\expandafter\expandafter} 6438 6439% Another one, add an item to a commalist, was only used in definesymbol 6440 6441\permanent\protected\def\addvalue#1#2% cs item 6442 {\ifcsname#1\endcsname\else\letcsname#1\endcsname\empty\fi 6443 \normalexpanded{\addtocommalist{#2}\expandafter\noexpand\csname#1\endcsname}} 6444 6445%D Are these ever used? Anyway, these variants are somewhat more efficient than 6446%D the \MKIV variants for larger strings. 6447 6448% A variant: 6449% 6450% \permanent\def\unspaced#1% 6451% {\localcontrolled{\begingroup\catcode\spaceasciicode\ignorecatcode}% 6452% \tokenized{#1}% 6453% \localcontrolled{\endgroup}} 6454% 6455% but we can also do this: 6456 6457\permanent\def\unspaced#1% 6458 {\tokenized \s!catcodetable \ctdcatcodes {#1}} 6459 6460\permanent\protected\def\unspaceargument#1\to#2% 6461 {\edef#2{\tokenized \s!catcodetable \ctdcatcodes {#1}}} 6462 6463\permanent\protected\def\unspaceafter#1#2% 6464 {\expandafter#1\expandafter{\tokenized \s!catcodetable \ctdcatcodes {#2}}} 6465 6466\permanent\protected\def\doifelsehasspace#1% 6467 {\expandafter\ifhastok\space{#1}% 6468 \expandafter\firstoftwoarguments 6469 \else 6470 \expandafter\secondoftwoarguments 6471 \fi} 6472 6473\aliased\let\doifhasspaceelse\doifelsehasspace 6474 6475% this will replace loadfile once and alike !!! todo 6476 6477\installsystemnamespace{flag} 6478 6479\permanent\protected\def\setflag #1{\dodoglobal\letcsname\??flag#1\endcsname\zerocount} 6480\permanent\protected\def\resetflag#1{\dodoglobal\letcsname\??flag#1\endcsname\plusone} 6481 6482\permanent\def\flag#1{\csname\??flag#1\endcsname} 6483 6484\permanent\def\doifelseflagged#1% 6485 {\expandafter\ifrelax\csname\??flag#1\endcsname 6486 \expandafter\secondoftwoarguments 6487 \orelse\ifcase\csname\??flag#1\endcsname 6488 \expandafter\firstoftwoarguments 6489 \else 6490 \expandafter\secondoftwoarguments 6491 \fi} 6492 6493\aliased\let\doifflaggedelse\doifelseflagged 6494 6495\permanent\def\doifnotflagged#1% 6496 {\expandafter\ifrelax\csname\??flag#1\endcsname 6497 \expandafter\firstofoneargument 6498 \orelse\ifcase\csname\??flag#1\endcsname 6499 \expandafter\gobbleoneargument 6500 \else 6501 \expandafter\firstofoneargument 6502 \fi} 6503 6504\permanent\protected\def\inheritparameter[#1]#2[#3]#4[#5]% tag tokey fromkey 6505 {\defcsname#1#3\expandafter\endcsname\expandafter{\csname#1#5\endcsname}} 6506 6507\def\syst_helpers_if_non_zero_positive_else#1#2\end % #3#4% 6508 {\ifrelax#1% 6509 \ifcase\privatescratchcounter 6510 \endgroup 6511 \doubleexpandafter\secondoftwoarguments 6512 \else 6513 \endgroup 6514 \doubleexpandafter\firstoftwoarguments 6515 \fi 6516 \else 6517 \endgroup 6518 \expandafter\secondoftwoarguments 6519 \fi} 6520 6521% used ? 6522 6523\permanent\def\doifelsenonzeropositive#1% 6524 {\begingroup\afterassignment\syst_helpers_if_non_zero_positive_else\privatescratchcounter=0#1\relax\empty\end} 6525 6526\aliased\let\doifnonzeropositiveelse\doifelsenonzeropositive 6527 6528%D Some old|-|time favourites: 6529 6530% \permanent\protected\def\dosetrawvalue #1#2#3{\defcsname #1#2\endcsname{#3}} 6531% \permanent\protected\def\dosetrawevalue#1#2#3{\edefcsname#1#2\endcsname{#3}} 6532% \permanent\protected\def\dosetrawgvalue#1#2#3{\gdefcsname#1#2\endcsname{#3}} 6533% \permanent\protected\def\dosetrawxvalue#1#2#3{\xdefcsname#1#2\endcsname{#3}} 6534% 6535% \permanent\protected\def\getrawparameters {\dogetparameters\dosetrawvalue } 6536% \permanent\protected\def\getraweparameters {\dogetparameters\dosetrawevalue} 6537% \permanent\protected\def\getrawgparameters {\dogetparameters\dosetrawgvalue} 6538% \permanent\protected\def\getrawxparameters {\dogetparameters\dosetrawxvalue} 6539% 6540% \permanent\protected\def\globalgetrawparameters{\dogetparameters\dosetrawgvalue} % obsolete 6541 6542\aliased\let\getrawparameters \getparameters % obsolete 6543\aliased\let\getraweparameters\geteparameters % obsolete 6544\aliased\let\getrawgparameters\getgparameters % obsolete 6545\aliased\let\getrawxparameters\getxparameters % obsolete 6546 6547%D Sort of obsolete: 6548 6549\newinteger\c_syst_helpers_mod 6550 6551\permanent\protected\def\dosetmodulo#1#2#3% 6552 {\c_syst_helpers_mod#1\divideby\c_syst_helpers_mod#2\multiplyby\c_syst_helpers_mod#2% 6553 #3#1\advanceby#3-\c_syst_helpers_mod} 6554 6555\permanent\protected\def\dosetdivision#1#2#3% 6556 {#3#1\divideby#3 #2\relax} 6557 6558\permanent\protected\def\DoMod#1by#2to#3{\dosetmodulo {#1}{#2}{#3}} 6559\permanent\protected\def\DoDiv#1by#2to#3{\dosetdivision{#1}{#2}{#3}} 6560 6561%D This is obsolete, just use \type {\unprotect} and \type {\protect} instead. 6562 6563% \def\syst_helpers_unprotected#1\par 6564% {#1\protect} 6565% 6566% \permanent\protected\def\unprotected 6567% {\unprotect 6568% \syst_helpers_unprotected} 6569 6570\aliased\let\resettimer % todo: at lua end 6571\aliased\let\elapsedtime % todo: at lua end 6572\aliased\let\elapsedseconds\elapsedtime 6573 6574\aliased\let\elapsedsteps\!!zerocount 6575 6576\permanent\protected\def\elapsedsteptime % unexpanded ! a bit useless but who knows ... 6577 {\elapsedsteps\relax} 6578 6579\newinteger\c_syst_helpers_test_feature_n 6580\newinteger\c_syst_helpers_test_feature_m 6581 6582\let\syst_helpers_test_feature_yes\relax 6583\let\syst_helpers_test_feature_nop\relax 6584 6585\permanent\def\currentfeaturetest{\the\c_syst_helpers_test_feature_n} 6586 6587\permanent\protected\def\testfeature#1#2% 6588 {\c_syst_helpers_test_feature_m\ifparameter#1\or#1\else\maxiterator\fi\relax 6589 \enforced\xdef\elapsedsteps{\the\c_syst_helpers_test_feature_m}% 6590 \def\syst_helpers_test_feature_yes 6591 {\advanceby\c_syst_helpers_test_feature_n\plusone 6592 \ifnum\c_syst_helpers_test_feature_n>\c_syst_helpers_test_feature_m\else 6593 #2\expandafter\syst_helpers_test_feature_yes 6594 \fi}% 6595 \def\syst_helpers_test_feature_nop 6596 {\advanceby\c_syst_helpers_test_feature_n\plusone 6597 \ifnum\c_syst_helpers_test_feature_n>\c_syst_helpers_test_feature_m\else 6598 \expandafter\syst_helpers_test_feature_nop 6599 \fi}% 6600 \retestfeature} 6601 6602\permanent\protected\def\retestfeature % timer support is new per 10/5/2005 6603 {\bgroup 6604 \ifcase\interactionmode\enforced\let\wait\relax\fi 6605 6606 \c_syst_helpers_test_feature_n\zerocount 6607 \syst_helpers_test_feature_nop 6608 6609 \writestatus\m!system 6610 {starting feature test: % 6611 \the\c_syst_helpers_test_feature_m\space steps}% 6612 \c_syst_helpers_test_feature_n\zerocount 6613 \syst_helpers_test_feature_yes 6614 \writestatus\m!system 6615 {feature test done: % 6616 \the\c_syst_helpers_test_feature_m\space steps, % 6617 \space seconds, % 6618 \elapsedsteps\space\space per step}% 6619 \egroup} 6620 6621\permanent\protected\def\showtimer#1% 6622 {\writestatus{runtime}{\elapsedseconds\space s / #1}} 6623 6624\permanent\protected\def\testfeatureonce#1#2% 6625 {\begingroup 6626 \enforced\let\wait\relax 6627 \testfeature{#1}{#2}% 6628 \endgroup} 6629 6630%D \macros 6631%D {freezedimenmacro} 6632%D 6633%D This macro is use as: 6634%D 6635%D \starttyping 6636%D \freezedimenmacro\leftmargindistance 6637%D \stoptyping 6638 6639\permanent\protected\def\freezedimenmacro#1% 6640 {\edef#1{\todimension{#1}}} 6641 6642%D The next macro negates a macro (dimension or number, or actually, whatever. It's 6643%D a typical example of \type {\if} usage: 6644%D 6645%D \starttyping 6646%D \if-\whatever \else-\whatever\fi => else => -whatever 6647%D \if--\whatever\else-\whatever\fi => then => whatever 6648%D \stoptyping 6649%D 6650%D Do we still need this? If not it will go away: 6651 6652\permanent\def\negated#1{\if-#1\else-#1\fi} % does only work in macros or text 6653 6654% \def\gobbleassigndimen#1\\{} 6655% 6656% \permanent\def\assigndimen#1#2% 6657% {\afterassignment\gobbleassigndimen#1=#2\zeropoint\\} 6658 6659%D Maybe ... toksapp should deal with this ... 6660 6661\permanent\protected\def\appendvalue#1#2% 6662 {\edefcsname#1\endcsname{% 6663 \normalunexpanded\expandafter\expandafter\expandafter{\begincsname#1\endcsname}% 6664 \normalunexpanded{#2}}} 6665 6666\permanent\protected\def\prependvalue#1#2% 6667 {\edefcsname#1\endcsname{% 6668 \normalunexpanded{#2}% 6669 \normalunexpanded\expandafter\expandafter\expandafter{\begincsname#1\endcsname}}} 6670 6671\permanent\protected\def\appendgvalue #1#2{\global\appendvalue} 6672\permanent\protected\def\prependgvalue#1#2{\global\prependvalue} 6673 6674%D \macros 6675%D {dowithrange} 6676%D 6677%D This one is for Mojca Miklavec, who made me aware of the fact that \type 6678%D {page-imp.tex} was not the best place to hide it. 6679%D 6680%D \startbuffer 6681%D \def\DoSomething#1{ [item #1] } 6682%D 6683%D \processranges[1,4:5]\DoSomething \par 6684%D \dowithrange {1,4:5}\DoSomething \par 6685%D \stopbuffer 6686%D 6687%D \typebuffer \blank \getbuffer \blank 6688 6689%D We have a better splitter at the \LUA\ end. 6690 6691\integerdef\c_helpers_range_min \zerocount 6692\integerdef\c_helpers_range_max \zerocount 6693\integerdef\c_helpers_range_abs_min\zerocount 6694\integerdef\c_helpers_range_abs_max\zerocount 6695 6696\lettonothing\m_helpers_range_action 6697 6698\def\syst_helpers_with_range#1% 6699 {\splitstring#1\at:\to\m_syst_helpers_range_from\and\m_syst_helpers_range_to 6700 \ifx\m_syst_helpers_range_from\wildcardsymbol 6701 \edef\m_syst_helpers_range_from{\number\c_helpers_range_min}% 6702 \fi 6703 \ifx\m_syst_helpers_range_to\wildcardsymbol 6704 \edef\m_syst_helpers_range_to{\number\c_helpers_range_max}% 6705 \fi 6706 \ifempty\m_syst_helpers_range_to 6707 \let\m_syst_helpers_range_to\m_syst_helpers_range_from 6708 \fi 6709 \dostepwiserecurse\m_syst_helpers_range_from\m_syst_helpers_range_to\plusone{\m_helpers_range_action{##1}}}% 6710 6711\permanent\protected\def\processranges[#1]#2% #1= n:m,p,q:r 6712 {\def\m_helpers_range_action{#2}% 6713 \integerdef\c_helpers_range_min\zerocount 6714 \integerdef\c_helpers_range_max\zerocount 6715 \processcommacommand[#1]\syst_helpers_with_range} 6716 6717\permanent\protected\def\dowithrange#1#2% set action 6718 {\def\m_helpers_range_action{#2}% 6719 \integerdef\c_helpers_range_min\zerocount 6720 \integerdef\c_helpers_range_max\zerocount 6721 \processcommacommand[#1]\syst_helpers_with_range} 6722 6723\permanent\protected\def\dowithminmaxrange#1#2#3#4% min max set action 6724 {\def\m_helpers_range_action{#4}% 6725 \integerdef\c_helpers_range_min#1\relax 6726 \integerdef\c_helpers_range_max#2\relax 6727 \processcommacommand[#3]\syst_helpers_with_range} 6728 6729% todo: use the helper that we made in lua (sets) 6730 6731\def\syst_helpers_with_range_slice#1% 6732 {\splitstring#1\at:\to\m_syst_helpers_range_from\and\m_syst_helpers_range_to 6733 \ifx\m_syst_helpers_range_from\wildcardsymbol 6734 \edef\m_syst_helpers_range_from{\number\c_helpers_range_min}% 6735 \fi 6736 \ifx\m_syst_helpers_range_to\wildcardsymbol 6737 \edef\m_syst_helpers_range_to{\number\c_helpers_range_max}% 6738 \fi 6739 \ifempty\m_syst_helpers_range_to 6740 \let\m_syst_helpers_range_to\m_syst_helpers_range_from 6741 \fi 6742 \ifnum\m_syst_helpers_range_from<\zerocount 6743 \dostepwiserecurse 6744 \m_syst_helpers_range_from 6745 \m_syst_helpers_range_to 6746 {\ifnum\m_syst_helpers_range_from>\m_syst_helpers_range_to\minusone\else\plusone\fi}% 6747 {%(\the\numexpr\c_helpers_range_abs_max+##1+\plusone\relax) 6748 \m_helpers_range_action{\tointeger{\c_helpers_range_abs_max+##1+\plusone}}}% 6749 \else 6750 \dostepwiserecurse 6751 \m_syst_helpers_range_from 6752 \m_syst_helpers_range_to 6753 {\ifnum\m_syst_helpers_range_from>\m_syst_helpers_range_to\minusone\else\plusone\fi} 6754 {%(\the\numexpr\c_helpers_range_abs_min+##1-\plusone\relax) 6755 \m_helpers_range_action{\tointeger{\c_helpers_range_abs_min+##1-\plusone}}}% 6756 \fi} 6757 6758\permanent\protected\def\dowithminmaxrangeslice#1#2#3#4#5#6% absmin absmax min max set action 6759 {\def\m_helpers_range_action{#6}% 6760 \integerdef\c_helpers_range_abs_min#1\relax 6761 \integerdef\c_helpers_range_abs_max#2\relax 6762 \integerdef\c_helpers_range_min #3\relax 6763 \integerdef\c_helpers_range_max #4\relax 6764 \processcommacommand[#5]\syst_helpers_with_range_slice} 6765 6766% \def\DoSomething#1{ [item #1] } 6767% \dowithrange[1,4:5]\DoSomething 6768 6769%D \macros 6770%D {ignoreimplicitspaces} 6771%D 6772%D \startbuffer 6773%D \def\whatever[#1]{\expanded{\definedfont[#1 at 12pt]}\ignorespaces} 6774%D {a\whatever[Serif]b a\whatever[Serif] b a\whatever[Serif]\space b} 6775%D \def\whatever[#1]{\expanded{\definedfont[#1 at 12pt]}\ignoreimplicitspaces} 6776%D {a\whatever[Serif]b a\whatever[Serif] b a\whatever[Serif]\space b} 6777%D \stopbuffer 6778%D 6779%D \typebuffer \getbuffer 6780 6781\permanent\protected\def\ignoreimplicitspaces 6782 {\doifelsenextchar\relax\relax\relax} 6783 6784% %D \macros 6785% %D {processwords} 6786% %D 6787% %D Not that sophisticated but sometimes users (like in metafun). 6788% 6789% %D This will be overloaded. 6790% 6791% \def\syst_helpers_process_word#1 #2\s!e_o_t_token 6792% {\doifsomething{#1}{\processword{#1} \syst_helpers_process_word#2 \s!e_o_t_token}} 6793% 6794% \def\processwords#1% 6795% {\syst_helpers_process_word#1 \s!e_o_t_token}% no \unskip 6796% 6797% \let\processword\relax 6798 6799%D \macros 6800%D {startnointerference} 6801%D 6802%D \starttyping 6803%D \startnointerference 6804%D all kind of code 6805%D \stopnointerference 6806%D \stoptyping 6807 6808\newbox\b_syst_helpers_no_interference 6809 6810\permanent\protected\def\startnointerference % not even grouped ! 6811 {\setbox\b_syst_helpers_no_interference\vbox 6812 \bgroup} 6813 6814\permanent\protected\def\stopnointerference 6815 {\egroup 6816 \setbox\b_syst_helpers_no_interference\emptybox} 6817 6818%D A variant for \type {\executeifdefined}: 6819 6820\permanent\def\expandcheckedcsname#1#2% #2 is often a \xxxparameter so let's expand it once 6821 {\normalexpanded{\noexpand\syst_helpers_expand_checked_csname{#1}{#2}}} 6822 6823\def\syst_helpers_expand_checked_csname#1#2#3% 6824 {\csname#1\ifcsname#1#2\endcsname#2\else#3\fi\endcsname} 6825 6826%D Signal. Some fonts have a char0 rendering so we need to make sure that it is not 6827%D set in the font! (This will be overloaded) 6828 6829\protected\def\signalcharacter{\char\zerocount} % \zwj 6830 6831% %D A few secial variants of commands defined here. Some more will be moved here 6832% %D e.g.from the table modules. This code is no longer needed. 6833% 6834% \permanent\def\dodirectdoubleempty#1#2% used in math (lookahead issues) 6835% {\ifx#2[% 6836% \expandafter\syst_helpers_direct_double_empty_one_yes 6837% \else 6838% \expandafter\syst_helpers_direct_double_empty_one_nop 6839% \fi#1#2} 6840% 6841% \def\syst_helpers_direct_double_empty_one_yes#1[#2]#3% 6842% {\ifx#3[\else\expandafter\syst_helpers_direct_double_empty_two_nop\fi#1[#2]#3} 6843% 6844% \def\syst_helpers_direct_double_empty_one_nop#1{#1[][]} 6845% \def\syst_helpers_direct_double_empty_two_nop#1[#2]{#1[#2][]} 6846 6847% %D Used in math definitions (in an \type {\edef}). This will be replaced. 6848% %D 6849% %D \startbuffer 6850% %D [\docheckedpair{}] 6851% %D [\docheckedpair{a}] 6852% %D [\docheckedpair{a,b}] 6853% %D [\docheckedpair{a,b,c}] 6854% %D \stopbuffer 6855% %D 6856% %D \typebuffer \startlines \getbuffer \stoplines 6857% 6858% \permanent\def\docheckedpair#1% 6859% {\syst_helpers_checked_pair#1,,\s!e_o_t_token} 6860% 6861% \def\syst_helpers_checked_pair#1,#2,#-\s!e_o_t_token 6862% {#1,#2} 6863 6864%D Here are some nasty helpers. They can be used to fill often expanded token 6865%D lists efficiently (see tabulate for an example). 6866 6867\permanent\def\constantnumber#1% 6868 {\ifcase#1\zerocount 6869 \or \plusone 6870 \or \plustwo 6871 \or \plusthree 6872 \or \plusfour 6873 \or \plusfive 6874 \or \plussix 6875 \or \plusseven 6876 \or \pluseight 6877 \or \plusnine 6878 \or \plusten 6879 \else \number#1\relax\fi} 6880 6881\permanent\def\constantnumberargument#1% 6882 {\ifcase#1\zerocount 6883 \or \plusone 6884 \or \plustwo 6885 \or \plusthree 6886 \or \plusfour 6887 \or \plusfive 6888 \or \plussix 6889 \or \plusseven 6890 \or \pluseight 6891 \or \plusnine 6892 \or \plusten 6893 \else {\number#1}\fi} 6894 6895% \permanent\def\constantdimen#1% takes register 6896% {\ifdim#1=\zeropoint 6897% \zeropoint 6898% \else 6899% \the#1\relax 6900% \fi} 6901 6902\permanent\def\constantdimen#1% takes register 6903 {\ifzeropt#1\norelax 6904 \zeropoint 6905 \else 6906 \todimension#1\norelax 6907 \fi} 6908 6909% \permanent\def\constantdimenargument#1% takes register 6910% {\ifdim#1=\zeropoint 6911% \zeropoint 6912% \else 6913% {\the#1}% 6914% \fi} 6915 6916\permanent\def\constantdimenargument#1% 6917 {\ifzeropt#1%\norelax 6918 \zeropoint 6919 \else 6920 {\todimension#1}% 6921 \fi} 6922 6923\permanent\def\constantemptyargument#1% 6924 {\ifempty#1% 6925 \noexpand\empty 6926 \else 6927 {#1}% 6928 \fi} 6929 6930%D \macros 6931%D {getsubstring} 6932%D \startbuffer 6933%D 6934%D \getsubstring{4}{}{Who Wants This} 6935%D \getsubstring{4}{9}{Who Wants This} 6936%D \getsubstring{9}{-2}{Who Wants This} 6937%D \getsubstring{1}{5}{Who Wants This} 6938%D \stopbuffer 6939%D 6940%D \typebuffer 6941%D 6942%D \startlines 6943%D \getbuffer 6944%D \stoplines 6945 6946% expandable: 6947 6948\permanent\def\getsubstring#1#2#3{{#3}{#1}{#2}} 6949 6950%D Other dimensions than pt (used in mb-mp) 6951 6952\permanent\def\converteddimen#1#2{{#1}{#2}} 6953 6954%D Maybe (looks ugly): 6955%D 6956%D \starttyping 6957%D \doifcase {foo} 6958%D {bar} {BAR} 6959%D {foo} {FOO} 6960%D {default} {DEFAULT} 6961%D 6962%D \doifcase {foo} 6963%D {bar} {BAR} 6964%D {foo} {\doifcase {bar} 6965%D {bar} {BAR} 6966%D {foo} {FOO} 6967%D {default} {DEFAULT} 6968%D } 6969%D {default} {DEFAULT} 6970%D \stoptyping 6971 6972% \doifcase {\btxfoundname{author}} 6973% {author} {\btxflush{author}} 6974% {editor} {\texdefinition{btx:apa:editor-or-editors}} 6975% {title} {\texdefinition{btx:apa:title-subtitle-type}} 6976% {default} {\btxflush{author}} 6977 6978% \protected\def\doifcase#1% 6979% {\edef\m_case_asked{#1}% 6980% \syst_aux_case} 6981% 6982% \def\syst_aux_case#1% 6983% {\edef\m_case_temp{#1}% 6984% \ifx\m_case_temp\m_case_asked 6985% \expandafter\syst_aux_case_yes 6986% \orelse\ifx\m_case_temp\s!default 6987% \expandafter\firstofoneargument 6988% \else 6989% \expandafter\syst_aux_case_nop 6990% \fi} 6991% 6992% \def\syst_aux_skip#1#2% 6993% {\edef\m_case_temp{#1}% 6994% \ifx\m_case_temp\s!default 6995% \expandafter\syst_aux_done 6996% \else 6997% \expandafter\syst_aux_skip 6998% \fi} 6999% 7000% \def\syst_aux_case_yes#1% 7001% {\def\syst_aux_done{#1}% 7002% \syst_aux_skip} 7003% 7004% \def\syst_aux_case_nop#1% 7005% {\syst_aux_case} 7006 7007%D \macros 7008%D {ntimes} 7009%D 7010%D some repetition: 7011%D 7012%D \startbuffer 7013%D \ntimes{*}{20} 7014%D \stopbuffer 7015%D 7016%D \typebuffer \blank gives: \getbuffer \blank 7017%D 7018%D This is not real fast but quite okay: 7019 7020%def\ntimes#1#2{\ifnum#2>\zerocount#1\ntimes{#1}{\numexpr#2-\plusone\relax}\fi} % 1.72 7021\permanent\def\ntimes#1#2{{#1}{#2}} % 0.33 7022 7023%D Experiment (sometimes looks nicer in code): 7024 7025\permanent\protected\def\sameargumentscondition#1#2% 7026 {\edef\m_syst_string_one{#1}% 7027 \edef\m_syst_string_two{#2}% 7028 \ifx\m_syst_string_one\m_syst_string_two} 7029 7030\permanent\protected\def\emptyargumentcondition#1% 7031 {\edef\m_syst_string_one{#1}% 7032 \ifempty\m_syst_string_one} 7033 7034%D New (also serves as an example): 7035%D 7036%D \starttyping 7037%D \commandflags\defineframed 7038%D \stoptyping 7039 7040\permanent\def\commandflags#1% 7041 {\beginlocalcontrol\begingroup\scratchtoks\emptytoks\donefalse 7042 \ifflags#1= \frozenflagcode\etoksapp\scratchtoks{\ifdone \space\fi frozen}\donetrue\fi 7043 \ifflags#1=\permanentflagcode\etoksapp\scratchtoks{\ifdone \space\fi permanent}\donetrue\fi 7044 \ifflags#1=\immutableflagcode\etoksapp\scratchtoks{\ifdone \space\fi immutable}\donetrue\fi 7045 \ifflags#1=\primitiveflagcode\etoksapp\scratchtoks{\ifdone \space\fi primitive}\donetrue\fi 7046 \ifflags#1= \mutableflagcode\etoksapp\scratchtoks{\ifdone \space\fi mutable}\donetrue\fi 7047 \ifflags#1=\noalignedflagcode\etoksapp\scratchtoks{\ifdone \space\fi noaligned}\donetrue\fi 7048 \ifflags#1= \instanceflagcode\etoksapp\scratchtoks{\ifdone \space\fi instance}\donetrue\fi 7049 %\ifflags#1= \reservedflagcode\etoksapp\scratchtoks{\ifdone \space\fi reserved}\donetrue\fi 7050 \ifflags#1= \tolerantflagcode\etoksapp\scratchtoks{\ifdone \space\fi tolerant}\donetrue\fi 7051 \ifflags#1=\protectedflagcode\etoksapp\scratchtoks{\ifdone \space\fi protected}\donetrue\fi 7052 \expandafter\endgroup\expandafter\endlocalcontrol\the\scratchtoks} 7053 7054%D \macros 7055%D {resetmacros} 7056%D 7057%D The next macro can be used to reset a macro: 7058%D 7059%D \starttyping 7060%D \resetmacros[startfoo,\stopfoo] 7061%D \stoptyping 7062 7063\permanent\protected\def\syst_reset_macro#1% 7064 {\overloaded\letcsname\csstring#1\endcsname\undefined} % so only frozen (instances( 7065 7066\permanent\protected\def\resetmacros[#1]% 7067 {\processcommalist[#1]\syst_reset_macro} 7068 7069%D These demos are for hvdm who needs ways to manipulate arguments but 7070%D in a fully expandable way (some explanation is given in the low level 7071%D expansion manual). 7072%D 7073%D \startbuffer 7074%D \edef\xxx{[\wipetokens {123}{abc123abc123abc123abc123abc}]}1 : \meaningless\xxx\par 7075%D \edef\xxx{[\wipetokens {123}{abc}]} 2 : \meaningless\xxx\par 7076%D \edef\xxx{[\wipetokens {123}{123}]} 3 : \meaningless\xxx\par 7077%D \edef\xxx{[\wipetokens {123}{123123}]} 4 : \meaningless\xxx\par 7078%D \edef\xxx{[\wipetokens {123}{}]} 5 : \meaningless\xxx\par 7079%D \edef\xxx{[\wipetokens {}{123}]} 6 : \meaningless\xxx\par 7080%D \edef\xxx{[\wipetokens {\relax}{1\relax2\relax3}]} 7 : \meaningless\xxx\par 7081%D \edef\xxx{[\wipetokens {1}{1{2}3}]} 8 : \meaningless\xxx\par 7082%D \edef\xxx{[\wipedtokens{{1}}{{1}23}]} 9 : \meaningless\xxx\par 7083%D \edef\xxx{[\wipedtokens{{1}}{123}]} 0 : \meaningless\xxx\par 7084%D \stopbuffer 7085%D 7086%D \typebuffer \startpacked \getbuffer \stoppacked 7087 7088\lettonothing\syst_helpers_wipe_tokens_yes 7089 7090\def\syst_helpers_wipe_tokens_nop#-^^04{}% 7091 7092\permanent\def\wipetokens#1#2% 7093 {\beginlocalcontrol 7094 \tolerant\def\syst_helpers_wipe_tokens_yes##1#1##2^^04% 7095 {##1% 7096 \ifparameter##2\or 7097 \expandafter\syst_helpers_wipe_tokens_yes 7098 \else 7099 \expandafter\syst_helpers_wipe_tokens_nop 7100 \fi 7101 ##2^^04}% 7102 \endlocalcontrol 7103 \syst_helpers_wipe_tokens_yes#2#1^^04} 7104 7105\permanent\def\wipedtokens#1#2% 7106 {\tokenized{\normalexpanded{\noexpand\wipetokens{\detokenize{#1}}{\detokenize{#2}}}}} 7107 7108%D \startbuffer 7109%D \def\abc{abc} 7110%D \semiprotected \def\xyz{xyz} 7111%D \edef\pqr{\expandtoken\notcatcodes`p% 7112%D \expandtoken\notcatcodes`q% 7113%D \expandtoken\notcatcodes`r} 7114%D 7115%D 1: \ifcondition\similartokens{abc} {def}YES\else NOP\fi (NOP) \quad 7116%D 2: \ifcondition\similartokens{abc}{\abc}YES\else NOP\fi (YES) 7117%D 7118%D 3: \ifcondition\similartokens{xyz} {pqr}YES\else NOP\fi (NOP) \quad 7119%D 4: \ifcondition\similartokens{xyz}{\xyz}YES\else NOP\fi (YES) 7120%D 7121%D 5: \ifcondition\similartokens{pqr} {pqr}YES\else NOP\fi (YES) \quad 7122%D 6: \ifcondition\similartokens{pqr}{\pqr}YES\else NOP\fi (YES) 7123%D \stopbuffer 7124%D 7125%D \typebuffer \startpacked \getbuffer \stoppacked 7126 7127\permanent\protected\def\similartokens#1#2% 7128 %{\normalexpanded{\noexpand\iftok{\noexpand\detokenize{#1}}{\noexpand\detokenize{#2}}}} 7129 {\semiexpanded{\noexpand\iftok{\noexpand\detokenize{#1}}{\noexpand\detokenize{#2}}}} 7130 7131\permanent\protected\def\doifelsesimilartokens#1#2% 7132 {\ifcondition\similartokens{#1}{#2}% 7133 \expandafter\firstoftwoarguments 7134 \else 7135 \expandafter\secondoftwoarguments 7136 \fi} 7137 7138%D This one is used in math-frc so don't change it 7139 7140\untraced\permanent\protected\def\glyphscaled#1% name will change 7141 {\dimexpr#1*\plusthousand/\glyphscale\relax} 7142 7143%D \macros {nologbuffering, linebuffering} 7144%D 7145%D This saves one sec on luametatex manual in console. And in the case of an error 7146%D no buffering will make sure what is cached gets flushed too. We define these two 7147%D at the LUA end: \typ {\nologbuffering} and \typ {\linebuffering}. 7148 7149\protect \endinput 7150 |