diff --git a/src/cfgrammar.c b/src/cfgrammar.c
index 80e600193c694e5f92cfc14e1761136bde7e8899..eb3239d2cbf30f5e3f3dbf66f447be01e1d8db7e 100644
--- a/src/cfgrammar.c
+++ b/src/cfgrammar.c
@@ -1,6 +1,7 @@
 /* Context-free grammar representation and analysis */
 
 #include "cfgrammar.h"
+#include "allocator.h"
 #include <assert.h>
 #include <ctype.h>
 
@@ -10,6 +11,7 @@ HCFGrammar *h_cfgrammar_new(HAllocator *mm__)
   HCFGrammar *g = h_new(HCFGrammar, 1);
   assert(g != NULL);
 
+  g->mm__   = mm__;
   g->arena  = h_new_arena(mm__, 0);     // default blocksize
   g->nts    = h_hashset_new(g->arena, h_eq_ptr, h_hash_ptr);
   g->geneps = NULL;
@@ -19,6 +21,16 @@ HCFGrammar *h_cfgrammar_new(HAllocator *mm__)
   return g;
 }
 
+/* Frees the given grammar and associated data.
+ * Does *not* free parsers' CFG forms as created by h_desugar.
+ */
+void h_cfgrammar_free(HCFGrammar *g)
+{
+  HAllocator *mm__ = g->mm__;
+  h_delete_arena(g->arena);
+  h_free(g);
+}
+
 
 // helpers
 static void collect_nts(HCFGrammar *grammar, HCFChoice *symbol);
diff --git a/src/cfgrammar.h b/src/cfgrammar.h
index 5e12ba07b87f03f2375cfabb37b198b14b4c3a85..f336db5c0ca6e52651034095b14289e8ff2a0e26 100644
--- a/src/cfgrammar.h
+++ b/src/cfgrammar.h
@@ -11,6 +11,7 @@ typedef struct HCFGrammar_ {
   HHashTable  *first;   // memoized first sets of the grammar's symbols
   HHashTable  *follow;  // memoized follow sets of the grammar's NTs
   HArena      *arena;
+  HAllocator  *mm__;
 } HCFGrammar;
 
 /* mapping input bytes or end to tokens
@@ -29,6 +30,11 @@ static const HCFToken end_token = 0x200;
  */
 HCFGrammar *h_cfgrammar(HAllocator* mm__, const HParser *parser);
 
+/* Frees the given grammar and associated data.
+ * Does *not* free parsers' CFG forms as created by h_desugar.
+ */
+void h_cfgrammar_free(HCFGrammar *g);
+
 /* Does the given symbol derive the empty string (under g)? */
 bool h_symbol_derives_epsilon(HCFGrammar *g, const HCFChoice *symbol);