From 106b8bb6a752d3aa064350806b19bd30241049c0 Mon Sep 17 00:00:00 2001
From: aegis <github@xn--g-3fa.is>
Date: Fri, 10 May 2013 12:05:20 +0200
Subject: [PATCH] JNI ready for testing. -fPIC enabled for objects in src and
 jni.

---
 Makefile                                      |   2 +-
 common.mk                                     |   2 +-
 jni/Example.java                              |  97 +++--
 jni/Makefile                                  |  42 +++
 jni/NOTES                                     |  30 +-
 jni/com/upstandinghackers/hammer/Action.java  |   8 +
 jni/com/upstandinghackers/hammer/HAction.java |   8 -
 .../hammer/HParseResult.java                  |   9 -
 .../hammer/HParsedToken.java                  |  28 --
 jni/com/upstandinghackers/hammer/HParser.java |   6 -
 .../upstandinghackers/hammer/HPredicate.java  |   6 -
 jni/com/upstandinghackers/hammer/Hammer.java  | 109 +++---
 .../upstandinghackers/hammer/ParseResult.java |  15 +
 .../upstandinghackers/hammer/ParsedToken.java |  40 +++
 jni/com/upstandinghackers/hammer/Parser.java  |  11 +
 .../upstandinghackers/hammer/Predicate.java   |   6 +
 jni/com_upstandinghackers_hammer_Hammer.c     | 335 ++++++++++++++++++
 ...com_upstandinghackers_hammer_ParseResult.c |  45 +++
 ...com_upstandinghackers_hammer_ParsedToken.c | 195 ++++++++++
 jni/com_upstandinghackers_hammer_Parser.c     |  15 +
 jni/jhammer.h                                 |  31 ++
 src/Makefile                                  |   2 +
 src/hammer.h                                  |   8 +-
 src/parsers/choice.c                          |  24 ++
 src/parsers/sequence.c                        |  25 ++
 25 files changed, 954 insertions(+), 145 deletions(-)
 create mode 100644 jni/Makefile
 create mode 100644 jni/com/upstandinghackers/hammer/Action.java
 delete mode 100644 jni/com/upstandinghackers/hammer/HAction.java
 delete mode 100644 jni/com/upstandinghackers/hammer/HParseResult.java
 delete mode 100644 jni/com/upstandinghackers/hammer/HParsedToken.java
 delete mode 100644 jni/com/upstandinghackers/hammer/HParser.java
 delete mode 100644 jni/com/upstandinghackers/hammer/HPredicate.java
 create mode 100644 jni/com/upstandinghackers/hammer/ParseResult.java
 create mode 100644 jni/com/upstandinghackers/hammer/ParsedToken.java
 create mode 100644 jni/com/upstandinghackers/hammer/Parser.java
 create mode 100644 jni/com/upstandinghackers/hammer/Predicate.java
 create mode 100644 jni/com_upstandinghackers_hammer_Hammer.c
 create mode 100644 jni/com_upstandinghackers_hammer_ParseResult.c
 create mode 100644 jni/com_upstandinghackers_hammer_ParsedToken.c
 create mode 100644 jni/com_upstandinghackers_hammer_Parser.c
 create mode 100644 jni/jhammer.h

diff --git a/Makefile b/Makefile
index bd383a22..a70cc406 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 # and kick off a recursive make
 # Also, "make src/all" turns into "make -C src all"
 
-SUBDIRS = src examples
+SUBDIRS = src examples jni
 
 include config.mk
 
diff --git a/common.mk b/common.mk
index a57429db..0259d5fb 100644
--- a/common.mk
+++ b/common.mk
@@ -8,7 +8,7 @@ include $(TOPLEVEL)/config.mk
 TEST_CFLAGS = $(shell pkg-config --cflags glib-2.0) -DINCLUDE_TESTS
 TEST_LDFLAGS = $(shell pkg-config --libs glib-2.0)
 
-CFLAGS := -std=gnu99 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-attributes
+CFLAGS := -std=gnu99 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-attributes -g
 LDFLAGS :=
 
 CC ?= gcc
diff --git a/jni/Example.java b/jni/Example.java
index 5871d798..8cfa7242 100644
--- a/jni/Example.java
+++ b/jni/Example.java
@@ -1,38 +1,18 @@
+import com.upstandinghackers.hammer.*;
+import java.util.Arrays;
 /**
 * Example JHammer usage
 */
+
 public class Example
 {
 
-private HParser initParser()
-{
-    HParser digit = Hammer.chRange(0x30, 0x39);
-    HParser alpha = Hammer.choice({Hammer.chRange(0x41, 0x5a), Hammer.chRange(0x61, 0x7a)});
-    
-    HParser plus = Hammer.ch('+');
-    HParser slash = Hammer.ch('/');
-    HParser equals = Hammer.ch('=');
-    HParser bsfdig = Hammer.choice({alpha, digit, plus, slash});
-
-    byte[] AEIMQUYcgkosw048 = "AEIMQUYcgkosw048".getBytes();
-    HParser bsfdig_4bit = Hammer.in(AEIMQUYcgkosw048, AEIMQUYcgkosw048.length);
-    byte[] AQgw = "AQgw".getBytes();
-    HParser bsfdig_2bit = Hammer.in(AQgw, AQgw.length);
-    HParser base64_3 = Hammer.repeatN(bsfdig, 4);
-    HParser base64_2 = Hammer.sequence({bsfdig, bsfdig, bsfdig_4bit, equals});
-    HParser base64_1 = Hammer.sequence({bsfdig, bsfdig_2bit, equals, equals});
-    HParser base64 = Hammer.sequence({  Hammer.many(base64_3),
-                                        Hammer.optional(Hammer.choice({base64_2, base64_1}))
-                                    });
-
-    return Hammer.sequence({Hammer.whitespace(base64), Hammer.whitespace(Hammer.endP()}});
+static {
+    System.loadLibrary("jhammer");
 }
 
-public static void main(String args[])
+private static void handle(ParseResult result)
 {
-    byte[] input = "RXMgaXN0IFNwYXJnZWx6ZWl0IQo=".getBytes();
-    int length = input.length;
-    HParsedResult result = Hammer.parse(initParser(), input, length);
     if(result == null)
     {
         System.out.println("FAIL");
@@ -40,10 +20,73 @@ public static void main(String args[])
     else
     {
         System.out.println("PASS");
-        //TODO: Pretty print
+        handleToken(result.getAst());
+    }
+}
+
+private static void handleToken(ParsedToken p)
+{
+    if(p==null)
+    {
+        System.out.println("Empty AST");
+        return;
+    }
+    switch(p.getTokenType())
+    {
+        case NONE: out("NONE token type"); break;
+        case BYTES: out("BYTES token type, value: " + Arrays.toString(p.getBytesValue())); break;
+        case SINT: out("SINT token type, value: " + p.getSIntValue()); break;
+        case UINT: out("UINT token type, value: " + p.getUIntValue()); break;
+        case SEQUENCE: out("SEQUENCE token type"); for(ParsedToken tok : p.getSeqValue()) {handleToken(tok);} break;
+        case ERR: out("ERR token type"); break;
+        case USER: out("USER token type"); break;
     }
 }
 
+private static void out(String msg)
+{
+    System.out.println(">> " + msg);
+}
+
+public static void main(String args[])
+{
+    out("chRange");
+    handle(Hammer.parse(Hammer.chRange((byte)0x30, (byte)0x39), "1".getBytes(), 1));
+    handle(Hammer.parse(Hammer.chRange((byte)0x30, (byte)0x39), "a".getBytes(), 1));
+    
+    out("ch");
+    handle(Hammer.parse(Hammer.ch((byte)0x31), "1".getBytes(), 1));
+    handle(Hammer.parse(Hammer.ch((byte)0x31), "0".getBytes(), 1));
+    
+    out("token");
+    handle(Hammer.parse(Hammer.token("herp".getBytes(), 4), "herp".getBytes(), 4));
+    handle(Hammer.parse(Hammer.token("herp".getBytes(), 4), "derp".getBytes(), 4));
+    
+    out("intRange");
+    byte inbytes[] = {0x31, 0x31, 0x31, 0x31};
+    handle(Hammer.parse(Hammer.intRange(Hammer.uInt8(), 0L, 0x32), inbytes, inbytes.length));
+    handle(Hammer.parse(Hammer.intRange(Hammer.uInt8(), 0L, 0x30), inbytes, inbytes.length));
+
+    out("bits");
+    handle(Hammer.parse(Hammer.bits(7, false), inbytes, inbytes.length));
+    
+    out("int64");
+    byte ints[] = {(byte)0x8F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
+    handle(Hammer.parse(Hammer.int64(), ints, ints.length));
+    handle(Hammer.parse(Hammer.int64(), inbytes, inbytes.length));
+
+    out("choice");
+    Parser two32s[] = {Hammer.intRange(Hammer.uInt32(), 0x00, 0x01), Hammer.int32()};
+    handle(Hammer.parse(Hammer.choice(two32s), ints, ints.length));
+
+    out("sequence");
+    byte i3[] = {(byte)'i', (byte)3, (byte)0xFF};
+    Parser i3parsers[] = {Hammer.ch((byte)'i'), Hammer.uInt8(), Hammer.int8()};
+    handle(Hammer.parse(Hammer.sequence(i3parsers), i3, i3.length));
+
+    
+}
+
 
 
 }
diff --git a/jni/Makefile b/jni/Makefile
new file mode 100644
index 00000000..85be9733
--- /dev/null
+++ b/jni/Makefile
@@ -0,0 +1,42 @@
+JSOURCES := Action.java Hammer.java ParsedToken.java ParseResult.java Parser.java Predicate.java
+JSOURCES_NATIVE := Hammer ParsedToken Parser ParseResult
+CSOURCES := com_upstandinghackers_hammer_Hammer.c com_upstandinghackers_hammer_ParsedToken.c com_upstandinghackers_hammer_Parser.c com_upstandinghackers_hammer_ParseResult.c
+
+# ls *.h *.o *.so com/upstandinghackers/hammer/*.class | grep -v jhammer.h | tr '\n' ' '; replace single $ with $$
+OUTPUTS := com/upstandinghackers/hammer/Action.class com/upstandinghackers/hammer/Hammer.class com_upstandinghackers_hammer_Hammer.h com_upstandinghackers_hammer_Hammer.o com/upstandinghackers/hammer/Hammer\$TokenType.class com_upstandinghackers_hammer_Hammer_TokenType.h com/upstandinghackers/hammer/ParsedToken.class com_upstandinghackers_hammer_ParsedToken.h com_upstandinghackers_hammer_ParsedToken.o com/upstandinghackers/hammer/Parser.class com/upstandinghackers/hammer/ParseResult.class com_upstandinghackers_hammer_ParseResult.h com_upstandinghackers_hammer_ParseResult.o com_upstandinghackers_hammer_Parser.h com_upstandinghackers_hammer_Parser.o com/upstandinghackers/hammer/Predicate.class libjhammer.so
+
+TOPLEVEL := ../
+
+JC=javac
+JH=javah
+CP=com/upstandinghackers/hammer
+PACKAGE=com.upstandinghackers.hammer
+
+include ../common.mk
+
+JNI_INCLUDE := /usr/lib/jvm/java-6-openjdk/include/
+CFLAGS += -fPIC -I. -I $(TOPLEVEL)/src/ -I jni -I $(JNI_INCLUDE) 
+
+%.java: $(call ifsilent,| $(HUSH))
+	$(call hush, "Compiling Java source $@") $(JC) $(CP)/$@
+
+all: javacc prepare compile link
+
+link: compile
+	$(call hush, "Generating libjhammer.so") $(CC) -shared $(CFLAGS) -o libjhammer.so *.o ../src/*.o ../src/backends/*.o ../src/parsers/*.o
+
+$(CSOURCES): prepare
+	$(call hush, "Compiling $@") $(CC) -c $(CFLAGS) $@
+
+compile: prepare $(CSOURCES)
+
+prepare: javacc $(JSOURCES_NATIVE)
+
+$(JSOURCES_NATIVE): javacc
+	$(call hush, "Generating JNI headers for $@") $(JH) $(PACKAGE).$@
+
+javacc: $(JSOURCES)
+
+#TODO make this not-as-hardcoded
+#clean:
+#	rm $(CP)/*.class && rm com_upstandinghackers_*.h && rm com_upstandinghackers_*.o && rm libjhammer.so
diff --git a/jni/NOTES b/jni/NOTES
index 2e6e5bb2..564b3f5a 100644
--- a/jni/NOTES
+++ b/jni/NOTES
@@ -1,12 +1,26 @@
-Compilation:
-javac com/upstandinghackers/hammer/*.java
+USING THE JNI BINDINGS:
+1. import com.upstandinghackers.hammer.*;
+2. Add a static initializer block that loads the correct library, like this: static { System.loadLibrary("jhammer");  }
+3. Code stuff. Just look at Example.java for a few handy snippets (for walking the resulting syntax tree [AST] etc)
+4. Compile your java sources like always
+5. Add the folder containing libhammer.so/dll/whatever to Java's library path to run it, for example: java -Djava.library.path=. <CLASS>
 
-Conversion to JNI headers:
-find -name "*.class" | sed -e 's/.class$//' | tr '/' '.' | cut -c 3- | xargs javah
-
-Not working:
-enums aren't converted at all, no idea why
+UNIMPLEMENTED:
+User-defined types, predicates and actions are unimplemented.
+Memory leaks because there is no reliable garbage collection.
 
 TODO:
-Implement the entire JNI side
 Testing
+
+
+TYPE MAPPING:
+Hammer      Java        JNI
+uint8_t     byte        jbyte       jbyte/byte is signed
+char        byte        jbyte       jchar would be 16 bit wide
+size_t      int         jint        signed as well; jsize == jint, actually
+int64_t     long        jlong       
+uint64_t     long        jlong       signed!
+bool        boolean     jboolean    JNI_TRUE / JNI_FALSE
+float       float       jfloat
+double      double      jdouble
+void        void        void
diff --git a/jni/com/upstandinghackers/hammer/Action.java b/jni/com/upstandinghackers/hammer/Action.java
new file mode 100644
index 00000000..aecb713e
--- /dev/null
+++ b/jni/com/upstandinghackers/hammer/Action.java
@@ -0,0 +1,8 @@
+package com.upstandinghackers.hammer;
+
+import java.util.List;
+
+public interface Action
+{
+    public List<ParsedToken> execute(ParseResult p);
+}
diff --git a/jni/com/upstandinghackers/hammer/HAction.java b/jni/com/upstandinghackers/hammer/HAction.java
deleted file mode 100644
index 09aeaaff..00000000
--- a/jni/com/upstandinghackers/hammer/HAction.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.upstandinghackers.hammer;
-
-import java.util.List;
-
-public interface HAction
-{
-    public List<HParsedToken> execute(HParseResult p);
-}
diff --git a/jni/com/upstandinghackers/hammer/HParseResult.java b/jni/com/upstandinghackers/hammer/HParseResult.java
deleted file mode 100644
index ed5c85be..00000000
--- a/jni/com/upstandinghackers/hammer/HParseResult.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.upstandinghackers.hammer;
-
-import java.util.List;
-
-public class HParseResult
-{
-    public native List<HParsedToken> getAst();
-    public native long getBitLength();
-}
diff --git a/jni/com/upstandinghackers/hammer/HParsedToken.java b/jni/com/upstandinghackers/hammer/HParsedToken.java
deleted file mode 100644
index d8a70d6a..00000000
--- a/jni/com/upstandinghackers/hammer/HParsedToken.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.upstandinghackers.hammer;
-
-import java.util.List;
-
-public class HParsedToken
-{
-    public native Hammer.HTokenType getTokenType();
-    public native int getIndex();
-    public native byte getBitOffset();
-    public native byte[] getBytesValue();
-    public native long getSIntValue();
-    public native long getUIntValue();
-    public native double getDoubleValue();
-    public native float getFloatValue();
-    public native List<HParsedToken> getSeqValue();
-    public native Object getUserValue();
-
-    native void setTokenType(Hammer.HTokenType type);
-    native void setIndex(int index);
-    native void setBitOffset(byte offset);
-    native void setBytesValue(byte[] value);
-    native void setSIntValue(long value);
-    native void setUIntValue(long value);
-    native void setDoubleValue(double value);
-    native void setFloatValue(float value);
-    native void setSeqValue(List<HParsedToken> value);
-    native void setUserValue(Object value);
-}
diff --git a/jni/com/upstandinghackers/hammer/HParser.java b/jni/com/upstandinghackers/hammer/HParser.java
deleted file mode 100644
index 9f2387aa..00000000
--- a/jni/com/upstandinghackers/hammer/HParser.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.upstandinghackers.hammer;
-
-public class HParser
-{
-    public native void bindIndirect(HParser inner);
-}
diff --git a/jni/com/upstandinghackers/hammer/HPredicate.java b/jni/com/upstandinghackers/hammer/HPredicate.java
deleted file mode 100644
index 9c87875e..00000000
--- a/jni/com/upstandinghackers/hammer/HPredicate.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.upstandinghackers.hammer;
-
-public interface HPredicate
-{
-    public boolean apply(HParseResult p);
-}
diff --git a/jni/com/upstandinghackers/hammer/Hammer.java b/jni/com/upstandinghackers/hammer/Hammer.java
index e7ad571b..cd252d9d 100644
--- a/jni/com/upstandinghackers/hammer/Hammer.java
+++ b/jni/com/upstandinghackers/hammer/Hammer.java
@@ -1,4 +1,5 @@
 package com.upstandinghackers.hammer;
+import java.util.HashMap;
 
 public class Hammer
 {
@@ -7,59 +8,69 @@ public class Hammer
     public final static byte BYTE_LITTLE_ENDIAN = 0x0;
     public final static byte BIT_LITTLE_ENDIAN = 0x0;
    
-    public enum HTokenType
+    static final HashMap<Integer, TokenType> tokenTypeMap = new HashMap<Integer, TokenType>();
+
+    public enum TokenType
     {
-        TT_NONE(1),
-        TT_BYTES(2),
-        TT_SINT(4),
-        TT_UINT(8),
-        TT_SEQUENCE(16),
-        TT_ERR(32),
-        TT_USER(64),
+        NONE(1),
+        BYTES(2),
+        SINT(4),
+        UINT(8),
+        SEQUENCE(16),
+        ERR(32),
+        USER(64);
 
         private int value;
         public int getValue() { return this.value; }
-        private HTokenType(int value) { this.value = value; }
+        private TokenType(int value) { this.value = value; }
+    }
+
+    static
+    {
+        for(TokenType tt : TokenType.values())
+        {
+            Hammer.tokenTypeMap.put(new Integer(tt.getValue()), tt);
+        }
     }
 
-    public static native HParseResult parse(HParser parser, byte[] input, int length);
-    public static native HParser token(byte[] str, int length);
-    public static native HParser ch(byte c);
-    public static native HParser chRange(byte from, byte to);
-    public static native HParser intRange(HParser p, int lower, int upper);
-    public static native HParser bits(int len, boolean sign);
-    public static native HParser int64();
-    public static native HParser int32();
-    public static native HParser int16();
-    public static native HParser int8();
-    public static native HParser uInt64();
-    public static native HParser uInt32();
-    public static native HParser uInt16();
-    public static native HParser uInt8();
-    public static native HParser whitespace(HParser p);
-    public static native HParser left(HParser p, HParser q);
-    public static native HParser right(HParser p, HParser q);
-    public static native HParser middle(HParser p, HParser x, HParser q);
-    public static native HParser action(HParser p, HAction a);
-    public static native HParser in(byte[] charset, int length);
-    public static native HParser endP();
-    public static native HParser nothingP();
-    public static native HParser sequence(HParser[] parsers);
-    public static native HParser choice(HParser[] parsers);
-    public static native HParser butNot(HParser p1, HParser p2);
-    public static native HParser difference(HParser p1, HParser p2);
-    public static native HParser xor(HParser p1, HParser p2);
-    public static native HParser many(HParser p);
-    public static native HParser many1(HParser p);
-    public static native HParser repeatN(HParser p, int n);
-    public static native HParser optional(HParser p);
-    public static native HParser ignore(HParser p);
-    public static native HParser sepBy(HParser p, HParser sep);
-    public static native HParser sepBy1(HParser p, HParser sep);
-    public static native HParser epsilonP();
-    public static native HParser lengthValue(HParser length, HParser value);
-    public static native HParser attrBool(HParser p, HPredicate pred);
-    public static native HParser and(HParser p);
-    public static native HParser not(HParser p);
-    public static native HParser indirect();
+    public static native ParseResult parse(Parser parser, byte[] input, int length);
+    public static native Parser token(byte[] str, int length);
+    public static native Parser ch(byte c);
+    public static native Parser chRange(byte from, byte to);
+    public static native Parser intRange(Parser p, long lower, long upper);
+    public static native Parser bits(int len, boolean sign);
+    public static native Parser int64();
+    public static native Parser int32();
+    public static native Parser int16();
+    public static native Parser int8();
+    public static native Parser uInt64();
+    public static native Parser uInt32();
+    public static native Parser uInt16();
+    public static native Parser uInt8();
+    public static native Parser whitespace(Parser p);
+    public static native Parser left(Parser p, Parser q);
+    public static native Parser right(Parser p, Parser q);
+    public static native Parser middle(Parser p, Parser x, Parser q);
+//    public static native Parser action(Parser p, Action a);
+    public static native Parser in(byte[] charset, int length);
+    public static native Parser endP();
+    public static native Parser nothingP();
+    public static native Parser sequence(Parser[] parsers);
+    public static native Parser choice(Parser[] parsers);
+    public static native Parser butNot(Parser p1, Parser p2);
+    public static native Parser difference(Parser p1, Parser p2);
+    public static native Parser xor(Parser p1, Parser p2);
+    public static native Parser many(Parser p);
+    public static native Parser many1(Parser p);
+    public static native Parser repeatN(Parser p, int n);
+    public static native Parser optional(Parser p);
+    public static native Parser ignore(Parser p);
+    public static native Parser sepBy(Parser p, Parser sep);
+    public static native Parser sepBy1(Parser p, Parser sep);
+    public static native Parser epsilonP();
+    public static native Parser lengthValue(Parser length, Parser value);
+//    public static native Parser attrBool(Parser p, Predicate pred);
+    public static native Parser and(Parser p);
+    public static native Parser not(Parser p);
+    public static native Parser indirect();
 }
diff --git a/jni/com/upstandinghackers/hammer/ParseResult.java b/jni/com/upstandinghackers/hammer/ParseResult.java
new file mode 100644
index 00000000..e5ad6c94
--- /dev/null
+++ b/jni/com/upstandinghackers/hammer/ParseResult.java
@@ -0,0 +1,15 @@
+package com.upstandinghackers.hammer;
+
+import java.util.List;
+
+public class ParseResult
+{
+    public native ParsedToken getAst();
+    public native long getBitLength();
+    
+    public native void free();
+    public long getInner() {return this.inner;}
+
+    private long inner;
+    ParseResult(long inner) {this.inner=inner;}
+}
diff --git a/jni/com/upstandinghackers/hammer/ParsedToken.java b/jni/com/upstandinghackers/hammer/ParsedToken.java
new file mode 100644
index 00000000..efbc8ed7
--- /dev/null
+++ b/jni/com/upstandinghackers/hammer/ParsedToken.java
@@ -0,0 +1,40 @@
+package com.upstandinghackers.hammer;
+
+public class ParsedToken
+{
+    public Hammer.TokenType getTokenType()
+    {
+        int tt = this.getTokenTypeInternal();
+        if(0==tt)
+            return null;
+        return Hammer.tokenTypeMap.get(new Integer(tt));
+    }
+
+    private native int getTokenTypeInternal();
+    public native int getIndex();
+    public native byte getBitOffset();
+    public native byte[] getBytesValue();
+    public native long getSIntValue();
+    public native long getUIntValue();
+    public native double getDoubleValue();
+    public native float getFloatValue();
+    public native ParsedToken[] getSeqValue();
+//    public native Object getUserValue();
+
+    native void setTokenType(Hammer.TokenType type);
+    native void setIndex(int index);
+    native void setBitOffset(byte offset);
+    native void setBytesValue(byte[] value);
+    native void setSIntValue(long value);
+    native void setUIntValue(long value);
+    native void setDoubleValue(double value);
+    native void setFloatValue(float value);
+    native void setSeqValue(ParsedToken value[]);
+//    native void setUserValue(Object value);
+    
+//    public native void free();
+    public long getInner() {return this.inner;}
+
+    private long inner;
+    ParsedToken(long inner) {this.inner=inner;}
+}
diff --git a/jni/com/upstandinghackers/hammer/Parser.java b/jni/com/upstandinghackers/hammer/Parser.java
new file mode 100644
index 00000000..2e924cb2
--- /dev/null
+++ b/jni/com/upstandinghackers/hammer/Parser.java
@@ -0,0 +1,11 @@
+package com.upstandinghackers.hammer;
+
+public class Parser
+{
+    public native void bindIndirect(Parser inner);
+    public native void free();
+    public long getInner() {return this.inner;}
+
+    private long inner;
+    Parser(long inner) {this.inner=inner;}
+}
diff --git a/jni/com/upstandinghackers/hammer/Predicate.java b/jni/com/upstandinghackers/hammer/Predicate.java
new file mode 100644
index 00000000..52ca4bf7
--- /dev/null
+++ b/jni/com/upstandinghackers/hammer/Predicate.java
@@ -0,0 +1,6 @@
+package com.upstandinghackers.hammer;
+
+public interface Predicate
+{
+    public boolean apply(ParseResult p);
+}
diff --git a/jni/com_upstandinghackers_hammer_Hammer.c b/jni/com_upstandinghackers_hammer_Hammer.c
new file mode 100644
index 00000000..f83414bc
--- /dev/null
+++ b/jni/com_upstandinghackers_hammer_Hammer.c
@@ -0,0 +1,335 @@
+#include "jhammer.h"
+#include "com_upstandinghackers_hammer_Hammer.h"
+#include <stdlib.h>
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_parse
+  (JNIEnv *env, jclass class, jobject obj, jbyteArray input_, jint length_)
+{
+    HParser *parser;
+    uint8_t* input;
+    size_t length;
+    HParseResult *result;
+    jclass resultClass;
+    jobject retVal;
+
+    parser = UNWRAP(env, obj);
+    
+    input = (uint8_t *) ((*env)->GetByteArrayElements(env, input_, NULL));
+    length = (size_t) length_;
+
+    result = h_parse(parser, input, length);
+    
+    if(result==NULL)
+        return NULL;
+
+    FIND_CLASS(resultClass, env, "com/upstandinghackers/hammer/ParseResult");
+    
+    NEW_INSTANCE(retVal, env, resultClass, result);
+ 
+    return retVal;
+}
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_token
+  (JNIEnv *env, jclass class, jbyteArray str, jint len)
+{
+    RETURNWRAP(env, h_token((uint8_t *) ((*env)->GetByteArrayElements(env, str, NULL)), (size_t) len));
+}
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_ch
+  (JNIEnv *env, jclass class, jbyte c)
+{
+    RETURNWRAP(env, h_ch((uint8_t) c));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_chRange
+  (JNIEnv *env, jclass class, jbyte lower, jbyte upper)
+{
+    
+    RETURNWRAP(env, h_ch_range((uint8_t) lower, (uint8_t) upper));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_intRange
+  (JNIEnv *env, jclass class, jobject obj, jlong lower, jlong upper)
+{
+    HParser *parser;
+    parser = UNWRAP(env, obj); 
+    RETURNWRAP(env, h_int_range(parser, (int64_t) lower, (int64_t) upper));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_bits
+  (JNIEnv *env, jclass class, jint len, jboolean sign)
+{
+    RETURNWRAP(env, h_bits((size_t) len, (bool)(sign & JNI_TRUE)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_int64
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_int64());
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_int32
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_int32());
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_int16
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_int16()); 
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_int8
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_int8());
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_uInt64
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_uint64());
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_uInt32
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_uint32()); 
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_uInt16
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_uint16());
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_uInt8
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_uint8());
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_whitespace
+  (JNIEnv *env, jclass class, jobject parser)
+{
+    RETURNWRAP(env, h_whitespace(UNWRAP(env, parser)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_left
+  (JNIEnv *env, jclass class, jobject p, jobject q)
+{
+    RETURNWRAP(env, h_left(UNWRAP(env, p), UNWRAP(env, q)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_right
+  (JNIEnv *env, jclass class, jobject p, jobject q)
+{
+    RETURNWRAP(env, h_right(UNWRAP(env, p), UNWRAP(env, q)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_middle
+  (JNIEnv *env, jclass class, jobject p, jobject x, jobject q)
+{
+    RETURNWRAP(env, h_middle(UNWRAP(env, p), UNWRAP(env, x), UNWRAP(env, q)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_in
+  (JNIEnv *env, jclass class, jbyteArray charset, jint length)
+{
+    RETURNWRAP(env, h_in((uint8_t *) ((*env)->GetByteArrayElements(env, charset, NULL)), (size_t)length));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_endP
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_end_p());
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_nothingP
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_nothing_p());
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_sequence
+  (JNIEnv *env, jclass class, jobjectArray sequence)
+{
+    jsize length;
+    void **parsers;
+    int i;
+    jobject current;
+    const HParser *result;
+
+    length = (*env)->GetArrayLength(env, sequence);
+    parsers = malloc(sizeof(void *)*(length+1));
+    if(NULL==parsers)
+    {
+        return NULL;
+    }
+
+    for(i=0; i<length; i++)
+    {
+        current = (*env)->GetObjectArrayElement(env, sequence, (jsize)i);
+        parsers[i] = UNWRAP(env, current);
+    }
+    parsers[length] = NULL;
+    
+    result = h_sequence__a(parsers);
+    RETURNWRAP(env, result);
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_choice
+  (JNIEnv *env, jclass class, jobjectArray choices)
+{
+    jsize length;
+    void **parsers;
+    int i;
+    jobject current;
+    const HParser *result;
+
+    length = (*env)->GetArrayLength(env, choices);
+    parsers = malloc(sizeof(HParser *)*(length+1));
+    if(NULL==parsers)
+    {
+        return NULL;
+    }
+
+    for(i=0; i<length; i++)
+    {
+        current = (*env)->GetObjectArrayElement(env, choices, (jsize)i);
+        parsers[i] = UNWRAP(env, current);
+    }
+    parsers[length] = NULL;
+
+    result = h_choice__a(parsers);
+    RETURNWRAP(env, result);
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_butNot
+  (JNIEnv *env, jclass class, jobject p, jobject q)
+{
+    RETURNWRAP(env, h_butnot(UNWRAP(env, p), UNWRAP(env, q)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_difference
+  (JNIEnv *env, jclass class, jobject p, jobject q)
+{
+    RETURNWRAP(env, h_difference(UNWRAP(env, p), UNWRAP(env, q)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_xor
+  (JNIEnv *env, jclass class, jobject p, jobject q)
+{
+    RETURNWRAP(env, h_xor(UNWRAP(env, p), UNWRAP(env, q)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_many
+  (JNIEnv *env, jclass class, jobject p)
+{
+    RETURNWRAP(env, h_many(UNWRAP(env, p)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_many1
+  (JNIEnv *env, jclass class, jobject p)
+{
+    RETURNWRAP(env, h_many1(UNWRAP(env, p)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_repeatN
+  (JNIEnv *env, jclass class, jobject p, jint n)
+{
+    RETURNWRAP(env, h_repeat_n(UNWRAP(env, p), (size_t)n));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_optional
+  (JNIEnv *env, jclass class, jobject p)
+{
+    RETURNWRAP(env, h_optional(UNWRAP(env, p)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_ignore
+  (JNIEnv *env, jclass class, jobject p)
+{
+    RETURNWRAP(env, h_ignore(UNWRAP(env, p)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_sepBy
+  (JNIEnv *env, jclass class, jobject p, jobject sep)
+{
+    RETURNWRAP(env, h_sepBy(UNWRAP(env, p), UNWRAP(env, sep)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_sepBy1
+  (JNIEnv *env, jclass class, jobject p, jobject sep)
+{
+    RETURNWRAP(env, h_sepBy1(UNWRAP(env, p), UNWRAP(env, sep)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_epsilonP
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_epsilon_p());
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_lengthValue
+  (JNIEnv *env, jclass class, jobject length, jobject value)
+{
+    RETURNWRAP(env, h_length_value(UNWRAP(env, length), UNWRAP(env, value)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_and
+  (JNIEnv *env, jclass class, jobject p)
+{
+    RETURNWRAP(env, h_and(UNWRAP(env, p)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_not
+  (JNIEnv *env, jclass class, jobject p)
+{
+    RETURNWRAP(env, h_not(UNWRAP(env, p)));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_Hammer_indirect
+  (JNIEnv *env, jclass class)
+{
+    RETURNWRAP(env, h_indirect());
+}
+
+
+
diff --git a/jni/com_upstandinghackers_hammer_ParseResult.c b/jni/com_upstandinghackers_hammer_ParseResult.c
new file mode 100644
index 00000000..ad45fe3a
--- /dev/null
+++ b/jni/com_upstandinghackers_hammer_ParseResult.c
@@ -0,0 +1,45 @@
+#include "jhammer.h"
+#include "com_upstandinghackers_hammer_ParseResult.h"
+
+HParseResult *unwrap_parse_result(JNIEnv *env, jobject obj)
+{
+    jclass parseResultClass;
+    jfieldID parseResultInner;
+    FIND_CLASS(parseResultClass, env, "com/upstandinghackers/hammer/ParseResult");
+    parseResultInner =  (*env)->GetFieldID(env, parseResultClass, "inner", "J");
+    return (HParseResult *)((*env)->GetLongField(env, obj, parseResultInner));
+}
+
+
+JNIEXPORT jobject JNICALL Java_com_upstandinghackers_hammer_ParseResult_getAst
+  (JNIEnv *env, jobject this)
+{
+    HParseResult *inner;
+    jclass parsedTokenClass;
+    jobject retVal;
+
+    if(this == NULL)
+        return NULL; // parse unsuccessful
+    inner = unwrap_parse_result(env, this);
+    if(inner->ast == NULL)
+        return NULL; // parse successful, but empty
+
+    FIND_CLASS(parsedTokenClass, env, "com/upstandinghackers/hammer/ParsedToken");
+    NEW_INSTANCE(retVal, env, parsedTokenClass, inner->ast);
+    return retVal;
+    
+}
+
+JNIEXPORT jlong JNICALL Java_com_upstandinghackers_hammer_ParseResult_getBitLength
+  (JNIEnv *env, jobject this)
+{
+    HParseResult *inner = unwrap_parse_result(env, this);
+    return (jlong) (inner->bit_length);
+}
+
+JNIEXPORT void JNICALL Java_com_upstandinghackers_hammer_ParseResult_free
+  (JNIEnv *env, jobject this)
+{
+    //XXX: NOT IMPLEMENTED
+}
+
diff --git a/jni/com_upstandinghackers_hammer_ParsedToken.c b/jni/com_upstandinghackers_hammer_ParsedToken.c
new file mode 100644
index 00000000..e863738a
--- /dev/null
+++ b/jni/com_upstandinghackers_hammer_ParsedToken.c
@@ -0,0 +1,195 @@
+#include "jhammer.h"
+#include "com_upstandinghackers_hammer_ParsedToken.h"
+
+#define HPT_UNWRAP(env, this) HParsedToken *inner = unwrap_parsed_token(env, this); assert(inner!=NULL)
+
+HParsedToken *unwrap_parsed_token(JNIEnv *env, jobject obj)
+{
+    jclass parsedTokenClass;
+    jfieldID parsedTokenInner;
+    FIND_CLASS(parsedTokenClass, env, "com/upstandinghackers/hammer/ParsedToken");
+    parsedTokenInner =  (*env)->GetFieldID(env, parsedTokenClass, "inner", "J");
+    return (HParsedToken *)((*env)->GetLongField(env, obj, parsedTokenInner));
+}
+
+
+JNIEXPORT jint JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getTokenTypeInternal
+  (JNIEnv *env, jobject this)
+{
+    HPT_UNWRAP(env, this);
+    if(inner==NULL)
+        return (jint)0;
+    return (jint)(inner->token_type);
+}
+
+JNIEXPORT jint JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getIndex
+  (JNIEnv *env, jobject this)
+{
+    HPT_UNWRAP(env, this);
+    return (jint) (inner->index);
+}
+
+JNIEXPORT jbyte JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getBitOffset
+  (JNIEnv *env, jobject this)
+{
+    HPT_UNWRAP(env, this);
+    return (jbyte) (inner->bit_offset);
+}
+
+JNIEXPORT jbyteArray JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getBytesValue
+  (JNIEnv *env, jobject this)
+{
+    jbyteArray outArray;
+    HPT_UNWRAP(env, this);
+    outArray = (*env)->NewByteArray(env, (jsize)inner->bytes.len);
+    (*env)->SetByteArrayRegion(env, outArray, (jsize) 0, (jsize)(inner->bytes.len), (jbyte *)(inner->bytes.token));
+    return outArray; 
+}
+
+JNIEXPORT jlong JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getSIntValue
+  (JNIEnv *env, jobject this)
+{
+    HPT_UNWRAP(env, this);
+    return (jlong) (inner->sint);
+}
+
+JNIEXPORT jlong JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getUIntValue
+  (JNIEnv *env, jobject this)
+{
+    HPT_UNWRAP(env, this);
+    return (jlong) (inner->uint);
+}
+
+JNIEXPORT jdouble JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getDoubleValue
+  (JNIEnv *env, jobject this)
+{
+    HPT_UNWRAP(env, this);
+    return (jdouble) (inner->dbl);
+}
+
+JNIEXPORT jfloat JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getFloatValue
+  (JNIEnv *env, jobject this)
+{
+    HPT_UNWRAP(env, this);
+    return (jfloat) (inner->flt);
+}
+
+JNIEXPORT jobjectArray JNICALL Java_com_upstandinghackers_hammer_ParsedToken_getSeqValue
+  (JNIEnv *env, jobject this)
+{
+    jsize i;
+    HPT_UNWRAP(env, this);
+    jsize returnSize = inner->seq->used;
+    jobject currentObject;
+    jclass returnClass;
+    FIND_CLASS(returnClass, env, "com/upstandinghackers/hammer/ParsedToken");
+    jobjectArray retVal = (*env)->NewObjectArray(env, returnSize, returnClass, NULL);
+    for(i = 0; i<returnSize; i++)
+    {
+        NEW_INSTANCE(currentObject, env, returnClass, inner->seq->elements[i]);
+        (*env)->SetObjectArrayElement(env, retVal, i, currentObject);
+    }
+    return retVal;
+}
+
+JNIEXPORT void JNICALL Java_com_upstandinghackers_hammer_ParsedToken_setTokenType
+  (JNIEnv *env, jobject this, jobject tokenType)
+{
+    jclass tokenTypeClass;
+    jmethodID getValue;
+    jint typeVal;
+    HPT_UNWRAP(env, this);
+    
+    FIND_CLASS(tokenTypeClass, env, "com/upstandinghackers/hammer/Hammer$TokenType");
+    getValue = (*env)->GetMethodID(env, tokenTypeClass, "getValue", "()I");
+    typeVal = (*env)->CallIntMethod(env, tokenType, getValue);
+
+    inner->token_type = (int32_t) typeVal; // unsafe cast, but enums should be of type int
+}
+
+JNIEXPORT void JNICALL Java_com_upstandinghackers_hammer_ParsedToken_setIndex
+  (JNIEnv *env, jobject this, jint index)
+{
+    HPT_UNWRAP(env, this);
+    inner->index = (size_t)index;
+}
+
+JNIEXPORT void JNICALL Java_com_upstandinghackers_hammer_ParsedToken_setBitOffset
+  (JNIEnv *env, jobject this, jbyte bit_offset)
+{
+    HPT_UNWRAP(env, this);
+    inner->bit_offset = (char)bit_offset;
+}
+
+JNIEXPORT void JNICALL Java_com_upstandinghackers_hammer_ParsedToken_setBytesValue
+  (JNIEnv *env, jobject this, jbyteArray bytes_)
+{
+    HBytes bytes;
+    HPT_UNWRAP(env, this);
+    
+    bytes.token = (uint8_t *) ((*env)->GetByteArrayElements(env, bytes_, NULL));
+    bytes.len = (size_t) (*env)->GetArrayLength(env, bytes_);
+
+    inner->bytes = bytes;
+    inner->token_type = TT_BYTES;
+}
+
+JNIEXPORT void JNICALL Java_com_upstandinghackers_hammer_ParsedToken_setSIntValue
+  (JNIEnv *env, jobject this, jlong sint)
+{
+    HPT_UNWRAP(env, this);
+    inner->token_type = TT_SINT;
+    inner->sint = (int64_t)sint;
+}
+
+JNIEXPORT void JNICALL Java_com_upstandinghackers_hammer_ParsedToken_setUIntValue
+  (JNIEnv *env, jobject this, jlong uint)
+{
+    HPT_UNWRAP(env, this);
+    inner->token_type = TT_UINT;
+    inner->uint = (uint64_t)uint;
+}
+
+JNIEXPORT void JNICALL Java_com_upstandinghackers_hammer_ParsedToken_setDoubleValue
+  (JNIEnv *env, jobject this, jdouble dbl)
+{
+    HPT_UNWRAP(env, this);
+    //token_type?
+    inner->dbl = (double)dbl;
+}
+
+JNIEXPORT void JNICALL Java_com_upstandinghackers_hammer_ParsedToken_setFloatValue
+  (JNIEnv *env, jobject this, jfloat flt)
+{
+    HPT_UNWRAP(env, this);
+    //token_type?
+    inner->flt = (float)flt;
+}
+
+JNIEXPORT void JNICALL Java_com_upstandinghackers_hammer_ParsedToken_setSeqValue
+  (JNIEnv *env, jobject this, jobjectArray values)
+{
+    HArena *arena;
+    size_t len, i;
+    jobject currentValue;
+    HParsedToken *currentValueInner;
+    HCountedArray *seq;
+    HPT_UNWRAP(env, this);
+    len = (size_t) (*env)->GetArrayLength(env, values); 
+    arena = h_new_arena(&system_allocator, 0);
+    seq = h_carray_new_sized(arena, len);
+    
+    // unwrap each value and append it to the new HCountedArray
+    for(i = 0; i<len; i++)
+    {
+        currentValue = (*env)->GetObjectArrayElement(env, values, (jsize)i);
+        if(NULL == currentValue) 
+            continue;
+        currentValueInner = unwrap_parsed_token(env, currentValue);
+        if(currentValueInner)
+            h_carray_append(seq, (void *)currentValueInner);
+    }
+    
+    inner->token_type = TT_SEQUENCE; 
+    inner->seq = seq;
+}
diff --git a/jni/com_upstandinghackers_hammer_Parser.c b/jni/com_upstandinghackers_hammer_Parser.c
new file mode 100644
index 00000000..4e95e234
--- /dev/null
+++ b/jni/com_upstandinghackers_hammer_Parser.c
@@ -0,0 +1,15 @@
+#include "jhammer.h"
+#include "com_upstandinghackers_hammer_Parser.h"
+
+JNIEXPORT void JNICALL Java_com_upstandinghackers_hammer_Parser_bindIndirect
+  (JNIEnv *env, jobject this, jobject parser)
+{
+    h_bind_indirect(UNWRAP(env, this), UNWRAP(env, parser));
+}
+
+JNIEXPORT void JNICALL Java_com_upstandinghackers_hammer_Parser_free
+  (JNIEnv *env, jobject this)
+{
+    //XXX NOT IMPLEMENTED
+    //h_free(UNWRAP(env, this));
+}
diff --git a/jni/jhammer.h b/jni/jhammer.h
new file mode 100644
index 00000000..077777b7
--- /dev/null
+++ b/jni/jhammer.h
@@ -0,0 +1,31 @@
+#ifndef JHAMMER_H
+#define JHAMMER_H
+#include <jni.h>
+#include "internal.h"
+#include <assert.h>
+
+// Unsafe (non-asserting) helpers
+#define FIND_CLASS_(env, class) (*env)->FindClass(env, class)
+#define REFCONSTRUCTOR_(env, class) (*env)->GetMethodID(env, class, "<init>", "(J)V")
+#define NEW_INSTANCE_(env, class, inner) (*env)->NewObject(env, class, REFCONSTRUCTOR_(env, class), (jlong)inner)
+
+// Safer versions, assert that the result is not NULL
+// If one of those asserts fails, it most likely means that there's a typo (wrong class name or method signature) or big trouble (OOM)
+#define FIND_CLASS(target, env, class) target = FIND_CLASS_(env, class); assert(target != NULL)
+#define REFCONSTRUCTOR(target, env, class) target = REFCONSTRUCTOR_(env, class); assert(target != NULL)
+#define NEW_INSTANCE(target, env, class, inner) target = NEW_INSTANCE_(env, class, inner); assert(target != NULL)
+
+
+// Since there's a LOT of wrapping/unwrapping HParsers, these macros make it a bit more readable
+#define PARSER_CLASS "com/upstandinghackers/hammer/Parser"
+#define PARSER_REF(env) (*env)->GetFieldID(env, FIND_CLASS_(env, PARSER_CLASS), "inner", "J")
+
+#define RETURNWRAP(env, inner) jclass __cls=FIND_CLASS_(env, PARSER_CLASS); \
+                                assert(__cls != NULL); \
+                                jmethodID __constructor = REFCONSTRUCTOR_(env, __cls); \
+                                assert(__constructor != NULL); \
+                                return (*env)->NewObject(env, __cls, __constructor, (jlong)inner)
+
+#define UNWRAP(env, object) (HParser *)((*env)->GetLongField(env, object, PARSER_REF(env)))
+
+#endif
diff --git a/src/Makefile b/src/Makefile
index ead05168..18d3c3f8 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -62,6 +62,8 @@ include ../common.mk
 $(TESTS): CFLAGS += $(TEST_CFLAGS)
 $(TESTS): LDFLAGS += $(TEST_LDFLAGS)
 
+CFLAGS += -fPIC
+
 all: libhammer.a
 
 libhammer.a: $(HAMMER_PARTS)
diff --git a/src/hammer.h b/src/hammer.h
index d1e86884..31bc021a 100644
--- a/src/hammer.h
+++ b/src/hammer.h
@@ -167,14 +167,18 @@ typedef struct HBenchmarkResults_ {
   rtype_t name(__VA_ARGS__, ...);					\
   rtype_t name##__m(HAllocator* mm__, __VA_ARGS__, ...);		\
   rtype_t name##__mv(HAllocator* mm__, __VA_ARGS__, va_list ap);	\
-  rtype_t name##__v(__VA_ARGS__, va_list ap)
+  rtype_t name##__v(__VA_ARGS__, va_list ap);   \
+  rtype_t name##__a(void *args[]);  \
+  rtype_t name##__ma(HAllocator *mm__, void *args[])
 
 // Note: this drops the attributes on the floor for the __v versions
 #define HAMMER_FN_DECL_VARARGS_ATTR(attr, rtype_t, name, ...)		\
   rtype_t name(__VA_ARGS__, ...) attr;					\
   rtype_t name##__m(HAllocator* mm__, __VA_ARGS__, ...) attr;		\
   rtype_t name##__mv(HAllocator* mm__, __VA_ARGS__, va_list ap);	\
-  rtype_t name##__v(__VA_ARGS__, va_list ap)
+  rtype_t name##__v(__VA_ARGS__, va_list ap); \
+  rtype_t name##__a(void *args[]);  \
+  rtype_t name##__ma(HAllocator *mm__, void *args[])
 
 // }}}
 
diff --git a/src/parsers/choice.c b/src/parsers/choice.c
index 73dedde6..6ea7c5e0 100644
--- a/src/parsers/choice.c
+++ b/src/parsers/choice.c
@@ -72,3 +72,27 @@ const HParser* h_choice__mv(HAllocator* mm__, const HParser* p, va_list ap_) {
   return ret;
 }
 
+const HParser* h_choice__a(void *args[]) {
+  return h_choice__ma(&system_allocator, args);
+}
+
+const HParser* h_choice__ma(HAllocator* mm__, void *args[]) {
+  size_t len = -1; // because do...while
+  const HParser *arg;
+
+  do {
+    arg=((HParser **)args)[++len];
+  } while(arg);
+
+  HSequence *s = h_new(HSequence, 1);
+  s->p_array = h_new(const HParser *, len);
+
+  for (size_t i = 0; i < len; i++) {
+    s->p_array[i] = ((HParser **)args)[i];
+  }
+
+  s->len = len;
+  HParser *ret = h_new(HParser, 1);
+  ret->vtable = &choice_vt; ret->env = (void*)s;
+  return ret;
+}
diff --git a/src/parsers/sequence.c b/src/parsers/sequence.c
index 21ae31d9..564cd142 100644
--- a/src/parsers/sequence.c
+++ b/src/parsers/sequence.c
@@ -73,3 +73,28 @@ const HParser* h_sequence__mv(HAllocator* mm__, const HParser *p, va_list ap_) {
   ret->vtable = &sequence_vt; ret->env = (void*)s;
   return ret;
 }
+
+const HParser* h_sequence__a(void *args[]) {
+  return h_sequence__ma(&system_allocator, args);
+}
+
+const HParser* h_sequence__ma(HAllocator* mm__, void *args[]) {
+  size_t len = -1; // because do...while
+  const HParser *arg;
+  
+  do {
+    arg=((HParser **)args)[++len];
+  } while(arg);
+  
+  HSequence *s = h_new(HSequence, 1);
+  s->p_array = h_new(const HParser *, len);
+
+  for (size_t i = 0; i < len; i++) {
+    s->p_array[i] = ((HParser **)args)[i];
+  }
+  
+  s->len = len;
+  HParser *ret = h_new(HParser, 1);
+  ret->vtable = &sequence_vt; ret->env = (void*)s;
+  return ret;
+}
-- 
GitLab