diff --git a/src/SConscript b/src/SConscript
index ccb6f5d8cb758219aa1ad83bc44124c0368e32ba..cb6469673c973c16c16030485f91e92143a9c17b 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -6,6 +6,9 @@ import os.path
 
 Import('env testruns')
 
+# Bump this if you break binary compatibility (e.g. renumber backends)
+hammer_shlib_version = "1.0.0"
+
 dist_headers = [
     'hammer.h',
     'allocator.h',
@@ -21,7 +24,9 @@ parsers_headers = [
 
 backends_headers = [
     'backends/regex.h',
-    'backends/contextfree.h'
+    'backends/contextfree.h',
+    'backends/missing.h',
+    'backends/params.h'
 ]
 
 parsers = ['parsers/%s.c'%s for s in
@@ -56,7 +61,7 @@ parsers = ['parsers/%s.c'%s for s in
             'seek']]
 
 backends = ['backends/%s.c' % s for s in
-            ['packrat', 'llk', 'regex', 'glr', 'lalr', 'lr', 'lr0']]
+            ['missing', 'packrat', 'llk', 'regex', 'glr', 'lalr', 'lr', 'lr0', 'params']]
 
 misc_hammer_parts = [
     'allocator.c',
@@ -88,6 +93,7 @@ ctests = ['t_benchmark.c',
           't_grammar.c',
           't_misc.c',
           't_mm.c',
+          't_names.c',
           't_regression.c']
 
 
@@ -109,7 +115,8 @@ libhammer_static = None
 libhammer_shared = None
 
 if build_shared_library:
-    libhammer_shared = env.SharedLibrary('hammer', parsers + backends + misc_hammer_parts)
+    libhammer_shared = env.SharedLibrary('hammer', parsers + backends + misc_hammer_parts, \
+                                     SHLIBVERSION=hammer_shlib_version)
 libhammer_static = env.StaticLibrary(static_library_name, parsers + backends + misc_hammer_parts)
 
 if libhammer_shared is not None:
diff --git a/src/backends/glr.c b/src/backends/glr.c
index 06722b1963ed51d4aefc095d7a39a30d10633396..db7c4b314da17a09df2cbf2836bc092123827621 100644
--- a/src/backends/glr.c
+++ b/src/backends/glr.c
@@ -1,5 +1,6 @@
 #include <assert.h>
 #include "lr.h"
+#include "params.h"
 
 static bool glr_step(HParseResult **result, HSlist *engines,
                      HLREngine *engine, const HLRAction *action);
@@ -241,12 +242,54 @@ HParseResult *h_glr_parse(HAllocator* mm__, const HParser* parser, HInputStream*
   return result;
 }
 
+char * h_glr_get_description(HAllocator *mm__,
+                             HParserBackend be, void *param) {
+  const char *backend_name = "GLR";
+  size_t k;
+  char *descr = NULL;
 
+  k = h_get_param_k(param);
+
+  descr = h_format_description_with_param_k(mm__, backend_name, k);
+
+  return descr;
+}
+
+char * h_glr_get_short_name(HAllocator *mm__,
+                            HParserBackend be, void *param) {
+  const char *backend_name = "GLR";
+
+  size_t k;
+  char *name = NULL;
+
+  k = h_get_param_k(param);
+
+  name = h_format_name_with_param_k(mm__, backend_name, k);
+
+  return name;
+}
+
+
+int h_glr_extract_params(HParserBackendWithParams * be_with_params, backend_with_params_t * be_with_params_t) {
+
+  return h_extract_param_k(be_with_params, be_with_params_t);
+}
 
 HParserBackendVTable h__glr_backend_vtable = {
   .compile = h_glr_compile,
   .parse = h_glr_parse,
-  .free = h_glr_free
+  .free = h_glr_free,
+
+  .copy_params = h_copy_numeric_param,
+  /* No free_param needed, since it's not actually allocated */
+
+  /* Name/param resolution functions */
+  .backend_short_name = "glr",
+  .backend_description = "GLR(k) parser backend",
+  .get_description_with_params = h_glr_get_description,
+  .get_short_name_with_params = h_glr_get_short_name,
+
+  .extract_params = h_glr_extract_params
 };
 
 
diff --git a/src/backends/lalr.c b/src/backends/lalr.c
index 4b2bcaf1085584220456f904acc452d9002c4974..5954fcaa8a5cf236d3091331819ffe5be4f3ee75 100644
--- a/src/backends/lalr.c
+++ b/src/backends/lalr.c
@@ -1,8 +1,8 @@
 #include <assert.h>
 #include "contextfree.h"
 #include "lr.h"
+#include "params.h"
 
-static const size_t DEFAULT_K = 1;
 
 /* LALR-via-SLR grammar transformation */
 
@@ -275,7 +275,7 @@ HCFChoice *h_desugar_augmented(HAllocator *mm__, HParser *parser)
 
 int h_lalr_compile(HAllocator* mm__, HParser* parser, const void* params)
 {
-  size_t k = params? (uintptr_t)params : DEFAULT_K;
+  size_t k = params? (uintptr_t)params : DEFAULT_KMAX;
   // generate (augmented) CFG from parser
   // construct LR(0) DFA
   // build LR(0) table
@@ -370,10 +370,43 @@ void h_lalr_free(HParser *parser)
   HLRTable *table = parser->backend_data;
   h_lrtable_free(table);
   parser->backend_data = NULL;
-  parser->backend = PB_PACKRAT;
+  parser->backend_vtable = h_get_default_backend_vtable();
+  parser->backend = h_get_default_backend();
 }
 
+char * h_lalr_get_description(HAllocator *mm__,
+                              HParserBackend be, void *param) {
+  const char *backend_name = "LALR";
+  size_t k;
+  char *descr = NULL;
 
+  k = h_get_param_k(param);
+
+  descr = h_format_description_with_param_k(mm__, backend_name, k);
+
+  return descr;
+}
+
+char * h_lalr_get_short_name(HAllocator *mm__,
+                            HParserBackend be, void *param) {
+  const char *backend_name = "LALR";
+
+  size_t k;
+  char *name = NULL;
+
+  k = h_get_param_k(param);
+
+  name = h_format_name_with_param_k(mm__, backend_name, k);
+
+  return name;
+}
+
+
+int h_lalr_extract_params(HParserBackendWithParams * be_with_params, backend_with_params_t * be_with_params_t) {
+
+  return h_extract_param_k(be_with_params, be_with_params_t);
+
+}
 
 HParserBackendVTable h__lalr_backend_vtable = {
   .compile = h_lalr_compile,
@@ -381,11 +414,18 @@ HParserBackendVTable h__lalr_backend_vtable = {
   .free = h_lalr_free,
   .parse_start = h_lr_parse_start,
   .parse_chunk = h_lr_parse_chunk,
-  .parse_finish = h_lr_parse_finish
-};
-
+  .parse_finish = h_lr_parse_finish,
+  .copy_params = h_copy_numeric_param,
+  /* No free_param needed, since it's not actually allocated */
 
+  /* Name/param resolution functions */
+  .backend_short_name = "lalr",
+  .backend_description = "LALR(k) parser backend",
+  .get_description_with_params = h_lalr_get_description,
+  .get_short_name_with_params = h_lalr_get_short_name,
 
+  .extract_params = h_lalr_extract_params
+};
 
 // dummy!
 int test_lalr(void)
diff --git a/src/backends/llk.c b/src/backends/llk.c
index 19944a20930eef5fcb71efd3d055aebeb93e9cfa..812af5542594def30a8413576652b6130bb59fa7 100644
--- a/src/backends/llk.c
+++ b/src/backends/llk.c
@@ -2,8 +2,7 @@
 #include "../internal.h"
 #include "../cfgrammar.h"
 #include "../parsers/parser_internal.h"
-
-static const size_t DEFAULT_KMAX = 1;
+#include "params.h"
 
 
 /* Generating the LL(k) parse table */
@@ -254,7 +253,8 @@ void h_llk_free(HParser *parser)
   HLLkTable *table = parser->backend_data;
   h_llktable_free(table);
   parser->backend_data = NULL;
-  parser->backend = PB_PACKRAT;
+  parser->backend_vtable = h_get_default_backend_vtable();
+  parser->backend = h_get_default_backend();
 }
 
 
@@ -606,6 +606,38 @@ HParseResult *h_llk_parse_finish(HSuspendedParser *s)
   return llk_parse_finish_(s->mm__, s->backend_state);
 }
 
+char * h_llk_get_description(HAllocator *mm__,
+                             HParserBackend be, void *param) {
+  const char *backend_name = "LL";
+  size_t k, len;
+  char *descr = NULL;
+
+  k = h_get_param_k(param);
+
+  descr = h_format_description_with_param_k(mm__, backend_name, k);
+
+  return descr;
+}
+
+char * h_llk_get_short_name(HAllocator *mm__,
+                            HParserBackend be, void *param) {
+  const char *backend_name = "LL";
+
+  size_t k;
+  char *name = NULL;
+
+  k = h_get_param_k(param);
+
+  name = h_format_name_with_param_k(mm__, backend_name, k);
+
+  return name;
+}
+
+int h_llk_extract_params(HParserBackendWithParams * be_with_params, backend_with_params_t *be_with_params_t) {
+
+  return h_extract_param_k(be_with_params, be_with_params_t);
+}
+
 
 HParserBackendVTable h__llk_backend_vtable = {
   .compile = h_llk_compile,
@@ -614,7 +646,19 @@ HParserBackendVTable h__llk_backend_vtable = {
 
   .parse_start = h_llk_parse_start,
   .parse_chunk = h_llk_parse_chunk,
-  .parse_finish = h_llk_parse_finish
+  .parse_finish = h_llk_parse_finish,
+
+  .copy_params = h_copy_numeric_param,
+  /* No free_param needed, since it's not actually allocated */
+
+  /* Name/param resolution functions */
+  .backend_short_name = "llk",
+  .backend_description = "LL(k) parser backend",
+  .get_description_with_params = h_llk_get_description,
+  .get_short_name_with_params = h_llk_get_short_name,
+
+  /*extraction of params from string*/
+  .extract_params = h_llk_extract_params
 };
 
 
diff --git a/src/backends/missing.c b/src/backends/missing.c
new file mode 100644
index 0000000000000000000000000000000000000000..f159bf192329088fac3b5987d498d63f728fb9e6
--- /dev/null
+++ b/src/backends/missing.c
@@ -0,0 +1,25 @@
+#include "missing.h"
+
+/* Placeholder backend that always fails */
+
+int h_missing_compile(HAllocator* mm__, HParser* parser, const void* params) {
+  /* Always fail */
+
+  return -1;
+}
+
+HParseResult *h_missing_parse(HAllocator* mm__, const HParser* parser, HInputStream* stream) {
+  /* Always fail */
+
+  return NULL;
+}
+
+void h_missing_free(HParser *parser) {
+  /* No-op */
+}
+
+HParserBackendVTable h__missing_backend_vtable = {
+  .compile = h_missing_compile,
+  .parse = h_missing_parse,
+  .free = h_missing_free,
+};
diff --git a/src/backends/missing.h b/src/backends/missing.h
new file mode 100644
index 0000000000000000000000000000000000000000..4efe5f350331a1dcc75dcd894bf1075e8f62bbd8
--- /dev/null
+++ b/src/backends/missing.h
@@ -0,0 +1,7 @@
+#ifndef HAMMER_BACKENDS_MISSING__H
+#define HAMMER_BACKENDS_MISSING__H
+
+#include "../hammer.h"
+#include "../internal.h"
+
+#endif /* !defined(HAMMER_BACKENDS_MISSING__H) */
diff --git a/src/backends/packrat.c b/src/backends/packrat.c
index 0f7bf476ff8159a214b9b1b8d01728982aaf1307..e69e02b4dc5e522cf2ff7f28caac8536d7691fdd 100644
--- a/src/backends/packrat.c
+++ b/src/backends/packrat.c
@@ -265,13 +265,15 @@ HParseResult* h_do_parse(const HParser* parser, HParseState *state) {
 }
 
 int h_packrat_compile(HAllocator* mm__, HParser* parser, const void* params) {
+  parser->backend_vtable = &h__packrat_backend_vtable;
   parser->backend = PB_PACKRAT;
   return 0; // No compilation necessary, and everything should work
 	    // out of the box.
 }
 
 void h_packrat_free(HParser *parser) {
-  parser->backend = PB_PACKRAT; // revert to default, oh that's us
+  parser->backend_vtable = h_get_default_backend_vtable();
+  parser->backend = h_get_default_backend();
 }
 
 static uint32_t cache_key_hash(const void* key) {
@@ -446,8 +448,12 @@ HParserBackendVTable h__packrat_backend_vtable = {
   .compile = h_packrat_compile,
   .parse = h_packrat_parse,
   .free = h_packrat_free,
-
   .parse_start = h_packrat_parse_start,
   .parse_chunk = h_packrat_parse_chunk,
-  .parse_finish = h_packrat_parse_finish
+  .parse_finish = h_packrat_parse_finish,
+  /* Name/param resolution functions */
+  .backend_short_name = "packrat",
+  .backend_description = "Packrat parser with Warth's recursion",
+  .get_description_with_params = h_get_description_with_no_params,
+  .get_short_name_with_params = h_get_short_name_with_no_params
 };
diff --git a/src/backends/regex.c b/src/backends/regex.c
index 0337949f948e06f332bbbada27170c4e99fae2ef..4894a8805d34d95471819f7c6f219d9f8af18518 100644
--- a/src/backends/regex.c
+++ b/src/backends/regex.c
@@ -417,7 +417,8 @@ static void h_regex_free(HParser *parser) {
   h_free(prog->actions);
   h_free(prog);
   parser->backend_data = NULL;
-  parser->backend = PB_PACKRAT;
+  parser->backend_vtable = h_get_default_backend_vtable();
+  parser->backend = h_get_default_backend();
 }
 
 static int h_regex_compile(HAllocator *mm__, HParser* parser, const void* params) {
@@ -452,7 +453,12 @@ static HParseResult *h_regex_parse(HAllocator* mm__, const HParser* parser, HInp
 HParserBackendVTable h__regex_backend_vtable = {
   .compile = h_regex_compile,
   .parse = h_regex_parse,
-  .free = h_regex_free
+  .free = h_regex_free,
+  /* Name/param resolution functions */
+  .backend_short_name = "regex",
+  .backend_description = "Regular expression matcher (broken)",
+  .get_description_with_params = h_get_description_with_no_params,
+  .get_short_name_with_params = h_get_short_name_with_no_params
 };
 
 #ifndef NDEBUG
diff --git a/src/hammer.c b/src/hammer.c
index a88374d70dbaa9742b44a85385ca38e8ab40f8cc..e19336760917623ae870cab8f2d1b614c73c60e7 100644
--- a/src/hammer.c
+++ b/src/hammer.c
@@ -24,13 +24,15 @@
 #include "internal.h"
 #include "allocator.h"
 #include "parsers/parser_internal.h"
+#include "glue.h"
 
 static HParserBackendVTable *backends[PB_MAX + 1] = {
-  &h__packrat_backend_vtable,
-  &h__regex_backend_vtable,
-  &h__llk_backend_vtable,
-  &h__lalr_backend_vtable,
-  &h__glr_backend_vtable,
+  &h__missing_backend_vtable, /* For PB_INVALID */
+  &h__packrat_backend_vtable, /* For PB_PACKRAT */
+  &h__regex_backend_vtable, /* For PB_REGULAR */
+  &h__llk_backend_vtable, /* For PB_LLk */
+  &h__lalr_backend_vtable, /* For PB_LALR */
+  &h__glr_backend_vtable /* For PB_GLR */
 };
 
 
@@ -41,6 +43,529 @@ typedef struct {
   const HParser *p2;
 } HTwoParsers;
 
+/* Backend-related inquiries */
+
+int h_is_backend_available(HParserBackend backend) {
+  if (backend >= PB_MIN && backend <= PB_MAX) {
+    return (backends[backend] != &h__missing_backend_vtable) ? 1 : 0;
+  } else return 0;
+}
+
+HParserBackend h_get_default_backend(void) {
+  /* Call the inline version in internal.h */
+  return h_get_default_backend__int();
+}
+
+HParserBackendVTable * h_get_default_backend_vtable(void){
+  return h_get_default_backend_vtable__int();
+}
+
+/*
+ * Copy an HParserBackendWithParams, using the backend-supplied copy
+ * method.
+ */
+
+HParserBackendWithParams * h_copy_backend_with_params(
+    HParserBackendWithParams *be_with_params) {
+  return h_copy_backend_with_params__m(&system_allocator, be_with_params);
+}
+
+HParserBackendWithParams * h_copy_backend_with_params__m(HAllocator *mm__,
+    HParserBackendWithParams *be_with_params) {
+  HParserBackendWithParams *r = NULL;
+  int s;
+
+  if (be_with_params && be_with_params->backend >= PB_MIN &&
+      be_with_params->backend <= PB_MAX) {
+    if (mm__ == NULL) {
+      /* use the allocator from the input */
+      mm__ = be_with_params->mm__;
+    }
+
+    /* got an allocator? */
+    if (mm__) {
+      r = h_new(HParserBackendWithParams, 1);
+      if (r) {
+        r->mm__ = mm__;
+        r->requested_name = be_with_params->requested_name;
+        r->backend = be_with_params->backend;
+        r->backend_vtable = be_with_params->backend_vtable;
+        if (be_with_params->backend_vtable != NULL
+        		&& be_with_params->backend_vtable->copy_params) {
+          s = be_with_params->backend_vtable->copy_params(mm__,
+              &(r->params), be_with_params->params);
+          if (s != 0) {
+            /* copy_params() failed */
+            h_free(r);
+            r = NULL;
+          }
+        } else {
+          /* else just ignore it and set it to NULL */
+          r->params = NULL;
+        }
+      }
+      /* else fail out */
+    }
+    /* else give up and return NULL */
+  }
+  /* else return NULL, nothing to copy */
+
+  return r;
+}
+
+/*
+ * copy_params for backends where the parameter is actually a number cast to
+ * void *, such as LLk, LALR and GLR.  Use NULL for free_params in this
+ * case.
+ */
+
+int h_copy_numeric_param(HAllocator *mm__, void **out, void *in) {
+  int rv = 0;
+
+  if (out) *out = in; 
+  else rv = -1; /* error return, nowhere to write result */
+
+  return rv; 
+}
+
+/* Free this HParserBackendWithParams, and free the params too */
+
+void h_free_backend_with_params(HParserBackendWithParams *be_with_params) {
+  HAllocator *mm__ = &system_allocator;
+
+  if (be_with_params) {
+    if (be_with_params->mm__) mm__ = be_with_params->mm__;
+
+    if (h_is_backend_available(be_with_params->backend)) {
+      if (be_with_params->backend_vtable != NULL
+    		  && be_with_params->backend_vtable->free_params) {
+        be_with_params->backend_vtable->
+          free_params(be_with_params->mm__, be_with_params->params);
+      }
+    }
+
+    if(be_with_params->requested_name != NULL) {
+    	h_free(be_with_params->requested_name);
+    }
+
+    h_free(be_with_params);
+  }
+}
+
+/*
+ * Internal function to query name or descriptive text; the logic is
+ * the same in both cases.
+ */
+
+static const char * h_get_string_for_backend(
+    HParserBackend be, int description) {
+  /*
+   * For names, failure to resolve should return null - those are
+   * cases we can't look up by name either.  Human readable descriptions
+   * should have human-readable error strings
+   */
+  const char *text = description ? "this is a bug!" : NULL;
+  const char **ptr;
+
+  if (be >= PB_MIN && be <= PB_MAX) {
+    /* the invalid backend is special */
+    if (be == PB_INVALID) {
+      text = description ? "invalid backend" : NULL;
+    } else {
+      if (backends[be] != backends[PB_INVALID]) {
+        /* Point to the name or description */
+        ptr = description ?
+          &(backends[be]->backend_description) :
+          &(backends[be]->backend_short_name);
+        /* See if the backend knows how to describe itself */
+        if (*ptr != NULL) {
+          text = *ptr;
+        } else {
+          /* nope */
+          text = description ? "no description available" : NULL;
+        }
+      } else {
+        /*
+         * This is the case for backends which weren't compiled into this
+         * library.
+         */
+        text = description ? "unsupported backend" : NULL;
+      }
+    }
+  } else {
+    text = description ? "bad backend number" : NULL;
+  }
+
+  return text;
+}
+
+/* Return an identifying name for this backend */
+
+const char * h_get_name_for_backend(HParserBackend be) {
+  /*
+   * call h_get_string_for_backend(), 0 indicates name
+   * rather than description requested
+   */
+  return h_get_string_for_backend(be, 0);
+}
+
+/*
+ * Return some human-readable descriptive text for this backend
+ */
+
+const char * h_get_descriptive_text_for_backend(HParserBackend be) {
+  /*
+   * call h_get_string_for_backend(), 1 indicates a
+   * description is requested.
+   */
+  return h_get_string_for_backend(be, 1);
+}
+
+/*
+ * Internal function to query name or descriptive text for a backend with
+ * params; the logic is the same in both cases.
+ */
+
+static char * h_get_string_for_backend_with_params__m(HAllocator *mm__,
+    HParserBackendWithParams *be_with_params, int description) {
+  char *text = NULL;
+  const char *generic_text = NULL;
+  HParserBackend be = PB_INVALID;
+  char * (**text_getter_func)(HAllocator *mm__,
+                              HParserBackend be,
+                              void *params) = NULL;
+
+
+  if (mm__ == NULL || be_with_params == NULL) goto done;
+
+  /* check if we can compute custom text with params for this backend */
+  be = be_with_params->backend;
+  if (be >= PB_MIN && be <= PB_MAX && be != PB_INVALID &&
+      backends[be] != backends[PB_INVALID]) {
+    text_getter_func = description ?
+      &(backends[be]->get_description_with_params) :
+      &(backends[be]->get_short_name_with_params);
+
+    if (*text_getter_func != NULL) {
+        text = (*text_getter_func)(mm__, be,
+            be_with_params->params);
+    }
+  }
+
+  /* got it? */
+  if (!text) {
+    /* fall back to the generic descriptive text */
+    generic_text = h_get_string_for_backend(be, description);
+    if (generic_text) {
+      text = h_new(char, strlen(generic_text) + 1);
+      strncpy(text, generic_text, strlen(generic_text) + 1);
+    }
+  }
+
+ done:
+  return text;
+}
+/* Allocate and return an identifying name for this backend
+ * with parameters.  The caller is responsible for freeing the result.
+ */
+char * h_get_name_for_backend_with_params__m(HAllocator *mm__,
+    HParserBackendWithParams *be_with_params) {
+  return h_get_string_for_backend_with_params__m(mm__, be_with_params, 0);
+}
+
+char * h_get_name_for_backend_with_params(HParserBackendWithParams *be_with_params) {
+  return h_get_name_for_backend_with_params__m(&system_allocator, be_with_params);
+}
+/*
+ * Allocate and return some human-readable descriptive text for this backend
+ * with parameters.  The caller is responsible for freeing the result.
+ */
+
+char * h_get_descriptive_text_for_backend_with_params__m(HAllocator *mm__,
+    HParserBackendWithParams *be_with_params) {
+  return h_get_string_for_backend_with_params__m(mm__, be_with_params, 1);
+}
+
+char * h_get_descriptive_text_for_backend_with_params(
+    HParserBackendWithParams *be_with_params) {
+  return h_get_descriptive_text_for_backend_with_params__m(
+      &system_allocator, be_with_params);
+}
+
+/* Helpers for the above for backends with no params */
+
+static char * h_get_backend_text_with_no_params(HAllocator *mm__,
+                                                HParserBackend be,
+                                                int description) {
+  char *text = NULL;
+  const char *src = NULL;
+  int size;
+
+  if (!(mm__ != NULL && be != PB_INVALID &&
+        be >= PB_MIN && be <= PB_MAX)) goto done;
+
+  src = description ?
+    backends[be]->backend_description :
+    backends[be]->backend_short_name;
+
+  if (src) {
+    size = strlen(src) + 1;
+    text = h_new(char, size);
+    if (text) strncpy(text, src, size);
+  }
+
+ done:
+  return text;
+}
+
+/* Query a backend by short name; return PB_INVALID if no match */
+
+HParserBackend h_query_backend_by_name(const char *name) {
+  HParserBackend result = PB_INVALID, i;
+
+  if (name != NULL) {
+    /* Okay, iterate over the backends PB_MIN <= i <= PB_MAX and check */
+    i = PB_MIN;
+    do {
+      if (i != PB_INVALID) {
+        if (backends[i]->backend_short_name != NULL) {
+          if (strcmp(name, backends[i]->backend_short_name) == 0) {
+            result = i;
+          }
+        }
+      }
+      ++i;
+    } while (i <= PB_MAX && result == PB_INVALID);
+  }
+
+  return result;
+}
+
+char * h_get_description_with_no_params(HAllocator *mm__,
+                                        HParserBackend be, void *params) {
+  return h_get_backend_text_with_no_params(mm__, be, 1);
+}
+
+char * h_get_short_name_with_no_params(HAllocator *mm__,
+                                           HParserBackend be, void *params) {
+  return h_get_backend_text_with_no_params(mm__, be, 0);
+}
+
+
+/*TODO: possibly move to its own file?
+ * If so, move include of glue.h as well
+ * this parser is the only code using glue.h in here
+ */
+
+HParsedToken *act_backend_with_params(const HParseResult *p, void* user_data)
+{
+  backend_with_params_t *be_with_params = H_ALLOC(backend_with_params_t);
+
+  backend_name_t *name = H_FIELD(backend_name_t, 0);
+  be_with_params->name = *name;
+
+  HParsedToken * param_list = p->ast->seq->elements[1];
+  if (param_list->token_type == TT_SEQUENCE && param_list->seq->used == 3) {
+    backend_params_t *params = H_INDEX(backend_params_t, param_list, 1);
+    be_with_params->params = *params;
+  }
+
+  return H_MAKE(backend_with_params_t, (void*)be_with_params);
+}
+
+HParsedToken* act_backend_name(const HParseResult *p, void* user_data) {
+  backend_name_t *r = H_ALLOC(backend_name_t);
+
+  HParsedToken *flat = h_act_flatten(p, user_data);
+
+  r->len = h_seq_len(flat);
+  r->name = h_arena_malloc(p->arena, r->len + 1);
+  for (size_t i=0; i<r->len; ++i) {
+
+    r->name[i] = flat->seq->elements[i]->uint;
+  }
+  r->name[r->len] = 0;
+
+  return H_MAKE(backend_name_t, r);
+}
+
+HParsedToken *act_param(const HParseResult *p, void* user_data)
+{
+  backend_param_t *r = H_ALLOC(backend_param_t);
+
+  r->len = h_seq_len(p->ast);
+  r->param = h_arena_malloc(p->arena, r->len + 1);
+  for (size_t i=0; i<r->len; ++i)
+    r->param[i] = H_FIELD_UINT(i);
+  r->param[r->len] = 0;
+
+  return H_MAKE(backend_param_t, r);
+
+}
+
+HParsedToken *act_param_name(const HParseResult *p, void* user_data)
+{
+  backend_param_name_t *r = H_ALLOC(backend_param_name_t);
+
+  HParsedToken *flat = h_act_flatten(p, user_data);
+
+  r->len = h_seq_len(flat);
+  r->param_name = h_arena_malloc(p->arena, r->len + 1);
+  for (size_t i=0; i<r->len; ++i)
+    r->param_name[i] = flat->seq->elements[i]->uint;
+  r->param_name[r->len] = 0;
+
+  return H_MAKE(backend_param_name_t, r);
+
+}
+
+HParsedToken *act_param_with_name(const HParseResult *p, void* user_data)
+{
+  backend_param_with_name_t *backend_param_with_name = H_ALLOC(backend_param_with_name_t);
+
+  HParsedToken *tok = p->ast->seq->elements[0];
+
+  if (tok->token_type == TT_SEQUENCE) {
+    backend_param_name_t *param_name = H_INDEX(backend_param_name_t, tok, 0);
+    backend_param_with_name->param_name = *param_name;
+  }
+
+  backend_param_t *param = H_FIELD(backend_param_t, 1);
+  backend_param_with_name->param = *param;
+
+  return H_MAKE(backend_param_with_name_t, backend_param_with_name);
+}
+
+HParsedToken *act_backend_params(const HParseResult *p, void* user_data)
+{
+  HParsedToken *res = H_MAKE(backend_params_t, (void*)p->ast);
+
+  backend_params_t *bp = H_ALLOC(backend_params_t);
+
+  HParsedToken **fields = h_seq_elements(p->ast);
+
+  bp->len  = h_seq_len(p->ast);
+  bp->params = h_arena_malloc(p->arena, sizeof(backend_param_with_name_t)*bp->len);
+  for(size_t i=0; i<bp->len; i++) {
+    bp->params[i] = *H_INDEX(backend_param_with_name_t, p->ast, i);
+  }
+
+  return H_MAKE(backend_params_t, bp);
+
+  return res;
+}
+
+static HParser * build_hparser_rule(void) {
+
+  H_RULE(alpha, h_choice(h_ch_range('A', 'Z'), h_ch_range('a', 'z'), NULL));
+  H_RULE(digit, h_ch_range(0x30, 0x39));
+  H_RULE(sp, h_ch(' '));
+  H_RULE(eq, h_ch('='));
+  H_RULE(comma, h_ch(','));
+  H_RULE(sep, h_sequence(comma, h_optional(sp), NULL));
+  H_RULE(left_paren, h_ch('('));
+  H_RULE(right_paren, h_ch(')'));
+
+  H_RULE(alphas, h_many1(alpha));
+  H_RULE(digits, h_many1(digit));
+
+  H_ARULE(backend_name, h_sequence(
+      alphas,
+      h_many(h_choice(digit, alpha, NULL)),
+      NULL));
+  H_ARULE(param, h_choice(digits, alphas, NULL));
+  H_ARULE(param_name, h_sequence(alphas, h_optional(digits), NULL));
+  H_ARULE(param_with_name,
+      h_sequence(h_optional(h_sequence(
+        param_name, eq, NULL)), param, NULL));
+  H_ARULE(backend_params, h_sepBy(param_with_name, sep));
+
+  H_RULE(param_list, h_sequence(left_paren, backend_params, right_paren, NULL) );
+
+  H_ARULE(backend_with_params, h_sequence(backend_name, h_optional(param_list), NULL));
+
+  return backend_with_params;
+}
+
+
+
+static HParser * build_hparser(void) {
+
+  HParser *p = NULL;
+  int r;
+  p = build_hparser_rule();
+  r = h_compile(p, PB_PACKRAT, NULL);
+
+  if (r == 0) {
+    return p;
+  } else {
+    printf("Compiling parser failed\n");
+    return NULL;
+  }
+}
+
+HParserBackendWithParams * h_get_backend_with_params_by_name(const char *name_with_params) {
+  HAllocator *mm__ = &system_allocator;
+  HParserBackendWithParams *result = NULL;
+
+  HParser *parser = NULL;
+  HParseResult *r = NULL;
+
+  if(name_with_params != NULL) {
+    result = h_new(HParserBackendWithParams, 1);
+    if (result) {
+      result->mm__ = mm__;
+      result->requested_name = NULL;
+
+      parser = build_hparser();
+      if (!parser) {
+        return NULL;
+      }
+
+      r = h_parse(parser, (const uint8_t *)name_with_params, strlen(name_with_params));
+
+      if (r) {
+
+        backend_with_params_t *be_w_params = r->ast->user;
+
+
+        backend_name_t *name = &be_w_params->name;
+
+        backend_params_t *params = &be_w_params->params;
+
+        result->requested_name = h_new(char,name->len+1);
+        memset(result->requested_name, '\0', name->len+1);
+        strncpy(result->requested_name, (char*) name->name, name->len);
+
+        result->backend = h_query_backend_by_name(result->requested_name);
+
+
+
+        if (result->backend >= PB_MIN && result->backend <= PB_MAX)
+          result->backend_vtable =  backends[result->backend];
+        else
+          result->backend_vtable = backends[PB_INVALID];
+
+        /* use the backend supplied method to extract any params from the input */
+        result->params = NULL;
+        if(params->len > 0) {
+          if (result->backend_vtable->extract_params) {
+            result->backend_vtable->extract_params(result, be_w_params);
+          }
+        }
+        // free the parse result
+        h_parse_result_free(r);
+        r = NULL;
+
+        //free the memory used by the backend for parser data
+        //(TODO: code to completely free the memory used for this parser)
+        parser->backend_vtable->free(parser);
+      }
+    }
+  }
+
+  return result;
+}
 
 
 HParseResult* h_parse(const HParser* parser, const uint8_t* input, size_t length) {
@@ -58,8 +583,8 @@ HParseResult* h_parse__m(HAllocator* mm__, const HParser* parser, const uint8_t*
     .input = input,
     .last_chunk = true
   };
-  
-  return backends[parser->backend]->parse(mm__, parser, &input_stream);
+
+  return parser->backend_vtable->parse(mm__, parser, &input_stream);
 }
 
 void h_parse_result_free__m(HAllocator *alloc, HParseResult *result) {
@@ -86,15 +611,37 @@ bool h_not_regular(HRVMProg *prog, void *env) {
   return false;
 }
 
+int h_compile_for_backend_with_params(HParser* parser, HParserBackendWithParams* be_with_params){
+  HAllocator *mm__ = &system_allocator;
+
+  if (be_with_params) {
+    if (be_with_params->mm__) mm__ = be_with_params->mm__;
+  }
+
+  return h_compile_for_backend_with_params__m(mm__, parser, be_with_params);
+}
+
+int h_compile_for_backend_with_params__m(HAllocator* mm__, HParser* parser, HParserBackendWithParams* be_with_params) {
+  int ret = h_compile__m(mm__, parser, be_with_params->backend, be_with_params->params);
+  if (!ret)
+    be_with_params->backend_vtable = parser->backend_vtable;
+  return ret;
+}
+
 int h_compile(HParser* parser, HParserBackend backend, const void* params) {
   return h_compile__m(&system_allocator, parser, backend, params);
 }
 
 int h_compile__m(HAllocator* mm__, HParser* parser, HParserBackend backend, const void* params) {
-  backends[parser->backend]->free(parser);
+  if (parser->backend >= PB_MIN && parser->backend <= PB_MAX &&
+      backends[parser->backend]->free != NULL) {
+    backends[parser->backend]->free(parser);
+  }
   int ret = backends[backend]->compile(mm__, parser, params);
-  if (!ret)
+  if (!ret) {
     parser->backend = backend;
+    parser->backend_vtable = backends[backend];
+  }
   return ret;
 }
 
@@ -103,7 +650,7 @@ HSuspendedParser* h_parse_start(const HParser* parser) {
   return h_parse_start__m(&system_allocator, parser);
 }
 HSuspendedParser* h_parse_start__m(HAllocator* mm__, const HParser* parser) {
-  if(!backends[parser->backend]->parse_start)
+  if(!parser->backend_vtable || !parser->backend_vtable->parse_start)
     return NULL;
 
   // allocate and init suspended state
@@ -120,13 +667,13 @@ HSuspendedParser* h_parse_start__m(HAllocator* mm__, const HParser* parser) {
 
   // backend-specific initialization
   // should allocate s->backend_state
-  backends[parser->backend]->parse_start(s);
+  parser->backend_vtable->parse_start(s);
 
   return s;
 }
 
 bool h_parse_chunk(HSuspendedParser* s, const uint8_t* input, size_t length) {
-  assert(backends[s->parser->backend]->parse_chunk != NULL);
+  assert(s->parser->backend_vtable->parse_chunk != NULL);
 
   // no-op if parser is already done
   if(s->done)
@@ -145,7 +692,7 @@ bool h_parse_chunk(HSuspendedParser* s, const uint8_t* input, size_t length) {
   };
 
   // process chunk
-  s->done = backends[s->parser->backend]->parse_chunk(s, &input_stream);
+  s->done = s->parser->backend_vtable->parse_chunk(s, &input_stream);
   s->endianness = input_stream.endianness;
   s->pos += input_stream.index;
   s->bit_offset = input_stream.bit_offset;
@@ -154,8 +701,8 @@ bool h_parse_chunk(HSuspendedParser* s, const uint8_t* input, size_t length) {
 }
 
 HParseResult* h_parse_finish(HSuspendedParser* s) {
-  assert(backends[s->parser->backend]->parse_chunk != NULL);
-  assert(backends[s->parser->backend]->parse_finish != NULL);
+  assert(s->parser->backend_vtable->parse_chunk != NULL);
+  assert(s->parser->backend_vtable->parse_finish != NULL);
 
   HAllocator *mm__ = s->mm__;
 
@@ -172,12 +719,12 @@ HParseResult* h_parse_finish(HSuspendedParser* s) {
       .last_chunk = true
     };
 
-    s->done = backends[s->parser->backend]->parse_chunk(s, &empty);
+    s->done = s->parser->backend_vtable->parse_chunk(s, &empty);
     assert(s->done);
   }
 
   // extract result
-  HParseResult *r = backends[s->parser->backend]->parse_finish(s);
+  HParseResult *r = s->parser->backend_vtable->parse_finish(s);
   if(r)
     r->bit_length = s->pos * 8 + s->bit_offset;
 
diff --git a/src/hammer.h b/src/hammer.h
index 787af0b254a969226eeda985fc75d470796bd7cc..d84497d9a7f1ce302efea3cedea710f23834331e 100644
--- a/src/hammer.h
+++ b/src/hammer.h
@@ -41,7 +41,12 @@ typedef struct HParseState_ HParseState;
 
 typedef enum HParserBackend_ {
   PB_MIN = 0,
-  PB_PACKRAT = PB_MIN, // PB_MIN is always the default.
+  /*
+   * Have a backend that always fails to pass around "no such backend"
+   * indications
+   */
+  PB_INVALID = PB_MIN,
+  PB_PACKRAT,
   PB_REGULAR,
   PB_LLk,
   PB_LALR,
@@ -49,6 +54,26 @@ typedef enum HParserBackend_ {
   PB_MAX = PB_GLR
 } HParserBackend;
 
+typedef struct HParserBackendVTable_ HParserBackendVTable;
+
+typedef struct HParserBackendWithParams_ {
+  /* Name of backend extracted from a string if the choice of backend was specified in a call using a string  */
+  char *requested_name;
+  /* The backend (if backend is to be loaded from an external module set to invalid (?))*/
+  HParserBackend backend;
+  /* Backend vtable (TODO: use this instead of the enum so we can get rid of that) */
+  HParserBackendVTable * backend_vtable;
+  /*
+   * Backend-specific parameters - if this needs to be freed, the backend
+   * should provide a free_params method in its vtable; currently no backends
+   * do this - PB_PACKRAT and PB_REGULAR take no params, and PB_LLk, PB_LALR
+   * and PB_GLR take an integer cast to void *
+   */
+  void *params;
+  /* Allocator to use to free this (and the params if necessary) */
+  HAllocator *mm__;
+} HParserBackendWithParams;
+
 typedef enum HTokenType_ {
   // Before you change the explicit values of these, think of the poor bindings ;_;
   TT_INVALID = 0,
@@ -137,6 +162,7 @@ typedef struct HParserVtable_ HParserVtable;
 typedef struct HParser_ {
   const HParserVtable *vtable;
   HParserBackend backend;
+  HParserBackendVTable * backend_vtable;
   void* backend_data;
   void *env;
   HCFChoice *desugared; /* if the parser can be desugared, its desugared form */
@@ -175,6 +201,53 @@ typedef bool (*HPredicate)(HParseResult *p, void* user_data);
  */
 typedef HParser* (*HContinuation)(HAllocator *mm__, const HParsedToken *x, void *env);
 
+/*
+ * For parser used when extracting name and params for backend by name
+ * TODO: possibly move to its own file?
+ */
+
+enum BackendTokenType_ {
+  TT_backend_with_params_t = TT_USER,
+  TT_backend_name_t,
+  TT_backend_param_t,
+  TT_backend_param_name_t,
+  TT_backend_param_with_name_t,
+  TT_backend_params_t
+};
+
+typedef struct backend_param {
+  size_t len;
+  uint8_t *param;
+  uint8_t *param_name;
+} backend_param_t;
+
+typedef struct backend_param_name {
+  size_t len;
+  uint8_t *param_name;
+  size_t param_id;
+} backend_param_name_t;
+
+typedef struct backend_param_with_name {
+  backend_param_name_t param_name;
+  backend_param_t param;
+} backend_param_with_name_t;
+
+typedef struct {
+  uint8_t *name;
+  size_t len;
+} backend_name_t;
+
+typedef struct backend_params {
+  backend_param_with_name_t *params;
+  size_t len;
+} backend_params_t;
+
+typedef struct backend_with_params {
+  backend_name_t name;
+  backend_params_t params;
+} backend_with_params_t;
+
+
 // {{{ Stuff for benchmarking
 typedef struct HParserTestcase_ {
   unsigned char* input;
@@ -262,6 +335,89 @@ typedef struct HBenchmarkResults_ {
 #endif // SWIG
 // }}}
 
+/**
+ * Ask if this backend is available
+ */
+
+int h_is_backend_available(HParserBackend backend);
+
+/**
+ * Ask what the default backend is (currently always PB_PACKRAT)
+ */
+
+HParserBackend h_get_default_backend(void);
+
+HParserBackendVTable * h_get_default_backend_vtable(void);
+
+/**
+ * Copy a backend+params, using the backend-supplied copy method; the
+ * allocator used is the one passed in, or call the __m version with
+ * a NULL allocator to use the one from the source HParserBackendWithParams
+ */
+
+HAMMER_FN_DECL(HParserBackendWithParams *, h_copy_backend_with_params,
+               HParserBackendWithParams *be_with_params);
+
+/**
+ * Free a backend+params
+ */
+
+void h_free_backend_with_params(HParserBackendWithParams *be_with_params);
+
+/**
+ * Get a name string for a backend; this is constant per backend and so
+ * need not be freed; it will resolve to the backend under
+ * h_get_backend_by_name().
+ */
+
+const char * h_get_name_for_backend(HParserBackend be);
+
+/**
+ * Get a name string for a backend with parameters; it is the caller's
+ * responsibility to free it later.  This will resolve to the same
+ * backend and parameters under h_get_backend_with_params_by_name().
+ */
+
+HAMMER_FN_DECL(char *, h_get_name_for_backend_with_params,
+               HParserBackendWithParams *be_with_params);
+
+/**
+ * Get a human-readable descriptive string for a backend; this is constant
+ * per backend and so need not be freed.
+ */
+
+const char * h_get_descriptive_text_for_backend(HParserBackend be);
+
+/**
+ * Get a human-readable descriptive string for a backend with params; it is
+ * the caller's responsibility to free it later.  Sorry, but it's allowed
+ * to depend on the params and putting keeping the buffer elsewhere and
+ * replacing it on the next call wouldn't be thread-safe.
+ */
+
+HAMMER_FN_DECL(char *, h_get_descriptive_text_for_backend_with_params,
+               HParserBackendWithParams *be_with_params);
+
+/**
+ * Look up an HParserBackend by name; this should round-trip with
+ * h_get_name_for_backend().
+ */
+
+HParserBackend h_query_backend_by_name(const char *name);
+
+/**
+ * Get a Hammer Backend with params from a string of the form
+ * backend_name(params) for example "lalr(1)".
+ *
+ * If the backend is one of the existing backends in the HBackend enum,
+ * HBackend will be populated in the result.
+ *
+ * Otherwise the result will save the name for use in attempts later at
+ * loading the named module.
+ *
+ */
+
+HAMMER_FN_DECL(HParserBackendWithParams *, h_get_backend_with_params_by_name, const char *name_with_params);
 
 /**
  * Top-level function to call a parser that has been built over some
@@ -795,6 +951,8 @@ void h_pprintln(FILE* stream, const HParsedToken* tok);
  *
  * Consult each backend for details.
  */
+HAMMER_FN_DECL(int, h_compile_for_backend_with_params, HParser* parser, HParserBackendWithParams *be_with_params);
+
 HAMMER_FN_DECL(int, h_compile, HParser* parser, HParserBackend backend, const void* params);
 
 /**
diff --git a/src/internal.h b/src/internal.h
index 203e34126f225837a6d6b0e77d1267356c5b7998..a53c5914ad9ee2d5996dfe6c2d76aa51242ef43b 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -240,6 +240,33 @@ typedef struct HParserBackendVTable_ {
   HParseResult *(*parse_finish)(HSuspendedParser *s);
     // parse_finish must free s->backend_state.
     // parse_finish will not be called before parse_chunk reports done.
+
+  /* The backend knows how to free its params */
+  void (*free_params)(HAllocator *mm__, void *p);
+  /*
+   * ..and how to copy them
+   *
+   * Since the backend params need not actually be an allocated object,
+   * (and in fact no current backends use this, although it is permissible),
+   * but might (as in PB_GLR) be some numeric constant cast to void * which
+   * copy_params() should just pass through, we can't use returning NULL
+   * to signal allocation failure.  Hence, passing the result out in a
+   * void ** and returning a status code (0 indicates success).
+   */
+  int (*copy_params)(HAllocator *mm__, void **out, void *in);
+
+  /* Description/name handling */
+  const char *backend_short_name;
+  const char *backend_description;
+  char * (*get_description_with_params)(HAllocator *mm__,
+                                        HParserBackend be,
+                                        void *params);
+  char * (*get_short_name_with_params)(HAllocator *mm__,
+                                       HParserBackend be,
+                                       void *params);
+
+  /* extract params from the input string */
+  int (*extract_params)(HParserBackendWithParams * be_with_params, backend_with_params_t *be_with_params_t);
 } HParserBackendVTable;
 
 
@@ -320,6 +347,7 @@ struct HBitWriter_ {
 
 
 // Backends {{{
+extern HParserBackendVTable h__missing_backend_vtable;
 extern HParserBackendVTable h__packrat_backend_vtable;
 extern HParserBackendVTable h__llk_backend_vtable;
 extern HParserBackendVTable h__lalr_backend_vtable;
@@ -328,6 +356,16 @@ extern HParserBackendVTable h__glr_backend_vtable;
 
 // TODO(thequux): Set symbol visibility for these functions so that they aren't exported.
 
+/*
+ * Helper functions for backend with params names and descriptions for
+ * backends which take no params.
+ */
+
+char * h_get_description_with_no_params(HAllocator *mm__,
+                                        HParserBackend be, void *params);
+char * h_get_short_name_with_no_params(HAllocator *mm__,
+                                       HParserBackend be, void *params);
+
 int64_t h_read_bits(HInputStream* state, int count, char signed_p);
 void h_skip_bits(HInputStream* state, size_t count);
 void h_seek_bits(HInputStream* state, size_t pos);
@@ -345,12 +383,38 @@ static inline size_t h_input_stream_length(HInputStream *state) {
 HParseResult* h_do_parse(const HParser* parser, HParseState *state);
 void put_cached(HParseState *ps, const HParser *p, HParseResult *cached);
 
+/*
+ * Inline this for benefit of h_new_parser() below, then make
+ * the API h_get_default_backend() call it.
+ */
+static inline HParserBackend h_get_default_backend__int(void) {
+  return PB_PACKRAT;
+}
+
+static inline HParserBackendVTable * h_get_default_backend_vtable__int(void) {
+  return &h__packrat_backend_vtable;
+}
+
+static inline HParserBackendVTable * h_get_missing_backend_vtable__int(void) {
+  return &h__missing_backend_vtable;
+}
+
+/* copy_params for backends where the parameter is not actually a pointer */
+
+int h_copy_numeric_param(HAllocator *mm__, void **out, void *in);
+
 static inline
 HParser *h_new_parser(HAllocator *mm__, const HParserVtable *vt, void *env) {
   HParser *p = h_new(HParser, 1);
   memset(p, 0, sizeof(HParser));
   p->vtable = vt;
   p->env = env;
+  /*
+   * Current limitation: if we specify backends solely by HParserBackend, we
+   * can't set a default backend that requires any parameters to h_compile()
+   */
+  p->backend = h_get_default_backend__int();
+  p->backend_vtable = h_get_default_backend_vtable__int();
   return p;
 }
 
diff --git a/src/parsers/choice.c b/src/parsers/choice.c
index 20bb7b79e6c5a214cf8653fc782968028e6692d9..882ecbf85162c6ad11493141a0a406f9dd5c2a29 100644
--- a/src/parsers/choice.c
+++ b/src/parsers/choice.c
@@ -165,7 +165,8 @@ HParser* h_choice__ma(HAllocator* mm__, void *args[]) {
   HParser *ret = h_new(HParser, 1);
   ret->vtable = &choice_vt; 
   ret->env = (void*)s;
-  ret->backend = PB_MIN;
+  ret->backend = h_get_default_backend();
+  ret->backend_vtable = h_get_default_backend_vtable();
   ret->desugared = NULL;
   return ret;
 }
diff --git a/src/parsers/epsilon.c b/src/parsers/epsilon.c
index be614489cecfec6f30e4c2bfdd18c323be894446..849015a014bbd471f92047e8268d4b19ce346561 100644
--- a/src/parsers/epsilon.c
+++ b/src/parsers/epsilon.c
@@ -29,7 +29,8 @@ HParser* h_epsilon_p__m(HAllocator* mm__) {
   HParser *epsilon_p = h_new(HParser, 1);
   epsilon_p->desugared = NULL;
   epsilon_p->backend_data = NULL;
-  epsilon_p->backend = 0;
+  epsilon_p->backend = h_get_default_backend();
+  epsilon_p->backend_vtable = h_get_default_backend_vtable();
   epsilon_p->vtable = &epsilon_vt;
   return epsilon_p;
 }
diff --git a/src/parsers/permutation.c b/src/parsers/permutation.c
index 07dcaa71267d8b455dc0be00e0cdf7c0cbc542e6..f6f170a6093001d0d0c9941c4885e237445ddee2 100644
--- a/src/parsers/permutation.c
+++ b/src/parsers/permutation.c
@@ -181,7 +181,8 @@ HParser* h_permutation__ma(HAllocator* mm__, void *args[]) {
   HParser *ret = h_new(HParser, 1);
   ret->vtable = &permutation_vt; 
   ret->env = (void*)s;
-  ret->backend = PB_MIN;
+  ret->backend = h_get_default_backend();
+  ret->backend_vtable = h_get_default_backend_vtable();
   ret->desugared = NULL;
   return ret;
 }
diff --git a/src/parsers/sequence.c b/src/parsers/sequence.c
index 2e7b4bc7286ec0ac32af012126e4289226297be0..e532fd43529e327383c5fdb691381e9fe6610c30 100644
--- a/src/parsers/sequence.c
+++ b/src/parsers/sequence.c
@@ -174,7 +174,8 @@ HParser* h_sequence__ma(HAllocator* mm__, void *args[]) {
   HParser *ret = h_new(HParser, 1);
   ret->vtable = &sequence_vt; 
   ret->env = (void*)s; 
-  ret->backend = PB_MIN;
+  ret->backend = h_get_default_backend();
+  ret->backend_vtable = h_get_default_backend_vtable();
   ret->desugared = NULL;
   return ret;
 }
diff --git a/src/t_names.c b/src/t_names.c
new file mode 100644
index 0000000000000000000000000000000000000000..85fbd92ed1dcabcfe4cac98d8694472008dc9e87
--- /dev/null
+++ b/src/t_names.c
@@ -0,0 +1,197 @@
+#include <glib.h>
+#include <string.h>
+#include "test_suite.h"
+#include "hammer.h"
+
+static void test_tt_backend_description(void) {
+  const char *desc = NULL;
+
+  /* Check that we get some */
+  desc = h_get_descriptive_text_for_backend(PB_PACKRAT);
+  g_check_cmp_ptr(desc, !=, NULL);
+  desc = h_get_descriptive_text_for_backend(PB_REGULAR);
+  g_check_cmp_ptr(desc, !=, NULL);
+  desc = h_get_descriptive_text_for_backend(PB_LLk);
+  g_check_cmp_ptr(desc, !=, NULL);
+  desc = h_get_descriptive_text_for_backend(PB_LALR);
+  g_check_cmp_ptr(desc, !=, NULL);
+  desc = h_get_descriptive_text_for_backend(PB_GLR);
+  g_check_cmp_ptr(desc, !=, NULL);
+}
+
+/* Reference backend names */
+static const char * packrat_name = "packrat";
+static const char * regular_name = "regex";
+static const char * llk_name = "llk";
+static const char * lalr_name = "lalr";
+static const char * glr_name = "glr";
+
+static void test_tt_backend_short_name(void) {
+  const char *name = NULL;
+
+  name = h_get_name_for_backend(PB_PACKRAT);
+  g_check_maybe_string_eq(name, packrat_name);
+  name = h_get_name_for_backend(PB_REGULAR);
+  g_check_maybe_string_eq(name, regular_name);
+  name = h_get_name_for_backend(PB_LLk);
+  g_check_maybe_string_eq(name, llk_name);
+  name = h_get_name_for_backend(PB_LALR);
+  g_check_maybe_string_eq(name, lalr_name);
+  name = h_get_name_for_backend(PB_GLR);
+  g_check_maybe_string_eq(name, glr_name);
+}
+
+static void test_tt_query_backend_by_name(void) {
+  HParserBackend be;
+
+  be = h_query_backend_by_name(packrat_name);
+  g_check_inttype("%d", HParserBackend, be, ==, PB_PACKRAT);
+  be = h_query_backend_by_name(regular_name);
+  g_check_inttype("%d", HParserBackend, be, ==, PB_REGULAR);
+
+}
+
+static void test_tt_get_backend_with_params_by_name(void) {
+  HParserBackendWithParams * be_w_p = NULL;
+
+  /*requests to use default params, or for backends without params*/
+  be_w_p = h_get_backend_with_params_by_name(packrat_name);
+  g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_PACKRAT);
+  g_check_maybe_string_eq(be_w_p->requested_name, packrat_name);
+  h_free_backend_with_params(be_w_p);
+
+  be_w_p = h_get_backend_with_params_by_name(glr_name);
+  g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_GLR);
+  g_check_maybe_string_eq(be_w_p->requested_name, glr_name);
+  h_free_backend_with_params(be_w_p);
+
+  /* request with params */
+  be_w_p = h_get_backend_with_params_by_name("lalr(1)");
+  g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_LALR);
+  g_check_maybe_string_eq(be_w_p->requested_name, lalr_name);
+  g_check_cmp_size((uintptr_t)be_w_p->params, ==, 1);
+  h_free_backend_with_params(be_w_p);
+
+  /*request for default params - alternative possible style */
+  be_w_p = h_get_backend_with_params_by_name("glr()");
+  g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_GLR);
+  g_check_maybe_string_eq(be_w_p->requested_name, glr_name);;
+  h_free_backend_with_params(be_w_p);
+
+  /*request for a backend not in the enum of backends included in hammer */
+  be_w_p = h_get_backend_with_params_by_name("llvm()");
+  g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_INVALID);
+  g_check_maybe_string_eq(be_w_p->requested_name, "llvm");
+  h_free_backend_with_params(be_w_p);
+
+  be_w_p = h_get_backend_with_params_by_name("packrat(0)");
+  g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_PACKRAT);
+  g_check_maybe_string_eq(be_w_p->requested_name, packrat_name);
+  h_free_backend_with_params(be_w_p);
+
+  be_w_p = h_get_backend_with_params_by_name("glr(1,2)");
+  g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_GLR);
+  g_check_maybe_string_eq(be_w_p->requested_name, glr_name);
+  g_check_cmp_size((uintptr_t)be_w_p->params, ==, 1);
+  h_free_backend_with_params(be_w_p);
+
+  be_w_p = h_get_backend_with_params_by_name("glr(1,vnvnvn)");
+  g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_GLR);
+  g_check_maybe_string_eq(be_w_p->requested_name, glr_name);
+  g_check_cmp_size((uintptr_t)be_w_p->params, ==, 1);
+  h_free_backend_with_params(be_w_p);
+
+  be_w_p = h_get_backend_with_params_by_name("lalr(1, 2)");
+  g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_LALR);
+  g_check_maybe_string_eq(be_w_p->requested_name, lalr_name);
+  g_check_cmp_size((uintptr_t)be_w_p->params, ==, 1);
+  h_free_backend_with_params(be_w_p);
+
+  be_w_p = h_get_backend_with_params_by_name("llk(k=2)");
+  g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_LLk);
+  g_check_maybe_string_eq(be_w_p->requested_name, llk_name);
+  g_check_cmp_size((uintptr_t)be_w_p->params, ==, 2);
+  h_free_backend_with_params(be_w_p);
+
+}
+
+static void test_tt_h_get_descriptive_text_for_backend_with_params(void){
+  HAllocator *mm__ = &system_allocator;
+  HParserBackendWithParams *be_with_params = h_get_backend_with_params_by_name("llk(1)");
+  char * descr = h_get_descriptive_text_for_backend_with_params(be_with_params);
+  g_check_maybe_string_eq(descr, "LL(1) parser backend");
+  h_free(descr);
+  h_free_backend_with_params(be_with_params);
+  be_with_params = h_get_backend_with_params_by_name("lalr(1)");
+  descr = h_get_descriptive_text_for_backend_with_params(be_with_params);
+  g_check_maybe_string_eq(descr, "LALR(1) parser backend");
+  h_free(descr);
+  h_free_backend_with_params(be_with_params);
+  be_with_params = h_get_backend_with_params_by_name("glr(2)");
+  descr = h_get_descriptive_text_for_backend_with_params(be_with_params);
+  g_check_maybe_string_eq(descr, "GLR(2) parser backend");
+  h_free(descr);
+  h_free_backend_with_params(be_with_params);
+  be_with_params = h_get_backend_with_params_by_name("glr()");
+  descr = h_get_descriptive_text_for_backend_with_params(be_with_params);
+  g_check_maybe_string_eq(descr, "GLR(k) parser backend (default k is 1)");
+  h_free(descr);
+  h_free_backend_with_params(be_with_params);
+}
+
+static void test_tt_h_get_name_for_backend_with_params(void){
+  HAllocator *mm__ = &system_allocator;
+  HParserBackendWithParams *be_with_params = h_get_backend_with_params_by_name("llk(1)");
+  char * descr = h_get_name_for_backend_with_params(be_with_params);
+  g_check_maybe_string_eq(descr, "LL(1)");
+  h_free(descr);
+  h_free_backend_with_params(be_with_params);
+  be_with_params = h_get_backend_with_params_by_name("lalr(2)");
+  descr = h_get_name_for_backend_with_params(be_with_params);
+  g_check_maybe_string_eq(descr, "LALR(2)");
+  h_free(descr);
+  h_free_backend_with_params(be_with_params);
+  be_with_params = h_get_backend_with_params_by_name("glr(1)");
+  descr = h_get_name_for_backend_with_params(be_with_params);
+  g_check_maybe_string_eq(descr, "GLR(1)");
+  h_free(descr);
+  h_free_backend_with_params(be_with_params);
+  be_with_params = h_get_backend_with_params_by_name("glr");
+  descr = h_get_name_for_backend_with_params(be_with_params);
+  g_check_maybe_string_eq(descr, "GLR(k)");
+  h_free(descr);
+  h_free_backend_with_params(be_with_params);
+}
+
+/* test that we can request a backend with params from character
+ * and compile a parser using it */
+static void test_tt_h_compile_for_backend_with_params(void) {
+  HParserBackendWithParams * be_w_p = NULL;
+
+  be_w_p = h_get_backend_with_params_by_name("llk(1)");
+  g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_LLk);
+
+  HParser *p = h_many(h_sequence(h_ch('A'), h_ch('B'), NULL));
+
+  int r = h_compile_for_backend_with_params(p, be_w_p);
+
+  h_free_backend_with_params(be_w_p);
+  be_w_p = NULL;
+
+  if (r != 0) {
+    g_test_message("Compile failed");
+    g_test_fail();
+  }
+}
+
+void register_names_tests(void) {
+  g_test_add_func("/core/names/tt_backend_short_name", test_tt_backend_short_name);
+  g_test_add_func("/core/names/tt_backend_description", test_tt_backend_description);
+  g_test_add_func("/core/names/tt_query_backend_by_name", test_tt_query_backend_by_name);
+  g_test_add_func("/core/names/tt_get_backend_with_params_by_name", test_tt_get_backend_with_params_by_name);
+  g_test_add_func("/core/names/tt_test_tt_h_get_descriptive_text_for_backend_with_params",
+                  test_tt_h_get_descriptive_text_for_backend_with_params);
+  g_test_add_func("/core/names/test_tt_h_get_name_for_backend_with_params",
+                  test_tt_h_get_name_for_backend_with_params);
+  g_test_add_func("/core/names/tt_h_compile_for_backend_with_params", test_tt_h_compile_for_backend_with_params);
+ }
diff --git a/src/test_suite.c b/src/test_suite.c
index f569644295f4d12efd369b76a2a994a9fdc332f3..8bb303686410f2b4588846fc6bb13d1869a67981 100644
--- a/src/test_suite.c
+++ b/src/test_suite.c
@@ -25,6 +25,7 @@ extern void register_parser_tests();
 extern void register_grammar_tests();
 extern void register_misc_tests();
 extern void register_mm_tests();
+extern void register_names_tests();
 extern void register_benchmark_tests();
 extern void register_regression_tests();
 
@@ -38,6 +39,7 @@ int main(int argc, char** argv) {
   register_grammar_tests();
   register_misc_tests();
   register_mm_tests();
+  register_names_tests();
   register_regression_tests();
   if (g_test_slow() || g_test_perf())
     register_benchmark_tests();
diff --git a/src/test_suite.h b/src/test_suite.h
index dad0621db148431b5953a1291593d22d2a11cd55..93f3e19de01db05e8eb8aa3bc51371cee7a5a635 100644
--- a/src/test_suite.h
+++ b/src/test_suite.h
@@ -35,6 +35,18 @@
     }									\
   } while(0)
 
+/* Comparison for ptr types; only == and != will work */
+#define g_check_cmp_ptr(p1, op, p2) do { \
+    const void *_p1 = (p1); \
+    const void *_p2 = (p2); \
+    if (!(_p1 op _p2)) { \
+      g_test_message("Check failed: (%s): (%p %s %p)", \
+        #p1 " " #op " " #p2, \
+        _p1, #op, _p2); \
+      g_test_fail(); \
+    } \
+  } while (0);
+
 #define g_check_bytes(len, n1, op, n2) do {	\
     const uint8_t *_n1 = (n1);			\
     const uint8_t *_n2 = (n2);			\
@@ -56,6 +68,32 @@
     }							\
   } while(0)
 
+#define g_check_maybe_string_eq(n1, n2) do { \
+    const char *_n1 = (n1); \
+    const char *_n2 = (n2); \
+    if (_n1 != _n2 && _n1 != NULL && _n2 != NULL) { \
+      if (!(strcmp(_n1, _n2) == 0)) { \
+        g_test_message("Check failed: (%s) (\"%s\" == \"%s\")",	\
+		                   #n1 " == " #n2, _n1, _n2); \
+        g_test_fail(); \
+      } \
+    } else { \
+      if (_n1 != NULL || _n2 != NULL) { \
+        if (_n1 != NULL && _n2 == NULL) { \
+          g_test_message("Check failed: (%s) (\"%s\" == NULL)", \
+                         #n1 " == " #n2, _n1); \
+          g_test_fail(); \
+        } else if (_n1 == NULL && _n2 != NULL) { \
+          g_test_message("Check failed: (%s) (NULL == \"%s\")", \
+                         #n1 " == " #n2, _n2); \
+          g_test_fail(); \
+        } \
+        /* else both are not-NULL, but point to the same string - succeed */ \
+      } \
+      /* else both are NULL, succeed */ \
+    } \
+  } while(0)
+
 #define g_check_regular(lang) do {			\
     if (!lang->isValidRegular(lang->env)) {		\
       g_test_message("Language is not regular");	\
@@ -455,7 +493,6 @@
 #define g_check_cmp_uint32(n1, op, n2) g_check_inttype("%u", uint32_t, n1, op, n2)
 #define g_check_cmp_uint64(n1, op, n2) g_check_inttype("%" PRIu64, uint64_t, n1, op, n2)
 #define g_check_cmp_size(n1, op, n2) g_check_inttype("%zu", size_t, n1, op, n2)
-#define g_check_cmp_ptr(n1, op, n2) g_check_inttype("%p", void *, n1, op, n2)
 #define g_check_cmpfloat(n1, op, n2) g_check_inttype("%g", float, n1, op, n2)
 #define g_check_cmpdouble(n1, op, n2) g_check_inttype("%g", double, n1, op, n2)