diff --git a/src/internal.h b/src/internal.h
index 0e92e99e6facf5d04c6b13ca8de51272ba630a1d..79d6c9787dac2a444f18c5a456fc1aacb04b8112 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -428,6 +428,7 @@ typedef struct HTTEntry_ {
   const char* name;
   HTokenType value;
   void (*unamb_sub)(const HParsedToken *tok, struct result_buf *buf);
+  void (*pprint)(FILE* stream, const HParsedToken* tok, int indent, int delta);
 } HTTEntry;
 
 const HTTEntry* h_get_token_type_entry(HTokenType token_type);
diff --git a/src/pprint.c b/src/pprint.c
index 15c14f83ae7fe44f4dc6eaf32908cc9b99a268fc..2c9fbc2e96fdc3314d3df5b7d471a47c5d8c50a4 100644
--- a/src/pprint.c
+++ b/src/pprint.c
@@ -69,9 +69,11 @@ void h_pprint(FILE* stream, const HParsedToken* tok, int indent, int delta) {
     break;
   default:
     if(tok->token_type >= TT_USER) {
-      const char *name = h_get_token_type_name(tok->token_type);
+      const HTTEntry *e = h_get_token_type_entry(tok->token_type);
       int num = tok->token_type-TT_USER;
-      fprintf(stream, "%*sUSER:%s %d\n", indent, "", name, num);
+      fprintf(stream, "%*sUSER:%s %d\n", indent, "", e->name, num);
+      if (e->pprint)
+        e->pprint(stream, tok, indent + delta, delta);
     } else {
       assert_message(0, "Should not reach here.");
     }
diff --git a/src/registry.c b/src/registry.c
index 00486db46ca6c1fdece03a051242f4f05ad23514..f0201c61eb804c46caf47bbe305d9c1f7c5d1680 100644
--- a/src/registry.c
+++ b/src/registry.c
@@ -60,6 +60,7 @@ HTokenType h_allocate_token_new(
   new_entry->name = name;
   new_entry->value = 0;
   new_entry->unamb_sub = unamb_sub;
+  new_entry->pprint = NULL;
   HTTEntry* probe = *(HTTEntry**)tsearch(new_entry, &tt_registry, compare_entries);
   if (probe->value != 0) {
     // Token type already exists...