diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2013, Bitbase LLC
+
+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 of Samuel Hoffstaetter 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/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,16 @@
+Text.Parsec.Permutation is a permutation parser for parsec intended as
+a generalized replacement for Text.Parsec.Perm in parsec.
+
+Example usage:
+
+  > import Text.Parsec.Permutation
+  >
+  > fooParser :: ParsecT s u m a -> ParsecT s u m [a]
+  > fooParser = runPermParser $
+  >                 (,,) <$> oncePerm (char 'A')
+  >                      <*> manyPerm (char 'B')
+  >                      <*> optionMaybePerm (char 'C' >> char 'D')
+
+This parser will return ('A', "BBB", Just 'D') when parsing for example
+the strings "BCDABB", "CDBBAB", &etc.
+
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/Text/Parsec/Permutation.hs b/Text/Parsec/Permutation.hs
new file mode 100644
--- /dev/null
+++ b/Text/Parsec/Permutation.hs
@@ -0,0 +1,111 @@
+{-# LANGUAGE UndecidableInstances #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Text.Parsec.Permutation
+-- Copyright   :  (C) 2013 Bitbase, LLC
+-- License     :  BSD3
+-- Maintainer  :  Samuel Hoffstaetter (samuel@hoffstaetter.com)
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- Text.Parsec.Permutation is a permutation parser for parsec intended as
+-- a generalized replacement for Text.Parsec.Perm in parsec.
+--
+-- Example usage:
+--
+--   > import Text.Parsec.Permutation
+--   >
+--   > fooParser :: ParsecT s u m a -> ParsecT s u m [a]
+--   > fooParser = runPermParser $
+--   >                 (,,) <$> oncePerm (char 'A')
+--   >                      <*> manyPerm (char 'B')
+--   >                      <*> optionMaybePerm (char 'C' >> char 'D')
+--
+-- This parser will return ('A', \"BBB\", Just 'D') when parsing for example
+-- the strings \"BCDABB\", \"CDBBAB\", &etc.
+--
+----------------------------------------------------------------------------
+
+module Text.Parsec.Permutation
+  (PermParser, runPermParser, oncePerm, manyPerm, many1Perm,
+   optionPerm, optionMaybePerm)
+where
+
+import Control.Applicative ((<*>), (<$>), Applicative, pure)
+import Text.Parsec ((<|>), ParsecT, Stream, parserZero, optionMaybe, unexpected)
+
+data PermParser s u m a =
+  PermParser {
+      permValue :: Maybe a -- potential intermediate value parsed so far
+    , permParser :: ParsecT s u m (PermParser s u m a)
+    }
+
+instance Functor (PermParser s u m) where
+  fmap f (PermParser value parser) =
+      PermParser (f <$> value) (fmap f <$> parser)
+
+instance Stream s m t => Applicative (PermParser s u m) where
+  parser1 <*> parser2 =
+      PermParser (permValue parser1 <*> permValue parser2)
+                 (attemptParser1 <|> attemptParser2)
+    where attemptParser1 = do parser1 <- permParser parser1
+                              return $ parser1 <*> parser2
+          attemptParser2 = do parser2 <- permParser parser2
+                              return $ parser1 <*> parser2
+
+  pure value = PermParser (Just value) parserZero
+
+-- | Turns a permutation parser into a regular parsec parser.
+runPermParser :: Stream s m t => PermParser s u m a -> ParsecT s u m a
+runPermParser (PermParser value parser) =
+    do result <- optionMaybe parser
+       case result of
+         Nothing -> maybe (fail "Could not parse all permutations") return value
+         Just permParser -> runPermParser permParser
+
+-- | Attempt parsing a value once. Fails if parsing the value succeeds multiple
+--   times.
+oncePerm :: (Stream s m t) => ParsecT s u m a -> PermParser s u m a
+oncePerm parser =
+    PermParser Nothing $
+      do value <- parser
+         return $ PermParser (Just value) $
+                    parser >> unexpected "duplicate occurrence.\
+                                         \ Expected only one occurrence."
+
+-- | Attempt parsing a value at most once. Fails when parsing the value
+--   succeeds multiple times. The first argument is the default value to be
+--   used when parsing never succeeds.
+optionPerm :: (Stream s m t)
+           => a -> ParsecT s u m a -> PermParser s u m a
+optionPerm defaultValue parser =
+    PermParser (Just defaultValue) $
+      do value <- parser
+         return $ PermParser (Just value) $
+                    parser >> unexpected "duplicate optional occurrence.\
+                                         \ Expected at most one occurrence."
+
+-- | Similar to "optionPerm", but uses Nothing as the default value.
+optionMaybePerm :: (Stream s m t)
+                => ParsecT s u m a -> PermParser s u m (Maybe a)
+optionMaybePerm parser = optionPerm Nothing (Just <$> parser)
+
+-- | Parses a given value as many times as possible in the permutation. As with
+--   Parsec.Prim.many in parsec, you need to make sure that the provided parser
+--   consumes input when succeeding to prevent infinite recursion.
+manyPerm :: ParsecT s u m a -> PermParser s u m [a]
+manyPerm  parser = manyPermAccum (Just []) parser
+
+-- | Same as "manyPerm", but fails when the parsing doesn't succeed at least
+--   once.
+many1Perm :: ParsecT s u m a -> PermParser s u m [a]
+many1Perm parser = manyPermAccum Nothing   parser
+
+-- helper function for manyPerm / many1Perm
+manyPermAccum :: Maybe [a] -> ParsecT s u m a -> PermParser s u m [a]
+manyPermAccum accumValue parser =
+    PermParser (reverse <$> accumValue) $
+      do value <- parser
+         let combinedValue = maybe [value] (value:) accumValue
+         return $ manyPermAccum (Just combinedValue) parser
+
diff --git a/parsec-permutation.cabal b/parsec-permutation.cabal
new file mode 100644
--- /dev/null
+++ b/parsec-permutation.cabal
@@ -0,0 +1,31 @@
+name:                parsec-permutation
+version:             0.1.0.0
+synopsis:            Applicative permutation parser for Parsec intended as
+                     a replacement for Text.Parsec.Perm.
+license:             BSD3
+license-file:        LICENSE
+author:              Samuel Hoffstaetter
+maintainer:          samuel@hoffstaetter.com
+copyright:           Bitbase, LLC
+category:            Parsing
+build-type:          Simple
+cabal-version:       >=1.8
+extra-source-files:  tests/PermutationTest.hs README
+
+library
+  exposed-modules:     Text.Parsec.Permutation
+  build-depends:       base >= 4 && < 5, parsec >= 3
+  ghc-options:         -Wall -fwarn-tabs -fno-warn-name-shadowing
+
+test-suite tests
+  type:                exitcode-stdio-1.0
+  build-depends:       parsec-permutation, base >= 4 && < 5, parsec >= 3,
+                       QuickCheck >= 2.4
+  hs-source-dirs:      tests
+  main-is:             PermutationTest.hs
+  ghc-options:         -Wall -fwarn-tabs -fno-warn-name-shadowing
+
+source-repository head
+  type:     git
+  location: https://github.com/bitbasenyc/parsec-permutation.git
+
diff --git a/tests/PermutationTest.hs b/tests/PermutationTest.hs
new file mode 100644
--- /dev/null
+++ b/tests/PermutationTest.hs
@@ -0,0 +1,257 @@
+{-# LANGUAGE TemplateHaskell, TupleSections, FlexibleContexts #-}
+module Main where
+
+import System.Exit
+import Test.QuickCheck
+import Test.QuickCheck.All
+import Text.Parsec
+import Control.Applicative
+import Data.List
+
+import Text.Parsec.Permutation
+
+requireParse :: String -> Parsec String () a -> a
+requireParse str parser =
+  case parse (parser <* eof) str str of
+       Left err -> error $ "Unsuccessful parse: " ++ show err
+       Right value -> value
+
+prop_purePerm :: Property
+prop_purePerm = once $
+  (requireParse "" $ runPermParser $ pure "test") == "test"
+
+prop_purePerm2 :: Property
+prop_purePerm2 = once $
+  case parse (runPermParser $ pure (, "asdf") <*> pure "qwer") "" "" of
+       Left _ -> False
+       Right x -> x == ("qwer", "asdf")
+
+prop_oncePerm :: Property
+prop_oncePerm = once $
+  case parse (runPermParser $ oncePerm $ char 'A') "" "A" of
+       Left _ -> False
+       Right x -> x == 'A'
+
+prop_oncePerm2 :: Property
+prop_oncePerm2 = once $
+  (requireParse "asdf" $ runPermParser $
+     (,,,,) <$> pure 'x'
+            <*> oncePerm (char 's')
+            <*> oncePerm (char 'a')
+            <*> oncePerm (char 'f')
+            <*> oncePerm (char 'd')) == ('x','s','a','f','d')
+
+prop_oncePerm3 :: Property
+prop_oncePerm3 = once $
+  (requireParse "a" $ runPermParser $
+     (,) <$> pure 'x'
+         <*> oncePerm (char 'a')) == ('x','a')
+
+prop_oncePerm4 :: Property
+prop_oncePerm4 = once $
+  (requireParse "as" $ runPermParser $
+    (,,) <$> pure 'x'
+         <*> oncePerm (char 's')
+         <*> oncePerm (char 'a')) == ('x','s','a')
+
+prop_oncePerm5 :: Property
+prop_oncePerm5 = once $
+  (requireParse "asf" $ runPermParser $
+   (,,,) <$> pure 'x'
+         <*> oncePerm (char 's')
+         <*> oncePerm (char 'a')
+         <*> oncePerm (char 'f')) == ('x','s','a','f')
+
+prop_oncePerm6 :: Property
+prop_oncePerm6 = once $
+  (requireParse "ab" $ runPermParser $
+     oncePerm (char 'a' >> return ('a',)) <*>
+     oncePerm (char 'b')) == ('a','b')
+
+prop_oncePerm7 :: Property
+prop_oncePerm7 = once $
+  (requireParse "ba" $ runPermParser $
+     oncePerm (char 'a' >> return ('a',)) <*>
+     oncePerm (char 'b')) == ('a','b')
+
+prop_oncePerm8 :: Property
+prop_oncePerm8 = once $
+  (requireParse "ba" $ runPermParser $
+     oncePerm (char 'a' >> return ('a',)) <*>
+     (oncePerm (char 'b' >> return ('b',)) <*> pure 'c')) == ('a',('b','c'))
+
+prop_oncePerm9 :: Property
+prop_oncePerm9 = once $
+  (requireParse "bac" $ runPermParser $
+     oncePerm (char 'a' >> return ('a',)) <*>
+     (oncePerm (char 'b' >> return ('b',)) <*>
+      oncePerm (char 'c'))) == ('a',('b','c'))
+
+prop_oncePerm10 :: Property
+prop_oncePerm10 = once $
+  (requireParse "" $ runPermParser $
+     pure ('a',) <*>
+     (pure ('b',) <*> pure 'c')) == ('a',('b','c'))
+
+prop_oncePermAll' :: Property
+prop_oncePermAll' = once $ all helper ["", "a", "ab", "bb", "abc", "abcb",
+                                       "abcdefg", "abccefaoeifjalasdfie"] where
+ helper str =
+  (requireParse str $ runPermParser $
+      foldr (\c parser ->
+             oncePerm (char c >> return (c:)) <*> parser)
+            (pure "Q") str
+  ) == str ++ "Q"
+
+prop_oncePermAll :: String -> Property
+prop_oncePermAll str = property $
+  (requireParse (nub $ sort str) $ runPermParser $
+      foldr (\c parser ->
+             oncePerm (char c >> return (c:)) <*> parser)
+            (pure "Q") (nub $ sort str)
+  ) == (nub $ sort str) ++ "Q"
+
+prop_oncePermAllReverse :: String -> Property
+prop_oncePermAllReverse str = property $
+  (requireParse (reverse $ nub $ sort str) $ runPermParser $
+      foldr (\c parser ->
+             oncePerm (char c >> return (c:)) <*> parser)
+            (pure "Q") (nub $ sort str)
+  ) == (nub $ sort str) ++ "Q"
+
+prop_oncePermMixedOrder1 :: Property
+prop_oncePermMixedOrder1 = once $ expectFailure $
+  (requireParse "abcc" $ runPermParser $
+     oncePerm (char 'c' >> return ('c',,)) <*>
+        (oncePerm (char 'b' >> return ('b',)) <*>
+         oncePerm (char 'c')) <*>
+     oncePerm (char 'a')) == ('c',('b','c'),'a')
+
+prop_oncePermMixedOrder1' :: Property
+prop_oncePermMixedOrder1' = once $ expectFailure $
+  (requireParse "abc" $ runPermParser $
+     oncePerm (char 'c' >> return ('c',,)) <*>
+     oncePerm (char 'b') <*>
+     oncePerm (char 'a')) == ('c','b','a')
+
+prop_oncePermMixedOrder2 :: Property
+prop_oncePermMixedOrder2 = once $ expectFailure $
+  (requireParse "abbc" $ runPermParser $
+     oncePerm (char 'c' >> return ('c',,)) <*>
+        (oncePerm (char 'b' >> return ('b',)) <*>
+         oncePerm (char 'c')) <*>
+     oncePerm (char 'a')) == ('c',('b','c'),'a')
+
+prop_oncePermMixedOrder3 :: Property
+prop_oncePermMixedOrder3 = once $
+  (requireParse "axcb" $ runPermParser $
+     oncePerm (char 'b' >> return ('b',,)) <*>
+        (oncePerm (char 'x' >> return ('x',)) <*>
+         oncePerm (char 'c')) <*>
+     oncePerm (char 'a')) == ('b',('x','c'),'a')
+
+prop_manyPermMixedOrder1 :: Property
+prop_manyPermMixedOrder1 = once $
+  (requireParse "abcc" $ runPermParser $
+     (,,) <$>
+     manyPerm (char 'c') <*>
+        ((,) <$>
+         manyPerm (char 'b') <*>
+         manyPerm (char 'c')) <*>
+     manyPerm (char 'a')) == ("cc",("b",""),"a")
+
+prop_manyPermMixedOrder1' :: Property
+prop_manyPermMixedOrder1' = once $ expectFailure $
+  (requireParse "abcc" $ runPermParser $
+     (,,) <$>
+     manyPerm (char 'c') <*>
+        ((,) <$>
+         manyPerm (char 'b') <*>
+         many1Perm (char 'c')) <*>
+     manyPerm (char 'a')) == ("cc",("b",""),"a")
+
+prop_manyPermMixedOrder2 :: Property
+prop_manyPermMixedOrder2 = once $
+  (requireParse "abbc" $ runPermParser $
+     (,,) <$>
+     manyPerm (char 'c') <*>
+        ((,) <$>
+         manyPerm (char 'b') <*>
+         manyPerm (char 'c')) <*>
+     manyPerm (char 'a')) == ("c",("bb",""),"a")
+
+prop_manyPermMixedOrder3 :: Property
+prop_manyPermMixedOrder3 = once $
+  (requireParse "abcb" $ runPermParser $
+     (,,) <$>
+     manyPerm (char 'b') <*>
+        ((,) <$>
+         manyPerm (char 'b') <*>
+         manyPerm (char 'c')) <*>
+     manyPerm (char 'a')) == ("bb",("","c"),"a")
+
+prop_manyPermMixedOrder4 :: Property
+prop_manyPermMixedOrder4 = once $ expectFailure $
+  (requireParse "abcbxb" $ runPermParser $
+     (,,) <$>
+     oncePerm (char 'x') <*>
+        ((,) <$>
+         optionMaybePerm (char 'b') <*>
+         manyPerm (anyChar)) <*>
+     manyPerm (char 'a')) /= ('q',(Just 'q',"qqq"),"qq")
+
+prop_manyPermMixedOrder4' :: Property
+prop_manyPermMixedOrder4' = once $
+  (requireParse "abczxb" $ runPermParser $
+     (,,) <$>
+     oncePerm (char 'x') <*>
+        ((,) <$>
+         optionMaybePerm (char 'z') <*>
+         manyPerm (anyChar)) <*>
+     manyPerm (char 'a')) == ('x',(Just 'z',"cbb"),"a")
+
+prop_manyPermAnyChar :: Property
+prop_manyPermAnyChar = once $
+  (requireParse "abcbdef" $ runPermParser $
+     (,,) <$>
+     manyPerm (char 'b') <*>
+     manyPerm (anyChar) <*>
+     manyPerm (anyChar)) == ("bb","acdef","")
+
+prop_optionPermAll :: String -> String -> Property
+prop_optionPermAll str' optionalStr = property $
+  whenFail (putStrLn $ "str: " ++ show str) $
+  whenFail (putStrLn $ "optional: " ++ show optionalStr) $
+  whenFail (putStrLn $ "remainder: " ++ show (str \\ optionalStr)) $
+  whenFail (putStrLn $ "values: " ++ show ((map snd $
+         tail $ scanl (\(str, _) c ->
+                       if c `elem` str
+                       then (str \\ [c], Just c)
+                       else (str, Nothing)) (str, Just 'Q')
+                optionalStr) ++ [Just 'Q'])) $
+  whenFail (putStrLn $ "actual: " ++ show (requireParse str $ runPermParser $
+      (,) <$>
+      foldr (\c parser ->
+             (:) <$> optionMaybePerm (char c) <*> parser)
+            (pure $ [Just 'Q']) optionalStr <*>
+      manyPerm anyChar)) $
+  (requireParse str $ runPermParser $
+      (,) <$>
+      foldr (\c parser ->
+             (:) <$> optionMaybePerm (char c) <*> parser)
+            (pure $ [Just 'Q']) optionalStr <*>
+      manyPerm anyChar
+  ) == ( (map snd $
+          tail $ scanl (\(str, _) c ->
+                        if c `elem` str
+                        then (str \\ [c], Just c)
+                        else (str, Nothing)) (str, Just 'Q')
+                 optionalStr) ++ [Just 'Q']
+        , str \\ optionalStr)
+  where str = nub $ sort str'
+
+main :: IO ()
+main = do
+  success <- $quickCheckAll
+  if success then exitSuccess else exitFailure
+
