chiark / gitweb /
8be1cf1bdfbd1c0ebe9c4bb3294fc69af2dcd283
[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 /*----- Header files ------------------------------------------------------*/
36
37 #ifndef MLIB_MACROS_H
38 #  include "macros.h"
39 #endif
40
41 /*----- Macros provided ---------------------------------------------------*/
42
43 /* @MCTRL__LABEL(tag)@ *
44  *
45  * Expand to a plausibly unique label based on the current line number and
46  * the @tag@.
47  */
48 #define MCTRL__LABEL(tag) GLUE(_mctrl__##tag##__, __LINE__)
49
50 /* @MC_ACT(stmt)@
51  * @MC_PASS@
52  *
53  * @MC_ACT@ is the main `trick' for constructing these flow-control
54  * operators.  It wraps up a statement as what we call an `action'.  Actions
55  * can be concatenated together to form a valid statement head, i.e., a
56  * sequence of actions can be followed by a statement, called the `body', to
57  * form a single syntactic statement.  The body can be simply `;', so an
58  * action can be treated as a simple statement.  However, if an action
59  * sequence is executed, only the first statement is executed.
60  *
61  * Actions can be labelled, e.g., using @MC_LABEL@, just like statements.  If
62  * control is passed to a label, e.g., by @MC_GOTO@, then the statement
63  * within the following action (only) is executed; the normal flow of control
64  * will then be to the statement following the containing action sequence and
65  * its body.
66  */
67 #define MC_ACT(stmt)    if (1) { stmt; } else
68 #define MC_PASS         MC_ACT(;)
69
70
71 /* @MC_LABEL(tag)@
72  * @MC_GOTO(tag)@
73  *
74  * @MC_LABEL@ just establishes a label which can be invoked (only) from the
75  * same top-level macro; and @MC_GOTO@ transfers control to it.
76  *
77  * The @MC_GOTO@ macro is special in that it can be used either as a plain
78  * statement, followed by a semicolon in the usual way, or as a prefix
79  * action in its own right, in place of @MC_ACT@.
80  */
81 #define MC_LABEL(tag)   MCTRL__LABEL(tag):
82 #define MC_GOTO(tag)    MC_ACT(goto MCTRL__LABEL(tag))
83
84 /* @BEFORE(tag, stmt_0) stmt_1@
85  *
86  * Execute @stmt_0@ and then @stmt_1@.
87  */
88 #define BEFORE(tag, stmt)                                               \
89                         MC_ACT(stmt; MC_GOTO(tag##__body))              \
90   MC_LABEL(tag##__body)
91
92 /* @AFTER(tag, stmt_0) stmt_1@
93  *
94  * Execute @stmt_1@ and then @stmt_0@.  If either statement invokes @break@
95  * then control immediately transfers to the statement following @AFTER@.  If
96  * either invokes @continue@, then control returns to @stmt_0@.
97  */
98 #define AFTER(tag, stmt)                                                \
99                         MC_GOTO(tag##__body)                            \
100   MC_LABEL(tag##__end)  MC_ACT(stmt)                                    \
101                         for (;;)                                        \
102                           MC_GOTO(tag##__end)                           \
103   MC_LABEL(tag##__body)
104
105 /* @WRAP(tag, before, onend, onbreak) stmt@
106  *
107  * Execute the @before@ statement, followed by @stmt@.  If @stmt@ invokes
108  * @break@, then @onbreak@ is immediately executed; if @stmt@ completes
109  * normally, or invokes @continue@ then @onend@ is immediately executed.
110  * Any @break@ and @continue@ in the @before@, @onend@, and @onbreak@
111  * statements behave as one would expect from their context.
112  */
113 #define WRAP(tag, before, onend, onbreak)                               \
114                         MC_ACT(before; MC_GOTO(tag##__body))            \
115   MC_LABEL(tag##__end)  MC_ACT(onend)                                   \
116   MC_LABEL(tag##__brk)  MC_ACT(onbreak)                                 \
117                         for (;;)                                        \
118                           MC_GOTO(tag##__brk)                           \
119                           for (;;)                                      \
120                             MC_GOTO(tag##__end)                         \
121   MC_LABEL(tag##__body)
122
123 /* @ALLOWELSE(tag) stmt_0 [else stmt_1]@
124  * @GOELSE(tag);@
125  *
126  * Executing @ALLOWELSE@ executes @stmt_0@, but not @stmt_1@.  If
127  * @GOELSE(tag)@ is executed, then control continues from @stmt_1@.
128  */
129 #define ALLOWELSE(tag)                                                  \
130                         MC_GOTO(tag##__body)                            \
131   MC_LABEL(tag##__else) if (0)                                          \
132   MC_LABEL(tag##__body)
133 #define GOELSE(tag)     do MC_GOTO(tag##__else); while (0)
134
135 /* @DOWHILE(tag, cond) stmt@
136  *
137  * Repeatedly execute @stmt@ until @cond@ evaluates to zero.  Execute @stmt@
138  * at least once.  The @break@ and @continue@ statements work within @stmt@
139  * as one would expect.
140  */
141 #define DOWHILE(tag, cond)                                              \
142                         MC_GOTO(tag##__body)                            \
143                         while (cond)                                    \
144   MC_LABEL(tag##__body)
145
146 /* @DECL(tag, decl) stmt@
147  *
148  * Execute @stmt@ with @decl@ in scope.  If @stmt@ completes or invokes
149  * @break@ or @continue@ then control continues with the statement following
150  * @DECL@.  Internally, this uses @for@, so it only works in C99 or later, or
151  * C++.
152  */
153 #if __STDC_VERSION__ >= 199901 || defined(__cplusplus)
154 #  define DECL(tag, decl)                                               \
155                         for (decl;;)                                    \
156                           MC_GOTO(tag##__body)                          \
157   MC_LABEL(tag##__end)    MC_ACT(break)                                 \
158                           for (;;)                                      \
159                             MC_GOTO(tag##__end)                         \
160   MC_LABEL(tag##__body)
161 #endif
162
163 /*----- That's all, folks -------------------------------------------------*/
164
165 #ifdef __cplusplus
166   }
167 #endif
168
169 #endif