diff --git a/src/cfgrammar.c b/src/cfgrammar.c
index 369e49b8f0cde7ac2ccba3c73a91c37910342d62..47a27f13644dc00838efebfb9956ef94c55c074f 100644
--- a/src/cfgrammar.c
+++ b/src/cfgrammar.c
@@ -379,6 +379,20 @@ bool h_stringmap_empty(const HStringMap *m)
           && h_hashtable_empty(m->char_branches));
 }
 
+static bool eq_stringmap(const void *a, const void *b)
+{
+  return h_stringmap_equal(a, b);
+}
+
+bool h_stringmap_equal(const HStringMap *a, const HStringMap *b)
+{
+  if (a->epsilon_branch != b->epsilon_branch)
+    return false;
+  if (a->end_branch != b->end_branch)
+    return false;
+  return h_hashtable_equal(a->char_branches, b->char_branches, eq_stringmap);
+}
+
 static const HStringMap *
 h_first_seq_work(size_t k, HCFGrammar *g, HHashTable **pws, HCFChoice **s);
 
diff --git a/src/cfgrammar.h b/src/cfgrammar.h
index fb96e09a35b68c14f046db4fd6cf8e2e6b9e8fb5..8945ecb97d0adc1aa1f69391f54726a156c91211 100644
--- a/src/cfgrammar.h
+++ b/src/cfgrammar.h
@@ -53,6 +53,7 @@ void *h_stringmap_get_lookahead(const HStringMap *m, HInputStream lookahead);
 bool h_stringmap_present(const HStringMap *m, const uint8_t *str, size_t n, bool end);
 bool h_stringmap_present_epsilon(const HStringMap *m);
 bool h_stringmap_empty(const HStringMap *m);
+bool h_stringmap_equal(const HStringMap *a, const HStringMap *b);
 
 static inline HStringMap *h_stringmap_get_char(const HStringMap *m, const uint8_t c)
  { return h_hashtable_get(m->char_branches, (void *)char_key(c)); }