    {-# LANGUAGE PatternGuards #-}
    {-# LANGUAGE FlexibleContexts #-}
    -- Copyright (c) 2005 Donald Bruce Stewart - http://www.cse.unsw.edu.au/~dons
    -- GPL version 2 or later (see http://www.gnu.org/copyleft/gpl.html)

    -- Written: Mon Dec 12 10:16:56 EST 2005

    -- | A binding to Djinn.
    module Lambdabot.Plugin.Haskell.Djinn (djinnPlugin) where

    import Lambdabot.Config.Haskell
    import Lambdabot.Logging
    import Lambdabot.Plugin
    import Lambdabot.Util

    import Control.Exception.Lifted as E
    import Control.Monad
    import Control.Monad.Trans
    import Data.Char
    import Data.List
    import Data.Maybe
    import System.Process (readProcess)
    import Text.Regex.TDFA

    -- | We can accumulate an interesting environment
    type DjinnEnv = ([Decl] {- prelude -}, [Decl])
    type Djinn = ModuleT (Maybe DjinnEnv) LB
    type Decl = String

    djinnPlugin :: Module (Maybe DjinnEnv)
    djinnPlugin :: Module (Maybe DjinnEnv)
    djinnPlugin = Module (Maybe DjinnEnv)
    forall st. Module st
    newModule
        { moduleSerialize = Nothing
        , moduleDefState = return Nothing

        -- gratuitous invocation at startup to let the user know if the command is missing
        , moduleInit = void (djinn [] "")

        , moduleCmds = return
            [ (command "djinn")
                { help = mapM_ say
                    [ "djinn <type>."
                    , "Generates Haskell code from a type."
                    , "https://github.com/augustss/djinn"
                    ]
                , process = rejectingCmds djinnCmd
                }
            , (command "djinn-add")
                { help = do
                    say "djinn-add <expr>."
                    say "Define a new function type or type synonym"
                , process = rejectingCmds djinnAddCmd
                }
            , (command "djinn-del")
                { help = do
                    say "djinn-del <ident>."
                    say "Remove a symbol from the environment"
                , process = rejectingCmds djinnDelCmd
                }
            , (command "djinn-env")
                { help = do
                    say "djinn-env."
                    say "Show the current djinn environment"
                , process = const djinnEnvCmd
                }
            , (command "djinn-names")
                { help = do
                    say "djinn-names."
                    say "Show the current djinn environment, compactly."
                , process = const djinnNamesCmd
                }
            , (command "djinn-clr")
                { help = do
                    say "djinn-clr."
                    say "Reset the djinn environment"
                , process = const djinnClrCmd
                }
            , (command "djinn-ver")
                { help = do
                    say "djinn-ver."
                    say "Show current djinn version"
                , process = const djinnVerCmd
                }
            ]
        }

    getSavedEnv :: Djinn DjinnEnv
    getSavedEnv :: Djinn DjinnEnv
    getSavedEnv = (LBState Djinn
     -> (LBState Djinn -> ModuleT (Maybe DjinnEnv) LB ())
     -> Djinn DjinnEnv)
    -> Djinn DjinnEnv
    forall a.
    (LBState Djinn
     -> (LBState Djinn -> ModuleT (Maybe DjinnEnv) LB ())
     -> ModuleT (Maybe DjinnEnv) LB a)
    -> ModuleT (Maybe DjinnEnv) LB a
    forall (m :: * -> *) a.
    MonadLBState m =>
    (LBState m -> (LBState m -> m ()) -> m a) -> m a
    withMS ((LBState Djinn
      -> (LBState Djinn -> ModuleT (Maybe DjinnEnv) LB ())
      -> Djinn DjinnEnv)
     -> Djinn DjinnEnv)
    -> (LBState Djinn
        -> (LBState Djinn -> ModuleT (Maybe DjinnEnv) LB ())
        -> Djinn DjinnEnv)
    -> Djinn DjinnEnv
    forall a b. (a -> b) -> a -> b
    $ \LBState Djinn
    st LBState Djinn -> ModuleT (Maybe DjinnEnv) LB ()
    write ->
        case LBState Djinn
    st of
            Just DjinnEnv
    env -> DjinnEnv -> Djinn DjinnEnv
    forall a. a -> ModuleT (Maybe DjinnEnv) LB a
    forall (m :: * -> *) a. Monad m => a -> m a
    return DjinnEnv
    env
            Maybe DjinnEnv
    LBState Djinn
    Nothing -> do
                st' <- DjinnEnv -> ModuleT (Maybe DjinnEnv) LB (Either [String] DjinnEnv)
    forall (m :: * -> *).
    MonadLB m =>
    DjinnEnv -> m (Either [String] DjinnEnv)
    getDjinnEnv ([],[]) -- get the prelude

                -- TODO: don't swallow errors here
                let newMS = (([String] -> [String])
    -> (DjinnEnv -> [String]) -> Either [String] DjinnEnv -> [String]
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either ([String] -> [String] -> [String]
    forall a b. a -> b -> a
    const []) DjinnEnv -> [String]
    forall a b. (a, b) -> b
    snd{-!-} Either [String] DjinnEnv
    st', [])
                write (Just newMS)
                return newMS

    getUserEnv :: Djinn [Decl]
    getUserEnv :: Djinn [String]
    getUserEnv = (DjinnEnv -> [String]) -> Djinn DjinnEnv -> Djinn [String]
    forall a b.
    (a -> b)
    -> ModuleT (Maybe DjinnEnv) LB a -> ModuleT (Maybe DjinnEnv) LB b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap DjinnEnv -> [String]
    forall a b. (a, b) -> b
    snd Djinn DjinnEnv
    getSavedEnv

    -- check the args, reject them if they start with a colon (ignoring whitespace)
    rejectingCmds :: Monad m => ([Char] -> Cmd m ()) -> [Char] -> Cmd m ()
    rejectingCmds :: forall (m :: * -> *).
    Monad m =>
    (String -> Cmd m ()) -> String -> Cmd m ()
    rejectingCmds String -> Cmd m ()
    action String
    args
        | Int -> String -> String
    forall a. Int -> [a] -> [a]
    take Int
    1 ((Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhile Char -> Bool
    isSpace String
    args) String -> String -> Bool
    forall a. Eq a => a -> a -> Bool
    == String
    ":"
                    = String -> Cmd m ()
    forall (m :: * -> *). Monad m => String -> Cmd m ()
    say String
    "Invalid command"
        | Bool
    otherwise = String -> Cmd m ()
    action String
    args

    -- Normal commands
    djinnCmd :: [Char] -> Cmd Djinn ()
    djinnCmd :: String -> Cmd Djinn ()
    djinnCmd String
    s = do
            env     <- Djinn [String] -> Cmd Djinn [String]
    forall (m :: * -> *) a. Monad m => m a -> Cmd m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift Djinn [String]
    getUserEnv
            e       <- djinn env $ ":set +sorted\nf ? " ++ dropForall s
            mapM_ say $ either id (parse . lines) e
        where
          dropForall :: source1 -> source1
    dropForall source1
    t = source1
    -> (MatchResult source1 -> source1)
    -> Maybe (MatchResult source1)
    -> source1
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe source1
    t MatchResult source1 -> source1
    forall a. MatchResult a -> a
    mrAfter (source1
    t source1 -> String -> Maybe (MatchResult source1)
    forall source source1 target (m :: * -> *).
    (RegexMaker Regex CompOption ExecOption source,
     RegexContext Regex source1 target, MonadFail m) =>
    source1 -> source -> m target
    =~~ String
    re)
          re :: String
    re = String
    "^forall [[:alnum:][:space:]]+\\."
          parse :: [String] -> [String]
          parse :: [String] -> [String]
    parse [String]
    x = if [String] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [String]
    x Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    2
                    then [String
    "No output from Djinn; installed?"]
                    else [String] -> [String]
    forall a. HasCallStack => [a] -> [a]
    tail [String]
    x

    -- Augment environment. Have it checked by djinn.
    djinnAddCmd :: [Char] -> Cmd Djinn ()
    djinnAddCmd :: String -> Cmd Djinn ()
    djinnAddCmd String
    s = do
        (p,st)  <- Djinn DjinnEnv -> Cmd Djinn DjinnEnv
    forall (m :: * -> *) a. Monad m => m a -> Cmd m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift Djinn DjinnEnv
    getSavedEnv
        est     <- getDjinnEnv (p, strip isSpace s : st)
        case est of
            Left [String]
    e     -> String -> Cmd Djinn ()
    forall (m :: * -> *). Monad m => String -> Cmd m ()
    say ([String] -> String
    forall a. HasCallStack => [a] -> a
    head [String]
    e)
            Right DjinnEnv
    st'  -> LBState (Cmd Djinn) -> Cmd Djinn ()
    forall (m :: * -> *). MonadLBState m => LBState m -> m ()
    writeMS (DjinnEnv -> Maybe DjinnEnv
    forall a. a -> Maybe a
    Just DjinnEnv
    st')

    -- Display the environment
    djinnEnvCmd :: Cmd Djinn ()
    djinnEnvCmd :: Cmd Djinn ()
    djinnEnvCmd = do
        (prelude,st) <- Djinn DjinnEnv -> Cmd Djinn DjinnEnv
    forall (m :: * -> *) a. Monad m => m a -> Cmd m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift Djinn DjinnEnv
    getSavedEnv
        mapM_ say $ prelude ++ st

    -- Display the environment's names (quarter-baked)
    djinnNamesCmd :: Cmd Djinn ()
    djinnNamesCmd :: Cmd Djinn ()
    djinnNamesCmd = do
        (prelude,st) <- Djinn DjinnEnv -> Cmd Djinn DjinnEnv
    forall (m :: * -> *) a. Monad m => m a -> Cmd m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift Djinn DjinnEnv
    getSavedEnv
        let names = [String] -> String
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat ([String] -> String) -> [String] -> String
    forall a b. (a -> b) -> a -> b
    $ String -> [String] -> [String]
    forall a. a -> [a] -> [a]
    intersperse String
    " " ([String] -> [String]) -> [String] -> [String]
    forall a b. (a -> b) -> a -> b
    $ (String -> [String]) -> [String] -> [String]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap String -> [String]
    extractNames ([String] -> [String]) -> [String] -> [String]
    forall a b. (a -> b) -> a -> b
    $ [String]
    prelude [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String]
    st
        say names
      where extractNames :: String -> [String]
    extractNames = (String -> Bool) -> [String] -> [String]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Char -> Bool
    isUpper (Char -> Bool) -> (String -> Char) -> String -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> Char
    forall a. HasCallStack => [a] -> a
    head) ([String] -> [String])
    -> (String -> [String]) -> String -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (String -> Maybe (String, String)) -> String -> [String]
    forall b a. (b -> Maybe (a, b)) -> b -> [a]
    unfoldr (\String
    x -> case String
    x of Char
    _:String
    _ -> [(String, String)] -> Maybe (String, String)
    forall a. [a] -> Maybe a
    listToMaybe (ReadS String
    lex String
    x); String
    _ -> Maybe (String, String)
    forall a. Maybe a
    Nothing)

    -- Reset the env
    djinnClrCmd :: Cmd Djinn ()
    djinnClrCmd :: Cmd Djinn ()
    djinnClrCmd = LBState (Cmd Djinn) -> Cmd Djinn ()
    forall (m :: * -> *). MonadLBState m => LBState m -> m ()
    writeMS Maybe DjinnEnv
    LBState (Cmd Djinn)
    forall a. Maybe a
    Nothing

    -- Remove sym from environment. We let djinn do the hard work of
    -- looking up the symbols.
    djinnDelCmd :: [Char] -> Cmd Djinn ()
    djinnDelCmd :: String -> Cmd Djinn ()
    djinnDelCmd String
    s =  do
        (_,env) <- Djinn DjinnEnv -> Cmd Djinn DjinnEnv
    forall (m :: * -> *) a. Monad m => m a -> Cmd m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift Djinn DjinnEnv
    getSavedEnv
        eenv <- djinn env $ ":delete " ++ strip isSpace s ++ "\n:environment"
        case eenv of
            Left [String]
    e     -> String -> Cmd Djinn ()
    forall (m :: * -> *). Monad m => String -> Cmd m ()
    say ([String] -> String
    forall a. HasCallStack => [a] -> a
    head [String]
    e)
            Right String
    env' -> (Maybe DjinnEnv -> Maybe DjinnEnv) -> Cmd Djinn ()
    (LBState (Cmd Djinn) -> LBState (Cmd Djinn)) -> Cmd Djinn ()
    forall (m :: * -> *).
    MonadLBState m =>
    (LBState m -> LBState m) -> m ()
    modifyMS ((Maybe DjinnEnv -> Maybe DjinnEnv) -> Cmd Djinn ())
    -> ((DjinnEnv -> DjinnEnv) -> Maybe DjinnEnv -> Maybe DjinnEnv)
    -> (DjinnEnv -> DjinnEnv)
    -> Cmd Djinn ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (DjinnEnv -> DjinnEnv) -> Maybe DjinnEnv -> Maybe DjinnEnv
    forall a b. (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap ((DjinnEnv -> DjinnEnv) -> Cmd Djinn ())
    -> (DjinnEnv -> DjinnEnv) -> Cmd Djinn ()
    forall a b. (a -> b) -> a -> b
    $ \([String]
    prel,[String]
    _) ->
                ([String]
    prel,(String -> Bool) -> [String] -> [String]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (String -> [String] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `notElem` [String]
    prel) ([String] -> [String])
    -> (String -> [String]) -> String -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [String] -> [String]
    forall a. Eq a => [a] -> [a]
    nub ([String] -> [String])
    -> (String -> [String]) -> String -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> [String]
    lines (String -> [String]) -> String -> [String]
    forall a b. (a -> b) -> a -> b
    $ String
    env')

    -- Version number
    djinnVerCmd :: Cmd Djinn ()
    djinnVerCmd :: Cmd Djinn ()
    djinnVerCmd = String -> Cmd Djinn ()
    forall (m :: * -> *). Monad m => String -> Cmd m ()
    say (String -> Cmd Djinn ()) -> Cmd Djinn String -> Cmd Djinn ()
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
    =<< Cmd Djinn String
    forall (m :: * -> *). MonadLB m => m String
    getDjinnVersion

    ------------------------------------------------------------------------

    -- | Extract the default environment
    getDjinnEnv :: (MonadLB m) => DjinnEnv -> m (Either [String] DjinnEnv)
    getDjinnEnv :: forall (m :: * -> *).
    MonadLB m =>
    DjinnEnv -> m (Either [String] DjinnEnv)
    getDjinnEnv ([String]
    prel,[String]
    env') = do
        env <- [String] -> String -> m (Either [String] String)
    forall (m :: * -> *).
    MonadLB m =>
    [String] -> String -> m (Either [String] String)
    djinn [String]
    env' String
    ":environment"
        return (either Left (Right . readEnv) env)
        where
            readEnv :: String -> DjinnEnv
    readEnv String
    o = let new :: [String]
    new = (String -> Bool) -> [String] -> [String]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (\String
    p -> String
    p String -> [String] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `notElem` [String]
    prel) ([String] -> [String])
    -> (String -> [String]) -> String -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [String] -> [String]
    forall a. Eq a => [a] -> [a]
    nub ([String] -> [String])
    -> (String -> [String]) -> String -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    .  String -> [String]
    lines (String -> [String]) -> String -> [String]
    forall a b. (a -> b) -> a -> b
    $ String
    o
                         in ([String]
    prel, [String]
    new)

    getDjinnVersion :: MonadLB m => m String
    getDjinnVersion :: forall (m :: * -> *). MonadLB m => m String
    getDjinnVersion = do
        binary <- Config String -> m String
    forall a. Config a -> m a
    forall (m :: * -> *) a. MonadConfig m => Config a -> m a
    getConfig Config String
    djinnBinary
        io (fmap readVersion (readProcess binary [] ":q"))
            `E.catch` \SomeException{} ->
                String -> m String
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return String
    "The djinn command does not appear to be installed."
        where
            readVersion :: String -> String
    readVersion = String -> String
    forall {source1}.
    RegexContext Regex source1 String =>
    source1 -> String
    extractVersion (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [String] -> String
    unlines ([String] -> String) -> (String -> [String]) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Int -> [String] -> [String]
    forall a. Int -> [a] -> [a]
    take Int
    1 ([String] -> [String])
    -> (String -> [String]) -> String -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> [String]
    lines
            extractVersion :: source1 -> String
    extractVersion source1
    str = case source1
    str source1 -> String -> Maybe String
    forall source source1 target (m :: * -> *).
    (RegexMaker Regex CompOption ExecOption source,
     RegexContext Regex source1 target, MonadFail m) =>
    source1 -> source -> m target
    =~~ String
    "version [0-9]+(-[0-9]+)*" of
                Maybe String
    Nothing -> String
    "Unknown"
                Just String
    m  -> String
    m

    -- | Call the binary:

    djinn :: MonadLB m => [Decl] -> String -> m (Either [String] String)
    djinn :: forall (m :: * -> *).
    MonadLB m =>
    [String] -> String -> m (Either [String] String)
    djinn [String]
    env String
    src = do
        binary <- Config String -> m String
    forall a. Config a -> m a
    forall (m :: * -> *) a. MonadConfig m => Config a -> m a
    getConfig Config String
    djinnBinary
        io (tryDjinn binary env src)
            `E.catch` \e :: SomeException
    e@SomeException{} -> do
                let cmdDesc :: String
    cmdDesc = case String
    binary of
                        String
    "djinn" -> String
    ""
                        String
    _       -> String
    "(" String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    binary String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    ") "
                    msg :: String
    msg = String
    "Djinn command " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    cmdDesc String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ String
    "failed: " String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ SomeException -> String
    forall a. Show a => a -> String
    show SomeException
    e
                String -> m ()
    forall (m :: * -> *). MonadLogging m => String -> m ()
    errorM String
    msg
                Either [String] String -> m (Either [String] String)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ([String] -> Either [String] String
    forall a b. a -> Either a b
    Left [String
    msg])

    tryDjinn :: String -> [Decl] -> String -> IO (Either [String] String)
    tryDjinn :: String -> [String] -> String -> IO (Either [String] String)
    tryDjinn String
    binary [String]
    env String
    src = do
        out <- String -> [String] -> String -> IO String
    readProcess String
    binary [] ([String] -> String
    unlines ([String]
    env [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ [String
    src, String
    ":q"]))
        let safeInit [] = []
            safeInit [a]
    xs = [a] -> [a]
    forall a. HasCallStack => [a] -> [a]
    init [a]
    xs
            o = (Char -> Bool) -> String -> String
    forall a. (a -> Bool) -> [a] -> [a]
    dropFromEnd (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    '\n') (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> String
    clean_ (String -> String) -> (String -> String) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [String] -> String
    unlines ([String] -> String) -> (String -> [String]) -> String -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [String] -> [String]
    forall {a}. [a] -> [a]
    safeInit ([String] -> [String])
    -> (String -> [String]) -> String -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Int -> [String] -> [String]
    forall a. Int -> [a] -> [a]
    drop Int
    2 ([String] -> [String])
    -> (String -> [String]) -> String -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> [String]
    lines (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ String
    out
        return $ case () of {()
    _
            | String
    o String -> String -> Bool
    forall source source1 target.
    (RegexMaker Regex CompOption ExecOption source,
     RegexContext Regex source1 target) =>
    source1 -> source -> target
    =~ String
    "Cannot parse command" Bool -> Bool -> Bool
    ||
              String
    o String -> String -> Bool
    forall source source1 target.
    (RegexMaker Regex CompOption ExecOption source,
     RegexContext Regex source1 target) =>
    source1 -> source -> target
    =~ String
    "cannot be realized"   Bool -> Bool -> Bool
    ||
              String
    o String -> String -> Bool
    forall source source1 target.
    (RegexMaker Regex CompOption ExecOption source,
     RegexContext Regex source1 target) =>
    source1 -> source -> target
    =~ String
    "^Error:"                -> [String] -> Either [String] String
    forall a b. a -> Either a b
    Left (String -> [String]
    lines String
    o)
            | Bool
    otherwise                     -> String -> Either [String] String
    forall a b. b -> Either a b
    Right String
    o
        }

    --
    -- Clean up djinn output
    --
    clean_ :: String -> String
    clean_ :: String -> String
    clean_ String
    s | Just MatchResult String
    mr <- String
    s String -> String -> Maybe (MatchResult String)
    forall source source1 target (m :: * -> *).
    (RegexMaker Regex CompOption ExecOption source,
     RegexContext Regex source1 target, MonadFail m) =>
    source1 -> source -> m target
    =~~ String
    prompt  = MatchResult String -> String
    forall a. MatchResult a -> a
    mrBefore MatchResult String
    mr String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ MatchResult String -> String
    forall a. MatchResult a -> a
    mrAfter MatchResult String
    mr
             | Bool
    otherwise                = String
    s
        where
            prompt :: String
    prompt = String
    "(Djinn> *)+"