diff --git a/src/Makefile b/src/Makefile
index 7fac881ae0882fb0d436005d65dd63125914af4c..1a2bff3530ed897f50814fdfae949ff7d8ef635b 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -28,6 +28,7 @@ PARSERS := \
 BACKENDS := \
 	packrat \
 	llk \
+	lalr \
 	regex
 
 HAMMER_PARTS := \
diff --git a/src/backends/lalr.c b/src/backends/lalr.c
new file mode 100644
index 0000000000000000000000000000000000000000..22cd3890ded111238ba9c3bbff5b1fbebf6d5548
--- /dev/null
+++ b/src/backends/lalr.c
@@ -0,0 +1,86 @@
+#include <assert.h>
+#include "../internal.h"
+#include "../cfgrammar.h"
+#include "../parsers/parser_internal.h"
+
+
+
+void h_lalr_free(HParser *parser)
+{
+  // XXX free data structures
+  parser->backend_data = NULL;
+  parser->backend = PB_PACKRAT;
+}
+
+
+/* LALR table generation */
+
+int h_lalr_compile(HAllocator* mm__, HParser* parser, const void* params)
+{
+  return -1;
+}
+
+
+/* LR driver */
+
+HParseResult *h_lr_parse(HAllocator* mm__, const HParser* parser, HInputStream* stream)
+{
+  return NULL;
+}
+
+
+
+
+HParserBackendVTable h__lalr_backend_vtable = {
+  .compile = h_lalr_compile,
+  .parse = h_lr_parse,
+  .free = h_lalr_free
+};
+
+
+
+
+// dummy!
+int test_lalr(void)
+{
+  /* for k=2:
+
+     S -> A | B
+     A -> X Y a
+     B -> Y b
+     X -> x | ''
+     Y -> y         -- for k=3 use "yy"
+  */
+
+  // XXX make LALR example
+  HParser *X = h_optional(h_ch('x'));
+  HParser *Y = h_sequence(h_ch('y'), h_ch('y'), NULL);
+  HParser *A = h_sequence(X, Y, h_ch('a'), NULL);
+  HParser *B = h_sequence(Y, h_ch('b'), NULL);
+  HParser *p = h_choice(A, B, NULL);
+
+  HCFGrammar *g = h_cfgrammar(&system_allocator, p);
+
+  if(g == NULL) {
+    fprintf(stderr, "h_cfgrammar failed\n");
+    return 1;
+  }
+
+  h_pprint_grammar(stdout, g, 0);
+  // print states of the LR(0) automaton
+  // print LALR(1) table
+
+  if(h_compile(p, PB_LALR, NULL)) {
+    fprintf(stderr, "does not compile\n");
+    return 2;
+  }
+
+
+  HParseResult *res = h_parse(p, (uint8_t *)"xyya", 4);
+  if(res)
+    h_pprint(stdout, res->ast, 0, 2);
+  else
+    printf("no parse\n");
+
+  return 0;
+}
diff --git a/src/hammer.c b/src/hammer.c
index 5f94142908f48f86a0dde79ccd376c2625063635..7d5b4e90b2edd224f6f28a57d0b65bacf2de94b7 100644
--- a/src/hammer.c
+++ b/src/hammer.c
@@ -30,6 +30,7 @@ static HParserBackendVTable *backends[PB_MAX + 1] = {
   &h__packrat_backend_vtable,
   &h__regex_backend_vtable,
   &h__llk_backend_vtable,
+  &h__lalr_backend_vtable,
 };
 
 
diff --git a/src/hammer.h b/src/hammer.h
index 455684cc92edbfbf9b9352625e373ca408f61261..a5ebcfff640bd33ba4459b1b59ea106033cb5eb2 100644
--- a/src/hammer.h
+++ b/src/hammer.h
@@ -34,11 +34,11 @@ typedef struct HParseState_ HParseState;
 typedef enum HParserBackend_ {
   PB_MIN = 0,
   PB_PACKRAT = PB_MIN, // PB_MIN is always the default.
-  PB_REGULAR,	// 
-  PB_LLk,	//
-  PB_LALR,	// Not Implemented
+  PB_REGULAR,
+  PB_LLk,
+  PB_LALR,
   PB_GLR,	// Not Implemented
-  PB_MAX = PB_LLk
+  PB_MAX = PB_LALR
 } HParserBackend;
 
 typedef enum HTokenType_ {
diff --git a/src/internal.h b/src/internal.h
index 926bf02a6e54da52cf193443a12d0e3c7547ef35..01861f5e70189dedb3313a0db28f1ddf02f3c9a1 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -219,6 +219,7 @@ struct HBitWriter_ {
 // Backends {{{
 extern HParserBackendVTable h__packrat_backend_vtable;
 extern HParserBackendVTable h__llk_backend_vtable;
+extern HParserBackendVTable h__lalr_backend_vtable;
 // }}}
 
 // TODO(thequux): Set symbol visibility for these functions so that they aren't exported.