diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2011 factis research GmbH
+
+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.
+
+    * Neither the name offactis research GmbH nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"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
+OWNER OR CONTRIBUTORS 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.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/roundtrip-string.cabal b/roundtrip-string.cabal
new file mode 100644
--- /dev/null
+++ b/roundtrip-string.cabal
@@ -0,0 +1,28 @@
+Name:           roundtrip-string
+Version:        0.1.0.0
+Synopsis:       Bidirectional (de-)serialization
+Description:    Roundtrip allows the definition of bidirectional
+                (de-)serialization specifications. This package provides
+                concrete implementations of these specifications
+                for parsing/pretty-printing strings.
+                .
+                See the roundtrip package for more details.
+License:        BSD3
+License-file:   LICENSE
+Author:         Stefan Wehr <wehr@factisresearch.com>,
+                David Leuschner <leuschner@factisresearch.com>
+Maintainer:     Stefan Wehr <wehr@factisresearch.com>,
+Category:       Text
+Build-type:     Simple
+Cabal-version:  >=1.8
+
+Library
+  Hs-Source-Dirs: src
+  Exposed-modules:
+      Text.Roundtrip.Parser
+    , Text.Roundtrip.Printer
+  Build-depends:
+      base == 4.*
+    , mtl >= 1.1.1.1
+    , parsec == 3.1.*
+    , roundtrip == 0.2.*
diff --git a/src/Text/Roundtrip/Parser.hs b/src/Text/Roundtrip/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Roundtrip/Parser.hs
@@ -0,0 +1,77 @@
+{-# LANGUAGE FlexibleContexts, TypeSynonymInstances #-}
+module Text.Roundtrip.Parser (
+
+  module Text.Parsec, Pos.newPos, Pos.initialPos,
+
+  PParser, parsecApply, parsecConcat, parsecAlternative1Lookahead,
+  parsecAlternativeInfLookahead, parsecEmpty, parsecPure,
+
+  runStringParser, P.runParser, mkParseError
+
+) where
+
+import Control.Monad.Identity (Identity, runIdentity)
+
+import Text.Parsec hiding (runParser)
+import qualified Text.Parsec as P
+import Text.Parsec.Char
+import qualified Text.Parsec.Pos as Pos
+import qualified Text.Parsec.Prim as Prim
+import qualified Text.Parsec.Error as Perror
+import Text.Parsec.Prim ()
+
+import Text.Roundtrip
+
+type PParser s u m = ParsecT s u m
+
+parsecApply :: Iso a b -> PParser s u m a -> PParser s u m b
+parsecApply iso p =
+    do a <- p
+       case apply iso a of
+         Just b -> return b
+         Nothing -> fail $ isoFailedErrorMessageL iso a
+
+parsecConcat :: PParser s u m a -> PParser s u m b -> PParser s u m (a, b)
+parsecConcat p q =
+    do x <- p
+       y <- q
+       return (x, y)
+
+parsecAlternative1Lookahead :: PParser s u m a -> PParser s u m a -> PParser s u m a
+parsecAlternative1Lookahead p q = p P.<|> q
+
+parsecAlternativeInfLookahead :: PParser s u m a -> PParser s u m a -> PParser s u m a
+parsecAlternativeInfLookahead p q = try p P.<|> q
+
+parsecEmpty :: PParser s u m a
+parsecEmpty = parserZero
+
+parsecPure :: a -> PParser s u m a
+parsecPure x = return x
+
+instance Monad m => IsoFunctor (PParser s u m) where
+    (<$>) = parsecApply
+
+instance Monad m => ProductFunctor (PParser s u m) where
+    (<*>) = parsecConcat
+
+instance Monad m => Alternative (PParser s u m) where
+    (<|>) = parsecAlternative1Lookahead
+    (<||>) = parsecAlternativeInfLookahead
+    empty = parsecEmpty
+
+instance Monad m => Syntax (PParser s u m) where
+    pure = parsecPure
+
+instance (Monad m, Stream s m Char) => StringSyntax (PParser s u m) where
+    token f = Prim.tokenPrim showChar nextPos testChar
+        where
+          showChar x      = '\'' : x : ['\'']
+          testChar x      = if f x then Just x else Nothing
+          nextPos pos x _ = Pos.updatePosChar pos x
+
+runStringParser :: Stream s Identity Char => PParser s () Identity a -> SourceName -> s -> Either ParseError a
+runStringParser p src s = runIdentity $ Prim.runParserT p () src s
+
+mkParseError :: SourcePos -> String -> ParseError
+mkParseError pos msg = Perror.newErrorMessage (Perror.Message msg) pos
diff --git a/src/Text/Roundtrip/Printer.hs b/src/Text/Roundtrip/Printer.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Roundtrip/Printer.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE BangPatterns, TypeSynonymInstances, FlexibleInstances #-}
+module Text.Roundtrip.Printer (
+
+  Printer(..),
+  printerApply, printerConcat, printerAlternative, printerEmpty,
+  printerPure, runPrinter, runStringPrinter
+
+) where
+
+import Control.Monad.Identity (Identity, runIdentity)
+
+import Data.Monoid
+
+import Text.Roundtrip
+
+newtype Printer m r a = Printer { unPrinter :: a -> m (Maybe r) }
+
+instance Monad m => IsoFunctor (Printer m r) where
+    (<$>) = printerApply
+
+printerApply :: Monad m => Iso a b -> Printer m r a -> Printer m r b
+printerApply iso (Printer p) = Printer $ \b ->
+    case unapply iso b of
+      Just x -> p x
+      Nothing -> return Nothing
+
+instance (Monad m, Monoid r) => ProductFunctor (Printer m r) where
+    (<*>) = printerConcat
+
+printerConcat :: (Monoid r, Monad m) => Printer m r a -> Printer m r b -> Printer m r (a, b)
+printerConcat (Printer p) (Printer q) = Printer $ \(a, b) ->
+    do ma <- p a
+       case ma of
+         Nothing -> return Nothing
+         Just !ea -> do mb <- q b
+                        case mb of
+                          Nothing -> return Nothing
+                          Just eb -> return (Just (ea `mappend` eb))
+
+instance Monad m => Alternative (Printer m r) where
+    (<|>) = printerAlternative
+    (<||>) = printerAlternative
+    empty = printerEmpty
+
+printerEmpty :: Monad m => Printer m r a
+printerEmpty = Printer $ \_ -> return Nothing
+
+printerAlternative :: Monad m => Printer m r a -> Printer m r a -> Printer m r a
+printerAlternative (Printer p) (Printer q) = Printer $ \a ->
+    do ma <- p a
+       case ma of
+         Nothing -> q a
+         Just ea -> return (Just ea)
+
+instance (Monad m, Monoid r) => Syntax (Printer m r) where
+    pure = printerPure
+
+printerPure :: (Monad m, Monoid r, Eq a) => a -> Printer m r a
+printerPure x = Printer $ \y -> if x == y then return (Just mempty) else return Nothing
+
+instance Monad m => StringSyntax (Printer m String) where
+  token f = Printer $ \c -> return (if f c then Just [c] else Nothing)
+
+runPrinter :: Printer Identity r a -> a -> Maybe r
+runPrinter (Printer p) x = runIdentity (p x)
+
+runStringPrinter :: Printer Identity String a -> a -> Maybe String
+runStringPrinter = runPrinter
