@@@ better tests
[mLib] / utils / control.h
CommitLineData
e6591bec
MW
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 */
65135ebd
MW
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)
e6591bec
MW
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); \
65135ebd 81 else if (1) { MCTRL__LABEL(tag##__after_end): stmt } \
e6591bec 82 else for (;;) \
65135ebd 83 if (1) goto MCTRL__LABEL(tag##__after_end); \
e6591bec
MW
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); } \
65135ebd
MW
96 else if (1) { MCTRL__LABEL(tag##__wrap_end): onend } \
97 else if (1) { MCTRL__LABEL(tag##__wrap_break): onbreak } \
e6591bec
MW
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): ; \
65135ebd
MW
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 (;;) \
e6591bec
MW
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