From 13088c9d7a97ec49edee0d40cf6f9e8d5b5fbc48 Mon Sep 17 00:00:00 2001
From: Dan Hirsch <thequux@upstandinghackers.com>
Date: Fri, 26 Apr 2013 20:36:54 -0700
Subject: [PATCH] Finshed up the regex backend

---
 .gitignore               |  1 +
 Makefile                 |  3 ++
 common.mk                |  2 +-
 docs/milestone2.dot      | 62 +++++++++++++--------------
 docs/milestone3.dot      | 65 ++++++++++++++++++++++++++++
 src/Makefile             |  1 -
 src/backends/packrat.c   | 40 ++++++++++++++++-
 src/backends/regex.c     | 80 +++++++++++++++++++++++++++++++---
 src/backends/regex.h     | 19 ++++++---
 src/benchmark.c          |  8 ++--
 src/compile.c            | 15 -------
 src/hammer.c             | 65 ++++++++++++----------------
 src/hammer.h             | 92 ++++++++++++++++++++--------------------
 src/internal.h           |  7 +--
 src/parsers/action.c     |  4 +-
 src/parsers/and.c        |  4 +-
 src/parsers/attr_bool.c  |  4 +-
 src/parsers/bits.c       | 23 ++++++++--
 src/parsers/butnot.c     |  4 +-
 src/parsers/ch.c         |  7 +--
 src/parsers/charset.c    | 18 ++++----
 src/parsers/choice.c     | 12 +++---
 src/parsers/difference.c |  4 +-
 src/parsers/end.c        |  6 +--
 src/parsers/epsilon.c    | 10 +++--
 src/parsers/ignore.c     | 14 ++++--
 src/parsers/ignoreseq.c  | 19 +++++----
 src/parsers/int_range.c  |  8 ++--
 src/parsers/many.c       | 36 +++++++++-------
 src/parsers/not.c        |  6 +--
 src/parsers/nothing.c    |  9 ++--
 src/parsers/optional.c   | 18 +++++++-
 src/parsers/sequence.c   | 14 +++---
 src/parsers/token.c      |  6 +--
 src/parsers/whitespace.c | 14 +++++-
 src/parsers/xor.c        |  4 +-
 src/system_allocator.c   | 17 ++++++--
 src/t_benchmark.c        |  2 +-
 src/test_suite.h         |  4 +-
 39 files changed, 479 insertions(+), 248 deletions(-)
 create mode 100644 docs/milestone3.dot
 delete mode 100644 src/compile.c

diff --git a/.gitignore b/.gitignore
index 2368f793..40bd0e33 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@ TAGS
 \#*
 .*
 docs/milestone2.dot.pdf
+*.dot.pdf
diff --git a/Makefile b/Makefile
index bd383a22..fbd96c4f 100644
--- a/Makefile
+++ b/Makefile
@@ -17,6 +17,9 @@ CONFIG_VARS= INCLUDE_TESTS
 test: src/test_suite
 	$<
 
+examples/all: src/all
+examples/compile: src/compile
+
 define SUBDIR_TEMPLATE
 $(1)/%:
 	$$(MAKE) -C $(1) $$*
diff --git a/common.mk b/common.mk
index a57429db..143a0f40 100644
--- a/common.mk
+++ b/common.mk
@@ -6,7 +6,7 @@ endif
 include $(TOPLEVEL)/config.mk
 
 TEST_CFLAGS = $(shell pkg-config --cflags glib-2.0) -DINCLUDE_TESTS
-TEST_LDFLAGS = $(shell pkg-config --libs glib-2.0)
+TEST_LDFLAGS = $(shell pkg-config --libs glib-2.0) -lrt
 
 CFLAGS := -std=gnu99 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-attributes
 LDFLAGS :=
diff --git a/docs/milestone2.dot b/docs/milestone2.dot
index cc75a097..0bbd9b23 100644
--- a/docs/milestone2.dot
+++ b/docs/milestone2.dot
@@ -1,36 +1,36 @@
 digraph {
 	graph [rankdir=LR];
-subgraph complete {
-	 node [color="gray",fontcolor="gray"];
-	 glue;
-	 regex_svm;
-	 regex_rvm;
-}
-/* The end result of the milestone, along with the subtasks listed */
-milestone2 [color="green",style="filled"];
-llk -> milestone2;
-lr -> milestone2;
-lalr8_gen -> lr;
-glr_gen -> lr;
-lr_driver -> lr;
-regex -> milestone2;
-glue -> milestone2; // Meredith knows what glue referred to here.
-tests -> milestone2;
-
-regex_gen -> regex; // partially done
-regex_driver -> regex;
-regex_svm -> regex_driver;
-regex_rvm -> regex_driver;
-regex_svm_actions -> regex_driver;
-llk_driver -> llk;
-llk_gen -> llk;
+	subgraph complete {
+		node [color="gray",fontcolor="gray"];
+		glue;
+		regex_svm;
+		regex_rvm;
+		desugaring; // Needs merged.
+	}
+	/* The end result of the milestone, along with the subtasks listed */
+	milestone2 [color="green",style="filled"];
+	llk -> milestone2;
+	lr -> milestone2;
+	lalr8_gen -> lr; // Generate parse tables for LALR(8)
+	glr_gen -> lr; // Generate parse tables for GLR
+	llk_gen -> llk; // Generate parse tables for LL(k)
+	lr_driver -> lr; // Write driver for all LR-type algs; analagous to SVM and RVM implementations
+	llk_driver -> llk; // Write driver for LL(k)
+	regex -> milestone2;
+	glue -> milestone2;
+	tests -> milestone2;
+	
+	regex_gen -> regex; // should be mostly done; the rest is concurrent with regex_svm_actions
+	regex_driver -> regex;
+	regex_svm -> regex_driver;
+	regex_rvm -> regex_driver;
+	regex_svm_actions -> regex_driver; // 1 for each way that an HParsedToken can be extracted from the stack.
 
 
-/*
- * 
- */
- desugaring -> llk_gen;
- desugaring -> lalr8_gen;
- desugaring -> glr_gen;
- 
+	/*
+	* 
+	*/
+	desugaring -> llk_gen;
+	desugaring -> lalr8_gen;
+	desugaring -> glr_gen;
 }
diff --git a/docs/milestone3.dot b/docs/milestone3.dot
new file mode 100644
index 00000000..66a5fb35
--- /dev/null
+++ b/docs/milestone3.dot
@@ -0,0 +1,65 @@
+digraph {
+	graph [rankdir=LR];
+
+	subgraph complete {
+		node [color="gray",fontcolor="gray"];
+	}
+
+	subgraph groups {
+		node [color="blue",fontcolor="blue"];
+		cpp;
+		python;
+		ruby;
+		go;
+		php;
+		dotnet;
+	}
+	
+	milestone3 [color="green",style="filled"];
+
+
+	function_desc_fmt -> function_descs;
+	function_desc_fmt -> binding_generator;
+
+	binding_generator -> cpp_gen;
+	binding_generator -> python_gen;
+	binding_generator -> ruby_gen;
+	binding_generator -> go_gen;
+	binding_generator -> php_gen;
+	binding_generator -> dotnet_gen;
+
+	function_descs -> cpp_gen;
+	function_descs -> python_gen;
+	function_descs -> ruby_gen;
+	function_descs -> go_gen;
+	function_descs -> php_gen;
+	function_descs -> dotnet_gen;
+
+
+	// Plugins to generate a type of code
+	cpp_gen -> cpp;
+	python_gen -> python;
+	ruby_gen -> ruby;
+	go_gen -> go;
+	php_gen -> php;
+	dotnet_gen -> dotnet;
+	
+	// base code... developed concurrently with _gen's
+	cpp_base -> cpp;
+	python_base -> python;
+	ruby_base -> ruby;
+	go_base -> go;
+	php_base -> php;
+	dotnet_base -> dotnet;
+	
+	// Bindings for various languages. These are just groupings.
+	cpp -> milestone3;
+	python -> milestone3;
+	ruby -> milestone3;
+	go -> milestone3;
+	php -> milestone3;
+	dotnet -> milestone3;
+
+	
+
+}
\ No newline at end of file
diff --git a/src/Makefile b/src/Makefile
index bb83e839..78687d4c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -38,7 +38,6 @@ HAMMER_PARTS := \
 	datastructures.o \
 	system_allocator.o \
 	benchmark.o \
-	compile.o \
 	$(PARSERS:%=parsers/%.o) \
 	$(BACKENDS:%=backends/%.o)
 
diff --git a/src/backends/packrat.c b/src/backends/packrat.c
index d05129d7..a61e9700 100644
--- a/src/backends/packrat.c
+++ b/src/backends/packrat.c
@@ -1,7 +1,16 @@
 #include <assert.h>
+#include <string.h>
 #include "../internal.h"
 #include "../parsers/parser_internal.h"
 
+static uint32_t djbhash(const uint8_t *buf, size_t len) {
+  uint32_t hash = 5381;
+  while (len--) {
+    hash = hash * 33 + *buf++;
+  }
+  return hash;
+}
+
 // short-hand for constructing HCachedResult's
 static HCachedResult *cached_result(const HParseState *state, HParseResult *result) {
   HCachedResult *ret = a_new(HCachedResult, 1);
@@ -190,12 +199,39 @@ HParseResult* h_do_parse(const HParser* parser, HParseState *state) {
   }
 }
 
-int h_packrat_compile(HAllocator* mm__, const HParser* parser, const void* params) {
+int h_packrat_compile(HAllocator* mm__, HParser* parser, const void* params) {
+  parser->backend = PB_PACKRAT;
   return 0; // No compilation necessary, and everything should work
 	    // out of the box.
 }
 
-HParseResult *h_packrat_parse(HAllocator* mm__, const HParser* parser, HParseState* parse_state) {
+static uint32_t cache_key_hash(const void* key) {
+  return djbhash(key, sizeof(HParserCacheKey));
+}
+static bool cache_key_equal(const void* key1, const void* key2) {
+  return memcmp(key1, key2, sizeof(HParserCacheKey)) == 0;
+}
+
+HParseResult *h_packrat_parse(HAllocator* mm__, const HParser* parser, HInputStream *input_stream) {
+  HArena * arena = h_new_arena(mm__, 0);
+  HParseState *parse_state = a_new_(arena, HParseState, 1);
+  parse_state->cache = h_hashtable_new(arena, cache_key_equal, // key_equal_func
+				       cache_key_hash); // hash_func
+  parse_state->input_stream = *input_stream;
+  parse_state->lr_stack = h_slist_new(arena);
+  parse_state->recursion_heads = h_hashtable_new(arena, cache_key_equal,
+						 cache_key_hash);
+  parse_state->arena = arena;
+  HParseResult *res = h_do_parse(parser, parse_state);
+  h_slist_free(parse_state->lr_stack);
+  h_hashtable_free(parse_state->recursion_heads);
+  // tear down the parse state
+  h_hashtable_free(parse_state->cache);
+  if (!res)
+    h_delete_arena(parse_state->arena);
+
+  return res;
+
   return h_do_parse(parser, parse_state);
 }
 
diff --git a/src/backends/regex.c b/src/backends/regex.c
index df9a52d7..3cbbb2d4 100644
--- a/src/backends/regex.c
+++ b/src/backends/regex.c
@@ -245,10 +245,6 @@ HParseResult *run_trace(HAllocator *mm__, HRVMProg *orig_prog, HRVMTrace *trace,
   return NULL;
 }
 
-bool h_compile_regex(HRVMProg *prog, const HParser *parser) {
-  return parser->vtable->compile_to_rvm(prog, parser->env);
-}
-
 uint16_t h_rvm_create_action(HRVMProg *prog, HSVMActionFunc action_func, void* env) {
   for (uint16_t i = 0; i < prog->action_count; i++) {
     if (prog->actions[i].action == action_func && prog->actions[i].env == env)
@@ -293,4 +289,78 @@ void h_rvm_patch_arg(HRVMProg *prog, uint16_t ip, uint16_t new_val) {
   prog->insns[ip].arg = new_val;
 }
 
-    // TODO: Implement the primitive actions
+size_t h_svm_count_to_mark(HSVMContext *ctx) {
+  size_t ctm;
+  for (ctm = 0; ctm < ctx->stack_count-1; ctm++) {
+    if (ctx->stack[ctx->stack_count - 1 - ctm]->token_type == TT_MARK)
+      return ctm;
+  }
+  return ctx->stack_count;
+}
+
+// TODO: Implement the primitive actions
+bool h_svm_action_make_sequence(HArena *arena, HSVMContext *ctx, void* env) {
+  size_t n_items = h_svm_count_to_mark(ctx);
+  assert (n_items < ctx->stack_count);
+  HParsedToken *res = ctx->stack[ctx->stack_count - 1 - n_items];
+  assert (res->token_type == TT_MARK);
+  res->token_type = TT_SEQUENCE;
+  
+  HCountedArray *ret_carray = h_carray_new_sized(arena, n_items);
+  res->seq = ret_carray;
+  // res index and bit offset are the same as the mark.
+  for (size_t i = 0; i < n_items; i++) {
+    ret_carray->elements[i] = ctx->stack[ctx->stack_count - n_items + i];
+  }
+  ctx->stack_count -= n_items;
+  return true;
+}
+
+bool h_svm_action_clear_to_mark(HArena *arena, HSVMContext *ctx, void* env) {
+  while (ctx->stack_count > 0) {
+    if (ctx->stack[--ctx->stack_count]->token_type == TT_MARK)
+      return true;
+  }
+  return false; // no mark found.
+}
+
+// Glue regex backend to rest of system
+
+bool h_compile_regex(HRVMProg *prog, const HParser *parser) {
+  return parser->vtable->compile_to_rvm(prog, parser->env);
+}
+
+static void h_regex_free(HParser *parser) {
+  HRVMProg *prog = (HRVMProg*)parser->backend_data;
+  HAllocator *mm__ = prog->allocator;
+  h_free(prog->insns);
+  h_free(prog->actions);
+  h_free(prog);
+  parser->backend_data = NULL;
+  parser->backend = PB_PACKRAT;
+}
+
+static int h_regex_compile(HAllocator *mm__, HParser* parser, const void* params) {
+  if (!parser->vtable->isValidRegular(parser->env))
+    return 1;
+  HRVMProg *prog = h_new(HRVMProg, 1);
+  prog->allocator = mm__;
+  if (!h_compile_regex(prog, parser)) {
+    h_free(prog->insns);
+    h_free(prog->actions);
+    h_free(prog);
+    return 2;
+  }
+  parser->backend_data = prog;
+  return 0;
+}
+
+static HParseResult *h_regex_parse(HAllocator* mm__, const HParser* parser, HInputStream *input_stream) {
+  return h_rvm_run__m(mm__, (HRVMProg*)parser->backend_data, input_stream->input, input_stream->length);
+}
+
+HParserBackendVTable h__regex_backend_vtable = {
+  .compile = h_regex_compile,
+  .parse = h_regex_parse,
+  .free = h_regex_free
+};
diff --git a/src/backends/regex.h b/src/backends/regex.h
index 3528e82f..a84904d0 100644
--- a/src/backends/regex.h
+++ b/src/backends/regex.h
@@ -10,9 +10,12 @@ typedef enum HRVMOp_ {
   RVM_ACCEPT,  // [a]
   RVM_GOTO,    // [c] parameter is an offset into the instruction table
   RVM_FORK,    // [c] parameter is an offset into the instruction table
-  RVM_PUSH,    // [a] No arguments, just pushes a mark onto the stack
+  RVM_PUSH,    // [a] No arguments, just pushes a mark (pointer to some
+               //     character in the input string) onto the stack
   RVM_ACTION,  // [a] argument is an action ID
-  RVM_CAPTURE, // [a] Capture the last string, and push it on the stack. No arg.
+  RVM_CAPTURE, // [a] Capture the last string (up to the current
+	       //     position, non-inclusive), and push it on the
+	       //     stack. No arg.
   RVM_EOF,     // [m] Succeeds only if at EOF.
   RVM_MATCH,   // [m] The high byte of the parameter is an upper bound
 	       //     and the low byte is a lower bound, both
@@ -31,7 +34,7 @@ typedef struct HRVMInsn_{
 
 typedef struct HSVMContext_ {
   HParsedToken **stack;
-  size_t stack_count;
+  size_t stack_count; // number of items on the stack. Thus stack[stack_count] is the first unused item on the stack.
   size_t stack_capacity;
 } HSVMContext;
 
@@ -43,13 +46,13 @@ typedef struct HSVMAction_ {
   void* env;
 } HSVMAction;
 
-typedef struct HRVMProg_ {
+struct HRVMProg_ {
   HAllocator *allocator;
   size_t length;
   size_t action_count;
   HRVMInsn *insns;
   HSVMAction *actions;
-} HRVMProg;
+};
 
 // Returns true IFF the provided parser could be compiled.
 bool h_compile_regex(HRVMProg *prog, const HParser* parser);
@@ -68,4 +71,10 @@ uint16_t h_rvm_get_ip(HRVMProg *prog);
 // correct target is known.
 void h_rvm_patch_arg(HRVMProg *prog, uint16_t ip, uint16_t new_val);
 
+// Common SVM action funcs...
+bool h_svm_action_make_sequence(HArena *arena, HSVMContext *ctx, void* env);
+bool h_svm_action_clear_to_mark(HArena *arena, HSVMContext *ctx, void* env);
+
+extern HParserBackendVTable h__regex_backend_vtable;
+
 #endif
diff --git a/src/benchmark.c b/src/benchmark.c
index 577a3801..918f87c1 100644
--- a/src/benchmark.c
+++ b/src/benchmark.c
@@ -21,11 +21,11 @@
 
 */
 
-HBenchmarkResults *h_benchmark(const HParser* parser, HParserTestcase* testcases) {
+HBenchmarkResults *h_benchmark(HParser* parser, HParserTestcase* testcases) {
   return h_benchmark__m(&system_allocator, parser, testcases);
 }
 
-HBenchmarkResults *h_benchmark__m(HAllocator* mm__, const HParser* parser, HParserTestcase* testcases) {
+HBenchmarkResults *h_benchmark__m(HAllocator* mm__, HParser* parser, HParserTestcase* testcases) {
   // For now, just output the results to stderr
   HParserTestcase* tc = testcases;
   HParserBackend backend = PB_MIN;
@@ -33,10 +33,10 @@ HBenchmarkResults *h_benchmark__m(HAllocator* mm__, const HParser* parser, HPars
   ret->len = PB_MAX-PB_MIN;
   ret->results = h_new(HBackendResults, ret->len);
 
-  for (backend = PB_MIN; backend < PB_MAX; backend++) {
+  for (backend = PB_MIN; backend <= PB_MAX; backend++) {
     ret->results[backend].backend = backend;
     // Step 1: Compile grammar for given parser...
-    if (h_compile(parser, PB_MIN, NULL) == -1) {
+    if (h_compile(parser, backend, NULL) == -1) {
       // backend inappropriate for grammar...
       fprintf(stderr, "failed\n");
       ret->results[backend].compile_success = false;
diff --git a/src/compile.c b/src/compile.c
deleted file mode 100644
index e151cfb8..00000000
--- a/src/compile.c
+++ /dev/null
@@ -1,15 +0,0 @@
-// This file contains functions related to managing multiple parse backends
-#include "hammer.h"
-#include "internal.h"
-
-static HParserBackendVTable *backends[PB_MAX] = {
-  &h__packrat_backend_vtable,
-};
-
-int h_compile(const HParser* parser, HParserBackend backend, const void* params) {
-  return h_compile__m(&system_allocator, parser, backend, params);
-}
-
-int h_compile__m(HAllocator* mm__, const HParser* parser, HParserBackend backend, const void* params) {
-  return backends[backend]->compile(mm__, parser, params);
-}
diff --git a/src/hammer.c b/src/hammer.c
index b1d4ec94..cba6be6b 100644
--- a/src/hammer.c
+++ b/src/hammer.c
@@ -26,13 +26,11 @@
 #include "allocator.h"
 #include "parsers/parser_internal.h"
 
-static uint32_t djbhash(const uint8_t *buf, size_t len) {
-  uint32_t hash = 5381;
-  while (len--) {
-    hash = hash * 33 + *buf++;
-  }
-  return hash;
-}
+static HParserBackendVTable *backends[PB_MAX + 1] = {
+  &h__packrat_backend_vtable,
+  &h__regex_backend_vtable,
+};
+
 
 /* Helper function, since these lines appear in every parser */
 
@@ -42,42 +40,24 @@ typedef struct {
 } HTwoParsers;
 
 
-static uint32_t cache_key_hash(const void* key) {
-  return djbhash(key, sizeof(HParserCacheKey));
-}
-static bool cache_key_equal(const void* key1, const void* key2) {
-  return memcmp(key1, key2, sizeof(HParserCacheKey)) == 0;
-}
 
 
 HParseResult* h_parse(const HParser* parser, const uint8_t* input, size_t length) {
   return h_parse__m(&system_allocator, parser, input, length);
 }
-HParseResult* h_parse__m(HAllocator* mm__, const HParser* parser, const uint8_t* input, size_t length) { 
+HParseResult* h_parse__m(HAllocator* mm__, const HParser* parser, const uint8_t* input, size_t length) {
+  // TODO: split the creation of the parse state into h_packrat_parse
   // Set up a parse state...
-  HArena * arena = h_new_arena(mm__, 0);
-  HParseState *parse_state = a_new_(arena, HParseState, 1);
-  parse_state->cache = h_hashtable_new(arena, cache_key_equal, // key_equal_func
-					      cache_key_hash); // hash_func
-  parse_state->input_stream.input = input;
-  parse_state->input_stream.index = 0;
-  parse_state->input_stream.bit_offset = 8; // bit big endian
-  parse_state->input_stream.overrun = 0;
-  parse_state->input_stream.endianness = BIT_BIG_ENDIAN | BYTE_BIG_ENDIAN;
-  parse_state->input_stream.length = length;
-  parse_state->lr_stack = h_slist_new(arena);
-  parse_state->recursion_heads = h_hashtable_new(arena, cache_key_equal,
-						  cache_key_hash);
-  parse_state->arena = arena;
-  HParseResult *res = h_do_parse(parser, parse_state);
-  h_slist_free(parse_state->lr_stack);
-  h_hashtable_free(parse_state->recursion_heads);
-  // tear down the parse state
-  h_hashtable_free(parse_state->cache);
-  if (!res)
-    h_delete_arena(parse_state->arena);
-
-  return res;
+  HInputStream input_stream = {
+    .index = 0,
+    .bit_offset = 8,
+    .overrun = 0,
+    .endianness = BIT_BIG_ENDIAN | BYTE_BIG_ENDIAN,
+    .length = length,
+    .input = input
+  };
+  
+  return backends[parser->backend]->parse(mm__, parser, &input_stream);
 }
 
 void h_parse_result_free(HParseResult *result) {
@@ -98,3 +78,14 @@ bool h_not_regular(HRVMProg *prog, void *env) {
   (void)env;
   return false;
 }
+
+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) {
+  int ret = backends[backend]->compile(mm__, parser, params);
+  if (!ret)
+    parser->backend = backend;
+  return ret;
+}
diff --git a/src/hammer.h b/src/hammer.h
index ccb10c8d..38e40dae 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_LALR,	// Not Implemented
   PB_LLk,	// Not Implemented
   PB_GLR,	// Not Implemented
-  PB_REGULAR,	// Not Implemented
-  PB_MAX
+  PB_MAX = PB_REGULAR
 } HParserBackend;
 
 typedef enum HTokenType_ {
@@ -47,7 +47,7 @@ typedef enum HTokenType_ {
   TT_SINT,
   TT_UINT,
   TT_SEQUENCE,
-  TT_RESERVED_1, // reserved for internal use
+  TT_RESERVED_1, // reserved for backend-specific internal use
   TT_USER = 64,
   TT_ERR,
   TT_MAX
@@ -122,11 +122,13 @@ typedef struct HParserVtable_ {
   HParseResult* (*parse)(void *env, HParseState *state);
   bool (*isValidRegular)(void *env);
   bool (*isValidCF)(void *env);
-  bool (*compile_to_rvm)(HRVMProg *prog, void* env);
+  bool (*compile_to_rvm)(HRVMProg *prog, void* env); // FIXME: forgot what the bool return value was supposed to mean.
 } HParserVtable;
 
 typedef struct HParser_ {
   const HParserVtable *vtable;
+  HParserBackend backend;
+  void* backend_data;
   void *env;
 } HParser;
 
@@ -199,7 +201,7 @@ HAMMER_FN_DECL(HParseResult*, h_parse, const HParser* parser, const uint8_t* inp
  * 
  * Result token type: TT_BYTES
  */
-HAMMER_FN_DECL(const HParser*, h_token, const uint8_t *str, const size_t len);
+HAMMER_FN_DECL(HParser*, h_token, const uint8_t *str, const size_t len);
 
 /**
  * Given a single character, returns a parser that parses that 
@@ -207,7 +209,7 @@ HAMMER_FN_DECL(const HParser*, h_token, const uint8_t *str, const size_t len);
  * 
  * Result token type: TT_UINT
  */
-HAMMER_FN_DECL(const HParser*, h_ch, const uint8_t c);
+HAMMER_FN_DECL(HParser*, h_ch, const uint8_t c);
 
 /**
  * Given two single-character bounds, lower and upper, returns a parser
@@ -216,14 +218,14 @@ HAMMER_FN_DECL(const HParser*, h_ch, const uint8_t c);
  * 
  * Result token type: TT_UINT
  */
-HAMMER_FN_DECL(const HParser*, h_ch_range, const uint8_t lower, const uint8_t upper);
+HAMMER_FN_DECL(HParser*, h_ch_range, const uint8_t lower, const uint8_t upper);
 
 /**
  * Given an integer parser, p, and two integer bounds, lower and upper,
  * returns a parser that parses an integral value within the range 
  * [lower, upper] (inclusive).
  */
-HAMMER_FN_DECL(const HParser*, h_int_range, const HParser *p, const int64_t lower, const int64_t upper);
+HAMMER_FN_DECL(HParser*, h_int_range, const HParser *p, const int64_t lower, const int64_t upper);
 
 /**
  * Returns a parser that parses the specified number of bits. sign == 
@@ -231,63 +233,63 @@ HAMMER_FN_DECL(const HParser*, h_int_range, const HParser *p, const int64_t lowe
  *
  * Result token type: TT_SINT if sign == true, TT_UINT if sign == false
  */
-HAMMER_FN_DECL(const HParser*, h_bits, size_t len, bool sign);
+HAMMER_FN_DECL(HParser*, h_bits, size_t len, bool sign);
 
 /**
  * Returns a parser that parses a signed 8-byte integer value. 
  *
  * Result token type: TT_SINT
  */
-HAMMER_FN_DECL_NOARG(const HParser*, h_int64);
+HAMMER_FN_DECL_NOARG(HParser*, h_int64);
 
 /**
  * Returns a parser that parses a signed 4-byte integer value. 
  *
  * Result token type: TT_SINT
  */
-HAMMER_FN_DECL_NOARG(const HParser*, h_int32);
+HAMMER_FN_DECL_NOARG(HParser*, h_int32);
 
 /**
  * Returns a parser that parses a signed 2-byte integer value. 
  *
  * Result token type: TT_SINT
  */
-HAMMER_FN_DECL_NOARG(const HParser*, h_int16);
+HAMMER_FN_DECL_NOARG(HParser*, h_int16);
 
 /**
  * Returns a parser that parses a signed 1-byte integer value. 
  *
  * Result token type: TT_SINT
  */
-HAMMER_FN_DECL_NOARG(const HParser*, h_int8);
+HAMMER_FN_DECL_NOARG(HParser*, h_int8);
 
 /**
  * Returns a parser that parses an unsigned 8-byte integer value. 
  *
  * Result token type: TT_UINT
  */
-HAMMER_FN_DECL_NOARG(const HParser*, h_uint64);
+HAMMER_FN_DECL_NOARG(HParser*, h_uint64);
 
 /**
  * Returns a parser that parses an unsigned 4-byte integer value. 
  *
  * Result token type: TT_UINT
  */
-HAMMER_FN_DECL_NOARG(const HParser*, h_uint32);
+HAMMER_FN_DECL_NOARG(HParser*, h_uint32);
 
 /**
  * Returns a parser that parses an unsigned 2-byte integer value. 
  *
  * Result token type: TT_UINT
  */
-HAMMER_FN_DECL_NOARG(const HParser*, h_uint16);
+HAMMER_FN_DECL_NOARG(HParser*, h_uint16);
 
 /**
  * Returns a parser that parses an unsigned 1-byte integer value. 
  *
  * Result token type: TT_UINT
  */
-HAMMER_FN_DECL_NOARG(const HParser*, h_uint8);
+HAMMER_FN_DECL_NOARG(HParser*, h_uint8);
 
 /**
  * Given another parser, p, returns a parser that skips any whitespace 
@@ -295,7 +297,7 @@ HAMMER_FN_DECL_NOARG(const HParser*, h_uint8);
  *
  * Result token type: p's result type
  */
-HAMMER_FN_DECL(const HParser*, h_whitespace, const HParser* p);
+HAMMER_FN_DECL(HParser*, h_whitespace, const HParser* p);
 
 /**
  * Given two parsers, p and q, returns a parser that parses them in
@@ -303,7 +305,7 @@ HAMMER_FN_DECL(const HParser*, h_whitespace, const HParser* p);
  *
  * Result token type: p's result type
  */
-HAMMER_FN_DECL(const HParser*, h_left, const HParser* p, const HParser* q);
+HAMMER_FN_DECL(HParser*, h_left, const HParser* p, const HParser* q);
 
 /**
  * Given two parsers, p and q, returns a parser that parses them in
@@ -311,7 +313,7 @@ HAMMER_FN_DECL(const HParser*, h_left, const HParser* p, const HParser* q);
  *
  * Result token type: q's result type
  */
-HAMMER_FN_DECL(const HParser*, h_right, const HParser* p, const HParser* q);
+HAMMER_FN_DECL(HParser*, h_right, const HParser* p, const HParser* q);
 
 /**
  * Given three parsers, p, x, and q, returns a parser that parses them in
@@ -319,7 +321,7 @@ HAMMER_FN_DECL(const HParser*, h_right, const HParser* p, const HParser* q);
  *
  * Result token type: x's result type
  */
-HAMMER_FN_DECL(const HParser*, h_middle, const HParser* p, const HParser* x, const HParser* q);
+HAMMER_FN_DECL(HParser*, h_middle, const HParser* p, const HParser* x, const HParser* q);
 
 /**
  * Given another parser, p, and a function f, returns a parser that 
@@ -327,21 +329,21 @@ HAMMER_FN_DECL(const HParser*, h_middle, const HParser* p, const HParser* x, con
  *
  * Result token type: any
  */
-HAMMER_FN_DECL(const HParser*, h_action, const HParser* p, const HAction a);
+HAMMER_FN_DECL(HParser*, h_action, const HParser* p, const HAction a);
 
 /**
  * Parse a single character in the given charset. 
  *
  * Result token type: TT_UINT
  */
-HAMMER_FN_DECL(const HParser*, h_in, const uint8_t *charset, size_t length);
+HAMMER_FN_DECL(HParser*, h_in, const uint8_t *charset, size_t length);
 
 /**
  * Parse a single character *NOT* in the given charset. 
  *
  * Result token type: TT_UINT
  */
-HAMMER_FN_DECL(const HParser*, h_not_in, const uint8_t *charset, size_t length);
+HAMMER_FN_DECL(HParser*, h_not_in, const uint8_t *charset, size_t length);
 
 /**
  * A no-argument parser that succeeds if there is no more input to 
@@ -349,14 +351,14 @@ HAMMER_FN_DECL(const HParser*, h_not_in, const uint8_t *charset, size_t length);
  *
  * Result token type: None. The HParseResult exists but its AST is NULL.
  */
-HAMMER_FN_DECL_NOARG(const HParser*, h_end_p);
+HAMMER_FN_DECL_NOARG(HParser*, h_end_p);
 
 /**
  * This parser always fails. 
  *
  * Result token type: NULL. Always.
  */
-HAMMER_FN_DECL_NOARG(const HParser*, h_nothing_p);
+HAMMER_FN_DECL_NOARG(HParser*, h_nothing_p);
 
 /**
  * Given a null-terminated list of parsers, apply each parser in order.
@@ -364,7 +366,7 @@ HAMMER_FN_DECL_NOARG(const HParser*, h_nothing_p);
  *
  * Result token type: TT_SEQUENCE
  */
-HAMMER_FN_DECL_VARARGS_ATTR(__attribute__((sentinel)), const HParser*, h_sequence, const HParser* p);
+HAMMER_FN_DECL_VARARGS_ATTR(__attribute__((sentinel)), HParser*, h_sequence, const HParser* p);
 
 /**
  * Given an array of parsers, p_array, apply each parser in order. The 
@@ -373,7 +375,7 @@ HAMMER_FN_DECL_VARARGS_ATTR(__attribute__((sentinel)), const HParser*, h_sequenc
  *
  * Result token type: The type of the first successful parser's result.
  */
-HAMMER_FN_DECL_VARARGS_ATTR(__attribute__((sentinel)), const HParser*, h_choice, const HParser* p);
+HAMMER_FN_DECL_VARARGS_ATTR(__attribute__((sentinel)), HParser*, h_choice, const HParser* p);
 
 /**
  * Given two parsers, p1 and p2, this parser succeeds in the following 
@@ -383,7 +385,7 @@ HAMMER_FN_DECL_VARARGS_ATTR(__attribute__((sentinel)), const HParser*, h_choice,
  *
  * Result token type: p1's result type.
  */
-HAMMER_FN_DECL(const HParser*, h_butnot, const HParser* p1, const HParser* p2);
+HAMMER_FN_DECL(HParser*, h_butnot, const HParser* p1, const HParser* p2);
 
 /**
  * Given two parsers, p1 and p2, this parser succeeds in the following 
@@ -393,7 +395,7 @@ HAMMER_FN_DECL(const HParser*, h_butnot, const HParser* p1, const HParser* p2);
  *
  * Result token type: p1's result type.
  */
-HAMMER_FN_DECL(const HParser*, h_difference, const HParser* p1, const HParser* p2);
+HAMMER_FN_DECL(HParser*, h_difference, const HParser* p1, const HParser* p2);
 
 /**
  * Given two parsers, p1 and p2, this parser succeeds if *either* p1 or
@@ -401,7 +403,7 @@ HAMMER_FN_DECL(const HParser*, h_difference, const HParser* p1, const HParser* p
  *
  * Result token type: The type of the result of whichever parser succeeded.
  */
-HAMMER_FN_DECL(const HParser*, h_xor, const HParser* p1, const HParser* p2);
+HAMMER_FN_DECL(HParser*, h_xor, const HParser* p1, const HParser* p2);
 
 /**
  * Given a parser, p, this parser succeeds for zero or more repetitions
@@ -409,7 +411,7 @@ HAMMER_FN_DECL(const HParser*, h_xor, const HParser* p1, const HParser* p2);
  *
  * Result token type: TT_SEQUENCE
  */
-HAMMER_FN_DECL(const HParser*, h_many, const HParser* p);
+HAMMER_FN_DECL(HParser*, h_many, const HParser* p);
 
 /**
  * Given a parser, p, this parser succeeds for one or more repetitions 
@@ -417,7 +419,7 @@ HAMMER_FN_DECL(const HParser*, h_many, const HParser* p);
  *
  * Result token type: TT_SEQUENCE
  */
-HAMMER_FN_DECL(const HParser*, h_many1, const HParser* p);
+HAMMER_FN_DECL(HParser*, h_many1, const HParser* p);
 
 /**
  * Given a parser, p, this parser succeeds for exactly N repetitions 
@@ -425,7 +427,7 @@ HAMMER_FN_DECL(const HParser*, h_many1, const HParser* p);
  *
  * Result token type: TT_SEQUENCE
  */
-HAMMER_FN_DECL(const HParser*, h_repeat_n, const HParser* p, const size_t n);
+HAMMER_FN_DECL(HParser*, h_repeat_n, const HParser* p, const size_t n);
 
 /**
  * Given a parser, p, this parser succeeds with the value p parsed or 
@@ -433,7 +435,7 @@ HAMMER_FN_DECL(const HParser*, h_repeat_n, const HParser* p, const size_t n);
  *
  * Result token type: If p succeeded, the type of its result; if not, TT_NONE.
  */
-HAMMER_FN_DECL(const HParser*, h_optional, const HParser* p);
+HAMMER_FN_DECL(HParser*, h_optional, const HParser* p);
 
 /**
  * Given a parser, p, this parser succeeds if p succeeds, but doesn't 
@@ -441,7 +443,7 @@ HAMMER_FN_DECL(const HParser*, h_optional, const HParser* p);
  *
  * Result token type: None. The HParseResult exists but its AST is NULL.
  */
-HAMMER_FN_DECL(const HParser*, h_ignore, const HParser* p);
+HAMMER_FN_DECL(HParser*, h_ignore, const HParser* p);
 
 /**
  * Given a parser, p, and a parser for a separator, sep, this parser 
@@ -452,7 +454,7 @@ HAMMER_FN_DECL(const HParser*, h_ignore, const HParser* p);
  *
  * Result token type: TT_SEQUENCE
  */
-HAMMER_FN_DECL(const HParser*, h_sepBy, const HParser* p, const HParser* sep);
+HAMMER_FN_DECL(HParser*, h_sepBy, const HParser* p, const HParser* sep);
 
 /**
  * Given a parser, p, and a parser for a separator, sep, this parser matches a list of things that p can parse, separated by sep. Unlike sepBy, this ensures that the result has at least one element.
@@ -460,14 +462,14 @@ HAMMER_FN_DECL(const HParser*, h_sepBy, const HParser* p, const HParser* sep);
  *
  * Result token type: TT_SEQUENCE
  */
-HAMMER_FN_DECL(const HParser*, h_sepBy1, const HParser* p, const HParser* sep);
+HAMMER_FN_DECL(HParser*, h_sepBy1, const HParser* p, const HParser* sep);
 
 /**
  * This parser always returns a zero length match, i.e., empty string. 
  *
  * Result token type: None. The HParseResult exists but its AST is NULL.
  */
-HAMMER_FN_DECL_NOARG(const HParser*, h_epsilon_p);
+HAMMER_FN_DECL_NOARG(HParser*, h_epsilon_p);
 
 /**
  * This parser applies its first argument to read an unsigned integer
@@ -478,7 +480,7 @@ HAMMER_FN_DECL_NOARG(const HParser*, h_epsilon_p);
  *
  * Result token type: TT_SEQUENCE
  */
-HAMMER_FN_DECL(const HParser*, h_length_value, const HParser* length, const HParser* value);
+HAMMER_FN_DECL(HParser*, h_length_value, const HParser* length, const HParser* value);
 
 /**
  * This parser attaches a predicate function, which returns true or 
@@ -493,7 +495,7 @@ HAMMER_FN_DECL(const HParser*, h_length_value, const HParser* length, const HPar
  * 
  * Result token type: p's result type if pred succeeded, NULL otherwise.
  */
-HAMMER_FN_DECL(const HParser*, h_attr_bool, const HParser* p, HPredicate pred);
+HAMMER_FN_DECL(HParser*, h_attr_bool, const HParser* p, HPredicate pred);
 
 /**
  * The 'and' parser asserts that a conditional syntax is satisfied, 
@@ -510,7 +512,7 @@ HAMMER_FN_DECL(const HParser*, h_attr_bool, const HParser* p, HPredicate pred);
  *
  * Result token type: None. The HParseResult exists but its AST is NULL.
  */
-HAMMER_FN_DECL(const HParser*, h_and, const HParser* p);
+HAMMER_FN_DECL(HParser*, h_and, const HParser* p);
 
 /**
  * The 'not' parser asserts that a conditional syntax is *not* 
@@ -530,7 +532,7 @@ HAMMER_FN_DECL(const HParser*, h_and, const HParser* p);
  * 
  * Result token type: None. The HParseResult exists but its AST is NULL.
  */
-HAMMER_FN_DECL(const HParser*, h_not, const HParser* p);
+HAMMER_FN_DECL(HParser*, h_not, const HParser* p);
 
 /**
  * Create a parser that just calls out to another, as yet unknown, 
@@ -573,7 +575,7 @@ HAMMER_FN_DECL(void, h_pprint, FILE* stream, const HParsedToken* tok, int indent
  *
  * Returns -1 if grammar cannot be compiled with the specified options; 0 otherwise.
  */
-HAMMER_FN_DECL(int, h_compile, const HParser* parser, HParserBackend backend, const void* params);
+HAMMER_FN_DECL(int, h_compile, HParser* parser, HParserBackend backend, const void* params);
 
 /**
  * TODO: Document me
@@ -598,7 +600,7 @@ const uint8_t* h_bit_writer_get_buffer(HBitWriter* w, size_t *len);
 void h_bit_writer_free(HBitWriter* w);
 
 // {{{ Benchmark functions
-HAMMER_FN_DECL(HBenchmarkResults *, h_benchmark, const HParser* parser, HParserTestcase* testcases);
+HAMMER_FN_DECL(HBenchmarkResults *, h_benchmark, HParser* parser, HParserTestcase* testcases);
 void h_benchmark_report(FILE* stream, HBenchmarkResults* results);
 void h_benchmark_dump_optimized_code(FILE* stream, HBenchmarkResults* results);
 // }}}
diff --git a/src/internal.h b/src/internal.h
index f7082dd0..c3a02083 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -47,7 +47,7 @@ static inline void h_generic_free(HAllocator *allocator, void* ptr) {
   allocator->free(allocator, ptr);
 }
 
-HAllocator system_allocator;
+extern HAllocator system_allocator;
 
 
 typedef struct HInputStream_ {
@@ -110,8 +110,9 @@ struct HParseState_ {
 };
 
 typedef struct HParserBackendVTable_ {
-  int (*compile)(HAllocator *mm__, const HParser* parser, const void* params);
-  HParseResult* (*parse)(HAllocator *mm__, const HParser* parser, HParseState* parse_state);
+  int (*compile)(HAllocator *mm__, HParser* parser, const void* params);
+  HParseResult* (*parse)(HAllocator *mm__, const HParser* parser, HInputStream* parse_state);
+  void (*free)(HParser* parser);
 } HParserBackendVTable;
 
 
diff --git a/src/parsers/action.c b/src/parsers/action.c
index 05a9b224..cf51cd1c 100644
--- a/src/parsers/action.c
+++ b/src/parsers/action.c
@@ -41,11 +41,11 @@ static const HParserVtable action_vt = {
   .compile_to_rvm = action_ctrvm,
 };
 
-const HParser* h_action(const HParser* p, const HAction a) {
+HParser* h_action(const HParser* p, const HAction a) {
   return h_action__m(&system_allocator, p, a);
 }
 
-const HParser* h_action__m(HAllocator* mm__, const HParser* p, const HAction a) {
+HParser* h_action__m(HAllocator* mm__, const HParser* p, const HAction a) {
   HParser *res = h_new(HParser, 1);
   res->vtable = &action_vt;
   HParseAction *env = h_new(HParseAction, 1);
diff --git a/src/parsers/and.c b/src/parsers/and.c
index 6808d7f2..8107e54c 100644
--- a/src/parsers/and.c
+++ b/src/parsers/and.c
@@ -20,10 +20,10 @@ static const HParserVtable and_vt = {
 };
 
 
-const HParser* h_and(const HParser* p) {
+HParser* h_and(const HParser* p) {
   return h_and__m(&system_allocator, p);
 }
-const HParser* h_and__m(HAllocator* mm__, const HParser* p) {
+HParser* h_and__m(HAllocator* mm__, const HParser* p) {
   // zero-width postive lookahead
   HParser *res = h_new(HParser, 1);
   res->env = (void*)p;
diff --git a/src/parsers/attr_bool.c b/src/parsers/attr_bool.c
index 2ccabb5f..859f2924 100644
--- a/src/parsers/attr_bool.c
+++ b/src/parsers/attr_bool.c
@@ -40,10 +40,10 @@ static const HParserVtable attr_bool_vt = {
 };
 
 
-const HParser* h_attr_bool(const HParser* p, HPredicate pred) {
+HParser* h_attr_bool(const HParser* p, HPredicate pred) {
   return h_attr_bool__m(&system_allocator, p, pred);
 }
-const HParser* h_attr_bool__m(HAllocator* mm__, const HParser* p, HPredicate pred) { 
+HParser* h_attr_bool__m(HAllocator* mm__, const HParser* p, HPredicate pred) { 
   HParser *res = h_new(HParser, 1);
   res->vtable = &attr_bool_vt;
   HAttrBool *env = h_new(HAttrBool, 1);
diff --git a/src/parsers/bits.c b/src/parsers/bits.c
index 2f7f8a9d..8ce4deca 100644
--- a/src/parsers/bits.c
+++ b/src/parsers/bits.c
@@ -1,3 +1,4 @@
+#include <assert.h>
 #include "parser_internal.h"
 
 struct bits_env {
@@ -16,6 +17,19 @@ static HParseResult* parse_bits(void* env, HParseState *state) {
   return make_result(state, result);
 }
 
+static bool h_svm_action_bits(HArena *arena, HSVMContext *ctx, void* env) {
+  // BUG: relies un undefined behaviour: int64_t is a signed uint64_t; not necessarily true on 32-bit
+  struct bits_env *env_ = env;
+  HParsedToken *top = ctx->stack[ctx->stack_count-1];
+  assert(top->token_type == TT_BYTES);
+  uint64_t res = 0;
+  for (size_t i = 0; i < top->bytes.len; i++)
+    res = (res << 8) | top->bytes.token[i];   // TODO: Handle other endiannesses.
+  top->uint = res; // possibly cast to signed through union
+  top->token_type = (env_->signedp ? TT_SINT : TT_UINT);
+  return true;
+}
+
 static bool bits_ctrvm(HRVMProg *prog, void* env) {
   struct bits_env *env_ = (struct bits_env*)env;
   h_rvm_insert_insn(prog, RVM_PUSH, 0);
@@ -24,6 +38,7 @@ static bool bits_ctrvm(HRVMProg *prog, void* env) {
     h_rvm_insert_insn(prog, RVM_STEP, 0);
   }
   h_rvm_insert_insn(prog, RVM_CAPTURE, 0);
+  h_rvm_insert_insn(prog, RVM_ACTION, h_rvm_create_action(prog, h_svm_action_bits, env));
   return true;
 }
 
@@ -34,10 +49,10 @@ static const HParserVtable bits_vt = {
   .compile_to_rvm = bits_ctrvm,
 };
 
-const HParser* h_bits(size_t len, bool sign) {
+HParser* h_bits(size_t len, bool sign) {
   return h_bits__m(&system_allocator, len, sign);
 }
-const HParser* h_bits__m(HAllocator* mm__, size_t len, bool sign) {
+HParser* h_bits__m(HAllocator* mm__, size_t len, bool sign) {
   struct bits_env *env = h_new(struct bits_env, 1);
   env->length = len;
   env->signedp = sign;
@@ -48,10 +63,10 @@ const HParser* h_bits__m(HAllocator* mm__, size_t len, bool sign) {
 }
 
 #define SIZED_BITS(name_pre, len, signedp) \
-  const HParser* h_##name_pre##len () {				\
+  HParser* h_##name_pre##len () {				\
     return h_bits__m(&system_allocator, len, signedp);		\
   }								\
-  const HParser* h_##name_pre##len##__m(HAllocator* mm__) {	\
+  HParser* h_##name_pre##len##__m(HAllocator* mm__) {	\
     return h_bits__m(mm__, len, signedp);			\
   }
 SIZED_BITS(int, 8, true)
diff --git a/src/parsers/butnot.c b/src/parsers/butnot.c
index bf0cbd38..7a5b57e9 100644
--- a/src/parsers/butnot.c
+++ b/src/parsers/butnot.c
@@ -48,10 +48,10 @@ static const HParserVtable butnot_vt = {
   .compile_to_rvm = h_not_regular,
 };
 
-const HParser* h_butnot(const HParser* p1, const HParser* p2) {
+HParser* h_butnot(const HParser* p1, const HParser* p2) {
   return h_butnot__m(&system_allocator, p1, p2);
 }
-const HParser* h_butnot__m(HAllocator* mm__, const HParser* p1, const HParser* p2) {
+HParser* h_butnot__m(HAllocator* mm__, const HParser* p1, const HParser* p2) {
   HTwoParsers *env = h_new(HTwoParsers, 1);
   env->p1 = p1; env->p2 = p2;
   HParser *ret = h_new(HParser, 1);
diff --git a/src/parsers/ch.c b/src/parsers/ch.c
index 1f6526a5..68f4b06e 100644
--- a/src/parsers/ch.c
+++ b/src/parsers/ch.c
@@ -14,6 +14,7 @@ static HParseResult* parse_ch(void* env, HParseState *state) {
 
 static bool ch_ctrvm(HRVMProg *prog, void* env) {
   uint8_t c = (uint8_t)(unsigned long)(env);
+  // TODO: Does this capture anything?
   h_rvm_insert_insn(prog, RVM_MATCH, c & c << 8);
   h_rvm_insert_insn(prog, RVM_STEP, 0);
   return true;
@@ -26,12 +27,12 @@ static const HParserVtable ch_vt = {
   .compile_to_rvm = ch_ctrvm,
 };
 
-const HParser* h_ch(const uint8_t c) {
+HParser* h_ch(const uint8_t c) {
   return h_ch__m(&system_allocator, c);
 }
-const HParser* h_ch__m(HAllocator* mm__, const uint8_t c) {  
+HParser* h_ch__m(HAllocator* mm__, const uint8_t c) {  
   HParser *ret = h_new(HParser, 1);
   ret->vtable = &ch_vt;
   ret->env = (void*)(unsigned long)(c);
-  return (const HParser*)ret;
+  return ret;
 }
diff --git a/src/parsers/charset.c b/src/parsers/charset.c
index d09c5359..97c40ee2 100644
--- a/src/parsers/charset.c
+++ b/src/parsers/charset.c
@@ -59,21 +59,21 @@ static const HParserVtable charset_vt = {
   .compile_to_rvm = cs_ctrvm,
 };
 
-const HParser* h_ch_range(const uint8_t lower, const uint8_t upper) {
+HParser* h_ch_range(const uint8_t lower, const uint8_t upper) {
   return h_ch_range__m(&system_allocator, lower, upper);
 }
-const HParser* h_ch_range__m(HAllocator* mm__, const uint8_t lower, const uint8_t upper) {
+HParser* h_ch_range__m(HAllocator* mm__, const uint8_t lower, const uint8_t upper) {
   HParser *ret = h_new(HParser, 1);
   HCharset cs = new_charset(mm__);
   for (int i = 0; i < 256; i++)
     charset_set(cs, i, (lower <= i) && (i <= upper));
   ret->vtable = &charset_vt;
   ret->env = (void*)cs;
-  return (const HParser*)ret;
+  return ret;
 }
 
 
-static const HParser* h_in_or_not__m(HAllocator* mm__, const uint8_t *options, size_t count, int val) {
+static HParser* h_in_or_not__m(HAllocator* mm__, const uint8_t *options, size_t count, int val) {
   HParser *ret = h_new(HParser, 1);
   HCharset cs = new_charset(mm__);
   for (size_t i = 0; i < 256; i++)
@@ -83,22 +83,22 @@ static const HParser* h_in_or_not__m(HAllocator* mm__, const uint8_t *options, s
 
   ret->vtable = &charset_vt;
   ret->env = (void*)cs;
-  return (const HParser*)ret;
+  return ret;
 }
 
-const HParser* h_in(const uint8_t *options, size_t count) {
+HParser* h_in(const uint8_t *options, size_t count) {
   return h_in_or_not__m(&system_allocator, options, count, 1);
 }
 
-const HParser* h_in__m(HAllocator* mm__, const uint8_t *options, size_t count) {
+HParser* h_in__m(HAllocator* mm__, const uint8_t *options, size_t count) {
   return h_in_or_not__m(mm__, options, count, 1);
 }
 
-const HParser* h_not_in(const uint8_t *options, size_t count) {
+HParser* h_not_in(const uint8_t *options, size_t count) {
   return h_in_or_not__m(&system_allocator, options, count, 0);
 }
 
-const HParser* h_not_in__m(HAllocator* mm__, const uint8_t *options, size_t count) {
+HParser* h_not_in__m(HAllocator* mm__, const uint8_t *options, size_t count) {
   return h_in_or_not__m(mm__, options, count, 0);
 }
 
diff --git a/src/parsers/choice.c b/src/parsers/choice.c
index 435437af..4648f228 100644
--- a/src/parsers/choice.c
+++ b/src/parsers/choice.c
@@ -64,27 +64,27 @@ static const HParserVtable choice_vt = {
   .compile_to_rvm = choice_ctrvm,
 };
 
-const HParser* h_choice(const HParser* p, ...) {
+HParser* h_choice(const HParser* p, ...) {
   va_list ap;
   va_start(ap, p);
-  const HParser* ret = h_choice__mv(&system_allocator, p,  ap);
+  HParser* ret = h_choice__mv(&system_allocator, p,  ap);
   va_end(ap);
   return ret;
 }
 
-const HParser* h_choice__m(HAllocator* mm__, const HParser* p, ...) {
+HParser* h_choice__m(HAllocator* mm__, const HParser* p, ...) {
   va_list ap;
   va_start(ap, p);
-  const HParser* ret = h_choice__mv(mm__, p,  ap);
+  HParser* ret = h_choice__mv(mm__, p,  ap);
   va_end(ap);
   return ret;
 }
 
-const HParser* h_choice__v(const HParser* p, va_list ap) {
+HParser* h_choice__v(const HParser* p, va_list ap) {
   return h_choice__mv(&system_allocator, p, ap);
 }
 
-const HParser* h_choice__mv(HAllocator* mm__, const HParser* p, va_list ap_) {
+HParser* h_choice__mv(HAllocator* mm__, const HParser* p, va_list ap_) {
   va_list ap;
   size_t len = 0;
   HSequence *s = h_new(HSequence, 1);
diff --git a/src/parsers/difference.c b/src/parsers/difference.c
index 9c378625..891a512d 100644
--- a/src/parsers/difference.c
+++ b/src/parsers/difference.c
@@ -47,10 +47,10 @@ static HParserVtable difference_vt = {
   .compile_to_rvm = h_not_regular,
 };
 
-const HParser* h_difference(const HParser* p1, const HParser* p2) {
+HParser* h_difference(const HParser* p1, const HParser* p2) {
   return h_difference__m(&system_allocator, p1, p2);
 }
-const HParser* h_difference__m(HAllocator* mm__, const HParser* p1, const HParser* p2) { 
+HParser* h_difference__m(HAllocator* mm__, const HParser* p1, const HParser* p2) { 
   HTwoParsers *env = h_new(HTwoParsers, 1);
   env->p1 = p1; env->p2 = p2;
   HParser *ret = h_new(HParser, 1);
diff --git a/src/parsers/end.c b/src/parsers/end.c
index 2927cd01..5ec0e2f4 100644
--- a/src/parsers/end.c
+++ b/src/parsers/end.c
@@ -22,13 +22,13 @@ static const HParserVtable end_vt = {
   .compile_to_rvm = end_ctrvm,
 };
 
-const HParser* h_end_p() {
+HParser* h_end_p() {
   return h_end_p__m(&system_allocator);
 }
 
-const HParser* h_end_p__m(HAllocator* mm__) { 
+HParser* h_end_p__m(HAllocator* mm__) { 
   HParser *ret = h_new(HParser, 1);
   ret->vtable = &end_vt;
   ret->env = NULL;
-  return (const HParser*)ret;
+  return ret;
 }
diff --git a/src/parsers/epsilon.c b/src/parsers/epsilon.c
index 974cfa32..58ffcccf 100644
--- a/src/parsers/epsilon.c
+++ b/src/parsers/epsilon.c
@@ -24,9 +24,11 @@ static const HParser epsilon_p = {
   .env = NULL
 };
 
-const HParser* h_epsilon_p() {
-  return &epsilon_p;
+HParser* h_epsilon_p() {
+  return h_epsilon_p__m(&system_allocator);
 }
-const HParser* h_epsilon_p__m(HAllocator* mm__) {
-  return &epsilon_p;
+HParser* h_epsilon_p__m(HAllocator* mm__) {
+  HParser *epsilon_p = h_new(HParser, 1);
+  epsilon_p->vtable = &epsilon_vt;
+  return epsilon_p;
 }
diff --git a/src/parsers/ignore.c b/src/parsers/ignore.c
index ee734d81..5e715aca 100644
--- a/src/parsers/ignore.c
+++ b/src/parsers/ignore.c
@@ -1,5 +1,5 @@
+#include <assert.h>
 #include "parser_internal.h"
-#include "backends/regex_actions.h"
 
 static HParseResult* parse_ignore(void* env, HParseState* state) {
   HParseResult *res0 = h_do_parse((HParser*)env, state);
@@ -21,10 +21,16 @@ static bool ignore_isValidCF(void *env) {
   return (p->vtable->isValidCF(p->env));
 }
 
+static bool h_svm_action_pop(HArena *arena, HSVMContext *ctx, void* arg) {
+  assert(ctx->stack_count > 0);
+  ctx->stack_count--;
+  return true;
+}
+
 static bool ignore_ctrvm(HRVMProg *prog, void *env) {
   HParser *p = (HParser*)env;
   h_compile_regex(prog, p->env);
-  h_rvm_insert_insn(prog, RVM_ACTION, h_rvm_create_action(prog, h_svm_action_pop));
+  h_rvm_insert_insn(prog, RVM_ACTION, h_rvm_create_action(prog, h_svm_action_pop, NULL));
   return true;
 }
 
@@ -35,10 +41,10 @@ static const HParserVtable ignore_vt = {
   .compile_to_rvm = ignore_ctrvm,
 };
 
-const HParser* h_ignore(const HParser* p) {
+HParser* h_ignore(const HParser* p) {
   return h_ignore__m(&system_allocator, p);
 }
-const HParser* h_ignore__m(HAllocator* mm__, const HParser* p) {
+HParser* h_ignore__m(HAllocator* mm__, const HParser* p) {
   HParser* ret = h_new(HParser, 1);
   ret->vtable = &ignore_vt;
   ret->env = (void*)p;
diff --git a/src/parsers/ignoreseq.c b/src/parsers/ignoreseq.c
index 97b3e841..ae3e9e39 100644
--- a/src/parsers/ignoreseq.c
+++ b/src/parsers/ignoreseq.c
@@ -1,3 +1,4 @@
+#include <assert.h>
 #include "parser_internal.h"
 
 
@@ -49,14 +50,16 @@ static bool h_svm_action_ignoreseq(HArena *arena, HSVMContext *ctx, void* env) {
   HParsedToken* save;
   // We can assume that each subitem generated at most one item on the
   // stack.
+  assert(seq->len >= 1);
   for (int i = seq->len - 1; i>=0; i--) {
-    if (i == seq->which && ctx->stack[ctx->stack_count]->token_type != TT_MARK) 
+    if (i == (int)seq->which && ctx->stack[ctx->stack_count]->token_type != TT_MARK) 
       save = ctx->stack[ctx->stack_count-1];
     // skip over everything up to and including the mark.
     while (ctx->stack[--ctx->stack_count]->token_type != TT_MARK)
       ;
   }
   ctx->stack[ctx->stack_count++] = save;
+  return true;
 }
 
 static bool is_ctrvm(HRVMProg *prog, void* env) {
@@ -82,7 +85,7 @@ static const HParserVtable ignoreseq_vt = {
 // API frontends
 //
 
-static const HParser* h_leftright__m(HAllocator* mm__, const HParser* p, const HParser* q, size_t which) {
+static HParser* h_leftright__m(HAllocator* mm__, const HParser* p, const HParser* q, size_t which) {
   HIgnoreSeq *seq = h_new(HIgnoreSeq, 1);
   seq->parsers = h_new(const HParser*, 2);
   seq->parsers[0] = p;
@@ -96,25 +99,25 @@ static const HParser* h_leftright__m(HAllocator* mm__, const HParser* p, const H
   return ret;
 }
 
-const HParser* h_left(const HParser* p, const HParser* q) {
+HParser* h_left(const HParser* p, const HParser* q) {
   return h_leftright__m(&system_allocator, p, q, 0);
 }
-const HParser* h_left__m(HAllocator* mm__, const HParser* p, const HParser* q) {
+HParser* h_left__m(HAllocator* mm__, const HParser* p, const HParser* q) {
   return h_leftright__m(mm__, p, q, 0);
 }
 
-const HParser* h_right(const HParser* p, const HParser* q) {
+HParser* h_right(const HParser* p, const HParser* q) {
   return h_leftright__m(&system_allocator, p, q, 1);
 }
-const HParser* h_right__m(HAllocator* mm__, const HParser* p, const HParser* q) {
+HParser* h_right__m(HAllocator* mm__, const HParser* p, const HParser* q) {
   return h_leftright__m(mm__, p, q, 1);
 }
 
 
-const HParser* h_middle(const HParser* p, const HParser* x, const HParser* q) {
+HParser* h_middle(const HParser* p, const HParser* x, const HParser* q) {
   return h_middle__m(&system_allocator, p, x, q);
 }
-const HParser* h_middle__m(HAllocator* mm__, const HParser* p, const HParser* x, const HParser* q) {
+HParser* h_middle__m(HAllocator* mm__, const HParser* p, const HParser* x, const HParser* q) {
   HIgnoreSeq *seq = h_new(HIgnoreSeq, 1);
   seq->parsers = h_new(const HParser*, 3);
   seq->parsers[0] = p;
diff --git a/src/parsers/int_range.c b/src/parsers/int_range.c
index 40607d03..ed9d37c0 100644
--- a/src/parsers/int_range.c
+++ b/src/parsers/int_range.c
@@ -29,7 +29,7 @@ static HParseResult* parse_int_range(void *env, HParseState *state) {
 }
 
 bool h_svm_action_validate_int_range(HArena *arena, HSVMContext *ctx, void* env) {
-  HRange *r_env = (*HRange)env;
+  HRange *r_env = (HRange*)env;
   HParsedToken *head = ctx->stack[ctx->stack_count-1];
   switch (head-> token_type) {
   case TT_SINT: 
@@ -41,7 +41,7 @@ bool h_svm_action_validate_int_range(HArena *arena, HSVMContext *ctx, void* env)
   }
 }
 static bool ir_ctrvm(HRVMProg *prog, void *env) {
-  HRange *r_env = (*HRange)env;
+  HRange *r_env = (HRange*)env;
   
   h_compile_regex(prog, r_env->p);
   h_rvm_insert_insn(prog, RVM_ACTION, h_rvm_create_action(prog, h_svm_action_validate_int_range, env));
@@ -55,10 +55,10 @@ static const HParserVtable int_range_vt = {
   .compile_to_rvm = ir_ctrvm,
 };
 
-const HParser* h_int_range(const HParser *p, const int64_t lower, const int64_t upper) {
+HParser* h_int_range(const HParser *p, const int64_t lower, const int64_t upper) {
   return h_int_range__m(&system_allocator, p, lower, upper);
 }
-const HParser* h_int_range__m(HAllocator* mm__, const HParser *p, const int64_t lower, const int64_t upper) {
+HParser* h_int_range__m(HAllocator* mm__, const HParser *p, const int64_t lower, const int64_t upper) {
   // p must be an integer parser, which means it's using parse_bits
   // TODO: re-add this check
   //assert_message(p->vtable == &bits_vt, "int_range requires an integer parser"); 
diff --git a/src/parsers/many.c b/src/parsers/many.c
index 8b0786db..ed6e2900 100644
--- a/src/parsers/many.c
+++ b/src/parsers/many.c
@@ -61,16 +61,20 @@ static bool many_ctrvm(HRVMProg *prog, void *env) {
   // FIXME: Implement clear_to_mark
   uint16_t clear_to_mark = h_rvm_create_action(prog, h_svm_action_clear_to_mark, NULL);
   h_rvm_insert_insn(prog, RVM_PUSH, 0);
+  // TODO: implement min and max properly. Right now, it's always min==0, max==inf
   uint16_t insn = h_rvm_insert_insn(prog, RVM_FORK, 0);
   if (!h_compile_regex(prog, repeat->p))
     return false;
-  if (!h_compile_regex(prog, repeat->sep))
-    return false;
-  h_rvm_insert_insn(prog, RVM_ACTION, clear_to_mark);
+  if (repeat->sep != NULL) {
+    h_rvm_insert_insn(prog, RVM_PUSH, 0);
+    if (!h_compile_regex(prog, repeat->sep))
+      return false;
+    h_rvm_insert_insn(prog, RVM_ACTION, clear_to_mark);
+  }
   h_rvm_insert_insn(prog, RVM_GOTO, insn);
   h_rvm_patch_arg(prog, insn, h_rvm_get_ip(prog));
 
-  h_rvm_insert_insn(prog, RVM_ACTION, h_svm_action_make_sequence, NULL);
+  h_rvm_insert_insn(prog, RVM_ACTION, h_rvm_create_action(prog, h_svm_action_make_sequence, NULL));
   return true;
 }
 
@@ -81,10 +85,10 @@ static const HParserVtable many_vt = {
   .compile_to_rvm = many_ctrvm,
 };
 
-const HParser* h_many(const HParser* p) {
+HParser* h_many(const HParser* p) {
   return h_many__m(&system_allocator, p);
 }
-const HParser* h_many__m(HAllocator* mm__, const HParser* p) {
+HParser* h_many__m(HAllocator* mm__, const HParser* p) {
   HParser *res = h_new(HParser, 1);
   HRepeat *env = h_new(HRepeat, 1);
   env->p = p;
@@ -96,10 +100,10 @@ const HParser* h_many__m(HAllocator* mm__, const HParser* p) {
   return res;
 }
 
-const HParser* h_many1(const HParser* p) {
+HParser* h_many1(const HParser* p) {
   return h_many1__m(&system_allocator, p);
 }
-const HParser* h_many1__m(HAllocator* mm__, const HParser* p) {
+HParser* h_many1__m(HAllocator* mm__, const HParser* p) {
   HParser *res = h_new(HParser, 1);
   HRepeat *env = h_new(HRepeat, 1);
   env->p = p;
@@ -111,10 +115,10 @@ const HParser* h_many1__m(HAllocator* mm__, const HParser* p) {
   return res;
 }
 
-const HParser* h_repeat_n(const HParser* p, const size_t n) {
+HParser* h_repeat_n(const HParser* p, const size_t n) {
   return h_repeat_n__m(&system_allocator, p, n);
 }
-const HParser* h_repeat_n__m(HAllocator* mm__, const HParser* p, const size_t n) {
+HParser* h_repeat_n__m(HAllocator* mm__, const HParser* p, const size_t n) {
   HParser *res = h_new(HParser, 1);
   HRepeat *env = h_new(HRepeat, 1);
   env->p = p;
@@ -126,10 +130,10 @@ const HParser* h_repeat_n__m(HAllocator* mm__, const HParser* p, const size_t n)
   return res;
 }
 
-const HParser* h_sepBy(const HParser* p, const HParser* sep) {
+HParser* h_sepBy(const HParser* p, const HParser* sep) {
   return h_sepBy__m(&system_allocator, p, sep);
 }
-const HParser* h_sepBy__m(HAllocator* mm__, const HParser* p, const HParser* sep) {
+HParser* h_sepBy__m(HAllocator* mm__, const HParser* p, const HParser* sep) {
   HParser *res = h_new(HParser, 1);
   HRepeat *env = h_new(HRepeat, 1);
   env->p = p;
@@ -141,10 +145,10 @@ const HParser* h_sepBy__m(HAllocator* mm__, const HParser* p, const HParser* sep
   return res;
 }
 
-const HParser* h_sepBy1(const HParser* p, const HParser* sep) {
+HParser* h_sepBy1(const HParser* p, const HParser* sep) {
   return h_sepBy1__m(&system_allocator, p, sep);
 }
-const HParser* h_sepBy1__m(HAllocator* mm__, const HParser* p, const HParser* sep) {
+HParser* h_sepBy1__m(HAllocator* mm__, const HParser* p, const HParser* sep) {
   HParser *res = h_new(HParser, 1);
   HRepeat *env = h_new(HRepeat, 1);
   env->p = p;
@@ -184,10 +188,10 @@ static const HParserVtable length_value_vt = {
   .isValidCF = h_false,
 };
 
-const HParser* h_length_value(const HParser* length, const HParser* value) {
+HParser* h_length_value(const HParser* length, const HParser* value) {
   return h_length_value__m(&system_allocator, length, value);
 }
-const HParser* h_length_value__m(HAllocator* mm__, const HParser* length, const HParser* value) {
+HParser* h_length_value__m(HAllocator* mm__, const HParser* length, const HParser* value) {
   HParser *res = h_new(HParser, 1);
   res->vtable = &length_value_vt;
   HLenVal *env = h_new(HLenVal, 1);
diff --git a/src/parsers/not.c b/src/parsers/not.c
index b01282cd..4be95a33 100644
--- a/src/parsers/not.c
+++ b/src/parsers/not.c
@@ -14,13 +14,13 @@ static const HParserVtable not_vt = {
   .parse = parse_not,
   .isValidRegular = h_false,  /* see and.c for why */
   .isValidCF = h_false,       /* also see and.c for why */
-  .compile_to_rvm = h_not_regular,
+  .compile_to_rvm = h_not_regular, // Is actually regular, but the generation step is currently unable to handle it. TODO: fix this.
 };
 
-const HParser* h_not(const HParser* p) {
+HParser* h_not(const HParser* p) {
   return h_not__m(&system_allocator, p);
 }
-const HParser* h_not__m(HAllocator* mm__, const HParser* p) {
+HParser* h_not__m(HAllocator* mm__, const HParser* p) {
   HParser *res = h_new(HParser, 1);
   res->vtable = &not_vt;
   res->env = (void*)p;
diff --git a/src/parsers/nothing.c b/src/parsers/nothing.c
index 19b8bbda..b59e7455 100644
--- a/src/parsers/nothing.c
+++ b/src/parsers/nothing.c
@@ -6,7 +6,8 @@ static HParseResult* parse_nothing() {
 }
 
 static bool nothing_ctrvm(HRVMProg *prog, void* env) {
-  h_rvm_insert_insn(prog, RVM_MATCH, 0x00FF);
+  h_rvm_insert_insn(prog, RVM_MATCH, 0x0000);
+  h_rvm_insert_insn(prog, RVM_MATCH, 0xFFFF);
   return true;
 }
 
@@ -17,11 +18,11 @@ static const HParserVtable nothing_vt = {
   .compile_to_rvm = nothing_ctrvm,
 };
 
-const HParser* h_nothing_p() {
+HParser* h_nothing_p() {
   return h_nothing_p__m(&system_allocator);
 }
-const HParser* h_nothing_p__m(HAllocator* mm__) { 
+HParser* h_nothing_p__m(HAllocator* mm__) { 
   HParser *ret = h_new(HParser, 1);
   ret->vtable = &nothing_vt; ret->env = NULL;
-  return (const HParser*)ret;
+  return ret;
 }
diff --git a/src/parsers/optional.c b/src/parsers/optional.c
index 203d4a7f..4a3cb8a5 100644
--- a/src/parsers/optional.c
+++ b/src/parsers/optional.c
@@ -1,3 +1,4 @@
+#include <assert.h>
 #include "parser_internal.h"
 
 static HParseResult* parse_optional(void* env, HParseState* state) {
@@ -21,12 +22,25 @@ static bool opt_isValidCF(void *env) {
   return p->vtable->isValidCF(p->env);
 }
 
+static bool h_svm_action_optional(HArena *arena, HSVMContext *ctx, void *env) {
+  if (ctx->stack[ctx->stack_count-1]->token_type == TT_MARK) {
+    ctx->stack[ctx->stack_count-1]->token_type = TT_NONE;
+  } else {
+    ctx->stack_count--;
+    assert(ctx->stack[ctx->stack_count-1]->token_type == TT_MARK);
+    ctx->stack[ctx->stack_count-1] = ctx->stack[ctx->stack_count];
+  }
+  return true;
+}
+
 static bool opt_ctrvm(HRVMProg *prog, void* env) {
+  h_rvm_insert_insn(prog, RVM_PUSH, 0);
   uint16_t insn = h_rvm_insert_insn(prog, RVM_FORK, 0);
   HParser *p = (HParser*) env;
   if (!h_compile_regex(prog, p->env))
     return false;
   h_rvm_patch_arg(prog, insn, h_rvm_get_ip(prog));
+  h_rvm_insert_insn(prog, RVM_ACTION, h_rvm_create_action(prog, h_svm_action_optional, NULL));
   return true;
 }
 
@@ -37,10 +51,10 @@ static const HParserVtable optional_vt = {
   .compile_to_rvm = opt_ctrvm,
 };
 
-const HParser* h_optional(const HParser* p) {
+HParser* h_optional(const HParser* p) {
   return h_optional__m(&system_allocator, p);
 }
-const HParser* h_optional__m(HAllocator* mm__, const HParser* p) {
+HParser* h_optional__m(HAllocator* mm__, const HParser* p) {
   // TODO: re-add this
   //assert_message(p->vtable != &ignore_vt, "Thou shalt ignore an option, rather than the other way 'round.");
   HParser *ret = h_new(HParser, 1);
diff --git a/src/parsers/sequence.c b/src/parsers/sequence.c
index 6bfab0d5..2eeafde1 100644
--- a/src/parsers/sequence.c
+++ b/src/parsers/sequence.c
@@ -44,10 +44,12 @@ static bool sequence_isValidCF(void *env) {
 
 static bool sequence_ctrvm(HRVMProg *prog, void *env) {
   HSequence *s = (HSequence*)env;
+  h_rvm_insert_insn(prog, RVM_PUSH, 0);
   for (size_t i=0; i<s->len; ++i) {
     if (!s->p_array[i]->vtable->compile_to_rvm(prog, s->p_array[i]->env))
       return false;
   }
+  h_rvm_insert_insn(prog, RVM_ACTION, h_rvm_create_action(prog, h_svm_action_make_sequence, NULL));
   return true;
 }
 
@@ -58,27 +60,27 @@ static const HParserVtable sequence_vt = {
   .compile_to_rvm = sequence_ctrvm,
 };
 
-const HParser* h_sequence(const HParser* p, ...) {
+HParser* h_sequence(const HParser* p, ...) {
   va_list ap;
   va_start(ap, p);
-  const HParser* ret = h_sequence__mv(&system_allocator, p,  ap);
+  HParser* ret = h_sequence__mv(&system_allocator, p,  ap);
   va_end(ap);
   return ret;
 }
 
-const HParser* h_sequence__m(HAllocator* mm__, const HParser* p, ...) {
+HParser* h_sequence__m(HAllocator* mm__, const HParser* p, ...) {
   va_list ap;
   va_start(ap, p);
-  const HParser* ret = h_sequence__mv(mm__, p,  ap);
+  HParser* ret = h_sequence__mv(mm__, p,  ap);
   va_end(ap);
   return ret;
 }
 
-const HParser* h_sequence__v(const HParser* p, va_list ap) {
+HParser* h_sequence__v(const HParser* p, va_list ap) {
   return h_sequence__mv(&system_allocator, p, ap);
 }
 
-const HParser* h_sequence__mv(HAllocator* mm__, const HParser *p, va_list ap_) {
+HParser* h_sequence__mv(HAllocator* mm__, const HParser *p, va_list ap_) {
   va_list ap;
   size_t len = 0;
   const HParser *arg;
diff --git a/src/parsers/token.c b/src/parsers/token.c
index 7050724d..a91bc35a 100644
--- a/src/parsers/token.c
+++ b/src/parsers/token.c
@@ -38,14 +38,14 @@ const HParserVtable token_vt = {
   .compile_to_rvm = token_ctrvm,
 };
 
-const HParser* h_token(const uint8_t *str, const size_t len) {
+HParser* h_token(const uint8_t *str, const size_t len) {
   return h_token__m(&system_allocator, str, len);
 }
-const HParser* h_token__m(HAllocator* mm__, const uint8_t *str, const size_t len) { 
+HParser* h_token__m(HAllocator* mm__, const uint8_t *str, const size_t len) { 
   HToken *t = h_new(HToken, 1);
   t->str = (uint8_t*)str, t->len = len;
   HParser *ret = h_new(HParser, 1);
   ret->vtable = &token_vt;
   ret->env = t;
-  return (const HParser*)ret;
+  return ret;
 }
diff --git a/src/parsers/whitespace.c b/src/parsers/whitespace.c
index 325b4318..ebe170cc 100644
--- a/src/parsers/whitespace.c
+++ b/src/parsers/whitespace.c
@@ -26,6 +26,16 @@ static bool ws_isValidCF(void *env) {
 
 static bool ws_ctrvm(HRVMProg *prog, void *env) {
   HParser *p = (HParser*)env;
+  uint16_t start = h_rvm_get_ip(prog);
+  uint16_t next;
+  const char SPACE_CHRS[6] = {' ', '\f', '\n', '\r', '\t', '\v'};
+
+  for (int i = 0; i < 6; i++) {
+    next = h_rvm_insert_insn(prog, RVM_FORK, 0);
+    h_rvm_insert_insn(prog, RVM_MATCH, (SPACE_CHRS[i] << 8) | (SPACE_CHRS[i]));
+    h_rvm_insert_insn(prog, RVM_GOTO, start);
+    h_rvm_patch_arg(prog, next, h_rvm_get_ip(prog));
+  }
   return h_compile_regex(prog, p->env);
 }
 
@@ -36,10 +46,10 @@ static const HParserVtable whitespace_vt = {
   .compile_to_rvm = ws_ctrvm,
 };
 
-const HParser* h_whitespace(const HParser* p) {
+HParser* h_whitespace(const HParser* p) {
   return h_whitespace__m(&system_allocator, p);
 }
-const HParser* h_whitespace__m(HAllocator* mm__, const HParser* p) {
+HParser* h_whitespace__m(HAllocator* mm__, const HParser* p) {
   HParser *ret = h_new(HParser, 1);
   ret->vtable = &whitespace_vt;
   ret->env = (void*)p;
diff --git a/src/parsers/xor.c b/src/parsers/xor.c
index d6fff082..12d13aab 100644
--- a/src/parsers/xor.c
+++ b/src/parsers/xor.c
@@ -44,10 +44,10 @@ static const HParserVtable xor_vt = {
   .compile_to_rvm = h_not_regular,
 };
 
-const HParser* h_xor(const HParser* p1, const HParser* p2) {
+HParser* h_xor(const HParser* p1, const HParser* p2) {
   return h_xor__m(&system_allocator, p1, p2);
 }
-const HParser* h_xor__m(HAllocator* mm__, const HParser* p1, const HParser* p2) { 
+HParser* h_xor__m(HAllocator* mm__, const HParser* p1, const HParser* p2) { 
   HTwoParsers *env = h_new(HTwoParsers, 1);
   env->p1 = p1; env->p2 = p2;
   HParser *ret = h_new(HParser, 1);
diff --git a/src/system_allocator.c b/src/system_allocator.c
index 7248fd2f..80d7acf2 100644
--- a/src/system_allocator.c
+++ b/src/system_allocator.c
@@ -1,16 +1,27 @@
+#include <string.h>
 #include <stdlib.h>
 #include "internal.h"
 
 static void* system_alloc(HAllocator *allocator, size_t size) {
-  return malloc(size);
+  
+  void* ptr = calloc(size + sizeof(size_t), 1);
+  *(size_t*)ptr = size;
+  return ptr + sizeof(size_t);
 }
 
 static void* system_realloc(HAllocator *allocator, void* ptr, size_t size) {
-  return realloc(ptr, size);
+  if (ptr == NULL)
+    return system_alloc(allocator, size);
+  ptr = realloc(ptr - sizeof(size_t), size + sizeof(size_t));
+  size_t old_size = *(size_t*)ptr;
+  *(size_t*)ptr = size;
+  if (size > old_size)
+    memset(ptr+sizeof(size_t)+old_size, 0, size - old_size);
+  return ptr + sizeof(size_t);
 }
 
 static void system_free(HAllocator *allocator, void* ptr) {
-  free(ptr);
+  free(ptr - sizeof(size_t));
 }
 
 HAllocator system_allocator = {
diff --git a/src/t_benchmark.c b/src/t_benchmark.c
index 60d22c51..5cabbe58 100644
--- a/src/t_benchmark.c
+++ b/src/t_benchmark.c
@@ -11,7 +11,7 @@ HParserTestcase testcases[] = {
 };
 
 static void test_benchmark_1() {
-  const HParser *parser = h_sepBy1(h_choice(h_ch('1'), h_ch('2'), h_ch('3'), NULL), h_ch(',')); 
+  HParser *parser = h_sepBy1(h_choice(h_ch('1'), h_ch('2'), h_ch('3'), NULL), h_ch(',')); 
 
   HBenchmarkResults *res = h_benchmark(parser, testcases);
   h_benchmark_report(stderr, res);
diff --git a/src/test_suite.h b/src/test_suite.h
index 3a76beec..be4a0c40 100644
--- a/src/test_suite.h
+++ b/src/test_suite.h
@@ -99,14 +99,14 @@
     } else {								\
       char* cres = h_write_result_unamb(res->ast);			\
       g_check_string(cres, ==, result);					\
-      g_free(cres);							\
+      system_allocator.free(&system_allocator, cres);			\
       HArenaStats stats;						\
       h_allocator_stats(res->arena, &stats);				\
       g_test_message("Parse used %zd bytes, wasted %zd bytes. "		\
                      "Inefficiency: %5f%%",				\
 		     stats.used, stats.wasted,				\
 		     stats.wasted * 100. / (stats.used+stats.wasted));	\
-      h_delete_arena(res->arena);						\
+      h_delete_arena(res->arena);					\
     }									\
   } while(0)
 
-- 
GitLab