X-Git-Url: https://git.distorted.org.uk/~mdw/mLib/blobdiff_plain/55d45d31062ef774809ed151e9270ac1cbb6c12e..2edecb46edfcdc4b25c9d12bcfe23a5d66ec3f33:/utils/control.h?ds=sidebyside diff --git a/utils/control.h b/utils/control.h index fdc7125..77d985a 100644 --- a/utils/control.h +++ b/utils/control.h @@ -47,28 +47,47 @@ */ #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 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@ * @@ -77,11 +96,11 @@ * 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@ * @@ -92,44 +111,26 @@ * 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@ * @@ -138,8 +139,9 @@ * 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@ * @@ -150,12 +152,12 @@ */ #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 -------------------------------------------------*/