module System.Console.Quickterm.Deserializer
    ( Deserializer (..)
    , tryConvert
    ) where

import Control.Applicative
import Control.Monad


-- |Deserializers are used in marshaling process of cmd-line parameters.
newtype Deserializer a = Deserializer { deserialize :: [Char] -> Int -> [(a,[Char],Int)] }

instance Functor Deserializer where
  fmap f d = Deserializer $ \st p -> fmap (\(a,st',p') -> (f a,st',p')) (deserialize d st p)

instance Applicative Deserializer where
  pure a = Deserializer $ \st p -> [(a,st,p)]
  f <*> d = Deserializer $ \st p -> deserialize f st p
                         >>= \(g,st',p') -> deserialize d st' p'
                         >>= \(a,st'',p'') -> return (g a,st'',p'')

instance Alternative Deserializer where
  empty = Deserializer (const (const empty))
  d <|> h = Deserializer $ \st p -> deserialize d st p <|> deserialize h st p

instance Monad Deserializer where
  return = pure
  d >>= f = Deserializer $ \st p -> deserialize d st p >>= \(d',st',p') -> deserialize (f d') st' p'

instance MonadPlus Deserializer where
  mzero = empty
  mplus = (<|>)

-- |A pure computation abstraction layer for the Deserializer.
tryConvert :: (String -> [(a,Int)]) -> Deserializer a
tryConvert f = Deserializer $ \st p -> (\(a,i) -> (a,[],p+i)) <$> f st