diff --git a/src/hammer.h b/src/hammer.h
index 1be297c7a3b1230f2595ba47366a6591946b8777..984df31aee5bcdd413ca9e324380e5221622bea6 100644
--- a/src/hammer.h
+++ b/src/hammer.h
@@ -778,10 +778,25 @@ void h_benchmark_report(FILE* stream, HBenchmarkResults* results);
 //void h_benchmark_dump_optimized_code(FILE* stream, HBenchmarkResults* results);
 // }}}
 
+// {{{ result_buf printers (used by token type registry)
+
+struct result_buf;
+
+bool h_append_buf(struct result_buf *buf, const char* input, int len);
+bool h_append_buf_c(struct result_buf *buf, char v);
+bool h_append_buf_formatted(struct result_buf *buf, char* format, ...);
+
+// }}}
+
 // {{{ Token type registry
 /// Allocate a new, unused (as far as this function knows) token type.
 HTokenType h_allocate_token_type(const char* name);
 
+/// Allocate a new token type with an unambiguous print function.
+HTokenType h_allocate_token_new(
+    const char* name,
+    void (*unamb_sub)(const HParsedToken *tok, struct result_buf *buf));
+
 /// Get the token type associated with name. Returns -1 if name is unkown
 HTokenType h_get_token_type_number(const char* name);
 
diff --git a/src/internal.h b/src/internal.h
index 10db4b20f429283bbf0aa99928487b2754379d91..0e92e99e6facf5d04c6b13ca8de51272ba630a1d 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -422,6 +422,18 @@ struct HParserVtable_ {
   bool higher; // false if primitive
 };
 
+// {{{ Token type registry internal
+
+typedef struct HTTEntry_ {
+  const char* name;
+  HTokenType value;
+  void (*unamb_sub)(const HParsedToken *tok, struct result_buf *buf);
+} HTTEntry;
+
+const HTTEntry* h_get_token_type_entry(HTokenType token_type);
+
+// }}}
+
 bool h_false(void*);
 bool h_true(void*);
 bool h_not_regular(HRVMProg*, void*);
diff --git a/src/pprint.c b/src/pprint.c
index 9c7c6522c0a201cf544ffaea3227510fec4bf827..52f42eb6060230a8bb608b8e5ab1eafb6ef1467c 100644
--- a/src/pprint.c
+++ b/src/pprint.c
@@ -41,10 +41,10 @@ void h_pprint(FILE* stream, const HParsedToken* tok, int indent, int delta) {
     else {
       fprintf(stream, "%*s", indent, "");
       for (size_t i = 0; i < tok->bytes.len; i++) {
-	fprintf(stream,
-		"%c%02hhx",
-		(i == 0) ? '<' : '.',
-		tok->bytes.token[i]);
+        fprintf(stream,
+                "%c%02hhx",
+                (i == 0) ? '<' : '.',
+                tok->bytes.token[i]);
       }
       fprintf(stream, ">\n");
     }
@@ -54,7 +54,7 @@ void h_pprint(FILE* stream, const HParsedToken* tok, int indent, int delta) {
       fprintf(stream, "%*ss -%#" PRIx64 "\n", indent, "", -tok->sint);
     else
       fprintf(stream, "%*ss %#" PRIx64 "\n", indent, "", tok->sint);
-      
+
     break;
   case TT_UINT:
     fprintf(stream, "%*su %#" PRIx64 "\n", indent, "", tok->uint);
@@ -96,7 +96,7 @@ static inline bool ensure_capacity(struct result_buf *buf, int amt) {
   return true;
 }
 
-static inline bool append_buf(struct result_buf *buf, const char* input, int len) {
+bool h_append_buf(struct result_buf *buf, const char* input, int len) {
   if (ensure_capacity(buf, len)) {
     memcpy(buf->output + buf->len, input, len);
     buf->len += len;
@@ -106,7 +106,7 @@ static inline bool append_buf(struct result_buf *buf, const char* input, int len
   }
 }
 
-static inline bool append_buf_c(struct result_buf *buf, char v) {
+bool h_append_buf_c(struct result_buf *buf, char v) {
   if (ensure_capacity(buf, 1)) {
     buf->output[buf->len++] = v;
     return true;
@@ -116,7 +116,7 @@ static inline bool append_buf_c(struct result_buf *buf, char v) {
 }
 
 /** append a formatted string to the result buffer */
-static inline bool append_buf_formatted(struct result_buf *buf, char* format, ...)
+bool h_append_buf_formatted(struct result_buf *buf, char* format, ...)
 {
   char* tmpbuf;
   int len;
@@ -125,7 +125,7 @@ static inline bool append_buf_formatted(struct result_buf *buf, char* format, ..
 
   va_start(ap, format);
   len = h_platform_vasprintf(&tmpbuf, format, ap);
-  result = append_buf(buf, tmpbuf, len);
+  result = h_append_buf(buf, tmpbuf, len);
   free(tmpbuf);
   va_end(ap);
 
@@ -134,52 +134,59 @@ static inline bool append_buf_formatted(struct result_buf *buf, char* format, ..
 
 static void unamb_sub(const HParsedToken* tok, struct result_buf *buf) {
   if (!tok) {
-    append_buf(buf, "NULL", 4);
+    h_append_buf(buf, "NULL", 4);
     return;
   }
   switch (tok->token_type) {
   case TT_NONE:
-    append_buf(buf, "null", 4);
+    h_append_buf(buf, "null", 4);
     break;
   case TT_BYTES:
     if (tok->bytes.len == 0)
-      append_buf(buf, "<>", 2);
+      h_append_buf(buf, "<>", 2);
     else {
       for (size_t i = 0; i < tok->bytes.len; i++) {
-	const char *HEX = "0123456789abcdef";
-	append_buf_c(buf, (i == 0) ? '<': '.');
-	char c = tok->bytes.token[i];
-	append_buf_c(buf, HEX[(c >> 4) & 0xf]);
-	append_buf_c(buf, HEX[(c >> 0) & 0xf]);
+        const char *HEX = "0123456789abcdef";
+        h_append_buf_c(buf, (i == 0) ? '<': '.');
+        char c = tok->bytes.token[i];
+        h_append_buf_c(buf, HEX[(c >> 4) & 0xf]);
+        h_append_buf_c(buf, HEX[(c >> 0) & 0xf]);
       }
-      append_buf_c(buf, '>');
+      h_append_buf_c(buf, '>');
     }
     break;
   case TT_SINT:
     if (tok->sint < 0)
-      append_buf_formatted(buf, "s-%#" PRIx64, -tok->sint);
+      h_append_buf_formatted(buf, "s-%#" PRIx64, -tok->sint);
     else
-      append_buf_formatted(buf, "s%#" PRIx64, tok->sint);
+      h_append_buf_formatted(buf, "s%#" PRIx64, tok->sint);
     break;
   case TT_UINT:
-    append_buf_formatted(buf, "u%#" PRIx64, tok->uint);
+    h_append_buf_formatted(buf, "u%#" PRIx64, tok->uint);
     break;
   case TT_ERR:
-    append_buf(buf, "ERR", 3);
+    h_append_buf(buf, "ERR", 3);
     break;
   case TT_SEQUENCE: {
-    append_buf_c(buf, '(');
+    h_append_buf_c(buf, '(');
     for (size_t i = 0; i < tok->seq->used; i++) {
       if (i > 0)
-	append_buf_c(buf, ' ');
+        h_append_buf_c(buf, ' ');
       unamb_sub(tok->seq->elements[i], buf);
     }
-    append_buf_c(buf, ')');
+    h_append_buf_c(buf, ')');
   }
     break;
-  default:
-    fprintf(stderr, "Unexpected token type %d\n", tok->token_type);
-    assert_message(0, "Should not reach here.");
+  default: {
+    const HTTEntry *e = h_get_token_type_entry(tok->token_type);
+    if (e) {
+      h_append_buf_c(buf, '{');
+      e->unamb_sub(tok, buf);
+      h_append_buf_c(buf, '}');
+    } else {
+      assert_message(0, "Bogus token type.");
+    }
+  }
   }
 }
   
@@ -192,7 +199,7 @@ char* h_write_result_unamb(const HParsedToken* tok) {
   };
   assert(buf.output != NULL);
   unamb_sub(tok, &buf);
-  append_buf_c(&buf, 0);
+  h_append_buf_c(&buf, 0);
   return buf.output;
 }
   
diff --git a/src/registry.c b/src/registry.c
index 8a079b5200f7415f878fa7f74fdaa181ee313669..a8646c14ed97f42f263770d5ebdd3b7e8c032b10 100644
--- a/src/registry.c
+++ b/src/registry.c
@@ -26,13 +26,8 @@
 #define h_strdup strdup
 #endif
 
-typedef struct Entry_ {
-  const char* name;
-  HTokenType value;
-} Entry;
-
 static void *tt_registry = NULL;
-static Entry** tt_by_id = NULL;
+static HTTEntry** tt_by_id = NULL;
 static unsigned int tt_by_id_sz = 0;
 #define TT_START TT_USER
 static HTokenType tt_next = TT_START;
@@ -40,23 +35,31 @@ static HTokenType tt_next = TT_START;
 /*
   // TODO: These are for the extension registry, which does not yet have a good name.
 static void *ext_registry = NULL;
-static Entry** ext_by_id = NULL;
+static HTTEntry** ext_by_id = NULL;
 static int ext_by_id_sz = 0;
 static int ext_next = 0;
 */
 
 
 static int compare_entries(const void* v1, const void* v2) {
-  const Entry *e1 = (Entry*)v1, *e2 = (Entry*)v2;
+  const HTTEntry *e1 = (HTTEntry*)v1, *e2 = (HTTEntry*)v2;
   return strcmp(e1->name, e2->name);
 }
 
-HTokenType h_allocate_token_type(const char* name) {
-  Entry* new_entry = h_alloc(&system_allocator, sizeof(*new_entry));
+static void default_unamb_sub(const HParsedToken* tok,
+                              struct result_buf* buf) {
+  h_append_buf_formatted(buf, "XXX AMBIGUOUS USER TYPE %d", tok->token_type);
+}
+
+HTokenType h_allocate_token_new(
+    const char* name,
+    void (*unamb_sub)(const HParsedToken *tok, struct result_buf *buf)) {
+  HTTEntry* new_entry = h_alloc(&system_allocator, sizeof(*new_entry));
   assert(new_entry != NULL);
   new_entry->name = name;
   new_entry->value = 0;
-  Entry* probe = *(Entry**)tsearch(new_entry, &tt_registry, compare_entries);
+  new_entry->unamb_sub = unamb_sub;
+  HTTEntry* probe = *(HTTEntry**)tsearch(new_entry, &tt_registry, compare_entries);
   if (probe->value != 0) {
     // Token type already exists...
     // TODO: treat this as a bug?
@@ -81,10 +84,13 @@ HTokenType h_allocate_token_type(const char* name) {
     return probe->value;
   }
 }
+HTokenType h_allocate_token_type(const char* name) {
+  return h_allocate_token_new(name, default_unamb_sub);
+}
 HTokenType h_get_token_type_number(const char* name) {
-  Entry e;
+  HTTEntry e;
   e.name = name;
-  Entry **ret = (Entry**)tfind(&e, &tt_registry, compare_entries);
+  HTTEntry **ret = (HTTEntry**)tfind(&e, &tt_registry, compare_entries);
   if (ret == NULL)
     return 0;
   else
@@ -96,3 +102,9 @@ const char* h_get_token_type_name(HTokenType token_type) {
   else
     return tt_by_id[token_type - TT_START]->name;
 }
+const HTTEntry* h_get_token_type_entry(HTokenType token_type) {
+  if (token_type >= tt_next || token_type < TT_START)
+    return NULL;
+  else
+    return tt_by_id[token_type - TT_START];
+}