madlang-2.3.2.0: src/Text/Madlibs/Ana/ParseUtils.hs
-- | Helper functions to sort out parsing
module Text.Madlibs.Ana.ParseUtils (
modifierList
, strip
, takeTemplate
, sortKeys
, build
, buildTree
) where
import Text.Madlibs.Internal.Types
import Text.Madlibs.Internal.Utils
import Text.Madlibs.Cata.SemErr
import Data.List
import qualified Data.Text as T
import Control.Monad.State
import Data.Foldable
import Control.Arrow
import System.Random.Shuffle
import qualified Data.Map as M
import Data.Char
import Data.Monoid
import Data.Function.Contravariant.Syntax
import Control.Monad.Random.Class
--TODO consider moving Ana.ParseUtils to Cata.Sorting
(&) :: a -> (a -> b) -> b
(&) x f = f x
-- | A map with all the modifiers we
modifierList :: M.Map String (T.Text -> T.Text)
modifierList = M.fromList [("to_upper", T.map toUpper)
, ("to_lower", T.map toLower)
, ("capitalize", (\t -> toUpper (T.head t) `T.cons` T.tail t))
, ("reverse", T.reverse)
, ("reverse_words", T.unwords . reverse . T.words)
, ("oulipo", T.filter (/='e'))]
-- | Jumble the words in a string
jumble :: (MonadRandom m) => T.Text -> m T.Text
jumble = (fmap (T.pack . unwords)) . shuffleM . words . T.unpack
-- | Strip file extension
strip :: String -> T.Text
strip = T.pack . reverse . drop 4 . reverse
-- | Get the :return value
takeTemplate :: [(Key, RandTok)] -> RandTok
takeTemplate = snd . head . filter (\(i,j) -> i == "Return")
-- | Convert the stuff after the number to a `RandTok`
concatTok :: T.Text -> Context [PreTok] -> Context RandTok
concatTok param pretoks = do
ctx <- get
let unList (List a) = a
let toRand (Name str f) = (apply f) . List . snd . (head' str param) . (filter ((== str) . fst)) . (map (second unList)) $ ctx-- TODO fix head' which can fail because of lack of scope too
toRand (PreTok txt) = Value txt
fold . (map toRand) <$> pretoks
-- | Build token in tree structure, without concatenating.
buildTok :: T.Text -> Context [PreTok] -> Context RandTok
buildTok param pretoks = do
ctx <- get
let unList (List a) = a
let toRand (Name str f) = (apply f) . List . snd . (head' str param) . (filter ((==str) . fst)) . (map (second unList)) $ ctx
toRand (PreTok txt) = Value txt
List . zip ([1..]) . (map toRand) <$> pretoks
-- | Build the token without concatenating, yielding a `RandTok` suitable to be
-- printed as a tree.
buildTree :: [(Key, [(Prob, [PreTok])])] -> Context RandTok
buildTree list@[(key,pairs)] = do
toks <- mapM (\(i,j) -> buildTok key (pure j)) pairs
let probs = map fst pairs
let tok = List $ zip probs toks
state (\s -> (tok,((key,tok):s)))
buildTree list@(x:xs) = do
y <- buildTree [x]
ys <- pure <$> buildTree xs
pure . List . zip ([1..]) $ (y:ys)
-- | Given keys naming the tokens, and lists of `PreTok`, build our `RandTok`
build :: [(Key, [(Prob, [PreTok])])] -> Context RandTok
build list@[(key,pairs)] = do
toks <- mapM (\(i,j) -> concatTok key (pure j)) pairs
let probs = map fst pairs
let tok = List $ zip probs toks
state (\s -> (tok,((key, tok):s)))
build list@(x:xs) = do
y <- (build [x])
ys <- pure <$> build xs
pure $ fold (y:ys)
-- | Sort the keys that we have parsed so that dependencies are in the correct places
sortKeys :: [(Key, [(Prob, [PreTok])])] -> [(Key, [(Prob, [PreTok])])]
sortKeys = sortBy orderKeys
-- | Ordering on the keys to account for dependency
orderKeys :: (Key, [(Prob, [PreTok])]) -> (Key, [(Prob, [PreTok])]) -> Ordering
orderKeys (key1, l1) (key2, l2)
| key1 == "Return" = GT
| key2 == "Return" = LT
| any (\pair -> {--hyphenList key1 (map unTok . snd $ pair) ||--} key1 `elem` (map unTok . snd $ pair)) l2 = LT
| any (\pair -> {--hyphenList key2 (map unTok . snd $ pair) ||--} key2 `elem` (map unTok . snd $ pair)) l1 = GT
| otherwise = EQ
hyphenList :: T.Text -> [T.Text] -> Bool
hyphenList s1 = any (flip hyphenSuffix s1)
hyphenSuffix :: T.Text -> T.Text -> Bool
hyphenSuffix = ("-" <>) -.* T.isSuffixOf