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