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