diff --git a/Acme/LOLCAT.hs b/Acme/LOLCAT.hs
new file mode 100644
--- /dev/null
+++ b/Acme/LOLCAT.hs
@@ -0,0 +1,244 @@
+-- | Module:      Acme.LOLCAT
+--  Copyright:   (c) 2013 Antonio Nikishaev
+--  License:     BSD
+--  Maintainer:  me@lelf.lu
+--
+--
+-- LOLCAT translator.
+--
+-- Roughly based on <https://metacpan.org/pod/Acme::LOLCAT Acme::LOLCAT>
+-- for Perl.
+
+{-# LANGUAGE OverloadedStrings, FlexibleInstances, TupleSections #-}
+{-# LANGUAGE Trustworthy #-}
+
+module Acme.LOLCAT (translate, KindaText()) where
+
+import Data.Text (Text)
+import qualified Data.Text as T
+
+import Text.Parsec
+import Text.Parsec.Text()
+
+import Control.Applicative ((<$>),(<*),(<*>))
+import Control.Arrow
+import Control.Monad
+import Data.String (IsString(..))
+import Data.Monoid
+import Data.Char
+
+import System.Random
+import qualified System.Random.Shuffle as SHU (shuffle')
+
+import Acme.LOLCAT.IO
+
+
+{-# NOINLINE shuffle #-}
+shuffle :: [a] -> [a]
+shuffle xs = do io <- OH HAI I CAN HAZ IO? THXBYE
+                SHU.shuffle' xs (length xs) (io newStdGen)
+
+
+-- | Given a list, return a infinity list of its elements in random order
+variants :: [a] -> [a]
+variants xs = shuffle xs ++ variants xs
+
+
+
+type Parser = Parsec Text ParserState
+data ParserState = Word | Punct deriving Eq
+
+type Pattern = Parser String
+
+
+-- | Replace the part that matched by pattern PAT with TO in S.
+-- Return Right NEW-STRING or Left ORIGINAL if nothing matches.
+replaceOne :: Pattern -> Text -> Text -> Either Text Text
+replaceOne pat to s | Right s' <- parsed = Right s'
+                    | otherwise          = Left s
+    where repl (pref,r) = pref <> to <> T.drop (T.length pref + length r) s
+          parsed = repl <$> myRun (find pat) s
+
+
+-- | Run parser
+myRun :: Parser a -> Text -> Either ParseError a
+myRun p s = runP p Punct "" s
+
+
+-- | Replace PAT with TOS (cycling) in STR
+replace :: Pattern -> [Text] -> Text -> Text
+replace pat tos str = repl (cycle tos) $ Right str
+    where repl (t:ts) (Right s) = repl ts $ replaceOne pat t s
+          repl _ (Left s)       = s
+
+
+{-# NOINLINE translateT #-}
+translateT :: Text -> Text
+translateT src
+           | n == 1    = base <> " THX"
+           | n == 2    = base <> " THXBYE."
+           | otherwise = base
+    where
+      Just n = OH HAI I CAN HAZ IO? THXBYE
+               <*> Just (randomRIO (1,10)) :: Maybe Int
+
+      base = T.toUpper $ last $ scanl f src rules
+      f s (pat,repls) = replace pat (variants repls) s
+
+
+
+
+-- | Text/String-like things
+class KindaText a where
+    fromText :: Text -> a
+    toText :: a -> Text
+
+instance KindaText Text where
+    fromText = id
+    toText = id
+
+instance KindaText String where
+    fromText = T.unpack
+    toText = T.pack
+
+
+-- | >>> translate "You too can speak like a lolcat!"
+-- YOU 2 CAN SPEKK LIEK LOLCAT! THXBYE.
+translate :: KindaText s => s -> s
+translate = fromText . translateT . toText
+
+
+
+instance IsString (Parser String) where
+    fromString = string
+
+
+-- | Given a parser for X returns a new parser for “smth and then X”
+-- returning (SMTH,X)
+-- 
+-- Weird? This is Acme.*.
+find :: Pattern -> Parser (Text,String)
+find pat = try (("",) <$> pat)
+           <|> do c <- anyChar
+                  putState $ if isLetter c then Word else Punct
+                  first (T.cons c) <$> find pat
+
+
+-- | word S exactly
+word :: String -> Parser String
+word s = do Punct <- getState
+            string s <* wordEnd
+
+-- | S is inside word
+inside :: String -> Parser String
+inside s = do Word <- getState
+              string s <* letter
+
+-- | word S and then any punctuations
+wordPuncts :: String -> Parser String
+wordPuncts w = (++) <$> word w <*> many1 space
+
+-- | word ending with S
+wEnds :: String -> Parser String
+wEnds s = string s <* wordEnd
+
+
+wordEnd = notFollowedBy letter
+
+
+
+
+main = forever $ getLine >>= putStrLn . translate
+
+
+
+rules :: [(Parser String, [Text])]
+
+rules = [
+ ("what",                       ["wut", "whut"]),
+ (wEnds "you",		        ["yu", "yous", "yoo", "u"]),
+ ("cture",			["kshur"]),
+ ("unless",			["unles"]),
+ (wEnds "the",	         	["teh"]),
+ ("more",			["moar"]),
+ ("my",				["muh", "mah"]),
+ ("are",			["r", "is", "ar"]),
+ (word "a",			[""]),
+ ("eese",			["eez"]),
+ ("catamorphi",			["cat-a-murrphi"]),
+ ("morphi",			["murrphi"]),
+ ("ph",				["f"]),
+ (wEnds "as",			["az"]),
+ ("seriously",			["srsly"]),
+ (wEnds "er",			["r"]),
+ ("sion",			["shun"]),
+ ("just",			["jus"]),
+ (wEnds "ose",			["oze"]),
+ ("eady",			["eddy"]),
+ (wEnds "om" <|> wEnds "ome",	["um"]),
+ (wEnds "of",			["of", "ov", "of"]),
+ ("uestion",			["wesjun"]),
+ (word "want",			["wants"]),
+ (wEnds "ead",			["edd"]),
+ ("ucke",			["ukki", "ukke"]),
+ ("sion",			["shun"]),
+ ("eak",			["ekk"]),
+ ("age",			["uj"]),
+ ("like",			["likes", "liek"]),
+ ("love",			["loves", "lub", "lubs", "luv"]),
+ (word "is",			["ar teh","ar"]),
+ (wEnds "nd",			["n"]),
+ ("who",			["hoo"]),
+ ("'",				[""]),
+ (wEnds "ese",			["eez"]),
+ ("outh",			["owf"]),
+ ("scio",			["shu"]),
+ ("esque",			["esk"]),
+ ("ture",			["chur"]),
+ (word "too" <|> word "to",	["to", "t", "2", "to", "t"]),
+ ("tious",			["shus"]),
+ (wEnds "sure",			["shur"]),
+ (wEnds "tty",			["tteh"]),
+ ("were",			["was"]),
+ (wEnds "ok",			["k", "kay"]),
+ ("ym",				["im"]),
+ (wEnds "thy",			["fee"]),
+ (inside "ly",			["li"]),
+ ("que" <* letter,		["kwe"]),
+ ("oth",			["udd"]), -- ?
+ ("ease",			["eez"]),
+ (wEnds "ing",			["in", "ins", "ng", "ing"]),
+ ("your",			["yur", "ur", "yore", "yoar"]),
+ (wEnds "ove",			["oov", "ove", "uuv", "uv", "oove"]),
+ ("for",			["for", "4", "fr", "fur", "for", "foar"]),
+ ("thank",			["fank", "tank", "thx", "thnx"]),
+ ("good",			["gud", "goed", "guud", "gude", "gewd"]),
+ ("really",			["rly", "rily", "rilly", "rilley"]),
+ ("world",			["wurrld", "whirld", "wurld", "wrld"]),
+ (word "i'm" <|> word "im",	["im"]),
+
+ -- ("(?!e)ight",		["ite"]),
+ ("tion",			["shun"]),             -- ("(?!ues)tion",["shun"])
+ ("you're" <|> "youre" <|> "you are",
+				["yore", "yr"]),
+
+-- ("\\boh\\b(?!.*hai)",	["o", "ohs"]),
+ (word "oh",			["o", "ohs"]), -- ???
+
+-- ("can" <+spaces, [""]),
+
+ (join<$>sequence [wordPuncts "can",
+                   wordPuncts "i",
+                   wordPuncts "have",
+                   word "a"],	["i can has"]),
+
+ ("have",			["has", "hav", "haz a"]),
+
+ (word "hello" <|> word "hi"
+    <|> word "hey" <|> word "howdy" <|> word "yo",
+				["oh hai"]),
+
+ (word "god",			["ceiling cat"])
+
+ ]
+
diff --git a/Acme/LOLCAT/IO.hs b/Acme/LOLCAT/IO.hs
new file mode 100644
--- /dev/null
+++ b/Acme/LOLCAT/IO.hs
@@ -0,0 +1,24 @@
+-- | Module:      Acme.LOLCAT
+--   Copyright:   (c) 2013 Antonio Nikishaev
+--   License:     BSD
+--   Maintainer:  me@lelf.lu
+{-# LANGUAGE Unsafe #-}
+
+module Acme.LOLCAT.IO (OH(..),HAI(..),I(..),CAN(..),HAZ(..),
+                       IO(..),YO(..),(?),THXBYE(..)) where
+
+import Prelude hiding (IO)
+import System.IO.Unsafe (unsafePerformIO)
+
+
+data HAI a b c d e = HAI a b c d e
+data OH a b c d e = OH a b c d e
+data I = I
+data IO = IO
+data YO = YO
+data CAN = CAN
+data HAZ = HAZ
+data THXBYE = THXBYE
+
+_?_ = return unsafePerformIO
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2013, Antonio Nikishaev
+
+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 Anton Nikishaev 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.markdown b/README.markdown
new file mode 100644
--- /dev/null
+++ b/README.markdown
@@ -0,0 +1,3 @@
+LOLCAT translator.
+
+Roughly based on [Acme::LOLCAT](https://metacpan.org/pod/Acme::LOLCAT)
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+> import Distribution.Simple
+> main = defaultMain
diff --git a/acme-lolcat.cabal b/acme-lolcat.cabal
new file mode 100644
--- /dev/null
+++ b/acme-lolcat.cabal
@@ -0,0 +1,33 @@
+name:                acme-lolcat
+version:             0.1
+homepage:            https://github.com/llelf/acme-lolcat
+synopsis:            LOLSPEAK translator
+description:	     LOLSPEAK translator
+license:             BSD3
+license-file:        LICENSE
+author:              Antonio Nikishaev <me@lelf.lu>
+maintainer:          Antonio Nikishaev <me@lelf.lu>
+copyright:           2013,2014 Antonio Nikishaev
+category:            Acme
+build-type:          Simple
+cabal-version:       >=1.14
+extra-source-files:  README.markdown
+tested-with:         GHC==7.6.3, GHC==7.8.3, GHC==7.9.20140419
+
+library
+  exposed-modules:     Acme.LOLCAT, Acme.LOLCAT.IO
+  build-depends:       
+        base >= 4.5 && < 5, parsec >= 1.3.1, text >= 0.11.3.1,
+        random >= 1.0.1.1, random-shuffle >= 0.0.4
+  default-language:    Haskell2010
+--  ghc-options: -Wall
+
+source-repository head
+  type:     git
+  location: https://github.com/llelf/acme-lolcat
+
+source-repository head
+  type:     darcs
+  location: http://hub.darcs.net/lelf/acme-lolcat
+
+
