diff --git a/examples/base64.c b/examples/base64.c
index e9554067d314137ea9677c4c0cee2faa73131488..cdb89aafba4be064064c3204872e4c6999653f79 100644
--- a/examples/base64.c
+++ b/examples/base64.c
@@ -11,31 +11,76 @@
 #include <inttypes.h>
 #include "../src/hammer.h"
 
+#define DEBUG
+
 const HParser* document = NULL;
 
 void init_parser(void)
 {
     // CORE
-    HParser *digit = h_ch_range(0x30, 0x39);
-    HParser *alpha = h_choice(h_ch_range(0x41, 0x5a), h_ch_range(0x61, 0x7a), NULL);
+    const HParser *digit = h_ch_range(0x30, 0x39);
+    const HParser *alpha = h_choice(h_ch_range(0x41, 0x5a), h_ch_range(0x61, 0x7a), NULL);
 
     // AUX.
-    HParser *plus = h_ch('+');
-    HParser *slash = h_ch('/');
-    HParser *equals = h_ch('=');
-
-    HParser *bsfdig = h_choice(alpha, digit, plus, slash, NULL);
-    HParser *bsfdig_4bit = h_in((uint8_t *)"AEIMQUYcgkosw048", 16);
-    HParser *bsfdig_2bit = h_in((uint8_t *)"AQgw", 4);
-    HParser *base64_3 = h_repeat_n(bsfdig, 4);
-    HParser *base64_2 = h_sequence(bsfdig, bsfdig, bsfdig_4bit, equals, NULL);
-    HParser *base64_1 = h_sequence(bsfdig, bsfdig_2bit, equals, equals, NULL);
-    HParser *base64 = h_sequence(h_many(base64_3),
-                                       h_optional(h_choice(base64_2,
-                                                           base64_1, NULL)),
-                                       NULL);
-
-    document = h_sequence(h_whitespace(base64), h_whitespace(h_end_p()), NULL);
+    const HParser *plus = h_ch('+');
+    const HParser *slash = h_ch('/');
+    const HParser *equals = h_ch('=');
+
+    const HParser *bsfdig = h_choice(alpha, digit, plus, slash, NULL);
+    const HParser *bsfdig_4bit = h_choice(
+        h_ch('A'), h_ch('E'), h_ch('I'), h_ch('M'), h_ch('Q'), h_ch('U'),
+        h_ch('Y'), h_ch('c'), h_ch('g'), h_ch('k'), h_ch('o'), h_ch('s'),
+        h_ch('w'), h_ch('0'), h_ch('4'), h_ch('8'), NULL);
+    const HParser *bsfdig_2bit = h_choice(h_ch('A'), h_ch('Q'), h_ch('g'), h_ch('w'), NULL);
+
+    const HParser *base64_quad = h_sequence(bsfdig, bsfdig, bsfdig, bsfdig, NULL);
+    const HParser *base64_quads = h_many(base64_quad);
+
+    const HParser *base64_2 = h_sequence(bsfdig, bsfdig, bsfdig_4bit, equals, h_end_p(), NULL);
+    const HParser *base64_1 = h_sequence(bsfdig, bsfdig_2bit, equals, equals, h_end_p(), NULL);
+    const HParser *base64_ending = h_choice(h_end_p(), base64_2, base64_1, NULL);
+    const HParser *base64 = h_sequence(base64_quads, base64_ending, NULL);
+        // why does this parse "A=="?!
+        // why does this parse "aaA=" but not "aA=="?!
+
+    document = base64;
+}
+
+
+#include <string.h>
+#include <assert.h>
+#define TRUE (1)
+#define FALSE (0)
+
+void assert_parse(int expected, char *data) {
+    const HParseResult *result;
+
+    size_t datasize = strlen(data);
+    result = h_parse(document, (void*)data, datasize);
+    if((result != NULL) != expected) {
+        fprintf(stderr, "Test failed: %s\n", data);
+    }
+#ifdef DEBUG
+    else {
+        fprintf(stderr, "Test succeeded: %s\n", data);
+        fprintf(stderr, "parsed=%lld bytes\n", result->bit_length/8);
+        h_pprint(stdout, result->ast, 0, 0);
+    }
+#endif
+}
+
+void test() {
+    assert_parse(TRUE, "");
+    assert_parse(TRUE, "YQ==");
+    assert_parse(TRUE, "YXU=");
+    assert_parse(TRUE, "YXVy");
+    assert_parse(TRUE, "QVVSIFNBUkFG");
+    assert_parse(TRUE, "QVVSIEhFUlUgU0FSQUY=");
+    assert_parse(FALSE, "A");
+    assert_parse(FALSE, "A=");
+    assert_parse(FALSE, "A==");
+    assert_parse(FALSE, "AAA==");
+    assert_parse(FALSE, "aa==");
 }
 
 
@@ -49,6 +94,8 @@ int main(int argc, char **argv)
 
     init_parser();
 
+    test();
+
     inputsize = fread(input, 1, sizeof(input), stdin);
     fprintf(stderr, "inputsize=%zu\ninput=", inputsize);
     fwrite(input, 1, inputsize, stderr);
diff --git a/src/bindings/lua/hammer.lua b/src/bindings/lua/hammer.lua
index 2ee1656a098633801610a4ee181366d13dd69d10..011f5c3596179b63d74ec0a5e7ebcdfe627a4185 100644
--- a/src/bindings/lua/hammer.lua
+++ b/src/bindings/lua/hammer.lua
@@ -140,11 +140,51 @@ local function append(a, ...)
   return helper(a, select('#', ...), ...)
 end
 
+-- Exponents do a lot of heavy lifting in Lpeg,
+-- which is the overloading template we're going to follow
+local function __pow(rule, power)
+  assert(type(power) == "number")
+  if power == 0 then
+    return h.h_many(rule)
+  elseif power == 1 then
+    return h.h_many1(rule)
+  elseif power == -1 then
+    return h.h_optional(rule)
+  end
+end
+
+
 local mt = {
   __index = {
     parse = function(p, str) return h.h_parse(p, str, #str) end,
   },
+  __add = function(left, right)
+    return h.h_choice(left, right)
+  end,
+  __mul = function(left, right)
+    return h.h_sequence(left, right)
+  end,
+  __pow = __pow,
+  __len = function(rule)
+    return h.h_and(rule)
+  end,
+  __unm = function(rule)
+    return h.h_not(rule)
+  end,
+  __sub = function(left, right)
+    return h.h_sequence(left, h.h_not(right))
+  end,
+  -- Lpeg doesn't use modulus, let's use it for n number of
+  -- repetitions
+  __mod = function(rule, reps)
+    assert(type(reps) == "number")
+    return h.h_repeat_n(rule, reps)
+  end,
+  __div = function(rule, cb)
+    return hammer.action(rule, cb)
+  end
 }
+
 local hammer = {}
 hammer.parser = ffi.metatype("HParser", mt)
 
@@ -155,10 +195,10 @@ local arr_mt = {
   end,
   __len = function(table) return table.used end,
   __ipairs = function(table)
-    local i, n = 0, #table
+    local i, n = -1, #table
     return function()
       i = i + 1
-      if i <= n then
+      if i < n then
         return i, table.elements[i]
       end
     end
@@ -166,7 +206,7 @@ local arr_mt = {
   __call = function(self)
     ret = {}
     for i, v in ipairs(self)
-      do ret[#ret+1] = v
+      do ret[#ret+1] = v()
     end
     return ret
   end
@@ -184,6 +224,14 @@ local bytes_mt = {
 }
 local byte_string = ffi.metatype("HBytes", bytes_mt)
 
+local result_mt = {
+   __call = function(self)
+      return self.ast()
+   end
+}
+
+local parse_result = ffi.metatype("HParseResult", result_mt)
+
 local token_types = ffi.new("HTokenType")
 
 local parsed_token