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