@@@ major overhaul, new primitives
[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
2edecb46
MW
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@.
e6591bec 80 */
2edecb46
MW
81#define MC_LABEL(tag) MCTRL__LABEL(tag):
82#define MC_GOTO(tag) MC_ACT({ goto MCTRL__LABEL(tag); })
e6591bec
MW
83
84/* @BEFORE(tag, stmt_0) stmt_1@
85 *
86 * Execute @stmt_0@ and then @stmt_1@.
87 */
88#define BEFORE(tag, stmt) \
2edecb46
MW
89 MC_ACT({ stmt MC_GOTO(tag##__body); }) \
90 MC_LABEL(tag##__body)
e6591bec
MW
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) \
2edecb46
MW
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)
e6591bec
MW
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) \
2edecb46
MW
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]@
e6591bec
MW
124 * @GOELSE(tag);@
125 *
2edecb46
MW
126 * Executing @ALLOWELSE@ executes @stmt_0@, but not @stmt_1@. If
127 * @GOELSE(tag)@ is executed, then control continues from @stmt_1@.
e6591bec 128 */
2edecb46
MW
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)
e6591bec
MW
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) \
2edecb46
MW
142 MC_GOTO(tag##__body) \
143 while (cond) \
144 MC_LABEL(tag##__body)
e6591bec
MW
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) \
2edecb46
MW
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)
e6591bec
MW
161#endif
162
163/*----- That's all, folks -------------------------------------------------*/
164
165#ifdef __cplusplus
166 }
167#endif
168
169#endif