diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2016, Nikita Volkov
+
+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.
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/json-incremental-decoder.cabal b/json-incremental-decoder.cabal
new file mode 100644
--- /dev/null
+++ b/json-incremental-decoder.cabal
@@ -0,0 +1,99 @@
+name:
+  json-incremental-decoder
+version:
+  0.1.0.3
+synopsis:
+  Incremental JSON parser with early termination and a declarative DSL
+category:
+  Data, JSON, Parsing
+homepage:
+  https://github.com/nikita-volkov/json-incremental-decoder 
+bug-reports:
+  https://github.com/nikita-volkov/json-incremental-decoder/issues 
+author:
+  Nikita Volkov <nikita.y.volkov@mail.ru>
+maintainer:
+  Nikita Volkov <nikita.y.volkov@mail.ru>
+copyright:
+  (c) 2016, Nikita Volkov
+license:
+  MIT
+license-file:
+  LICENSE
+build-type:
+  Simple
+cabal-version:
+  >=1.10
+
+
+source-repository head
+  type:
+    git
+  location:
+    git://github.com/nikita-volkov/json-incremental-decoder.git
+
+
+library
+  hs-source-dirs:
+    library
+  default-extensions:
+    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
+  default-language:
+    Haskell2010
+  other-modules:
+    JSONIncrementalDecoder.Prelude
+    JSONIncrementalDecoder.Parsers
+    JSONIncrementalDecoder.Parsers.Aeson
+    JSONIncrementalDecoder.SupplementedParsers
+  exposed-modules:
+    JSONIncrementalDecoder
+  build-depends:
+    -- 
+    monad-par >= 0.3.4 && < 0.4,
+    -- 
+    attoparsec >= 0.13 && < 0.14,
+    --
+    bytestring >= 0.10 && < 0.12,
+    text == 1.*,
+    scientific == 0.3.*,
+    unordered-containers >= 0.2.6 && < 0.3,
+    vector >= 0.10 && < 0.12,
+    hashable >= 1.2 && < 2,
+    --
+    success >= 0.2.6 && < 0.3,
+    matcher >= 0.1 && < 0.2,
+    interspersed >= 0.1 && < 0.2,
+    unsequential >= 0.5 && < 0.6,
+    supplemented >= 0.5 && < 0.6,
+    transformers >= 0.4 && < 0.6,
+    -- 
+    ghc-prim >= 0.3 && < 0.5,
+    base >= 4.7 && < 5,
+    base-prelude < 2
+
+
+test-suite parsing-test
+  type:
+    exitcode-stdio-1.0
+  hs-source-dirs:
+    parsing-test
+  main-is:
+    Main.hs
+  other-modules:
+    Main.Decoders
+  default-extensions:
+    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
+  default-language:
+    Haskell2010
+  build-depends:
+    --
+    json-incremental-decoder,
+    -- testing:
+    tasty == 0.11.*,
+    tasty-quickcheck == 0.8.*,
+    tasty-smallcheck == 0.8.*,
+    tasty-hunit == 0.9.*,
+    quickcheck-instances >= 0.3.11 && < 0.4,
+    QuickCheck >= 2.8.1 && < 2.9,
+    --
+    rebase >= 0.4 && < 2
diff --git a/library/JSONIncrementalDecoder.hs b/library/JSONIncrementalDecoder.hs
new file mode 100644
--- /dev/null
+++ b/library/JSONIncrementalDecoder.hs
@@ -0,0 +1,280 @@
+-- |
+-- A DSL for specification of a single-pass incremental and possibly partial parser of JSON.
+-- 
+module JSONIncrementalDecoder
+(
+  -- * Execution
+  valueToSupplementedParser,
+  valueToParser,
+  valueToByteStringToEither,
+  valueToLazyByteStringToEither,
+  -- * Value
+  Value,
+  null,
+  nullable,
+  bool,
+  numberAsInt,
+  numberAsInteger,
+  numberAsDouble,
+  numberAsScientific,
+  string,
+  objectRows,
+  objectLookup,
+  arrayElements,
+  -- * ObjectRows
+  ObjectRows,
+  row,
+  anyRow,
+  -- * ObjectLookup
+  ObjectLookup,
+  atKey,
+  -- * ArrayElements
+  ArrayElements,
+  element,
+  anyElement,
+  -- * Matcher
+  Matcher,
+  equals,
+  satisfies,
+  converts,
+  whatever,
+)
+where
+
+import JSONIncrementalDecoder.Prelude hiding (String, null, bool)
+import Data.Attoparsec.ByteString.Char8 (Parser)
+import qualified Data.Attoparsec.ByteString.Char8
+import qualified Data.Attoparsec.ByteString.Lazy
+import qualified Data.ByteString.Lazy
+import qualified JSONIncrementalDecoder.SupplementedParsers as SupplementedParsers
+import qualified JSONIncrementalDecoder.Parsers as Parsers
+import qualified Matcher
+
+
+-- |
+-- Converts the Value specification into a Supplemented Attoparsec Parser.
+valueToSupplementedParser :: Value a -> Supplemented Parser a
+valueToSupplementedParser (Value impl) =
+  {-# SCC "valueToSupplementedParser" #-} 
+  impl
+
+-- |
+-- Essentially just a helper, which is the same as
+-- 
+-- @
+-- 'runSupplemented' . 'valueToSupplementedParser'
+-- @
+valueToParser :: Value a -> Parser (a, Parser ())
+valueToParser =
+  {-# SCC "valueToParser" #-} 
+  runSupplemented .
+  valueToSupplementedParser
+
+-- |
+-- Converts the Value specification into a function,
+-- which decodes a strict ByteString.
+valueToByteStringToEither :: Value a -> ByteString -> Either Text a
+valueToByteStringToEither value input =
+  {-# SCC "valueToByteStringToEither" #-} 
+  either (Left . fromString) Right $
+  Data.Attoparsec.ByteString.Char8.parseOnly parser input
+  where
+    parser =
+      fmap fst $
+      valueToParser value
+
+-- |
+-- Converts the Value specification into a function,
+-- which decodes a strict LazyByteString.
+valueToLazyByteStringToEither :: Value a -> Data.ByteString.Lazy.ByteString -> Either Text a
+valueToLazyByteStringToEither value input =
+  {-# SCC "valueToLazyByteStringToEither" #-} 
+  either (Left . fromString) Right $
+  Data.Attoparsec.ByteString.Lazy.eitherResult $
+  Data.Attoparsec.ByteString.Lazy.parse parser input
+  where
+    parser =
+      fmap fst $
+      valueToParser value
+
+
+-- * Value
+-------------------------
+
+newtype Value a =
+  Value (Supplemented Parser a)
+  deriving (Functor)
+
+-- |
+-- Provides support for alternatives.
+-- 
+-- E.g,
+-- 
+-- >fmap Left bool <> fmap Right string
+-- 
+-- will succeed for either a Boolean or String value.
+instance Monoid (Value a) where
+  {-# INLINE mempty #-}
+  mempty =
+    Value empty
+  {-# INLINE mappend #-}
+  mappend (Value a) (Value b) =
+    Value (a <|> b)
+
+{-# INLINE null #-}
+null :: Value ()
+null =
+  {-# SCC "null" #-} 
+  Value $
+  SupplementedParsers.null
+
+{-# INLINE nullable #-}
+nullable :: Value a -> Value (Maybe a)
+nullable (Value p) =
+  {-# SCC "nullable" #-} 
+  Value (mplus (fmap Just p) (fmap (const Nothing) SupplementedParsers.null))
+
+{-# INLINE bool #-}
+bool :: Value Bool
+bool =
+  {-# SCC "bool" #-} 
+  Value (lift Parsers.bool)
+
+{-# INLINE numberAsInt #-}
+numberAsInt :: Value Int
+numberAsInt =
+  {-# SCC "numberAsInt" #-} 
+  Value (lift Parsers.numberLitAsIntegral)
+
+{-# INLINE numberAsInteger #-}
+numberAsInteger :: Value Integer
+numberAsInteger =
+  {-# SCC "numberAsInteger" #-} 
+  Value (lift Parsers.numberLitAsIntegral)
+
+{-# INLINE numberAsDouble #-}
+numberAsDouble :: Value Double
+numberAsDouble =
+  {-# SCC "numberAsDouble" #-} 
+  Value (lift Parsers.numberLitAsDouble)
+
+{-# INLINE numberAsScientific #-}
+numberAsScientific :: Value Scientific
+numberAsScientific =
+  {-# SCC "numberAsScientific" #-} 
+  Value (lift Parsers.numberLitAsScientific)
+
+{-# INLINE string #-}
+string :: Value Text
+string =
+  {-# SCC "string" #-} 
+  Value $
+  SupplementedParsers.stringLit
+
+{-# INLINABLE stringMatcher #-}
+stringMatcher :: Matcher Text a -> Value a
+stringMatcher matcher =
+  {-# SCC "stringMatcher" #-} 
+  Value $
+  SupplementedParsers.stringLit >>=
+  either (const mzero) return . Matcher.run matcher
+
+{-# INLINABLE objectRows #-}
+objectRows :: ObjectRows a -> Value a
+objectRows (ObjectRows interspersedSupplementedParser) =
+  {-# SCC "objectRows" #-} 
+  Value (SupplementedParsers.object supplementedParser)
+  where
+    supplementedParser =
+      runInterspersed interspersedSupplementedParser SupplementedParsers.comma
+
+{-# INLINABLE objectLookup #-}
+objectLookup :: ObjectLookup a -> Value a
+objectLookup (ObjectLookup lookupImpl) =
+  {-# SCC "objectLookup" #-} 
+  objectRows $
+  runUnsequential lookupImpl anyRow <*
+  remainders
+  where
+    remainders =
+      ObjectRows $
+      optional $
+      interspersed $
+      SupplementedParsers.anyRows
+
+{-# INLINABLE arrayElements #-}
+arrayElements :: ArrayElements a -> Value a
+arrayElements (ArrayElements interspersedSupplementedParser) =
+  {-# SCC "arrayElements" #-} 
+  Value (SupplementedParsers.array supplementedParser)
+  where
+    supplementedParser =
+      runInterspersed interspersedSupplementedParser SupplementedParsers.comma
+
+-- |
+-- Matches any value.
+{-# INLINE anyValue #-}
+anyValue :: Value ()
+anyValue =
+  {-# SCC "anyValue" #-} 
+  Value SupplementedParsers.anyValue
+
+
+-- * ObjectRows
+-------------------------
+
+newtype ObjectRows a =
+  ObjectRows (Interspersed (Supplemented Parser) a)
+  deriving (Functor, Applicative, Alternative, Monad, MonadPlus)
+
+{-# INLINABLE row #-}
+row :: (a -> b -> c) -> Matcher Text a -> Value b -> ObjectRows c
+row combine keyMatcher (Value value) =
+  {-# SCC "row" #-} 
+  ObjectRows (lift (SupplementedParsers.row combine key value))
+  where
+    key =
+      SupplementedParsers.stringLit >>=
+      either (const mzero) return . Matcher.run keyMatcher
+
+{-# INLINE anyRow #-}
+anyRow :: ObjectRows ()
+anyRow =
+  {-# SCC "anyRow" #-} 
+  ObjectRows (lift (SupplementedParsers.anyRow))
+
+
+-- * ObjectLookup
+-------------------------
+
+newtype ObjectLookup a =
+  ObjectLookup (Unsequential ObjectRows a)
+  deriving (Functor, Applicative)
+
+{-# INLINE atKey #-}
+atKey :: Text -> Value a -> ObjectLookup a
+atKey key value =
+  {-# SCC "atKey" #-} 
+  ObjectLookup $
+  unsequential $
+  row (const id) (equals key) value
+
+
+-- * ArrayElements
+-------------------------
+
+newtype ArrayElements a =
+  ArrayElements (Interspersed (Supplemented Parser) a)
+  deriving (Functor, Applicative, Alternative, Monad, MonadPlus)
+
+{-# INLINE element #-}
+element :: Value a -> ArrayElements a
+element (Value value) =
+  {-# SCC "element" #-} 
+  ArrayElements (lift value)
+
+{-# INLINE anyElement #-}
+anyElement :: ArrayElements ()
+anyElement =
+  {-# SCC "anyElement" #-} 
+  ArrayElements (lift (SupplementedParsers.anyValue))
diff --git a/library/JSONIncrementalDecoder/Parsers.hs b/library/JSONIncrementalDecoder/Parsers.hs
new file mode 100644
--- /dev/null
+++ b/library/JSONIncrementalDecoder/Parsers.hs
@@ -0,0 +1,181 @@
+module JSONIncrementalDecoder.Parsers
+where
+
+import JSONIncrementalDecoder.Prelude hiding (scanl, isDigit, bool, null, takeWhile)
+import Data.Attoparsec.ByteString.Char8
+import qualified Data.HashMap.Strict
+import qualified Control.Monad.Par
+import qualified JSONIncrementalDecoder.Parsers.Aeson as Aeson
+
+
+-- * General Parser
+-------------------------
+
+-- |
+-- Composes two parsers to consume the same input.
+-- Each must consume it in whole.
+{-# INLINE parallelly #-}
+parallelly :: Parser a -> Parser b -> Parser (a, b)
+parallelly parser1 parser2 =
+  {-# SCC "parallelly" #-} 
+  do
+    (input2, result1) <- match parser1
+    result2 <- liftSubparser input2 (parser2 <* endOfInput)
+    return (result1, result2)
+
+{-# INLINABLE sequenceParallellyToList #-}
+sequenceParallellyToList :: [Parser a] -> Parser [a]
+sequenceParallellyToList =
+  {-# SCC "sequenceParallellyToList" #-} 
+  \case
+    head : tail -> fmap (uncurry (:)) (sequenceParallelly head tail)
+    _ -> return []
+
+{-# INLINABLE sequenceParallelly #-}
+sequenceParallelly :: Traversable t => Parser a -> t (Parser a) -> Parser (a, (t a))
+sequenceParallelly primaryParser secondaryParsers =
+  {-# SCC "sequenceParallelly" #-} 
+  do
+    (input, primaryResult) <- match primaryParser
+    secondaryResults <- liftSubparsers input (fmap (<* endOfInput) secondaryParsers)
+    return (primaryResult, secondaryResults)
+
+{-# INLINE liftSubparsers #-}
+liftSubparsers :: Traversable t => ByteString -> t (Parser a) -> Parser (t a)
+liftSubparsers input parsers =
+  {-# SCC "liftSubparsers" #-} 
+  traverse liftEither $
+  parMap parserToEither parsers
+  where
+    parserToEither parser =
+      parseOnly parser input
+    parMap f xs =
+      Control.Monad.Par.runPar $
+      traverse (Control.Monad.Par.spawn_ . return . f) xs >>=
+      traverse Control.Monad.Par.get
+
+{-# INLINE liftSubparser #-}
+liftSubparser :: ByteString -> Parser a -> Parser a
+liftSubparser input parser =
+  {-# SCC "liftSubparser" #-} 
+  liftEither (parseOnly (parser <* endOfInput) input)
+
+{-# INLINE liftEither #-}
+liftEither :: Either String a -> Parser a
+liftEither =
+  {-# SCC "liftEither" #-} 
+  either fail return
+
+
+-- * Specific
+-------------------------
+
+null :: Parser ()
+null =
+  {-# SCC "null" #-} 
+  stringCI "null" $> ()
+
+bool :: Parser Bool
+bool =
+  {-# SCC "bool" #-} 
+  stringCI "false" $> False <|>
+  stringCI "true" $> True
+
+{-# INLINE stringLitAsText #-}
+stringLitAsText :: Parser Text
+stringLitAsText =
+  {-# SCC "stringLitAsText" #-} 
+  Aeson.jstring
+
+{-# INLINE numberLitAsIntegral #-}
+numberLitAsIntegral :: Integral a => Parser a
+numberLitAsIntegral =
+  {-# SCC "numberLitAsIntegral" #-} 
+  signed decimal <* shouldFail (char '.')
+
+{-# INLINE numberLitAsDouble #-}
+numberLitAsDouble :: Parser Double
+numberLitAsDouble =
+  {-# SCC "numberLitAsDouble" #-} 
+  signed double
+
+{-# INLINE numberLitAsScientific #-}
+numberLitAsScientific :: Parser Scientific
+numberLitAsScientific =
+  {-# SCC "numberLitAsScientific" #-} 
+  signed scientific
+
+-- |
+-- An optimized parser, which skips the next valid JSON literal.
+skipJSONLit :: Parser ()
+skipJSONLit =
+  {-# SCC "skipJSONLit" #-} 
+  skipStringLit <|>
+  skipNumberLit <|>
+  void bool <|>
+  null <|>
+  skipArrayLit <|>
+  skipObjectLit
+
+skipStringLit :: Parser ()
+skipStringLit =
+  {-# SCC "skipStringLit" #-} 
+  char '"' *> contents *> char '"' $> ()
+  where
+    contents =
+      skipWhile (\c -> c /= '"' && c /= '\\') *> ((escapeSeq *> contents) <|> pure ())
+      where
+        escapeSeq =
+          char '\\' *> anyChar
+
+skipNumberLit :: Parser ()
+skipNumberLit =
+  {-# SCC "skipNumberLit" #-} 
+  sign *> oneOrMoreDigits *> pointAndAfter
+  where
+    oneOrMoreDigits =
+      satisfy isDigit *> skipWhile isDigit
+    sign =
+      (satisfy (\c -> c == '-' || c == '+') $> ()) <|> pure ()
+    pointAndAfter =
+      (char '.' *> oneOrMoreDigits) <|> pure ()
+
+skipObjectRow :: Parser ()
+skipObjectRow =
+  {-# SCC "skipObjectRow" #-} 
+  skipStringLit *> skipSpace *> char ':' *> skipSpace *> skipJSONLit
+
+skipObjectLit :: Parser ()
+skipObjectLit =
+  {-# SCC "skipObjectLit" #-} 
+  objectBody (skipSepBy skipObjectRow comma)
+
+skipArrayLit :: Parser ()
+skipArrayLit =
+  {-# SCC "skipArrayLit" #-} 
+  arrayBody (skipSepBy skipJSONLit comma)
+
+objectBody :: Parser a -> Parser a
+objectBody body =
+  {-# SCC "objectBody" #-} 
+  char '{' *> skipSpace *> body <* skipSpace <* char '}'
+
+arrayBody :: Parser a -> Parser a
+arrayBody body =
+  {-# SCC "arrayBody" #-} 
+  char '[' *> skipSpace *> body <* skipSpace <* char ']'
+
+colon :: Parser ()
+colon =
+  {-# SCC "colon" #-} 
+  skipSpace *> char ':' *> skipSpace
+
+comma :: Parser ()
+comma =
+  {-# SCC "comma" #-} 
+  skipSpace *> char ',' *> skipSpace
+
+objectKey :: Parser Text
+objectKey =
+  {-# SCC "objectKey" #-} 
+  stringLitAsText <* skipSpace <* char ':' <* skipSpace
diff --git a/library/JSONIncrementalDecoder/Parsers/Aeson.hs b/library/JSONIncrementalDecoder/Parsers/Aeson.hs
new file mode 100644
--- /dev/null
+++ b/library/JSONIncrementalDecoder/Parsers/Aeson.hs
@@ -0,0 +1,203 @@
+{-# LANGUAGE BangPatterns, CPP, OverloadedStrings #-}
+#if MIN_VERSION_ghc_prim(0,3,1)
+{-# LANGUAGE MagicHash #-}
+#endif
+
+-- |
+-- Copyright:   (c) 2011-2016 Bryan O'Sullivan
+--              (c) 2011 MailRank, Inc.
+-- License:     BSD3
+--
+-- The contents of this module are a copy-and-paste from the "aeson" library.
+
+module JSONIncrementalDecoder.Parsers.Aeson where
+
+import Prelude
+import Control.Monad.IO.Class (liftIO)
+import Data.Attoparsec.ByteString.Char8 (Parser, char, endOfInput, scientific,
+                                         skipSpace, string)
+import Data.Bits ((.|.), shiftL)
+import Data.ByteString.Internal (ByteString(..))
+import Data.Char (chr)
+import Data.Text (Text)
+import Data.Text.Encoding (decodeUtf8')
+import Data.Text.Internal.Encoding.Utf8 (ord2, ord3, ord4)
+import Data.Text.Internal.Unsafe.Char (ord)
+import Data.Vector as Vector (Vector, empty, fromList, reverse)
+import Data.Word (Word8)
+import Foreign.ForeignPtr (withForeignPtr)
+import Foreign.Ptr (Ptr, plusPtr)
+import Foreign.Ptr (minusPtr)
+import Foreign.Storable (poke)
+import System.IO.Unsafe (unsafePerformIO)
+import qualified Data.Attoparsec.ByteString as A
+import qualified Data.Attoparsec.Lazy as L
+import qualified Data.Attoparsec.Zepto as Z
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Internal as B
+import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString.Unsafe as B
+import qualified Data.HashMap.Strict as H
+
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative ((*>), (<$>), (<*), pure)
+#endif
+
+#if MIN_VERSION_ghc_prim(0,3,1)
+import GHC.Base (Int#, (==#), isTrue#, orI#, word2Int#)
+import GHC.Word (Word8(W8#))
+#endif
+
+#define BACKSLASH 92
+#define CLOSE_CURLY 125
+#define CLOSE_SQUARE 93
+#define COMMA 44
+#define DOUBLE_QUOTE 34
+#define OPEN_CURLY 123
+#define OPEN_SQUARE 91
+#define C_0 48
+#define C_9 57
+#define C_A 65
+#define C_F 70
+#define C_a 97
+#define C_f 102
+#define C_n 110
+#define C_t 116
+
+-- | Parse a quoted JSON string.
+jstring :: Parser Text
+jstring = A.word8 DOUBLE_QUOTE *> jstring_
+
+-- | Parse a string without a leading quote.
+jstring_ :: Parser Text
+{-# INLINE jstring_ #-}
+jstring_ = {-# SCC "jstring_" #-} do
+  (s, fin) <- A.runScanner startState go
+  _ <- A.anyWord8
+  s1 <- if isEscaped fin
+        then case unescape s of
+               Right r  -> return r
+               Left err -> fail err
+        else return s
+  case decodeUtf8' s1 of
+    Right r  -> return r
+    Left err -> fail $ show err
+ where
+#if MIN_VERSION_ghc_prim(0,3,1)
+    isEscaped (S _ escaped) = isTrue# escaped
+    startState              = S 0# 0#
+    go (S a b) (W8# c)
+      | isTrue# a                     = Just (S 0# b)
+      | isTrue# (word2Int# c ==# 34#) = Nothing   -- double quote
+      | otherwise = let a' = word2Int# c ==# 92#  -- backslash
+                    in Just (S a' (orI# a' b))
+
+data S = S Int# Int#
+#else
+    isEscaped (S _ escaped) = escaped
+    startState              = S False False
+    go (S a b) c
+      | a                  = Just (S False b)
+      | c == DOUBLE_QUOTE  = Nothing
+      | otherwise = let a' = c == backslash
+                    in Just (S a' (a' || b))
+      where backslash = BACKSLASH
+
+data S = S !Bool !Bool
+#endif
+
+unescape :: ByteString -> Either String ByteString
+unescape s = unsafePerformIO $ do
+  let len = B.length s
+  fp <- B.mallocByteString len
+  -- We perform no bounds checking when writing to the destination
+  -- string, as unescaping always makes it shorter than the source.
+  withForeignPtr fp $ \ptr -> do
+    ret <- Z.parseT (go ptr) s
+    case ret of
+      Left err -> return (Left err)
+      Right p -> do
+        let newlen = p `minusPtr` ptr
+            slop = len - newlen
+        Right <$> if slop >= 128 && slop >= len `quot` 4
+                  then B.create newlen $ \np -> B.memcpy np ptr newlen
+                  else return (PS fp 0 newlen)
+ where
+  go ptr = do
+    h <- Z.takeWhile (/=BACKSLASH)
+    let rest = do
+          start <- Z.take 2
+          let !slash = B.unsafeHead start
+              !t = B.unsafeIndex start 1
+              escape = case B.elemIndex t "\"\\/ntbrfu" of
+                         Just i -> i
+                         _      -> 255
+          if slash /= BACKSLASH || escape == 255
+            then fail "invalid JSON escape sequence"
+            else
+            if t /= 117 -- 'u'
+              then copy h ptr >>= word8 (B.unsafeIndex mapping escape) >>= go
+              else do
+                   a <- hexQuad
+                   if a < 0xd800 || a > 0xdfff
+                     then copy h ptr >>= charUtf8 (chr a) >>= go
+                     else do
+                       b <- Z.string "\\u" *> hexQuad
+                       if a <= 0xdbff && b >= 0xdc00 && b <= 0xdfff
+                         then let !c = ((a - 0xd800) `shiftL` 10) +
+                                       (b - 0xdc00) + 0x10000
+                              in copy h ptr >>= charUtf8 (chr c) >>= go
+                         else fail "invalid UTF-16 surrogates"
+    done <- Z.atEnd
+    if done
+      then copy h ptr
+      else rest
+  mapping = "\"\\/\n\t\b\r\f"
+
+hexQuad :: Z.ZeptoT IO Int
+hexQuad = do
+  s <- Z.take 4
+  let hex n | w >= C_0 && w <= C_9 = w - C_0
+            | w >= C_a && w <= C_f = w - 87
+            | w >= C_A && w <= C_F = w - 55
+            | otherwise          = 255
+        where w = fromIntegral $ B.unsafeIndex s n
+      a = hex 0; b = hex 1; c = hex 2; d = hex 3
+  if (a .|. b .|. c .|. d) /= 255
+    then return $! d .|. (c `shiftL` 4) .|. (b `shiftL` 8) .|. (a `shiftL` 12)
+    else fail "invalid hex escape"
+
+word8 :: Word8 -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
+word8 w ptr = do
+  liftIO $ poke ptr w
+  return $! ptr `plusPtr` 1
+
+copy :: ByteString -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
+copy (PS fp off len) ptr =
+  liftIO . withForeignPtr fp $ \src -> do
+    B.memcpy ptr (src `plusPtr` off) len
+    return $! ptr `plusPtr` len
+
+charUtf8 :: Char -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
+charUtf8 ch ptr
+  | ch < '\x80'   = liftIO $ do
+                       poke ptr (fromIntegral (ord ch))
+                       return $! ptr `plusPtr` 1
+  | ch < '\x800'  = liftIO $ do
+                       let (a,b) = ord2 ch
+                       poke ptr a
+                       poke (ptr `plusPtr` 1) b
+                       return $! ptr `plusPtr` 2
+  | ch < '\xffff' = liftIO $ do
+                       let (a,b,c) = ord3 ch
+                       poke ptr a
+                       poke (ptr `plusPtr` 1) b
+                       poke (ptr `plusPtr` 2) c
+                       return $! ptr `plusPtr` 3
+  | otherwise     = liftIO $ do
+                       let (a,b,c,d) = ord4 ch
+                       poke ptr a
+                       poke (ptr `plusPtr` 1) b
+                       poke (ptr `plusPtr` 2) c
+                       poke (ptr `plusPtr` 3) d
+                       return $! ptr `plusPtr` 4
diff --git a/library/JSONIncrementalDecoder/Prelude.hs b/library/JSONIncrementalDecoder/Prelude.hs
new file mode 100644
--- /dev/null
+++ b/library/JSONIncrementalDecoder/Prelude.hs
@@ -0,0 +1,163 @@
+module JSONIncrementalDecoder.Prelude
+( 
+  module Exports,
+  concatMany,
+  eventually,
+  eventuallyLifting,
+  scanl,
+  manyWithIndex,
+  shouldFail,
+  skipSepBy,
+  skipSepBy1,
+  skipMany,
+  skipMany1,
+)
+where
+
+
+-- base-prelude
+-------------------------
+import BasePrelude as Exports hiding (Alt, scanl)
+
+-- transformers
+-------------------------
+import Control.Monad.IO.Class as Exports
+import Control.Monad.Trans.Class as Exports
+import Control.Monad.Trans.Except as Exports hiding (liftCallCC, liftCatch, liftListen, liftPass)
+import Control.Monad.Trans.Reader as Exports hiding (liftCallCC, liftCatch, liftListen, liftPass)
+import Control.Monad.Trans.State.Strict as Exports hiding (liftCallCC, liftCatch, liftListen, liftPass)
+import Control.Monad.Trans.Writer.Strict as Exports
+
+-- supplemented
+-------------------------
+import Supplemented as Exports
+
+-- unsequential
+-------------------------
+import Unsequential as Exports
+
+-- interspersed
+-------------------------
+import Interspersed as Exports
+
+-- matcher
+-------------------------
+import Matcher as Exports hiding (run)
+
+-- success
+-------------------------
+import Success.Pure as Exports (Success)
+
+-- hashable
+-------------------------
+import Data.Hashable as Exports (Hashable)
+
+-- unordered-containers
+-------------------------
+import Data.HashMap.Strict as Exports (HashMap)
+
+-- bytestring
+-------------------------
+import Data.ByteString as Exports (ByteString)
+
+-- text
+-------------------------
+import Data.Text as Exports (Text)
+
+-- scientific
+-------------------------
+import Data.Scientific as Exports (Scientific)
+
+-- Utils
+-------------------------
+
+concatMany :: (Alternative m, Monoid a) => m a -> m a
+concatMany consume =
+  step <|> end
+  where
+    step =
+      mappend <$> consume <*> concatMany consume
+    end =
+      pure mempty
+
+{-# INLINABLE eventually #-}
+eventually :: Alternative f => f () -> f a -> f a
+eventually skip consume =
+  fix $ \loop -> consume <|> (skip *> loop)
+
+-- |
+-- Given an immediate consumption function,
+-- which executes a deeper effect,
+-- and such an effect,
+-- produces an outer effect,
+-- which traverses the input until the deeper effect succeeds.
+-- If the deeper effect never succeeds, the outer one fails as well.
+-- 
+-- This combinator is useful for matching any of the remaining object rows or
+-- array elements.
+-- I.e., one of the remaining.
+-- E.g.,
+-- 
+-- @
+-- eventuallyLifting 'row' :: 'Row' a -> 'Rows' a
+-- @
+-- 
+-- where
+-- 
+-- @
+-- row :: Row a -> Rows a
+-- @
+-- 
+{-# INLINE eventuallyLifting #-}
+eventuallyLifting :: (Alternative g, Applicative f) => (forall b. f b -> g b) -> f a -> g a
+eventuallyLifting lift fx =
+  eventually (lift (pure ())) (lift fx)
+
+{-# INLINE scanl #-}
+scanl :: MonadPlus m => (a -> b -> a) -> a -> m b -> m a
+scanl snoc init parser =
+  loop init
+  where
+    loop acc =
+      mplus (parser >>= loop . snoc acc) (return acc)
+
+{-# INLINE manyWithIndex #-}
+manyWithIndex :: Alternative f => (Int -> f ()) -> f ()
+manyWithIndex indexHandler =
+  loop 0
+  where
+    loop index =
+      indexHandler index *> loop (succ index) <|> pure ()
+
+-- |
+-- Note: this parser does not consume any input.
+{-# INLINE shouldFail #-}
+shouldFail :: MonadPlus m => m a -> m ()
+shouldFail p =
+  join (mplus (p >> return mzero) (return (return ())))
+
+{-# INLINE skipSepBy #-}
+skipSepBy :: Alternative m => m () -> m () -> m ()
+skipSepBy one sep =
+  skipSepBy1 one sep <|> pure ()
+
+{-# INLINABLE skipSepBy1 #-}
+skipSepBy1 :: Alternative m => m () -> m () -> m ()
+skipSepBy1 one sep =
+  one *> remainders
+  where
+    remainders =
+      (sep *> one *> remainders) <|> pure ()
+
+{-# INLINABLE skipMany #-}
+skipMany :: Alternative f => f a -> f ()
+skipMany fx =
+  loop
+  where
+    loop =
+      (fx *> loop) <|> pure ()
+
+{-# INLINE skipMany1 #-}
+skipMany1 :: Alternative f => f a -> f ()
+skipMany1 fx =
+  fx *> skipMany fx
diff --git a/library/JSONIncrementalDecoder/SupplementedParsers.hs b/library/JSONIncrementalDecoder/SupplementedParsers.hs
new file mode 100644
--- /dev/null
+++ b/library/JSONIncrementalDecoder/SupplementedParsers.hs
@@ -0,0 +1,67 @@
+module JSONIncrementalDecoder.SupplementedParsers
+where
+
+import JSONIncrementalDecoder.Prelude
+import JSONIncrementalDecoder.Parsers as Parsers
+import Data.Attoparsec.ByteString.Char8
+
+
+{-# INLINABLE object #-}
+object :: Supplemented Parser a -> Supplemented Parser a
+object body =
+  {-# SCC "object" #-} 
+  essence openingParser *> body <* supplement closingParser
+  where
+    openingParser = 
+      char '{' *> skipSpace
+    closingParser =
+      skipSpace <* char '}'
+
+{-# INLINABLE array #-}
+array :: Supplemented Parser a -> Supplemented Parser a
+array body =
+  {-# SCC "array" #-} 
+  essence (char '[' *> skipSpace) *> body <* supplement (skipSpace <* char ']')
+
+{-# INLINABLE row #-}
+row :: (a -> b -> c) -> Supplemented Parser a -> Supplemented Parser b -> Supplemented Parser c
+row fn field value =
+  {-# SCC "row" #-} 
+  fn <$> (field <* supplement Parsers.colon) <*> value
+
+{-# INLINE comma #-}
+comma :: Supplemented Parser ()
+comma =
+  {-# SCC "comma" #-} 
+  supplement Parsers.comma
+
+{-# INLINE null #-}
+null :: Supplemented Parser ()
+null =
+  {-# SCC "null" #-} 
+  essence Parsers.null
+
+{-# INLINE stringLit #-}
+stringLit :: Supplemented Parser Text
+stringLit =
+  {-# SCC "stringLit" #-} 
+  essence Parsers.stringLitAsText
+
+{-# INLINE anyValue #-}
+anyValue :: Supplemented Parser ()
+anyValue =
+  {-# SCC "anyValue" #-} 
+  supplement skipJSONLit
+
+{-# INLINE anyRow #-}
+anyRow :: Supplemented Parser ()
+anyRow =
+  {-# SCC "anyRow" #-} 
+  supplement skipObjectRow
+
+{-# INLINE anyRows #-}
+anyRows :: Supplemented Parser ()
+anyRows =
+  {-# SCC "anyRows" #-} 
+  supplement $
+  skipSepBy Parsers.skipObjectRow Parsers.comma
diff --git a/parsing-test/Main.hs b/parsing-test/Main.hs
new file mode 100644
--- /dev/null
+++ b/parsing-test/Main.hs
@@ -0,0 +1,77 @@
+module Main where
+
+import Rebase.Prelude hiding (takeWhile)
+import Test.Tasty
+import Test.Tasty.Runners
+import Test.Tasty.HUnit
+import qualified Main.Decoders as Decoders
+
+
+main =
+  defaultMain $
+  testGroup "All tests"
+  [
+    testCase "Primitive" $
+    let
+      input =
+        "{\"year\" : 2001, \"month\": 1, \"day\": 2}"
+      result =
+        Decoders.valueToByteStringToEither Decoders.date input
+      in assertEqual (show result) (Right (2001, 1, 2)) result
+    ,
+    testCase "In different order" $
+    let
+      input =
+        "{\"month\": 1, \"day\": 2, \"year\" : 2001}"
+      result =
+        Decoders.valueToByteStringToEither Decoders.date input
+      in assertEqual (show result) (Right (2001, 1, 2)) result
+    ,
+    testCase "With redundant fields" $
+    let
+      input =
+        "{\"redundant1\": \"4\", \"month\": 1, \"redundant2\": \"3\", \"day\": 2, \"year\" : 2001}"
+      result =
+        Decoders.valueToByteStringToEither Decoders.date input
+      in assertEqual (show result) (Right (2001, 1, 2)) result
+    ,
+    testCase "With trailing fields" $
+    let
+      input =
+        "{\"month\": 1, \"day\": 2, \"year\" : 2001, \"trailing\": \"3\", \"trailing\": \"4\"}"
+      result =
+        Decoders.valueToByteStringToEither Decoders.date input
+      in assertEqual (show result) (Right (2001, 1, 2)) result
+    ,
+    testCase "Array" $
+    let
+      input =
+        "[2001, 1, 2]"
+      result =
+        Decoders.valueToByteStringToEither Decoders.date input
+      in assertEqual (show result) (Right (2001, 1, 2)) result
+    ,
+    testCase "Nested object lookup" $
+    let
+      input =
+        "{\"response\":{\"numFound\":1}}"
+      result =
+        Decoders.valueToByteStringToEither Decoders.solrSelectResponseNumFound input
+      in assertEqual (show result) (Right 1) result
+    ,
+    testCase "solrSelectResponseNumFound" $
+    let
+      input =
+        "{\"responseHeader\":{\"status\":0,\"QTime\":0,\"params\":{\"json\":\"{\\\"query\\\":\\\"text:Akuttmedisinsk*\\\",\\\"offset\\\":0,\\\"limit\\\":100}\",\"wt\":\"json\"}},\"response\":{\"numFound\":1,\"start\":0,\"docs\":[{\"id\":\"ark:9788205464377\",\"text\":[\"Akuttmedisinsk sykepleie: utenfor sykehus utenfor sykehus Boken gir en samlet fremstilling av akuttmedisinske tilstander og skader, tilpasset sykepleiere.\\n\\nL\195\166restoffet er forskningsbasert\\nog bygd opp med observasjoner og tiltak p\195\165 basalt og avansert niv\195\165. Boken er rikt illustrert og inneholder mange kasuistikker som\\nhjelper leseren \195\165 knytte sammen teori og praksis.\\nBoken dekker pensumlitteratur p\195\165 fagomr\195\165det akuttmedisin i bachelorutdanningen i\\nsykepleie. Innholdet har et tydelig prehospitalt fokus, men er ogs\195\165 egnet for sykepleiere som arbeider i sykehus. Boken kan leses\\nmed stort utbytte av helsepersonell som arbeider i ambulansetjenesten, akuttmottak, p\195\165 legevakter og AMK-sentraler.\\nAkuttmedisinsk\\nsykepleie - utenfor sykehus utkom f\195\184rste gang i 2001. I denne nye utgaven er stoffet oppdatert og omfattende revidert, og flere nye\\nkapitler er kommet til.\\nSend SMS med kodeord AKUTTMEDISINSK til 2030 og f\195\165 tilsendt bok portofritt hjem til mobiladressen Haugen, Jan Erik\"],\"_version_\":1530231185253335040}]}}"
+      result =
+        Decoders.valueToByteStringToEither Decoders.solrSelectResponseNumFound input
+      in assertEqual (show result) (Right 1) result
+    ,
+    testCase "solrSelectResponseDocumentIDs" $
+    let
+      input =
+        "{\"responseHeader\":{\"status\":0,\"QTime\":0,\"params\":{\"json\":\"{\\\"query\\\":\\\"text:Akuttmedisinsk*\\\",\\\"offset\\\":0,\\\"limit\\\":100}\",\"wt\":\"json\"}},\"response\":{\"numFound\":1,\"start\":0,\"docs\":[{\"id\":\"ark:9788205464377\",\"text\":[\"Akuttmedisinsk sykepleie: utenfor sykehus utenfor sykehus Boken gir en samlet fremstilling av akuttmedisinske tilstander og skader, tilpasset sykepleiere.\\n\\nL\195\166restoffet er forskningsbasert\\nog bygd opp med observasjoner og tiltak p\195\165 basalt og avansert niv\195\165. Boken er rikt illustrert og inneholder mange kasuistikker som\\nhjelper leseren \195\165 knytte sammen teori og praksis.\\nBoken dekker pensumlitteratur p\195\165 fagomr\195\165det akuttmedisin i bachelorutdanningen i\\nsykepleie. Innholdet har et tydelig prehospitalt fokus, men er ogs\195\165 egnet for sykepleiere som arbeider i sykehus. Boken kan leses\\nmed stort utbytte av helsepersonell som arbeider i ambulansetjenesten, akuttmottak, p\195\165 legevakter og AMK-sentraler.\\nAkuttmedisinsk\\nsykepleie - utenfor sykehus utkom f\195\184rste gang i 2001. I denne nye utgaven er stoffet oppdatert og omfattende revidert, og flere nye\\nkapitler er kommet til.\\nSend SMS med kodeord AKUTTMEDISINSK til 2030 og f\195\165 tilsendt bok portofritt hjem til mobiladressen Haugen, Jan Erik\"],\"_version_\":1530231185253335040}]}}"
+      result =
+        Decoders.valueToByteStringToEither Decoders.solrSelectResponseDocumentIDs input
+      in assertEqual (show result) (Right ["ark:9788205464377"]) result
+  ]
diff --git a/parsing-test/Main/Decoders.hs b/parsing-test/Main/Decoders.hs
new file mode 100644
--- /dev/null
+++ b/parsing-test/Main/Decoders.hs
@@ -0,0 +1,53 @@
+module Main.Decoders
+(
+  date,
+  solrSelectResponseDocumentIDs,
+  solrSelectResponseNumFound,
+  module JSONIncrementalDecoder,
+)
+where
+
+import Rebase.Prelude
+import JSONIncrementalDecoder
+
+
+date :: Value (Int, Int, Int)
+date =
+  objectDate `mappend` arrayDate
+
+objectDate :: Value (Int, Int, Int)
+objectDate =
+  objectLookup $
+  (,,) <$>
+  atKey "year" numberAsInt <*>
+  atKey "month" numberAsInt <*>
+  atKey "day" numberAsInt
+
+arrayDate :: Value (Int, Int, Int)
+arrayDate =
+  arrayElements $
+  (,,) <$>
+  element numberAsInt <*>
+  element numberAsInt <*>
+  element numberAsInt
+
+solrSelectResponseDocumentIDs :: Value [Text]
+solrSelectResponseDocumentIDs =
+  objectLookup $
+  atKey "response" $
+  objectLookup $
+  atKey "docs" $
+  arrayElements $
+  many $
+  element $
+  objectLookup $
+  atKey "id" $
+  string
+
+solrSelectResponseNumFound :: Value Int
+solrSelectResponseNumFound =
+  objectLookup $
+  atKey "response" $
+  objectLookup $
+  atKey "numFound" $
+  numberAsInt
