*/
#define MCTRL__LABEL(tag) GLUE(_mctrl__##tag##__, __LINE__)
-/* @FIRSTBRANCH(tag_0) stmt_0@
- * [@MIDBRANCH(tag_i) stmt_i ...@]
- * @LASTBRANCH(tag_n) stmt_n@
- * @GOBRANCH(tag);@
- *
- * If control enters at the top, then only <stmt_0> is executed, followed by
- * the statement after. Following @GOBRANCH(tag)@, control continues
- * from the correspondingly tagged statement, and continues with the
- * following statement again.
+/* @MC_ACT(stmt)@
+ * @MC_PASS@
+ *
+ * @MC_ACT@ is the main `trick' for constructing these flow-control
+ * operators. It wraps up a statement as what we call an `action'. Actions
+ * can be concatenated together to form a valid statement head, i.e., a
+ * sequence of actions can be followed by a statement, called the `body', to
+ * form a single syntactic statement. The body can be simply `;', so an
+ * action can be treated as a simple statement. However, if an action
+ * sequence is executed, only the first statement is executed.
+ *
+ * Actions can be labelled, e.g., using @MC_LABEL@, just like statements. If
+ * control is passed to a label, e.g., by @MC_GOTO@, then the statement
+ * within the following action (only) is executed; the normal flow of control
+ * will then be to the statement following the containing action sequence and
+ * its body.
+ */
+#define MC_ACT(stmt) if (1) stmt else
+#define MC_PASS MC_ACT(;)
+
+
+/* @MC_LABEL(tag)@
+ * @MC_GOTO(tag)@
+ *
+ * @MC_LABEL@ just establishes a label which can be invoked (only) from the
+ * same top-level macro; and @MC_GOTO@ transfers control to it.
+ *
+ * The @MC_GOTO@ macro is special in that it can be used either as a plain
+ * statement, followed by a semicolon in the usual way, or as a prefix
+ * action in its own right, in place of @MC_ACT@.
*/
-#define FIRSTBRANCH(tag) if (1) { goto MCTRL__LABEL(tag); MCTRL__LABEL(tag):
-#define MIDBRANCH(tag) } else if (0) MCTRL__LABEL(tag): {
-#define LASTBRANCH(tag) } else MCTRL__LABEL(tag):
-#define GOBRANCH(tag) goto MCTRL__LABEL(tag)
+#define MC_LABEL(tag) MCTRL__LABEL(tag):
+#define MC_GOTO(tag) MC_ACT({ goto MCTRL__LABEL(tag); })
/* @BEFORE(tag, stmt_0) stmt_1@
*
* Execute @stmt_0@ and then @stmt_1@.
*/
#define BEFORE(tag, stmt) \
- if (1) { stmt goto MCTRL__LABEL(tag##__before_body); } \
- else MCTRL__LABEL(tag##__before_body):
+ MC_ACT({ stmt MC_GOTO(tag##__body); }) \
+ MC_LABEL(tag##__body)
/* @AFTER(tag, stmt_0) stmt_1@
*
* either invokes @continue@, then control returns to @stmt_0@.
*/
#define AFTER(tag, stmt) \
- if (1) goto MCTRL__LABEL(tag##__after_body); \
- else if (1) { MCTRL__LABEL(tag##__after_end): stmt } \
- else for (;;) \
- if (1) goto MCTRL__LABEL(tag##__after_end); \
- else MCTRL__LABEL(tag##__after_body):
+ MC_GOTO(tag##__body) \
+ MC_LABEL(tag##__end) MC_ACT(stmt) \
+ for (;;) \
+ MC_GOTO(tag##__end) \
+ MC_LABEL(tag##__body)
/* @WRAP(tag, before, onend, onbreak) stmt@
*
* statements behave as one would expect from their context.
*/
#define WRAP(tag, before, onend, onbreak) \
- if (1) { before goto MCTRL__LABEL(tag##__wrap_body); } \
- else if (1) { MCTRL__LABEL(tag##__wrap_end): onend } \
- else if (1) { MCTRL__LABEL(tag##__wrap_break): onbreak } \
- else for (;;) \
- if (1) goto MCTRL__LABEL(tag##__wrap_break); \
- else for (;;) \
- if (1) goto MCTRL__LABEL(tag##__wrap_end); \
- else MCTRL__LABEL(tag##__wrap_body):
-
-/* @ALLOWELSE(tag, before, onend, onbreak) stmt_0 [else stmt_1]@
+ MC_ACT({ before MC_GOTO(tag##__body); }) \
+ MC_LABEL(tag##__end) MC_ACT(onend) \
+ MC_LABEL(tag##__brk) MC_ACT(onbreak) \
+ for (;;) \
+ MC_GOTO(tag##__brk) \
+ for (;;) \
+ MC_GOTO(tag##__end) \
+ MC_LABEL(tag##__body)
+
+/* @ALLOWELSE(tag) stmt_0 [else stmt_1]@
* @GOELSE(tag);@
*
- * Execute the @before@ statement, followed by @stmt_0@. If @stmt_0@
- * completes, or invokes @break@ or @continue@, then control continues with
- * the next statement. If @GOELSE(tag)@ is invoked anywhere in the
- * function, then @before@ is executed, followed by @stmt_1@ (if present).
- * If @stmt_1@ invokes @break@ then control passes to @onbreak@; if @stmt_1@
- * ends normally then control passes to @onend@. Any @break@ and @continue@
- * in the @before@, @onend@, and @onbreak@ statements behave as one would
- * expect from their context.
+ * Executing @ALLOWELSE@ executes @stmt_0@, but not @stmt_1@. If
+ * @GOELSE(tag)@ is executed, then control continues from @stmt_1@.
*/
-#define ALLOWELSE(tag, before, onend, onbreak) \
- if (1) goto MCTRL__LABEL(tag##__allowelse_body); \
- else if (1) MCTRL__LABEL(tag##__allowelse_body_end): ; \
- else if (1) { MCTRL__LABEL(tag##__allowelse_else_end): onend } \
- else if (1) { MCTRL__LABEL(tag##__allowelse_else_break): onbreak } \
- else if (1) { \
- MCTRL__LABEL(tag##__allowelse_before_else): \
- before goto MCTRL__LABEL(tag##__allowelse_else); \
- } else for (;;) \
- if (1) goto MCTRL__LABEL(tag##__allowelse_else_break); \
- else for (;;) \
- if (1) goto MCTRL__LABEL(tag##__allowelse_else_end); \
- else MCTRL__LABEL(tag##__allowelse_else): if (0) for (;;) \
- if (1) goto MCTRL__LABEL(tag##__allowelse_body_end); \
- else MCTRL__LABEL(tag##__allowelse_body):
-#define GOELSE(tag) \
- goto MCTRL__LABEL(tag##__allowelse_before_else)
+#define ALLOWELSE(tag) \
+ MC_GOTO(tag##__body) \
+ MC_LABEL(tag##__else) if (0) \
+ MC_LABEL(tag##__body)
+#define GOELSE(tag) do MC_GOTO(tag##__else); while (0)
/* @DOWHILE(tag, cond) stmt@
*
* as one would expect.
*/
#define DOWHILE(tag, cond) \
- if (1) goto MCTRL__LABEL(tag##__dowhile_body); \
- else while (cond) MCTRL__LABEL(tag##__dowhile_body):
+ MC_GOTO(tag##__body) \
+ while (cond) \
+ MC_LABEL(tag##__body)
/* @DECL(tag, decl) stmt@
*
*/
#if __STDC_VERSION__ >= 199901 || defined(__cplusplus)
# define DECL(tag, decl) \
- for (decl;;) \
- if (1) goto MCTRL__LABEL(tag##__decl_body); \
- else if (1) MCTRL__LABEL(tag##__decl_exit): break; \
- else for (;;) \
- if (1) goto MCTRL__LABEL(tag##__decl_exit); \
- else MCTRL__LABEL(tag##__decl_body):
+ for (decl;;) \
+ MC_GOTO(tag##__body) \
+ MC_LABEL(tag##__end) MC_ACT({ break; }) \
+ for (;;) \
+ MC_GOTO(tag##__end) \
+ MC_LABEL(tag##__body)
#endif
/*----- That's all, folks -------------------------------------------------*/
static void laststep(int s, const char *where)
{ check_step(s, where); step = 0; }
+#define FORELSE(head) \
+ MC_GOTO(top) \
+ MC_LABEL(out) MC_ACT({ ; }) \
+ MC_LABEL(top) ALLOWELSE(els) \
+ AFTER(outer, { GOELSE(els); }) \
+ for (head) \
+ WRAP(inner, { ; }, \
+ { ; }, \
+ { MC_GOTO(out); })
+
#define FOR_FIZZBUZZ(var, base, limit) \
- FIRSTBRANCH(fizzbuzz0) GOBRANCH(fizzbuzz2); \
- MIDBRANCH(fizzbuzz1) ; \
- LASTBRANCH(fizzbuzz2) \
- DECL(fizzbuzz3, int _i = base COMMA _limit = limit) \
- for (; _i < _limit; _i++) \
- DECL(fizzbuzz4, char _buf[24]) \
- DECL(fizzbuzz5, const char *var) \
- WRAP(fizzbuzz6, { \
- switch (_i%15) { \
- case 0: var = "fizzbuzz"; break; \
- case 3: case 6: case 9: case 12: var = "fizz"; break; \
- case 5: case 10: var = "buzz"; break; \
- default: sprintf(_buf, "%d", _i); var = _buf; break; \
- } \
- }, \
- { ; }, \
- { GOBRANCH(fizzbuzz1); })
+ MC_GOTO(top) \
+ MC_LABEL(out) MC_ACT({ ; }) \
+ MC_LABEL(top) DECL(bounds, \
+ int _i = base COMMA _limit = limit) \
+ for (; _i < _limit; _i++) \
+ DECL(buf, char _buf[24]) \
+ DECL(var, const char *var) \
+ WRAP(wrap, { \
+ switch (_i%15) { \
+ case 0: \
+ var = "fizzbuzz"; \
+ break; \
+ case 3: case 6: case 9: case 12: \
+ var = "fizz"; \
+ break; \
+ case 5: case 10: \
+ var = "buzz"; \
+ break; \
+ default: \
+ sprintf(_buf, "%d", _i); var = _buf; \
+ break; \
+ } \
+ }, \
+ { ; }, \
+ { MC_GOTO(out); })
int main(void)
{
+ int i;
+
BEFORE(before0, { STEP(0); }) STEP(1);
AFTER(after0, { STEP(3); }) STEP(2);
LASTSTEP(4);
}
LASTSTEP(3);
+ FORELSE (i = 0; i < 10; i++) {
+ STEP(i);
+ if (i == 7) break;
+ } else
+ MISSTEP;
+ LASTSTEP(8);
+
+ FORELSE (i = 0; i < 10; i++) {
+ STEP(i);
+ if (i == 12) break;
+ } else
+ STEP(10);
+ LASTSTEP(11);
+
+#define TEST \
+ MC_ACT({ STEP(0); MC_GOTO(in_plain); }) \
+ MC_LABEL(done_plain) MC_ACT({ STEP(5); GOELSE(elsie); }) \
+ MC_LABEL(in_plain) WRAP(outer_wrap, { STEP(1); }, \
+ { STEP(7); }, \
+ { MISSTEP; }) \
+ ALLOWELSE(elsie) \
+ WRAP(inner_wrap, { STEP(2); }, \
+ { STEP(4); \
+ MC_GOTO(done_plain); }, \
+ { MISSTEP; }) \
+ STEP(3); \
+ else \
+ STEP(6); \
+ LASTSTEP(8);
+ TEST
+#undef TEST
+
+#if __STDC_VERSION__ >= 199901 || defined(__cplusplus)
STEP(0);
- DECL(decl0, int i = 1) STEP(i);
+ DECL(decl0, int j = 1) STEP(j);
LASTSTEP(2);
+#endif
FOR_FIZZBUZZ(fb, 19, 32) printf("%s\n", fb);