diff --git a/Data/Attoparsec/Enumerator.hs b/Data/Attoparsec/Enumerator.hs
new file mode 100644
--- /dev/null
+++ b/Data/Attoparsec/Enumerator.hs
@@ -0,0 +1,42 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module: Data.Attoparsec.Enumerator
+-- Copyright: 2010 John Millikin
+-- License: MIT
+--
+-- Maintainer: jmillikin@gmail.com
+-- Portability: portable
+--
+-----------------------------------------------------------------------------
+module Data.Attoparsec.Enumerator
+	( ParseError (..)
+	, iterParser
+	) where
+import qualified Data.Attoparsec as A
+import qualified Data.ByteString as B
+import qualified Data.Enumerator as E
+
+-- | The context and message from a 'A.Fail' value.
+data ParseError = ParseError
+	{ errorContexts :: [String]
+	, errorMessage :: String
+	}
+	deriving (Show)
+
+-- | Convert an Attoparsec 'A.Parser' into an 'E.Iteratee'. The parser will
+-- be streamed bytes until it returns 'A.Done' or 'A.Fail'.
+iterParser :: Monad m => A.Parser a -> E.Iteratee ParseError B.ByteString m a
+iterParser p = E.continue (step (A.parse p)) where
+	step parse (E.Chunks xs) = parseLoop parse xs
+	step parse E.EOF = case parse B.empty of
+		A.Done extra a -> E.yield a (E.Chunks [extra])
+		A.Partial _ -> error "iterParser: divergent parser"
+		A.Fail _ ctx msg -> err ctx msg
+	
+	parseLoop parse [] = E.continue (step parse)
+	parseLoop parse (x:xs) = case parse x of
+		A.Done extra a -> E.yield a (E.Chunks (extra:xs))
+		A.Partial parse' -> parseLoop parse' xs
+		A.Fail _ ctx msg -> err ctx msg
+	
+	err ctx msg = E.throwError (ParseError ctx msg)
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/attoparsec-enumerator.cabal b/attoparsec-enumerator.cabal
new file mode 100644
--- /dev/null
+++ b/attoparsec-enumerator.cabal
@@ -0,0 +1,33 @@
+name: attoparsec-enumerator
+version: 0.1
+synopsis: Convert an Attoparsec parser into an iteratee
+license: MIT
+license-file: license.txt
+author: John Millikin <jmillikin@gmail.com>
+maintainer: jmillikin@gmail.com
+copyright: Copyright (c) John Millikin 2010
+build-type: Simple
+cabal-version: >=1.6
+category: Text, Parsing
+stability: experimental
+bug-reports: mailto:jmillikin@gmail.com
+tested-with: GHC==6.12.1
+
+library
+  ghc-options: -Wall -fno-warn-unused-do-bind
+
+  build-depends:
+      attoparsec >= 0.8 && < 0.9
+    , bytestring >= 0.9 && < 0.10
+    , enumerator >= 0.1 && < 0.2
+
+  if impl(ghc >= 6.10)
+    build-depends:
+        base >=4 && < 5
+  else
+    build-depends:
+        base >=3 && < 4
+      , extensible-exceptions
+
+  exposed-modules:
+    Data.Attoparsec.Enumerator
diff --git a/license.txt b/license.txt
new file mode 100644
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2010 John Millikin
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
