diff --git a/src/bindings/ruby/lib/hammer.rb b/src/bindings/ruby/lib/hammer.rb
index 54b2501c71bd21bcae02b44977c9ea7e70e625fb..0ae353c185ce9e60d5dce76f0fc7ac0fe31a4a5d 100644
--- a/src/bindings/ruby/lib/hammer.rb
+++ b/src/bindings/ruby/lib/hammer.rb
@@ -71,3 +71,5 @@ p parser.parse 'abcabdabd'
 p parser.parse 'abcabd'
 p parser.parse 'abdabd'
 p parser.parse 'abd'
+
+$r = parser.parse 'abcabd'
diff --git a/src/bindings/ruby/lib/hammer/internal.rb b/src/bindings/ruby/lib/hammer/internal.rb
index 3ef39a1fb48bf22ef28695543a8ecb25844412b7..9cd82fa2c146832c8c3a1f02d3c35d7b0344df37 100644
--- a/src/bindings/ruby/lib/hammer/internal.rb
+++ b/src/bindings/ruby/lib/hammer/internal.rb
@@ -7,10 +7,59 @@ module Hammer
     ffi_lib 'libhammer.dylib'
 
     typedef :pointer, :h_parser
-    typedef :pointer, :h_parse_result
+
+    HTokenType = enum(:none, 1,
+                      :bytes, 2,
+                      :sint, 4,
+                      :uint, 8,
+                      :sequence, 16,
+                      :reserved_1,
+                      :err, 32,
+                      :user, 64,
+                      :max)
+
+    class HCountedArray < FFI::Struct
+      layout  :capacity, :size_t,
+              :used, :size_t,
+              :arena, :pointer,
+              :elements, :pointer # TODO
+    end
+
+    class HBytes < FFI::Struct
+      layout  :token, :uint8,
+              :len, :size_t
+    end
+
+    class HParsedTokenDataUnion < FFI::Union
+      layout  :bytes, HBytes.by_value,
+              :sint, :int64,
+              :uint, :uint64,
+              :dbl, :double,
+              :flt, :float,
+              :seq, HCountedArray.by_ref,
+              :user, :pointer
+    end
+
+    class HParsedToken < FFI::Struct
+      layout  :token_type, HTokenType,
+              :data, HParsedTokenDataUnion.by_value,
+              :index, :size_t,
+              :bit_offset, :char
+    end
+
+    class HParseResult < FFI::Struct
+      layout  :ast, HParsedToken.by_ref,
+              :bit_length, :long_long,
+              :arena, :pointer
+
+      def self.release(ptr)
+        p "freeing #{ptr}"
+        Hammer::Internal.h_parse_result_free(ptr) unless ptr.null?
+      end
+    end
 
     # run a parser
-    attach_function :h_parse, [:h_parser, :string, :size_t], :h_parse_result
+    attach_function :h_parse, [:h_parser, :string, :size_t], HParseResult.auto_ptr
 
     # build a parser
     attach_function :h_token, [:string, :size_t], :h_parser
@@ -57,14 +106,8 @@ module Hammer
     #attach_function :h_action, [:h_parser, ...], :h_parser
     #attach_function :h_attr_bool, [:h_parser, ...], :h_parser
 
-    #class HParseResult < FFI::Struct
-    #  layout  :ast, :pointer,
-    #          :bit_length, :longlong,
-    #          :arena, :pointer
-    #end
-
     # free the parse result
-    attach_function :h_parse_result_free, [:h_parse_result], :void
+    attach_function :h_parse_result_free, [HParseResult.by_ref], :void
 
     # TODO: Does the HParser* need to be freed?
   end
diff --git a/src/bindings/ruby/lib/hammer/parser.rb b/src/bindings/ruby/lib/hammer/parser.rb
index 8ae6ee8bf5586ab65c7def6a1e856259757828ec..a7e175dbccf5e2b6e26da74adf6599c48e99a2e9 100644
--- a/src/bindings/ruby/lib/hammer/parser.rb
+++ b/src/bindings/ruby/lib/hammer/parser.rb
@@ -16,17 +16,15 @@ module Hammer
     attr_reader :name
     attr_reader :h_parser
 
-    # Parse the given data. Returns true if successful, false otherwise.
+    # Parse the given data. Returns the parse result if successful, nil otherwise.
     #
     # data: A string containing the data to parse.
     def parse(data)
       raise RuntimeError, '@h_parser is nil' if @h_parser.nil?
       raise ArgumentError, 'expecting a String' unless data.is_a? String # TODO: Not needed, FFI checks that.
+
       result = Hammer::Internal.h_parse(@h_parser, data, data.length)
-      # TODO: Do something with the data
-      #       (wrap in garbage-collected object, call h_parse_result_free when destroyed by GC)
-      Hammer::Internal.h_parse_result_free(result)
-      !result.null?
+      return result unless result.null?
     end
 
     # Binds an indirect parser.
@@ -36,6 +34,9 @@ module Hammer
     end
 
     def self.token(string)
+      # TODO:
+      # This might fail in JRuby.
+      # See "String Memory Allocation" at https://github.com/ffi/ffi/wiki/Core-Concepts
       h_string = string.dup
       h_parser = Hammer::Internal.h_token(h_string, h_string.length)