diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2011-2012 Omari Norman.
+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 Omari Norman nor the names of its
+    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
+HOLDER 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/Text/Matchers.hs b/Text/Matchers.hs
new file mode 100644
--- /dev/null
+++ b/Text/Matchers.hs
@@ -0,0 +1,3 @@
+module Text.Matchers (module Text.Matchers.String) where
+
+import Text.Matchers.String
diff --git a/Text/Matchers/CaseSensitive.hs b/Text/Matchers/CaseSensitive.hs
new file mode 100644
--- /dev/null
+++ b/Text/Matchers/CaseSensitive.hs
@@ -0,0 +1,3 @@
+module Text.Matchers.CaseSensitive where
+
+data CaseSensitive = Sensitive | Insensitive
diff --git a/Text/Matchers/Regex/PCRE.hs b/Text/Matchers/Regex/PCRE.hs
new file mode 100644
--- /dev/null
+++ b/Text/Matchers/Regex/PCRE.hs
@@ -0,0 +1,24 @@
+module Text.Matchers.Regex.PCRE (pcre) where
+
+import Control.Monad.Exception.Synchronous
+  ( Exceptional (Exception, Success))
+import qualified Data.ByteString as BS
+import qualified Text.Regex.PCRE.Light as PCRE
+
+import Text.Matchers.CaseSensitive
+  (CaseSensitive(Sensitive, Insensitive))
+
+pcre :: CaseSensitive
+        -> BS.ByteString
+        -> Exceptional String (BS.ByteString -> Bool)
+pcre c bs = let
+  u8 = [PCRE.utf8]
+  opts = case c of
+    Sensitive -> u8
+    Insensitive -> PCRE.caseless:u8 in
+  case PCRE.compileM bs opts of
+    (Left err) -> Exception err
+    (Right rx) -> Success $ \s ->
+      case PCRE.match rx s [] of
+        (Just _) -> True
+        Nothing -> False
diff --git a/Text/Matchers/Regex/TDFA.hs b/Text/Matchers/Regex/TDFA.hs
new file mode 100644
--- /dev/null
+++ b/Text/Matchers/Regex/TDFA.hs
@@ -0,0 +1,32 @@
+module Text.Matchers.Regex.TDFA (tdfa) where
+
+import Control.Monad.Exception.Synchronous
+  ( Exceptional (Exception))
+import qualified Text.Regex.TDFA as TDFA
+import qualified Text.Regex.Base.RegexLike as RL
+
+import Text.Matchers.CaseSensitive
+  (CaseSensitive(Sensitive, Insensitive))
+
+data StrErr a = Good a
+              | Bad String
+              deriving (Show, Eq)
+
+instance Monad StrErr where
+  return = Good
+  (Good a) >>= f = f a
+  (Bad s) >>= _ = Bad s
+  fail s = Bad s
+
+tdfa :: CaseSensitive -> String -> Exceptional String (String -> Bool)
+tdfa c regexStr = case RL.makeRegexOptsM comp exec regexStr of
+  (Bad s) -> Exception s
+  (Good rx) -> return (RL.matchTest rx)
+  where
+    comp = RL.defaultCompOpt { TDFA.caseSensitive = case c of
+                                  Sensitive -> True
+                                  Insensitive -> False
+                             , TDFA.newSyntax = True
+                             , TDFA.lastStarGreedy = True }
+    exec = RL.defaultExecOpt { TDFA.captureGroups = False }
+
diff --git a/Text/Matchers/String.hs b/Text/Matchers/String.hs
new file mode 100644
--- /dev/null
+++ b/Text/Matchers/String.hs
@@ -0,0 +1,43 @@
+module Text.Matchers.String (
+  tdfa, pcre, within, exact,
+  CaseSensitive(Sensitive, Insensitive)) where
+
+import Control.Monad.Exception.Synchronous
+  ( Exceptional (Exception, Success))
+import Data.Char (toLower)
+import Data.List (isInfixOf)
+import Data.String.UTF8 (fromString, toRep)
+
+import qualified Text.Matchers.Regex.TDFA as TDFA
+import qualified Text.Matchers.Regex.PCRE as PCRE
+import Text.Matchers.CaseSensitive
+  (CaseSensitive(Sensitive, Insensitive))
+
+tdfa :: CaseSensitive
+        -> String
+        -> Exceptional String (String -> Bool)
+tdfa = TDFA.tdfa
+
+pcre :: CaseSensitive
+        -> String
+        -> Exceptional String (String -> Bool)
+pcre c s = case PCRE.pcre c (toRep . fromString $ s) of
+  (Success f) -> return (f . toRep . fromString)
+  (Exception e) -> Exception e
+
+within :: CaseSensitive -> String -> String -> Bool
+within = strMatch isInfixOf
+
+exact :: CaseSensitive -> String -> String -> Bool
+exact = strMatch (==)
+
+strMatch :: (String -> String -> Bool)
+            -> CaseSensitive
+            -> String
+            -> String -> Bool
+strMatch f c s t = pat `f` str where
+  str = flipCase t
+  pat = flipCase s
+  flipCase = case c of
+    Sensitive -> id
+    Insensitive -> map toLower
diff --git a/Text/Matchers/Text.hs b/Text/Matchers/Text.hs
new file mode 100644
--- /dev/null
+++ b/Text/Matchers/Text.hs
@@ -0,0 +1,44 @@
+module Text.Matchers.Text (
+  tdfa, pcre, within, exact,
+  CaseSensitive(Sensitive, Insensitive)) where
+
+import Control.Monad.Exception.Synchronous
+  ( Exceptional (Exception, Success))
+import Data.Text (Text, pack, unpack, toCaseFold, isInfixOf)
+import Data.Text.Encoding (encodeUtf8)
+
+import qualified Text.Matchers.Regex.PCRE as PCRE
+import qualified Text.Matchers.Regex.TDFA as TDFA
+import Text.Matchers.CaseSensitive
+  (CaseSensitive(Sensitive, Insensitive))
+
+tdfa :: CaseSensitive
+        -> Text
+        -> Exceptional Text (Text -> Bool)
+tdfa c t = case TDFA.tdfa c (unpack t) of
+  (Exception e) -> Exception (pack e)
+  (Success f) -> return (f . unpack)
+
+pcre :: CaseSensitive
+        -> Text
+        -> Exceptional Text (Text -> Bool)
+pcre c t = case PCRE.pcre c (encodeUtf8 t) of
+  (Exception e) -> Exception (pack e)
+  (Success f) -> return (f . encodeUtf8)
+
+within :: CaseSensitive -> Text -> Text -> Bool
+within = txtMatch isInfixOf
+
+exact :: CaseSensitive -> Text -> Text -> Bool
+exact = txtMatch (==)
+
+txtMatch :: (Text -> Text -> Bool)
+            -> CaseSensitive
+            -> Text
+            -> Text -> Bool
+txtMatch f c p t = pat `f` txt where
+  txt = flipCase t
+  pat = flipCase p
+  flipCase = case c of
+    Sensitive -> id
+    Insensitive -> toCaseFold
diff --git a/matchers.cabal b/matchers.cabal
new file mode 100644
--- /dev/null
+++ b/matchers.cabal
@@ -0,0 +1,41 @@
+Name: matchers
+Version: 0.2.0.0
+Cabal-version: >=1.8
+Build-Type: Simple
+License: MIT
+Copyright: 2012 Omari Norman.
+author: Omari Norman
+maintainer: omari@smileystation.com
+stability: Experimental
+homepage: http://www.github.com/massysett/matchers
+bug-reports: omari@smileystation.com
+Category: Text
+License-File: LICENSE
+synopsis: Text matchers
+
+description: Helpers for performing text matches.
+
+source-repository head
+    type: git
+    location: git://github.com/massysett/matchers.git
+
+Library
+    Build-depends:
+        base ==4.*,
+        bytestring ==0.9.*,
+        explicit-exception ==0.1.*,
+        regex-base ==0.93.*,
+        regex-tdfa ==1.1.*,
+        text ==0.11.*,
+        utf8-string ==0.3.*,
+        pcre-light ==0.4.*
+
+    Exposed-modules:
+        Text.Matchers,
+        Text.Matchers.CaseSensitive,
+        Text.Matchers.Regex.PCRE,
+        Text.Matchers.Regex.TDFA,
+        Text.Matchers.String,
+        Text.Matchers.Text
+
+    ghc-options: -Wall
