chiark / gitweb /
97be8b6b51338b2c286a784df5312e036471b986
[mLib] / utils / control.h
1 /* -*-c-*-
2  *
3  * Control operators, after Simon Tatham
4  *
5  * (c) 2022 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the mLib utilities library.
11  *
12  * mLib is free software: you can redistribute it and/or modify it under
13  * the terms of the GNU Library General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or (at
15  * your option) any later version.
16  *
17  * mLib is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
20  * License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with mLib.  If not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25  * USA.
26  */
27
28 #ifndef MLIB_CONTROL_H
29 #define MLIB_CONTROL_H
30
31 #ifdef __cplusplus
32   extern "C" {
33 #endif
34
35 /*----- Notes on the control operator machinery ---------------------------*
36  *
37  * These macros owe an obvious and immense debt to Simon Tatham's article
38  * `Metaprogramming custom control structures in C', available from
39  * https://www.chiark.greenend.org.uk/~sgtatham/mp/.  The basic tricks are
40  * all Tatham's, as are most of the provided operators.  The focus on
41  * @MC_ACT@ as a significant primitive is probably my main original
42  * contribution.
43  */
44
45 /*----- Header files ------------------------------------------------------*/
46
47 #ifndef MLIB_MACROS_H
48 #  include "macros.h"
49 #endif
50
51 /*----- Macros provided ---------------------------------------------------*/
52
53 /* @MCTRL__LABEL(tag)@ *
54  *
55  * Expand to a plausibly unique label based on the current line number and
56  * the @tag@.
57  */
58 #define MCTRL__LABEL(tag) GLUE(_mctrl__##tag##__, __LINE__)
59
60 /* @MC_ACT(stmt)@
61  *
62  * @MC_ACT@ is the main `trick' for constructing these flow-control
63  * operators.  It wraps up a statement as what we call an `action'.  Actions
64  * can be concatenated together to form a valid statement head, i.e., a
65  * sequence of actions can be followed by a statement, called the `body', to
66  * form a single syntactic statement.  The body can be simply `;', so an
67  * action can be treated as a simple statement.  However, if an action
68  * sequence is executed, only the first statement is executed.
69  *
70  * Actions can be labelled, e.g., using @MC_LABEL@, just like statements.  If
71  * control is passed to a label, e.g., by @MC_GOTO@, then the statement
72  * within the following action (only) is executed; the normal flow of control
73  * will then be to the statement following the containing action sequence and
74  * its body.
75  */
76 #define MC_ACT(stmt)    if (1) { stmt; } else
77
78 /* @MC_LABEL(tag)@
79  * @MC_GOTO(tag);@
80  *
81  * @MC_LABEL@ just establishes a label which can be invoked (only) from the
82  * same top-level macro; and @MC_GOTO@ transfers control to it.
83  *
84  * The @MC_GOTO@ macro is special in that it can be used either as a plain
85  * statement, followed by a semicolon in the usual way, or as a prefix
86  * action in its own right, in place of @MC_ACT@.
87  */
88 #define MC_LABEL(tag)   MCTRL__LABEL(tag):
89 #define MC_GOTO(tag)    MC_ACT(goto MCTRL__LABEL(tag))
90
91 /* @MC_TARGET(tag, stmt) body@
92  * @MC_GOTARGET(tag);@
93  *
94  * Executing @TARGET@ statement executes the @body@, as if the @TARGET@
95  * weren't there.  Executing a @GOTARGET@ transfers control to the @stmt@
96  * (but not normally the @body@).  In either case, the normal flow of control
97  * is then to the following statement.
98  */
99 #define MC_TARGET(tag, stmt)                                            \
100                         MC_GOTO(tag##__body)                            \
101   MC_LABEL(tag##__tgt)  MC_ACT(stmt)                                    \
102   MC_LABEL(tag##__body)
103 #define MC_GOTARGET(tag) do MC_GOTO(tag##__tgt); while (0)
104
105 /* @MC_BEFORE(tag, stmt) body@
106  *
107  * Execute @stmt@ and then @body@.
108  */
109 #define MC_BEFORE(tag, stmt)                                            \
110                         MC_ACT(stmt; MC_GOTO(tag##__body))              \
111   MC_LABEL(tag##__body)
112
113 /* @MC_AFTER(tag, stmt) body@
114  *
115  * Execute @body@ and then @stmt@.  If @body@ invokes @break@ then control
116  * immediately transfers to the statement following @MC_AFTER@.  If @body
117  * invokes @continue@, then control returns to @stmt@.
118  */
119 #define MC_AFTER(tag, stmt)                                             \
120                         MC_GOTO(tag##__body)                            \
121   MC_LABEL(tag##__end)  MC_ACT(stmt)                                    \
122                         for (;;)                                        \
123                           MC_GOTO(tag##__end)                           \
124   MC_LABEL(tag##__body)
125
126 /* @MC_DOWHILE(tag, cond) body@
127  *
128  * Repeatedly execute @body@ until @cond@ evaluates to zero.  The @body@ is
129  * executed once before @cond@ is checked the first time.  The @break@ and
130  * @continue@ statements work within @body@ as one would expect.
131  */
132 #define MC_DOWHILE(tag, cond)                                           \
133                         MC_GOTO(tag##__body)                            \
134                         while (cond)                                    \
135   MC_LABEL(tag##__body)
136
137 /* @MC_ALLOWELSE(tag) apodosis_body [else haeresis_body]@
138  * @MC_GOELSE(tag);@
139  *
140  * Executing @MC_ALLOWELSE@ executes @apodosis_body@, but not
141  * @haeresis_body@.  If @MC_GOELSE(tag)@ is executed, then control continues
142  * from @haeresis_body@.
143  */
144 #define MC_ALLOWELSE(tag)                                               \
145                         MC_GOTO(tag##__body)                            \
146   MC_LABEL(tag##__else) if (0)                                          \
147   MC_LABEL(tag##__body)
148 #define MC_GOELSE(tag)  do MC_GOTO(tag##__else); while (0)
149
150 /* @MC_WRAP(tag, before, onend, onbreak) body@
151  *
152  * Execute the @before@ statement, followed by @body@.  If @body@ invokes
153  * @break@, then @onbreak@ is immediately executed; if @body@ completes
154  * normally, or invokes @continue@ then @onend@ is immediately executed.
155  * Any @break@ and @continue@ in the @before@, @onend@, and @onbreak@
156  * statements behave as one would expect from their context.
157  */
158 #define MC_WRAP(tag, before, onend, onbreak)                            \
159                         MC_ACT(before; MC_GOTO(tag##__body))            \
160   MC_LABEL(tag##__end)  MC_ACT(onend)                                   \
161   MC_LABEL(tag##__brk)  MC_ACT(onbreak)                                 \
162                         for (;;)                                        \
163                           MC_GOTO(tag##__brk)                           \
164                           for (;;)                                      \
165                             MC_GOTO(tag##__end)                         \
166   MC_LABEL(tag##__body)
167
168 /* @MC_FINALLY(tag, cleanup) body@
169  *
170  * Execute @cleanup@ when @body@ completes or ends with @break@.  In the
171  * latter case, propagate the @break@ to the enclosing context -- for which
172  * it must be syntactically appropriate.
173  *
174  * The @cleanup@ code is duplicated.  If it arrange to have private long-term
175  * state, e.g, by declaring @static@ variables, then the two copies will not
176  * share the same state, so probably don't do this.
177  */
178 #define MC_FINALLY(tag, cleanup)                                        \
179         MP_WRAP(tag##__final, { ; } cleanup, { cleanup break; })
180
181 /* @MC_DECL(tag, decl) body@
182  *
183  * Execute @body@ with @decl@ in scope.  If @body@ completes or invokes
184  * @continue@ then control continues with the statement following @MC_DECL@;
185  * if it invokes @break@ then it will be restarted without leaving the scope
186  * of @decl@.  Internally, this uses @for@, so it only works in C99 or later,
187  * or C++.
188  */
189 #if __STDC_VERSION__ >= 199901 || defined(__cplusplus)
190 #  define MC_DECL(tag, decl)                                            \
191                         for (decl;;)                                    \
192                           MC_GOTO(tag##__body)                          \
193   MC_LABEL(tag##__exit)   MC_ACT(break)                                 \
194                           for (;;)                                      \
195                             MC_GOTO(tag##__exit)                        \
196   MC_LABEL(tag##__body)
197 #endif
198
199 /* @MC_LOOPELSE(tag, head) loop_body [else else_body]@
200  *
201  * Python-like looping with optional @else@ clause.  @head loop_body@ must be
202  * a syntactically valid @for@, @while@, or @MC_DOWHILE@ loop; if the loop
203  * exits because of @break@ then control continues in the usual way;
204  * otherwise, the @else_body@ (if any) is executed.
205  */
206 #define MC_LOOPELSE(tag, head)                                          \
207         MC_TARGET(tag##__exit, { ; })                                   \
208         MC_ALLOWELSE(tag##__else)                                       \
209         MC_AFTER(tag##__after, { MC_GOELSE(tag##__else); })             \
210         head                                                            \
211           MC_WRAP(tag##__body, { ; }, { ; }, { MC_GOTARGET(tag##__exit); })
212
213 /* @MC_LOOPBETWEEN(tag, setup, cond, step) loop_body [else else_body]@
214  *
215  * This is essentially a @for@ loop with a twist.  The @setup@, @cond@, and
216  * @step@ arguments are the parts of the @for@ head clause; because of the
217  * limitations of the C macro syntax, they're separated by commas rather than
218  * semicolons.
219  *
220  * The twist is that, once the @loop_body@ has finished, the @step@
221  * expression evaluated, and the @cond@ evaluated and determined to be
222  * nonzero, the @else_body@ (if any) is executed before re-entering the
223  * @loop_body@.  This makes it a useful place to insert any kind of
224  * interstitial material, e.g., printing commas between list items.
225  *
226  * The @cond@ is textually duplicated.  You'll get some code bloat if the
227  * condition is very complex.  If it somehow arranges to have private
228  * long-term state (e.g., as a result of declaring static variables inside
229  * GCC statement expressions), then the two copies will not share this state,
230  * so probably don't do this.
231  *
232  * Note that by the time that the @else_body@ is executed, the decision has
233  * already been made that another iteration will be performed, and, in
234  * particular, the @step@ has occurred.  The @else_body@ is therefore looking
235  * at the next item to be processed, not the item that has just finished
236  * being processed.
237  */
238 #define MC_LOOPBETWEEN(tag, setup, cond, step)                          \
239         for (setup;;)                                                   \
240           if (!(cond)) break; else                                      \
241           MC_TARGET(tag##__exit, { break; })                            \
242           for (;;)                                                      \
243             MC_WRAP(tag##__tailwrap, { ; },                             \
244                                      { ; },                             \
245                                      { MC_GOTARGET(tag##__exit); })     \
246             MC_ALLOWELSE(tag##__tail)                                   \
247             MC_WRAP(tag##__bodywrap, { ; },                             \
248                                      { if ((step), !(cond))             \
249                                          MC_GOTARGET(tag##__exit);      \
250                                        else                             \
251                                          MC_GOELSE(tag##__tail); },     \
252                                      { MC_GOTARGET(tag##__exit); })
253
254 /*----- That's all, folks -------------------------------------------------*/
255
256 #ifdef __cplusplus
257   }
258 #endif
259
260 #endif