Index: compiler/generator.c
===================================================================
--- compiler/generator.c	(revision 449)
+++ compiler/generator.c	(working copy)
@@ -584,6 +584,29 @@
               "~M~i~C", p);
 }
 
+static void generate_GO_grouping(struct generator * g, struct node * p, int is_goto, int complement) {
+
+    struct grouping * q = p->name->grouping;
+    g->S[0] = p->mode == m_forward ? "" : "_b";
+    g->S[1] = complement ? "in" : "out";
+    g->S[2] = g->options->utf8 ? "_U" : "";
+    g->V[0] = p->name;
+    g->I[0] = q->smallest_ch;
+    g->I[1] = q->largest_ch;
+    if (is_goto) {
+	wp(g, "~Mif (~S1_grouping~S0~S2(z, ~V0, ~I0, ~I1, 1) < 0) ~f /* goto */~C", p);
+    } else {
+	wp(g, "~{ /* gopast */~C"
+	      "~Mint ret = ~S1_grouping~S0~S2(z, ~V0, ~I0, ~I1, 1);~N"
+	      "~Mif (ret < 0) ~f~N", p);
+	if (p->mode == m_forward)
+	    w(g, "~Mz->c += ret;~N");
+	else
+	    w(g, "~Mz->c -= ret;~N");
+	w(g, "~}");
+    }
+}
+
 static void generate_GO(struct generator * g, struct node * p, int style) {
     int keep_c = 0;
 
@@ -591,6 +614,17 @@
     int a0 = g->failure_label;
     const char * a1 = g->failure_string;
 
+    if (p->left->type == c_grouping || p->left->type == c_non) {
+	/* Special case for "goto" or "gopast" when used on a grouping or an
+	 * inverted grouping - the movement of c by the matching action is
+	 * exactly what we want! */
+#ifdef OPTIMISATION_WARNINGS
+	printf("Optimising %s %s\n", style ? "goto" : "gopast", p->left->type == c_non ? "non" : "grouping");
+#endif
+	generate_GO_grouping(g, p->left, style, p->left->type == c_non);
+	return;
+    }
+
     w(g, "~Mwhile(1) {"); wp(g, "~C~+", p);
 
     if (style == 1 || repeat_restore(g, p->left)) {
@@ -879,7 +913,7 @@
     g->V[0] = p->name;
     g->I[0] = q->smallest_ch;
     g->I[1] = q->largest_ch;
-    w(g, "~Mif (!(~S1_grouping~S0~S2(z, ~V0, ~I0, ~I1))) ~f~N");
+    w(g, "~Mif (~S1_grouping~S0~S2(z, ~V0, ~I0, ~I1, 0)) ~f~N");
 }
 
 static void generate_namedstring(struct generator * g, struct node * p) {
Index: runtime/utilities.c
===================================================================
--- runtime/utilities.c	(revision 449)
+++ runtime/utilities.c	(working copy)
@@ -93,70 +93,102 @@
     * slot = (p[c] & 0xF) << 12 | (b1 & 0x3F) << 6 | (b0 & 0x3F); return 3;
 }
 
-extern int in_grouping_U(struct SN_env * z, const unsigned char * s, int min, int max) {
-    int ch;
-    int w = get_utf8(z->p, z->c, z->l, & ch);
-    unless (w) return 0;
-    if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
-    z->c += w; return 1;
-}
-
-extern int in_grouping_b_U(struct SN_env * z, const unsigned char * s, int min, int max) {
-    int ch;
-    int w = get_b_utf8(z->p, z->c, z->lb, & ch);
-    unless (w) return 0;
-    if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
-    z->c -= w; return 1;
-}
-
-extern int out_grouping_U(struct SN_env * z, const unsigned char * s, int min, int max) {
-    int ch;
-    int w = get_utf8(z->p, z->c, z->l, & ch);
-    unless (w) return 0;
-    unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
-    z->c += w; return 1;
-}
-
-extern int out_grouping_b_U(struct SN_env * z, const unsigned char * s, int min, int max) {
-    int ch;
-    int w = get_b_utf8(z->p, z->c, z->lb, & ch);
-    unless (w) return 0;
-    unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
-    z->c -= w; return 1;
+extern int in_grouping_U(struct SN_env * z, const unsigned char * s, int min, int max, int repeat) {
+    do {
+	int ch;
+	int w = get_utf8(z->p, z->c, z->l, & ch);
+	unless (w) return -1;
+	if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0)
+	    return w;
+	z->c += w;
+    } while (repeat);
+    return 0;
+}
+
+extern int in_grouping_b_U(struct SN_env * z, const unsigned char * s, int min, int max, int repeat) {
+    do {
+	int ch;
+	int w = get_b_utf8(z->p, z->c, z->lb, & ch);
+	unless (w) return -1;
+	if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0)
+	    return w;
+	z->c -= w;
+    } while (repeat);
+    return 0;
+}
+
+extern int out_grouping_U(struct SN_env * z, const unsigned char * s, int min, int max, int repeat) {
+    do {
+	int ch;
+	int w = get_utf8(z->p, z->c, z->l, & ch);
+	unless (w) return -1;
+	unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0)
+	    return w;
+	z->c += w;
+    } while (repeat);
+    return 0;
+}
+
+extern int out_grouping_b_U(struct SN_env * z, const unsigned char * s, int min, int max, int repeat) {
+    do {
+	int ch;
+	int w = get_b_utf8(z->p, z->c, z->lb, & ch);
+	unless (w) return -1;
+	unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0)
+	    return w;
+	z->c -= w;
+    } while (repeat);
+    return 0;
 }
 
 /* Code for character groupings: non-utf8 cases */
 
-extern int in_grouping(struct SN_env * z, const unsigned char * s, int min, int max) {
-    int ch;
-    if (z->c >= z->l) return 0;
-    ch = z->p[z->c];
-    if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
-    z->c++; return 1;
-}
-
-extern int in_grouping_b(struct SN_env * z, const unsigned char * s, int min, int max) {
-    int ch;
-    if (z->c <= z->lb) return 0;
-    ch = z->p[z->c - 1];
-    if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
-    z->c--; return 1;
-}
-
-extern int out_grouping(struct SN_env * z, const unsigned char * s, int min, int max) {
-    int ch;
-    if (z->c >= z->l) return 0;
-    ch = z->p[z->c];
-    unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
-    z->c++; return 1;
-}
-
-extern int out_grouping_b(struct SN_env * z, const unsigned char * s, int min, int max) {
-    int ch;
-    if (z->c <= z->lb) return 0;
-    ch = z->p[z->c - 1];
-    unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0) return 0;
-    z->c--; return 1;
+extern int in_grouping(struct SN_env * z, const unsigned char * s, int min, int max, int repeat) {
+    do {
+	int ch;
+	if (z->c >= z->l) return -1;
+	ch = z->p[z->c];
+	if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0)
+	    return 1;
+	z->c++;
+    } while (repeat);
+    return 0;
+}
+
+extern int in_grouping_b(struct SN_env * z, const unsigned char * s, int min, int max, int repeat) {
+    do {
+	int ch;
+	if (z->c <= z->lb) return -1;
+	ch = z->p[z->c - 1];
+	if (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0)
+	    return 1;
+	z->c--;
+    } while (repeat);
+    return 0;
+}
+
+extern int out_grouping(struct SN_env * z, const unsigned char * s, int min, int max, int repeat) {
+    do {
+	int ch;
+	if (z->c >= z->l) return -1;
+	ch = z->p[z->c];
+	unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0)
+	    return 1;
+	z->c++;
+    } while (repeat);
+    return 0;
+}
+
+extern int out_grouping_b(struct SN_env * z, const unsigned char * s, int min, int max, int repeat) {
+    do {
+	int ch;
+	if (z->c <= z->lb) return -1;
+	ch = z->p[z->c - 1];
+	unless (ch > max || (ch -= min) < 0 || (s[ch >> 3] & (0X1 << (ch & 0X7))) == 0)
+	    return 1;
+	z->c--;
+    } while (repeat);
+    return 0;
 }
 
 extern int eq_s(struct SN_env * z, int s_size, const symbol * s) {
Index: runtime/header.h
===================================================================
--- runtime/header.h	(revision 449)
+++ runtime/header.h	(working copy)
@@ -25,15 +25,15 @@
 
 extern int skip_utf8(const symbol * p, int c, int lb, int l, int n);
 
-extern int in_grouping_U(struct SN_env * z, const unsigned char * s, int min, int max);
-extern int in_grouping_b_U(struct SN_env * z, const unsigned char * s, int min, int max);
-extern int out_grouping_U(struct SN_env * z, const unsigned char * s, int min, int max);
-extern int out_grouping_b_U(struct SN_env * z, const unsigned char * s, int min, int max);
-
-extern int in_grouping(struct SN_env * z, const unsigned char * s, int min, int max);
-extern int in_grouping_b(struct SN_env * z, const unsigned char * s, int min, int max);
-extern int out_grouping(struct SN_env * z, const unsigned char * s, int min, int max);
-extern int out_grouping_b(struct SN_env * z, const unsigned char * s, int min, int max);
+extern int in_grouping_U(struct SN_env * z, const unsigned char * s, int min, int max, int repeat);
+extern int in_grouping_b_U(struct SN_env * z, const unsigned char * s, int min, int max, int repeat);
+extern int out_grouping_U(struct SN_env * z, const unsigned char * s, int min, int max, int repeat);
+extern int out_grouping_b_U(struct SN_env * z, const unsigned char * s, int min, int max, int repeat);
+
+extern int in_grouping(struct SN_env * z, const unsigned char * s, int min, int max, int repeat);
+extern int in_grouping_b(struct SN_env * z, const unsigned char * s, int min, int max, int repeat);
+extern int out_grouping(struct SN_env * z, const unsigned char * s, int min, int max, int repeat);
+extern int out_grouping_b(struct SN_env * z, const unsigned char * s, int min, int max, int repeat);
 
 extern int eq_s(struct SN_env * z, int s_size, const symbol * s);
 extern int eq_s_b(struct SN_env * z, int s_size, const symbol * s);
