import Data.List (isPrefixOf) import Data.Maybe (catMaybes) import Control.Monad import Text.ParserCombinators.Parsec import System.IO import System.Environment (getArgs) main = do args <- getArgs unless (length args == 1) $ fail "Please provide exactly one source file as an argument." let sourcePath = head args source <- readFile sourcePath input <- getContents case parse markovParser sourcePath source of Right rules -> putStr $ runMarkov rules input Left err -> hPutStrLn stderr $ "Parse error at " ++ show err data Rule = Rule {from :: String, terminating :: Bool, to :: String} markovParser :: Parser [Rule] markovParser = liftM catMaybes $ (comment <|> rule) `sepEndBy` many1 newline where comment = char '#' >> skipMany nonnl >> return Nothing rule = liftM Just $ liftM3 Rule (manyTill (nonnl "pattern character") $ try arrow) (succeeds $ char '.') (many nonnl) arrow = ws >> string "->" >> ws "whitespace-delimited arrow" nonnl = noneOf "\n" ws = many1 $ oneOf " \t" succeeds p = option False $ p >> return True runMarkov :: [Rule] -> String -> String runMarkov rules s = f rules s where f [] s = s f (Rule from terminating to : rs) s = g "" s where g _ "" = f rs s g before ahead@(a : as) = if from `isPrefixOf` ahead then let new = reverse before ++ to ++ drop (length from) ahead in if terminating then new else f rules new else g (a : before) as