summaryrefslogtreecommitdiff
path: root/macros/generic/olsak-misc/seplist.tex
blob: 0eac4d541a43c999baa7fb6b582cc09a3aa85f82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
% seplist.tex
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Petr Olsak 2014

% This macro enables to declare more than one separator for parameter
% scanning of your macro. Usage:

% \long\def\yourmacro#1{usage of #1parameter}
% \seplist{list of separators}\yourmacro parameter-text separator
% example:
% \seplist{{sepA}{SepB}{SEPC}}\yourmacro text separated by sepA
% \seplist{{sepA}{SepB}{SEPC}}\yourmacro text separated by SepB
% \seplist{{sepA}{SepB}{SEPC}}\yourmacro text separated by SEPC
% All lines of this example are expanded to "usage of text separated by parameter".

% The actually used separator is stored globally to the \sepused macro.
% The macro programmer can use this.

% The input stream is read to the first instance of any of the listed
% separator, no more. The separator list includes separators in braces. 
% If there are only one-token separators, braces can be omited. Example

% \seplist{0123456789}\mymacro text to the first decimal digit 7

% The parameter have to be balanced. Thus, the separator hidden in braces
% is ignored. This behavior is similar like in normal separated parameters.

% The \par or spaces or arbitrary control sequences can be a part of
% separators. The # (of catcode 6) cannot be a part of the separator but
% it can be included in the parameter text.

% See implementation details at the end of this file

\long\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\newtoks\seplistT

\long\def\seplistD#1{%
   \seplistS##2\seplistE{\def\tmpa{##1}\def\tmpb{##2}\seplistE}%
   \def\tmpb{\tmpa #1}\expandafter\tmpb \tmp\seplistD\seplistE
}
\long\def\seplistE#1{%
   \ifx\tmpa\empty
      \seplistS\seplistD{\def\tmpb{##1}}\expandafter\tmpa\tmpb
      \ifx\tmpb\empty \seplistQ{#1}%
      \else \expandafter\addto\expandafter\seplistLx
                           \expandafter {\expandafter\seplistD\expandafter{\tmpb}{#1}}%
   \fi\fi
}
\def\seplistS{\long\expandafter\def\expandafter\tmpa\expandafter##\expandafter1\tmp}
\long\def\seplistQ#1#2\seplistA{\fi\fi\gdef\sepused{#1}\seplistZ}

\long\def\seplist#1#2{\begingroup
  \toks0={#2}\let\bgroup=\relax \let\egroup=\relax
  \def\seplistL{}\def\seplistLx{}\seplistI#1{}\gdef\sepused{}%
  \ifx\seplistL\empty \expandafter\endgroup \the\toks0\else
  \seplistT={}\expandafter\seplistA\fi
}
\def\seplistA{\futurelet\tmp\seplistB}
\def\seplistB{\let\next=\seplistP
   \expandafter\ifx\space\tmp \let\next=\seplistC \let\nexxt=\seplistM \fi
   \ifx##\tmp  \let\next=\seplistC \let\nexxt=\seplistH \fi
   \ifx{\tmp   \let\next=\seplistG \fi
   \ifx}\tmp   \let\next=\seplistC \let\nexxt=\seplistF \fi
   \next
}
\def\seplistC{\afterassignment\nexxt \let\next= }
\long\def\seplistP#1{\seplistX#1\def\tmp{#1}\seplistN}
\def\seplistM{\seplistX{ }\def\tmp{ }\seplistN}
\def\seplistH{\seplistX{##}\def\seplistLx{}\seplistA}
\def\seplistN{\edef\seplistLx{\expandafter}\seplistLx \seplistL \seplistA}
\long\def\seplistG#1{\def\seplistLx{}\seplistX{{#1}}\seplistA}
\def\seplistF{\seplistT\expandafter{\expandafter{\the\seplistT}}\seplistZ}
\long\def\seplistX#1{\seplistT\expandafter{\the\seplistT#1}}
\def\seplistZ{\let\tmp=\sepused 
   \expandafter\seplistS\expandafter{\the\toks0{##1}}%
   \expandafter\expandafter\expandafter\endgroup\expandafter\tmpa\the\seplistT
}
\long\def\seplistI#1{\ifx\seplistI#1\seplistI\else
   \addto\seplistL{\seplistD{#1}{#1}}\expandafter\seplistI \fi
}

\endinput

\def\m#1{\toks0={#1}\message{param: "\the\toks0", separator: "\sepused"}}
\seplist{ab{c a}{cb}0123456789}\m uv#wcd{6cb7}uffc a

% You can define \sepdef \macro #1[{sepA}{sepB}{sepC}]{usage of the parameter #1...}

\def\sepdef #1#2[#3]{\def#1{\seplist{#3}{\csname:\string#1\endcsname}}%
  \long\expandafter\def\csname:\string#1\endcsname ##1}

\sepdef\test #1[uvw]#2{\message{1="#1", 2="#2"}}

\test ahawx \test bhavy

\end

Comments to the implementation.

We read the parameter token-per-token similarly as in openbrace.tex or
eparam.tex and store these tokens in \seplistT token list. The internal
macro \seplistL includes the list of separators in the form:

\seplistD{sepA}{sepA}\seplistD{sepB}{sepB}...

We store the already read token to \tmp and run \seplistL. More exactly: at
the start, the temporary \seplistLx is emty. For each read token, we expand
\seplistLx and \seplistL to the input stream and before executing it we
reset \def\seplistLx{}. Now, the input stream is executed, i.e. the
\seplistD macro is processed for each separator. The task of
\seplistD{sepA}{sepA} is the following: to test if the \tmp is equal to the
first token of its first parameter ("s" in this example). If it is true,
then \seplistD (using \seplistE) adds the text \seplistD{epA}{sepA} (the
first token from the first parameter is removed) to the temporary list
\seplistLx which will be executed for the next token. If \tmp isn't equal to
the first token of the first parameter then \seplistD does nothing.

For example, the next read token \tmp is "e". Then \seplistD{epA}{sepA}
saves the \seplistD{pA}{sepA} to \seplistLx, because the first letter
\tmp="e". If the next token \tmp is "p", then \seplistD{A}{sepA} is stored
to the \seplistLx. And finally, if the next \tmp is "A", then
\seplistD{A}{sepA} does not store \seplistD{}{sepA}, but it decides that
separator is found because the first parameter is empty. It defines \sepused
to its second parameter "sepA" and it does the end of this game by \seplistQ
plus \seplistZ. If the last token \tmp isn't "A" then the \seplistD{A}{sepA}
does nothing and the chain is broken because the \seplistLx is set to empty
in each step. The new chain can be built because \seplistD{sepA}{sepA} is
still included in \seplistL which isn't changed during calculation.

Macro-programing in TeX is beautiful but it is different than the classical
technique used by "normal" programming.