chiark / gitweb /
fdc712584f53a27f82bcb45ea274ce522f5fb3c9
[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 /* @FIRSTBRANCH(tag_0) stmt_0@
51  * [@MIDBRANCH(tag_i) stmt_i ...@]
52  * @LASTBRANCH(tag_n) stmt_n@
53  * @GOBRANCH(tag);@
54  *
55  * If control enters at the top, then only <stmt_0> is executed, followed by
56  * the statement after.  Following @GOBRANCH(tag)@, control continues
57  * from the correspondingly tagged statement, and continues with the
58  * following statement again.
59  */
60 #define FIRSTBRANCH(tag)  if (1) { goto MCTRL__LABEL(tag); MCTRL__LABEL(tag):
61 #define MIDBRANCH(tag)    } else if (0) MCTRL__LABEL(tag): {
62 #define LASTBRANCH(tag)   } else MCTRL__LABEL(tag):
63 #define GOBRANCH(tag)     goto MCTRL__LABEL(tag)
64
65 /* @BEFORE(tag, stmt_0) stmt_1@
66  *
67  * Execute @stmt_0@ and then @stmt_1@.
68  */
69 #define BEFORE(tag, stmt)                                               \
70         if (1) { stmt goto MCTRL__LABEL(tag##__before_body); }          \
71         else MCTRL__LABEL(tag##__before_body):
72
73 /* @AFTER(tag, stmt_0) stmt_1@
74  *
75  * Execute @stmt_1@ and then @stmt_0@.  If either statement invokes @break@
76  * then control immediately transfers to the statement following @AFTER@.  If
77  * either invokes @continue@, then control returns to @stmt_0@.
78  */
79 #define AFTER(tag, stmt)                                                \
80         if (1) goto MCTRL__LABEL(tag##__after_body);                    \
81         else if (1) { MCTRL__LABEL(tag##__after_end): stmt }            \
82         else for (;;)                                                   \
83           if (1) goto MCTRL__LABEL(tag##__after_end);                   \
84           else MCTRL__LABEL(tag##__after_body):
85
86 /* @WRAP(tag, before, onend, onbreak) stmt@
87  *
88  * Execute the @before@ statement, followed by @stmt@.  If @stmt@ invokes
89  * @break@, then @onbreak@ is immediately executed; if @stmt@ completes
90  * normally, or invokes @continue@ then @onend@ is immediately executed.
91  * Any @break@ and @continue@ in the @before@, @onend@, and @onbreak@
92  * statements behave as one would expect from their context.
93  */
94 #define WRAP(tag, before, onend, onbreak)                               \
95         if (1) { before goto MCTRL__LABEL(tag##__wrap_body); }          \
96         else if (1) { MCTRL__LABEL(tag##__wrap_end): onend }            \
97         else if (1) { MCTRL__LABEL(tag##__wrap_break): onbreak }        \
98         else for (;;)                                                   \
99           if (1) goto MCTRL__LABEL(tag##__wrap_break);                  \
100           else for (;;)                                                 \
101             if (1) goto MCTRL__LABEL(tag##__wrap_end);                  \
102             else MCTRL__LABEL(tag##__wrap_body):
103
104 /* @ALLOWELSE(tag, before, onend, onbreak) stmt_0 [else stmt_1]@
105  * @GOELSE(tag);@
106  *
107  * Execute the @before@ statement, followed by @stmt_0@.  If @stmt_0@
108  * completes, or invokes @break@ or @continue@, then control continues with
109  * the next statement.  If @GOELSE(tag)@ is invoked anywhere in the
110  * function, then @before@ is executed, followed by @stmt_1@ (if present).
111  * If @stmt_1@ invokes @break@ then control passes to @onbreak@; if @stmt_1@
112  * ends normally then control passes to @onend@.  Any @break@ and @continue@
113  * in the @before@, @onend@, and @onbreak@ statements behave as one would
114  * expect from their context.
115  */
116 #define ALLOWELSE(tag, before, onend, onbreak)                          \
117         if (1) goto MCTRL__LABEL(tag##__allowelse_body);                \
118         else if (1) MCTRL__LABEL(tag##__allowelse_body_end): ;          \
119         else if (1) { MCTRL__LABEL(tag##__allowelse_else_end): onend }  \
120         else if (1) { MCTRL__LABEL(tag##__allowelse_else_break): onbreak } \
121         else if (1) {                                                   \
122         MCTRL__LABEL(tag##__allowelse_before_else):                     \
123           before goto MCTRL__LABEL(tag##__allowelse_else);              \
124         } else for (;;)                                                 \
125           if (1) goto MCTRL__LABEL(tag##__allowelse_else_break);        \
126           else for (;;)                                                 \
127             if (1) goto MCTRL__LABEL(tag##__allowelse_else_end);        \
128             else MCTRL__LABEL(tag##__allowelse_else): if (0) for (;;)   \
129               if (1) goto MCTRL__LABEL(tag##__allowelse_body_end);      \
130               else MCTRL__LABEL(tag##__allowelse_body):
131 #define GOELSE(tag)                                                     \
132         goto MCTRL__LABEL(tag##__allowelse_before_else)
133
134 /* @DOWHILE(tag, cond) stmt@
135  *
136  * Repeatedly execute @stmt@ until @cond@ evaluates to zero.  Execute @stmt@
137  * at least once.  The @break@ and @continue@ statements work within @stmt@
138  * as one would expect.
139  */
140 #define DOWHILE(tag, cond)                                              \
141         if (1) goto MCTRL__LABEL(tag##__dowhile_body);                  \
142         else while (cond) MCTRL__LABEL(tag##__dowhile_body):
143
144 /* @DECL(tag, decl) stmt@
145  *
146  * Execute @stmt@ with @decl@ in scope.  If @stmt@ completes or invokes
147  * @break@ or @continue@ then control continues with the statement following
148  * @DECL@.  Internally, this uses @for@, so it only works in C99 or later, or
149  * C++.
150  */
151 #if __STDC_VERSION__ >= 199901 || defined(__cplusplus)
152 #  define DECL(tag, decl)                                               \
153         for (decl;;)                                                    \
154           if (1) goto MCTRL__LABEL(tag##__decl_body);                   \
155           else if (1) MCTRL__LABEL(tag##__decl_exit): break;            \
156           else for (;;)                                                 \
157             if (1) goto MCTRL__LABEL(tag##__decl_exit);                 \
158             else MCTRL__LABEL(tag##__decl_body):
159 #endif
160
161 /*----- That's all, folks -------------------------------------------------*/
162
163 #ifdef __cplusplus
164   }
165 #endif
166
167 #endif