diff --git a/src/backends/lalr.c b/src/backends/lalr.c
index ec7a5b5f39b2e8bd47f7ee3574643bed8fcf8534..975735a16dab016576c625d5bb541d8028b776f7 100644
--- a/src/backends/lalr.c
+++ b/src/backends/lalr.c
@@ -346,7 +346,10 @@ void h_lalr_free(HParser *parser)
 HParserBackendVTable h__lalr_backend_vtable = {
   .compile = h_lalr_compile,
   .parse = h_lr_parse,
-  .free = h_lalr_free
+  .free = h_lalr_free,
+  .parse_start = h_lr_parse_start,
+  .parse_chunk = h_lr_parse_chunk,
+  .parse_finish = h_lr_parse_finish
 };
 
 
diff --git a/src/backends/lr.c b/src/backends/lr.c
index 161bbf96a11a1d967137c3b91a1cbf7a55b63546..3f99eb513ad59a0ba0bd59e886ed67413f01f08e 100644
--- a/src/backends/lr.c
+++ b/src/backends/lr.c
@@ -199,15 +199,14 @@ bool h_lrtable_row_empty(const HLRTable *table, size_t i)
 
 /* LR driver */
 
-HLREngine *h_lrengine_new(HArena *arena, HArena *tarena, const HLRTable *table,
-                          const HInputStream *stream)
+static
+HLREngine *h_lrengine_new_(HArena *arena, HArena *tarena, const HLRTable *table)
 {
   HLREngine *engine = h_arena_malloc(tarena, sizeof(HLREngine));
 
   engine->table = table;
   engine->state = 0;
   engine->stack = h_slist_new(tarena);
-  engine->input = *stream;
   engine->merged[0] = NULL;
   engine->merged[1] = NULL;
   engine->arena = arena;
@@ -216,6 +215,14 @@ HLREngine *h_lrengine_new(HArena *arena, HArena *tarena, const HLRTable *table,
   return engine;
 }
 
+HLREngine *h_lrengine_new(HArena *arena, HArena *tarena, const HLRTable *table,
+                          const HInputStream *stream)
+{
+  HLREngine *engine = h_lrengine_new_(arena, tarena, table);
+  engine->input = *stream;
+  return engine;
+}
+
 static const HLRAction *
 terminal_lookup(const HLREngine *engine, const HInputStream *stream)
 {
@@ -352,7 +359,7 @@ HParseResult *h_lrengine_result(HLREngine *engine)
     assert(!h_slist_empty(engine->stack));
     HParsedToken *tok = engine->stack->head->elem;
     HParseResult *res =  make_result(engine->arena, tok);
-    res->bit_length = engine->input.index * 8;
+    res->bit_length = (engine->input.pos + engine->input.index) * 8;
     return res;
   } else {
     return NULL;
@@ -379,7 +386,53 @@ HParseResult *h_lr_parse(HAllocator* mm__, const HParser* parser, HInputStream*
   return result;
 }
 
+void h_lr_parse_start(HSuspendedParser *s)
+{
+  HLRTable *table = s->parser->backend_data;
+  assert(table != NULL);
+
+  HArena *arena  = h_new_arena(s->mm__, 0); // will hold the results
+  HArena *tarena = h_new_arena(s->mm__, 0); // tmp, deleted after parse
+  HLREngine *engine = h_lrengine_new_(arena, tarena, table);
 
+  s->backend_state = engine;
+}
+
+bool h_lr_parse_chunk(HSuspendedParser* s, HInputStream *stream)
+{
+  HLREngine *engine = s->backend_state;
+  engine->input = *stream;
+
+  bool run = true;
+  while(run) {
+    // check input against table to determine which action to take
+    const HLRAction *action = h_lrengine_action(engine);
+    if(action == NEED_INPUT) {
+      // XXX assume lookahead 1
+      assert(engine->input.length - engine->input.index == 0);
+      break;
+    }
+
+    // execute action
+    run = h_lrengine_step(engine, action);
+    if(engine->input.overrun && !engine->input.last_chunk)
+      break;
+  }
+
+  *stream = engine->input;
+  return !run;  // done if engine no longer running
+}
+
+HParseResult *h_lr_parse_finish(HSuspendedParser *s)
+{
+  HLREngine *engine = s->backend_state;
+
+  HParseResult *result = h_lrengine_result(engine);
+  if(!result)
+    h_delete_arena(engine->arena);
+  h_delete_arena(engine->tarena);
+  return result;
+}
 
 /* Pretty-printers */
 
diff --git a/src/backends/lr.h b/src/backends/lr.h
index f9cb9a201cac0be4c43340a7ab525bf212a130f4..724d126ce106e6ed98f86fd7e30c1d42938dd1cd 100644
--- a/src/backends/lr.h
+++ b/src/backends/lr.h
@@ -134,6 +134,9 @@ const HLRAction *h_lrengine_action(const HLREngine *engine);
 bool h_lrengine_step(HLREngine *engine, const HLRAction *action);
 HParseResult *h_lrengine_result(HLREngine *engine);
 HParseResult *h_lr_parse(HAllocator* mm__, const HParser* parser, HInputStream* stream);
+void h_lr_parse_start(HSuspendedParser *s);
+bool h_lr_parse_chunk(HSuspendedParser* s, HInputStream *stream);
+HParseResult *h_lr_parse_finish(HSuspendedParser *s);
 HParseResult *h_glr_parse(HAllocator* mm__, const HParser* parser, HInputStream* stream);
 
 void h_pprint_lritem(FILE *f, const HCFGrammar *g, const HLRItem *item);
diff --git a/src/t_parser.c b/src/t_parser.c
index a8bdb0d008221dbc4c9734d86315e3c5ef98d134..3d54ff6bf3bd6d75fcb9c73732a7a04025d5bcf3 100644
--- a/src/t_parser.c
+++ b/src/t_parser.c
@@ -883,6 +883,9 @@ void register_parser_tests(void) {
   g_test_add_data_func("/core/parser/lalr/leftrec-ne", GINT_TO_POINTER(PB_LALR), test_leftrec_ne);
   g_test_add_data_func("/core/parser/lalr/rightrec", GINT_TO_POINTER(PB_LALR), test_rightrec);
   g_test_add_data_func("/core/parser/lalr/result_length", GINT_TO_POINTER(PB_LALR), test_result_length);
+  g_test_add_data_func("/core/parser/lalr/iterative", GINT_TO_POINTER(PB_LALR), test_iterative);
+  g_test_add_data_func("/core/parser/lalr/iterative/lookahead", GINT_TO_POINTER(PB_LALR), test_iterative_lookahead);
+  g_test_add_data_func("/core/parser/lalr/iterative/result_length", GINT_TO_POINTER(PB_LALR), test_iterative_result_length);
 
   g_test_add_data_func("/core/parser/glr/token", GINT_TO_POINTER(PB_GLR), test_token);
   g_test_add_data_func("/core/parser/glr/ch", GINT_TO_POINTER(PB_GLR), test_ch);