packages feed

owoify-hs-1.0.0.0: src/Data/Owoify/Internal/Parser/OwoifyParser.hs

{-# LANGUAGE RankNTypes #-}
module Data.Owoify.Internal.Parser.OwoifyParser
  ( count
  , OError(..)
  , OwoifyError
  , OwoifyParser
  , runParser
  )
  where

import Prelude

import Control.Monad ((>=>), foldM, replicateM)
import Data.Function ((&))
import Data.List (uncons)
import Data.Owoify.Internal.Entity.Word (InnerWord(InnerWord, innerWord, innerReplacedWords)) 
import Data.Text.Lazy (Text)

-- | Represents those types denoting errors when owoifying.
class OwoifyError e where
  -- | Representing that the source collection of strings has been exhausted.
  eof :: e
  -- | Representing general parser error. Currently not used.
  parseError :: Text -> e

-- | A simple type representing errors that occur during owoification.
data OError = EOF | ParseError Text deriving (Show)

instance OwoifyError OError where
  eof = EOF
  parseError = ParseError

type OwoifyResult a = ([Text], a)

type OwoifyFunction e a = OwoifyError e => [Text] -> Either e (OwoifyResult a)

newtype OwoifyParser e a = OwoifyParser (OwoifyFunction e a)

instance Functor (OwoifyParser e) where
  fmap f (OwoifyParser g) = OwoifyParser (fmap (fmap f) . g)

instance Applicative (OwoifyParser e) where
  (<*>) (OwoifyParser f) (OwoifyParser g) = OwoifyParser (f >=> \(s', ab) -> g s' >>= \(s'', a) -> pure (s'', ab a))
  pure x = OwoifyParser (\s -> pure (s, x))

instance Monad (OwoifyParser e) where
  (>>=) (OwoifyParser f) g = OwoifyParser (f >=> \(s', a) -> runParser (g a) s')

-- | Executes (unwraps) the parser inside the monad.
runParser :: OwoifyError e => OwoifyParser e a -> [Text] -> Either e (OwoifyResult a)
runParser (OwoifyParser f) = f

word ::
  (Foldable t, Monad m, OwoifyError e)
  => t (InnerWord -> m InnerWord)
  -> OwoifyParser e (m InnerWord)
word mappings = OwoifyParser (\s ->
  case uncons s of
    Nothing -> Left eof
    Just (head, tail) -> do
      let w = InnerWord { innerWord = head, innerReplacedWords = [] }
      let result = foldM (&) w mappings
      Right (tail, result))

-- | Replicate owoify parser according to the specified length (`n`) and a collection of owoify functions. 
count ::
  (Foldable t, Monad m, OwoifyError e)
  => Int
  -> t (InnerWord -> m InnerWord)
  -> OwoifyParser e [m InnerWord]
count n p | n <= 0 = pure []
          | otherwise = replicateM n $ word p