diff --git a/C2HS.hs b/C2HS.hs
new file mode 100644
--- /dev/null
+++ b/C2HS.hs
@@ -0,0 +1,220 @@
+--  C->Haskell Compiler: Marshalling library
+--
+--  Copyright (c) [1999...2005] Manuel M T Chakravarty
+--
+--  Redistribution and use in source and binary forms, with or without
+--  modification, are permitted provided that the following conditions are met:
+-- 
+--  1. Redistributions of source code must retain the above copyright notice,
+--     this list of conditions and the following disclaimer. 
+--  2. Redistributions in binary form must reproduce the above copyright
+--     notice, this list of conditions and the following disclaimer in the
+--     documentation and/or other materials provided with the distribution. 
+--  3. The name of the author may not be used to endorse or promote products
+--     derived from this software without specific prior written permission. 
+--
+--  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+--  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+--  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+--  NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+--  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+--  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+--  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+--  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+--  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--
+--- Description ---------------------------------------------------------------
+--
+--  Language: Haskell 98
+--
+--  This module provides the marshaling routines for Haskell files produced by 
+--  C->Haskell for binding to C library interfaces.  It exports all of the
+--  low-level FFI (language-independent plus the C-specific parts) together
+--  with the C->HS-specific higher-level marshalling routines.
+--
+
+module C2HS (
+
+  -- * Re-export the language-independent component of the FFI 
+  module Foreign,
+
+  -- * Re-export the C language component of the FFI
+  module CForeign,
+
+  -- * Composite marshalling functions
+  withCStringLenIntConv, peekCStringLenIntConv, withIntConv, withFloatConv,
+  peekIntConv, peekFloatConv, withBool, peekBool, withEnum, peekEnum,
+
+  -- * Conditional results using 'Maybe'
+  nothingIf, nothingIfNull,
+
+  -- * Bit masks
+  combineBitMasks, containsBitMask, extractBitMasks,
+
+  -- * Conversion between C and Haskell types
+  cIntConv, cFloatConv, cToBool, cFromBool, cToEnum, cFromEnum
+) where 
+
+
+import Foreign
+       hiding       (Word)
+		    -- Should also hide the Foreign.Marshal.Pool exports in
+		    -- compilers that export them
+import CForeign
+
+import Monad        (when, liftM)
+
+
+-- Composite marshalling functions
+-- -------------------------------
+
+-- Strings with explicit length
+--
+withCStringLenIntConv s f    = withCStringLen s $ \(p, n) -> f (p, cIntConv n)
+peekCStringLenIntConv (s, n) = peekCStringLen (s, cIntConv n)
+
+-- Marshalling of numerals
+--
+
+withIntConv   :: (Storable b, Integral a, Integral b) 
+	      => a -> (Ptr b -> IO c) -> IO c
+withIntConv    = with . cIntConv
+
+withFloatConv :: (Storable b, RealFloat a, RealFloat b) 
+	      => a -> (Ptr b -> IO c) -> IO c
+withFloatConv  = with . cFloatConv
+
+peekIntConv   :: (Storable a, Integral a, Integral b) 
+	      => Ptr a -> IO b
+peekIntConv    = liftM cIntConv . peek
+
+peekFloatConv :: (Storable a, RealFloat a, RealFloat b) 
+	      => Ptr a -> IO b
+peekFloatConv  = liftM cFloatConv . peek
+
+-- Passing Booleans by reference
+--
+
+withBool :: (Integral a, Storable a) => Bool -> (Ptr a -> IO b) -> IO b
+withBool  = with . fromBool
+
+peekBool :: (Integral a, Storable a) => Ptr a -> IO Bool
+peekBool  = liftM toBool . peek
+
+
+-- Passing enums by reference
+--
+
+withEnum :: (Enum a, Integral b, Storable b) => a -> (Ptr b -> IO c) -> IO c
+withEnum  = with . cFromEnum
+
+peekEnum :: (Enum a, Integral b, Storable b) => Ptr b -> IO a
+peekEnum  = liftM cToEnum . peek
+
+
+-- Storing of 'Maybe' values
+-- -------------------------
+
+instance Storable a => Storable (Maybe a) where
+  sizeOf    _ = sizeOf    (undefined :: Ptr ())
+  alignment _ = alignment (undefined :: Ptr ())
+
+  peek p = do
+	     ptr <- peek (castPtr p)
+	     if ptr == nullPtr
+	       then return Nothing
+	       else liftM Just $ peek ptr
+
+  poke p v = do
+	       ptr <- case v of
+		        Nothing -> return nullPtr
+			Just v' -> new v'
+               poke (castPtr p) ptr
+
+
+-- Conditional results using 'Maybe'
+-- ---------------------------------
+
+-- Wrap the result into a 'Maybe' type.
+--
+-- * the predicate determines when the result is considered to be non-existing,
+--   ie, it is represented by `Nothing'
+--
+-- * the second argument allows to map a result wrapped into `Just' to some
+--   other domain
+--
+nothingIf       :: (a -> Bool) -> (a -> b) -> a -> Maybe b
+nothingIf p f x  = if p x then Nothing else Just $ f x
+
+-- |Instance for special casing null pointers.
+--
+nothingIfNull :: (Ptr a -> b) -> Ptr a -> Maybe b
+nothingIfNull  = nothingIf (== nullPtr)
+
+
+-- Support for bit masks
+-- ---------------------
+
+-- Given a list of enumeration values that represent bit masks, combine these
+-- masks using bitwise disjunction.
+--
+combineBitMasks :: (Enum a, Bits b) => [a] -> b
+combineBitMasks = foldl (.|.) 0 . map (fromIntegral . fromEnum)
+
+-- Tests whether the given bit mask is contained in the given bit pattern
+-- (i.e., all bits set in the mask are also set in the pattern).
+--
+containsBitMask :: (Bits a, Enum b) => a -> b -> Bool
+bits `containsBitMask` bm = let bm' = fromIntegral . fromEnum $ bm
+			    in
+			    bm' .&. bits == bm'
+
+-- |Given a bit pattern, yield all bit masks that it contains.
+--
+-- * This does *not* attempt to compute a minimal set of bit masks that when
+--   combined yield the bit pattern, instead all contained bit masks are
+--   produced.
+--
+extractBitMasks :: (Bits a, Enum b, Bounded b) => a -> [b]
+extractBitMasks bits = 
+  [bm | bm <- [minBound..maxBound], bits `containsBitMask` bm]
+
+
+-- Conversion routines
+-- -------------------
+
+-- |Integral conversion
+--
+cIntConv :: (Integral a, Integral b) => a -> b
+cIntConv  = fromIntegral
+
+-- |Floating conversion
+--
+cFloatConv :: (RealFloat a, RealFloat b) => a -> b
+cFloatConv  = realToFrac
+-- As this conversion by default goes via `Rational', it can be very slow...
+{-# RULES 
+  "cFloatConv/Float->Float"   forall (x::Float).  cFloatConv x = x;
+  "cFloatConv/Double->Double" forall (x::Double). cFloatConv x = x
+ #-}
+
+-- |Obtain C value from Haskell 'Bool'.
+--
+cFromBool :: Num a => Bool -> a
+cFromBool  = fromBool
+
+-- |Obtain Haskell 'Bool' from C value.
+--
+cToBool :: Num a => a -> Bool
+cToBool  = toBool
+
+-- |Convert a C enumeration to Haskell.
+--
+cToEnum :: (Integral i, Enum e) => i -> e
+cToEnum  = toEnum . cIntConv
+
+-- |Convert a Haskell enumeration to C.
+--
+cFromEnum :: (Enum e, Integral i) => e -> i
+cFromEnum  = cIntConv . fromEnum
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+The following license covers this documentation, and the source code, except
+where otherwise indicated.
+
+Copyright 2008, Michael Snoyman. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,7 @@
+#!/usr/bin/env runhaskell
+
+> module Main where
+> import Distribution.Simple
+
+> main :: IO ()
+> main = defaultMain
diff --git a/Text/Libyaml.chs b/Text/Libyaml.chs
new file mode 100644
--- /dev/null
+++ b/Text/Libyaml.chs
@@ -0,0 +1,299 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module Text.Libyaml
+    ( Event (..)
+    , parserParse
+    , withParser
+    , emitEvents
+    , withEmitter
+    ) where
+
+#include <yaml.h>
+#include "../../../c/helper.h"
+
+import C2HS
+import qualified Data.ByteString.Internal as B
+import Control.Monad
+
+{#context lib="yaml" #}
+
+{#pointer *yaml_parser_t as Parser newtype#}
+parserSize :: Int
+parserSize = {#sizeof yaml_parser_t#}
+
+{#pointer *yaml_event_t as EventRaw newtype#}
+eventSize :: Int
+eventSize = {#sizeof yaml_event_t#}
+
+{#fun unsafe yaml_parser_initialize
+    { id `Parser'
+    } -> `Int' #}
+
+{#fun unsafe yaml_parser_delete
+    { id `Parser'
+    } -> `()' #}
+
+withParser :: B.ByteString -> (Parser -> IO a) -> IO a
+withParser bs f = do
+    allocaBytes parserSize $ \p ->
+      do
+        let p' = Parser p
+        _res <- yaml_parser_initialize p'
+        -- FIXME check res
+        let (fptr, offset, len) = B.toForeignPtr bs
+        ret <- withForeignPtr fptr $ \ptr ->
+                do
+                    let ptr' = castPtr ptr `plusPtr` offset
+                    yaml_parser_set_input_string p' ptr' len
+                    f p'
+        yaml_parser_delete p'
+        return ret
+
+withEventRaw :: (EventRaw -> IO a) -> IO a
+withEventRaw f = allocaBytes eventSize $ f . EventRaw
+
+{#fun unsafe yaml_parser_set_input_string
+    { id `Parser'
+    , id `Ptr CUChar'
+    , `Int'
+    } -> `()' #}
+
+{#fun unsafe yaml_parser_parse
+    { id `Parser'
+    , id `EventRaw'
+    } -> `Int' #}
+
+{#fun unsafe yaml_event_delete
+    { id `EventRaw'
+    } -> `()' #}
+
+parserParse :: Parser -> IO [Event]
+parserParse parser = do
+    event <- parserParseOne parser
+    case event of
+		EventStreamEnd -> return [event]
+		_ -> do
+			rest <- parserParse parser
+			return $! event : rest
+
+{#fun unsafe print_parser_error
+    { id `Parser'
+    } -> `()' #}
+
+parserParseOne :: Parser -> IO Event
+parserParseOne parser = withEventRaw $ \er -> do
+    res <- yaml_parser_parse parser er
+    when (res == 0) $
+        print_parser_error parser >> fail "yaml_parser_parse failed"
+    event <- getEvent er
+    yaml_event_delete er
+    return event
+
+data Event =
+    EventNone
+    | EventStreamStart
+    | EventStreamEnd
+    | EventDocumentStart
+    | EventDocumentEnd
+    | EventAlias
+    | EventScalar B.ByteString
+    | EventSequenceStart
+    | EventSequenceEnd
+    | EventMappingStart
+    | EventMappingEnd
+    deriving (Show)
+
+{#enum yaml_event_type_e as EventType {underscoreToCase} deriving (Show) #}
+getEvent :: EventRaw -> IO Event
+getEvent (EventRaw ptr') = do
+    et <- {#get yaml_event_t.type#} ptr'
+    case toEnum $ fromEnum et of
+		YamlNoEvent -> return EventNone
+		YamlStreamStartEvent -> return EventStreamStart
+		YamlStreamEndEvent -> return EventStreamEnd
+		YamlDocumentStartEvent -> return EventDocumentStart
+		YamlDocumentEndEvent -> return EventDocumentEnd
+		YamlAliasEvent -> return EventAlias
+		YamlScalarEvent -> do
+			yvalue <- {#get yaml_event_t.data.scalar.value#} ptr'
+			ylen <- {#get yaml_event_t.data.scalar.length#} ptr'
+			let yvalue' = castPtr yvalue
+			let ylen' = fromEnum ylen
+			let ylen'' = toEnum $ fromEnum ylen
+			bs <- B.create ylen' $ \dest -> B.memcpy dest yvalue' ylen''
+			return $ EventScalar bs
+		YamlSequenceStartEvent -> return EventSequenceStart
+		YamlSequenceEndEvent -> return EventSequenceEnd
+		YamlMappingStartEvent -> return EventMappingStart
+		YamlMappingEndEvent -> return EventMappingEnd
+
+-- Emitter
+{#pointer *yaml_emitter_t as Emitter newtype#}
+emitterSize :: Int
+emitterSize = {#sizeof yaml_emitter_t#}
+
+{#fun unsafe yaml_emitter_initialize
+    { id `Emitter'
+    } -> `Int' #}
+
+{#fun unsafe yaml_emitter_delete
+    { id `Emitter'
+    } -> `()' #}
+
+{#pointer *buffer_t as Buffer newtype#}
+bufferSize :: Int
+bufferSize = {#sizeof buffer_t#}
+
+{#fun unsafe buffer_init
+    { id `Buffer'
+    } -> `()' #}
+
+withBuffer :: (Buffer -> IO ()) -> IO B.ByteString
+withBuffer f = do
+    allocaBytes bufferSize $ \b -> do
+        let b' = Buffer b
+        buffer_init b'
+        f b'
+        ptr' <- {#get buffer_t.buff#} b
+        len <- {#get buffer_t.used#} b
+        fptr <- newForeignPtr_ $ castPtr ptr'
+        return $! B.fromForeignPtr fptr 0 $ fromIntegral len
+
+{#fun unsafe yaml_emitter_set_output
+    { id `Emitter'
+    , id `FunPtr (Ptr () -> Ptr CUChar -> CULong -> IO CInt)'
+    , id `Ptr ()'
+    } -> `()' #}
+
+foreign import ccall "buffer.h &buffer_append"
+    buffer_append :: FunPtr (Ptr () -> Ptr CUChar -> CULong -> IO CInt)
+
+withEmitter :: (Emitter -> IO ()) -> IO B.ByteString
+withEmitter f = do
+    allocaBytes emitterSize $ \e -> do
+        let e' = Emitter e
+        _res <- yaml_emitter_initialize e'
+        -- FIXME check res
+        bs <- withBuffer $ \(Buffer b) -> do
+                let fun = buffer_append
+                yaml_emitter_set_output e' buffer_append $ castPtr b
+                f e'
+        yaml_emitter_delete e'
+        return bs
+
+{#fun unsafe yaml_emitter_emit
+    { id `Emitter'
+    , id `EventRaw'
+    } -> `Int' #}
+
+{#fun unsafe print_emitter_error
+    { id `Emitter'
+    } -> `()' #}
+
+emitEvents :: Emitter -> [Event] -> IO ()
+emitEvents _ [] = return ()
+emitEvents emitter (e:rest) = do
+    res <- toEventRaw e $ yaml_emitter_emit emitter
+    when (res == 0) $
+        print_emitter_error emitter >> fail "yaml_emitter_emit failed"
+    emitEvents emitter rest
+
+{#fun unsafe yaml_stream_start_event_initialize
+    { id `EventRaw'
+    , `Int'
+    } -> `Int' #}
+
+{#fun unsafe yaml_stream_end_event_initialize
+    { id `EventRaw'
+    } -> `Int' #}
+
+{#fun unsafe yaml_scalar_event_initialize
+    { id `EventRaw'
+    , id `Ptr CUChar' -- anchor
+    , id `Ptr CUChar' -- tag
+    , id `Ptr CUChar' -- value
+    , `Int'
+    , `Int'
+    , `Int'
+    , `Int'
+    } -> `Int' #}
+
+{#fun unsafe simple_document_start
+    { id `EventRaw'
+    } -> `Int' #}
+
+{#fun unsafe yaml_document_end_event_initialize
+    { id `EventRaw'
+    , `Int'
+    } -> `Int' #}
+
+{#fun unsafe yaml_sequence_start_event_initialize
+    { id `EventRaw'
+    , id `Ptr CUChar'
+    , id `Ptr CUChar'
+    , `Int'
+    , `Int'
+    } -> `Int' #}
+
+{#fun unsafe yaml_sequence_end_event_initialize
+    { id `EventRaw'
+    } -> `Int' #}
+
+{#fun unsafe yaml_mapping_start_event_initialize
+    { id `EventRaw'
+    , id `Ptr CUChar'
+    , id `Ptr CUChar'
+    , `Int'
+    , `Int'
+    } -> `Int' #}
+
+{#fun unsafe yaml_mapping_end_event_initialize
+    { id `EventRaw'
+    } -> `Int' #}
+
+toEventRaw :: Event -> (EventRaw -> IO a) -> IO a
+toEventRaw e f = withEventRaw $ \er -> do
+    case e of
+        EventStreamStart ->
+            yaml_stream_start_event_initialize
+                er
+                0 -- YAML_ANY_ENCODING
+        EventStreamEnd ->
+            yaml_stream_end_event_initialize er
+        EventDocumentStart ->
+            simple_document_start er
+        EventDocumentEnd ->
+            yaml_document_end_event_initialize er 1
+        EventScalar bs -> do
+            let (fvalue, offset, len) = B.toForeignPtr bs
+            withForeignPtr fvalue $ \value -> do
+                let value' = value `plusPtr` offset
+                yaml_scalar_event_initialize
+                    er
+                    nullPtr
+                    nullPtr
+                    value'
+                    len
+                    0
+                    1
+                    0 -- YAML_ANY_SCALAR_STYLE
+        EventSequenceStart ->
+            yaml_sequence_start_event_initialize
+                er
+                nullPtr
+                nullPtr
+                1
+                0 -- YAML_ANY_SEQUENCE_STYLE
+        EventSequenceEnd ->
+            yaml_sequence_end_event_initialize er
+        EventMappingStart ->
+            yaml_mapping_start_event_initialize
+                er
+                nullPtr
+                nullPtr
+                1
+                0 -- YAML_ANY_SEQUENCE_STYLE
+        EventMappingEnd ->
+            yaml_mapping_end_event_initialize er
+        EventAlias -> error "toEventRaw: EventAlias not supported"
+        EventNone -> error "toEventRaw: EventNone not supported"
+    f er
diff --git a/Text/Yaml.hs b/Text/Yaml.hs
new file mode 100644
--- /dev/null
+++ b/Text/Yaml.hs
@@ -0,0 +1,98 @@
+---------------------------------------------------------
+--
+-- Module        : Text.Yaml
+-- Copyright     : Michael Snoyman
+-- License       : BSD3
+--
+-- Maintainer    : Michael Snoyman <michael@snoyman.com>
+-- Stability     : Stable
+-- Portability   : portable
+--
+-- Provides a user friendly interface for serializing Yaml data.
+--
+---------------------------------------------------------
+
+module Text.Yaml
+    ( module Data.Object
+    , encode
+    , encodeFile
+    , decode
+    , decodeFile
+    ) where
+
+import Prelude hiding (readList)
+import Text.Libyaml
+import qualified Data.ByteString as B
+import Data.ByteString.Class
+import Data.Object
+import System.IO.Unsafe
+
+encode :: (StrictByteString bs, ToObject o) => o -> bs
+encode = unsafePerformIO . encode'
+
+decode :: (Monad m, StrictByteString bs, FromObject o) => bs -> m o
+decode = unsafePerformIO . decode'
+
+encodeFile :: ToObject o => FilePath -> o -> IO ()
+encodeFile path o = encode' o >>= B.writeFile path
+
+decodeFile :: (Monad m, FromObject o) => FilePath -> IO (m o)
+decodeFile path = do
+    c <- B.readFile path
+    decode' c
+
+encode' :: (StrictByteString bs, ToObject o) => o -> IO bs
+encode' o =
+    let events = objectToEvents $ toObject o
+        result = withEmitter $ flip emitEvents $ events
+     in fromStrictByteString `fmap` result
+
+decode' :: (Monad m, StrictByteString bs, FromObject o) => bs -> IO (m o)
+decode' bs = do
+    let bs' = toStrictByteString bs
+        events = withParser bs' parserParse
+    (fromObject . eventsToObject) `fmap` events
+
+objectToEvents :: Object -> [Event]
+objectToEvents y = concat
+                    [ [EventStreamStart, EventDocumentStart]
+                    , writeSingle y
+                    , [EventDocumentEnd, EventStreamEnd]] where
+    writeSingle :: Object -> [Event]
+    writeSingle (Scalar bs) = [EventScalar bs]
+    writeSingle (Sequence ys) =
+        (EventSequenceStart : concatMap writeSingle ys)
+        ++ [EventSequenceEnd]
+    writeSingle (Mapping pairs) =
+        EventMappingStart : concatMap writePair pairs ++ [EventMappingEnd]
+    writePair :: (B.ByteString, Object) -> [Event]
+    writePair (k, v) = EventScalar k : writeSingle v
+
+eventsToObject :: [Event] -> Object
+eventsToObject = fst . readSingle . dropWhile isIgnored where
+    isIgnored EventAlias = False
+    isIgnored (EventScalar _) = False
+    isIgnored EventSequenceStart = False
+    isIgnored EventSequenceEnd = False
+    isIgnored EventMappingStart = False
+    isIgnored EventMappingEnd = False
+    isIgnored _ = True
+    readSingle :: [Event] -> (Object, [Event])
+    readSingle [] = error "readSingle: no more events"
+    readSingle (EventScalar bs:rest) = (Scalar bs, rest)
+    readSingle (EventSequenceStart:rest) = readList [] rest
+    readSingle (EventMappingStart:rest) = readMap [] rest
+    readSingle (x:_) = error $ "readSingle: " ++ show x
+    readList :: [Object] -> [Event] -> (Object, [Event])
+    readList nodes (EventSequenceEnd:rest) =
+        (Sequence $ reverse nodes, rest)
+    readList nodes events =
+        let (next, rest) = readSingle events
+         in readList (next : nodes) rest
+    readMap :: [(B.ByteString, Object)] -> [Event] -> (Object, [Event])
+    readMap pairs (EventMappingEnd:rest) = (Mapping $ reverse pairs, rest)
+    readMap pairs (EventScalar bs:events) =
+        let (next, rest) = readSingle events
+         in readMap ((fromStrictByteString bs, next) : pairs) rest
+    readMap _ (e:_) = error $ "Unexpected event in readMap: " ++ show e
+    readMap _ [] = error "Unexpected empty event list in readMap"
diff --git a/c/helper.c b/c/helper.c
new file mode 100644
--- /dev/null
+++ b/c/helper.c
@@ -0,0 +1,55 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "helper.h"
+
+void buffer_init(buffer_t *buffer)
+{
+	buffer->buff = 0;
+	buffer->size = buffer->used = 0;
+}
+
+int buffer_append(void *ext, unsigned char *str, size_t size)
+{
+	buffer_t *b = ext;
+	int new_size, new_used;
+	char *tmp;
+
+	new_used = b->used + size;
+	for (new_size = b->size || 8; new_size < new_used; new_size *= 2);
+
+	if (new_size != b->size) {
+		tmp = realloc(b->buff, new_size);
+		if (!tmp) return 0;
+		b->buff = tmp;
+		b->size = new_size;
+	}
+
+	memcpy(b->buff + b->used, str, size);
+	b->used = new_used;
+
+	return 1;
+}
+
+void print_parser_error(yaml_parser_t *p)
+{
+	fprintf(stderr, "Problem: %s\nContext: %s\nOffset: %u\n",
+		p->problem,
+		p->context,
+		(unsigned int) p->problem_offset);
+}
+
+void print_emitter_error(yaml_emitter_t *e)
+{
+	fprintf(stderr, "%s\n", e->problem);
+}
+
+int simple_document_start(yaml_event_t *e)
+{
+	return yaml_document_start_event_initialize
+		(e,
+		 0,
+		 0,
+		 0,
+		 1);
+}
diff --git a/yaml.cabal b/yaml.cabal
new file mode 100644
--- /dev/null
+++ b/yaml.cabal
@@ -0,0 +1,26 @@
+name:            yaml
+version:         0.0.0
+license:         BSD3
+license-file:    LICENSE
+author:          Michael Snoyman <michael@snoyman.com>
+maintainer:      Michael Snoyman <michael@snoyman.com>
+synopsis:        Support for serialising Haskell to and from Yaml.
+description:     Binds to the libyaml library
+category:        Web
+stability:       unstable
+cabal-version:   >= 1.2
+build-type:      Simple
+homepage:        http://github.com/snoyberg/yaml/tree/master
+
+library
+    build-depends:   base >= 4 && < 5,
+                     bytestring-class,
+                     data-object,
+                     bytestring >= 0.9.1.4 && < 1,
+                     haskell98
+    exposed-modules: Text.Yaml
+    other-modules:   Text.Libyaml,
+                     C2HS
+    ghc-options:     -Wall
+    c-sources:       c/helper.c
+    extra-libraries: yaml
