+/* @MC_LOOPELSE(tag, head) loop_body [else else_body]@
+ *
+ * Python-like looping with optional @else@ clause. @head loop_body@ must be
+ * a syntactically valid @for@, @while@, or @MC_DOWHILE@ loop; if the loop
+ * exits because of @break@ then control continues in the usual way;
+ * otherwise, the @else_body@ (if any) is executed.
+ */
+#define MC_LOOPELSE(tag, head) \
+ MC_TARGET(tag##__exit, { ; }) \
+ MC_ALLOWELSE(tag##__else) \
+ MC_AFTER(tag##__after, { MC_GOELSE(tag##__else); }) \
+ head \
+ MC_WRAP(tag##__body, { ; }, { ; }, { MC_GOTARGET(tag##__exit); })
+
+/* @MC_LOOPBETWEEN(tag, setup, cond, step) loop_body [else else_body]@
+ *
+ * This is essentially a @for@ loop with a twist. The @setup@, @cond@, and
+ * @step@ arguments are the parts of the @for@ head clause; because of the
+ * limitations of the C macro syntax, they're separated by commas rather than
+ * semicolons.
+ *
+ * The twist is that, once the @loop_body@ has finished, the @step@
+ * expression evaluated, and the @cond@ evaluated and determined to be
+ * nonzero, the @else_body@ (if any) is executed before re-entering the
+ * @loop_body@. This makes it a useful place to insert any kind of
+ * interstitial material, e.g., printing commas between list items.
+ *
+ * The @cond@ is textually duplicated. You'll get some code bloat if the
+ * condition is very complex. If it somehow arranges to have private
+ * long-term state (e.g., as a result of declaring static variables inside
+ * GCC statement expressions), then the two copies will not share this state,
+ * so probably don't do this.
+ *
+ * Note that by the time that the @else_body@ is executed, the decision has
+ * already been made that another iteration will be performed, and, in
+ * particular, the @step@ has occurred. The @else_body@ is therefore looking
+ * at the next item to be processed, not the item that has just finished
+ * being processed.
+ */
+#define MC_LOOPBETWEEN(tag, setup, cond, step) \
+ for (setup;;) \
+ if (!(cond)) break; else \
+ MC_TARGET(tag##__exit, { break; }) \
+ for (;;) \
+ MC_WRAP(tag##__tailwrap, { ; }, \
+ { ; }, \
+ { MC_GOTARGET(tag##__exit); }) \
+ MC_ALLOWELSE(tag##__tail) \
+ MC_WRAP(tag##__bodywrap, { ; }, \
+ { if ((step), !(cond)) \
+ MC_GOTARGET(tag##__exit); \
+ else \
+ MC_GOELSE(tag##__tail); }, \
+ { MC_GOTARGET(tag##__exit); })
+