diff --git a/src/Makefile b/src/Makefile
index 128de0506ec7f2b39232b79441890047ac4a95f0..84970c9263a7562e96e8fb480c7fdb7e5dfdc698 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -34,6 +34,7 @@ HAMMER_PARTS := \
 	bitwriter.o \
 	pprint.o \
 	allocator.o \
+	desugar.o \
 	datastructures.o \
 	system_allocator.o \
 	benchmark.o \
diff --git a/src/desugar.c b/src/desugar.c
new file mode 100644
index 0000000000000000000000000000000000000000..fbd15811a9e8a53342ef56f7c1c8338a79ac41d4
--- /dev/null
+++ b/src/desugar.c
@@ -0,0 +1,6 @@
+#include "hammer.h"
+#include "internal.h"
+
+HCFChoice *h_desugar(HAllocator *mm__, const HParser *parser) {
+  return parser->vtable->desugar(mm__, parser->env);
+}
diff --git a/src/internal.h b/src/internal.h
index 279f9980d008d2fa3cd80d01aeab20ee16067be9..6245efad57453d8c469b69469c4f7f17e69bfe95 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -224,6 +224,8 @@ long long h_read_bits(HInputStream* state, int count, char signed_p);
 HParseResult* h_do_parse(const HParser* parser, HParseState *state);
 void put_cached(HParseState *ps, const HParser *p, HParseResult *cached);
 
+HCFChoice *h_desugar(HAllocator *mm__, const HParser *parser);
+
 HCountedArray *h_carray_new_sized(HArena * arena, size_t size);
 HCountedArray *h_carray_new(HArena * arena);
 void h_carray_append(HCountedArray *array, void* item);
diff --git a/src/parsers/action.c b/src/parsers/action.c
index aec422180d5525ce7bb09ed81eef887a98c79735..ea409d76148aea9e27af4fdea774a7b0e9d6e395 100644
--- a/src/parsers/action.c
+++ b/src/parsers/action.c
@@ -23,7 +23,7 @@ static HCFChoice* desugar_action(HAllocator *mm__, void *env) {
   HParseAction *a = (HParseAction*)env;
   HCFSequence *seq = h_new(HCFSequence, 1);
   seq->items = h_new(HCFChoice*, 2);
-  seq->items[0] = a->p->vtable->desugar(mm__, a->p->env);
+  seq->items[0] = h_desugar(mm__, a->p);
   seq->items[1] = NULL;
   HCFChoice *ret = h_new(HCFChoice, 1);
   ret->type = HCF_CHOICE;
diff --git a/src/parsers/attr_bool.c b/src/parsers/attr_bool.c
index 9e69ebe7b352b9a60b4141283797858460ede76f..78bd30befdec06149418f22b4e99c08fdd029aff 100644
--- a/src/parsers/attr_bool.c
+++ b/src/parsers/attr_bool.c
@@ -36,7 +36,7 @@ static HCFChoice* desugar_ab(HAllocator *mm__, void *env) {
   HAttrBool *a = (HAttrBool*)env;
   HCFSequence *seq = h_new(HCFSequence, 1);
   seq->items = h_new(HCFChoice*, 2);
-  seq->items[0] = a->p->vtable->desugar(mm__, a->p->env);
+  seq->items[0] = h_desugar(mm__, a->p);
   seq->items[1] = NULL;
   HCFChoice *ret = h_new(HCFChoice, 1);
   ret->type = HCF_CHOICE;
diff --git a/src/parsers/choice.c b/src/parsers/choice.c
index c4a931abdc1a76b31748ac48d4be776f114a776b..88e908bdb3e99591bccd33345fc5474dc50939ce 100644
--- a/src/parsers/choice.c
+++ b/src/parsers/choice.c
@@ -47,7 +47,7 @@ static HCFChoice* desugar_choice(HAllocator *mm__, void *env) {
   for (size_t i=0; i<s->len; ++i) {
     ret->seq[i] = h_new(HCFSequence, 1);
     ret->seq[i]->items = h_new(HCFChoice*, 2);
-    ret->seq[i]->items[0] = s->p_array[i]->vtable->desugar(mm__, s->p_array[i]->env);
+    ret->seq[i]->items[0] = h_desugar(mm__, s->p_array[i]);
     ret->seq[i]->items[1] = NULL;
   }
   ret->seq[s->len] = NULL;
diff --git a/src/parsers/ignore.c b/src/parsers/ignore.c
index 1129e8834cc74e715022dd1759e7ffc99c683e18..6ae2803dd21d8f58aef7606d8a01ba5c56f2784b 100644
--- a/src/parsers/ignore.c
+++ b/src/parsers/ignore.c
@@ -22,7 +22,7 @@ static bool ignore_isValidCF(void *env) {
 
 static HCFChoice* desugar_ignore(HAllocator *mm__, void *env) {
   HParser *p = (HParser*)env;
-  return (p->vtable->desugar(mm__, p->env));
+  return (h_desugar(mm__, p));
 }
 
 static const HParserVtable ignore_vt = {
diff --git a/src/parsers/ignoreseq.c b/src/parsers/ignoreseq.c
index e248dc4aaf3c39ca0caa28b57da5c30e2abee148..2adffc6c7d40abcc801db587a930d1184a1e876d 100644
--- a/src/parsers/ignoreseq.c
+++ b/src/parsers/ignoreseq.c
@@ -31,7 +31,7 @@ static HCFChoice* desugar_ignoreseq(HAllocator *mm__, void *env) {
   HCFSequence *hseq = h_new(HCFSequence, 1);
   hseq->items = h_new(HCFChoice*, 1+seq->len);
   for (size_t i=0; i<seq->len; ++i) {
-    hseq->items[i] = seq->parsers[i]->vtable->desugar(mm__, seq->parsers[i]->env);
+    hseq->items[i] = h_desugar(mm__, seq->parsers[i]);
   }
   hseq->items[seq->len] = NULL;
   HCFChoice *ret = h_new(HCFChoice, 1);
diff --git a/src/parsers/many.c b/src/parsers/many.c
index 7ea63d9d7e5e50553a5368b41517b8ece9930ca1..c439b674f5216ea70a61a203828383aa9227007e 100644
--- a/src/parsers/many.c
+++ b/src/parsers/many.c
@@ -76,7 +76,7 @@ static HCFChoice* desugar_many(HAllocator *mm__, void *env) {
   /* create first subrule */
   HCFChoice *ma = h_new(HCFChoice, 3);
   ma->type = HCF_CHOICE;
-  ma->seq[0]->items[0] = repeat->p->vtable->desugar(mm__, repeat->p->env);
+  ma->seq[0]->items[0] = h_desugar(mm__, repeat->p);
 
   /* create second subrule */
   HCFChoice *mar = h_new(HCFChoice, 3);
@@ -84,8 +84,8 @@ static HCFChoice* desugar_many(HAllocator *mm__, void *env) {
   mar->seq = h_new(HCFSequence*, 2);
   mar->seq[0] = h_new(HCFSequence, 1);
   mar->seq[0]->items = h_new(HCFChoice*, 4);
-  mar->seq[0]->items[0] = repeat->sep->vtable->desugar(mm__, repeat->sep->env);
-  mar->seq[0]->items[1] = repeat->p->vtable->desugar(mm__, repeat->p->env);
+  mar->seq[0]->items[0] = h_desugar(mm__, repeat->sep);
+  mar->seq[0]->items[1] = h_desugar(mm__, repeat->p);
   mar->seq[0]->items[2] = mar; // woo recursion!
   mar->seq[0]->items[3] = NULL;
   mar->seq[1]->items = h_new(HCFChoice*, 2);
diff --git a/src/parsers/optional.c b/src/parsers/optional.c
index e22a4f61af5d7645a759ebb116bea79e3d3b159b..b8777d3c8c4f56dc2fcf3cffe400a26b2cb61b9b 100644
--- a/src/parsers/optional.c
+++ b/src/parsers/optional.c
@@ -23,7 +23,7 @@ static bool opt_isValidCF(void *env) {
 
 static HCFChoice* desugar_optional(HAllocator *mm__, void *env) {
   HParser *p = (HParser*) env;
-  return p->vtable->desugar(mm__, p->env);
+  return h_desugar(mm__, p);
 }
 
 static const HParserVtable optional_vt = {
diff --git a/src/parsers/sequence.c b/src/parsers/sequence.c
index b15b168d04aa0dc8ba2aee296913f2340aeb9d6d..dde2a319c9c927daf8bc45af1033661a560c68e3 100644
--- a/src/parsers/sequence.c
+++ b/src/parsers/sequence.c
@@ -47,7 +47,7 @@ static HCFChoice* desugar_sequence(HAllocator *mm__, void *env) {
   HCFSequence *seq = h_new(HCFSequence, 1);
   seq->items = h_new(HCFChoice*, s->len+1);
   for (size_t i=0; i<s->len; ++i) {
-    seq->items[i] = s->p_array[i]->vtable->desugar(mm__, s->p_array[i]->env);
+    seq->items[i] = h_desugar(mm__, s->p_array[i]);
   }
   seq->items[s->len] = NULL;
   HCFChoice *ret = h_new(HCFChoice, 1);