-- |A module providing simple Parser combinator functionality. Useful
-- for small parsing tasks such as identifier parsing or command-line
-- argument parsing
module Language.Parser (
  module Definitive,
  -- * The ParserT Type
  ParserT(..),Parser,ParserA(..),i'ParserA,

  -- ** The Stream class
  Stream(..),emptyStream,

  -- ** Converting to/from Parsers
  parserT,parser,ioParser,

  -- * Basic combinators
  (<+>),(>*>),(<*<),cut,
  token,satisfy,
  oneOf,noneOf,single,
  several,
  remaining,eoi,

  -- ** Specialized utilities
  readable,number,digit,letter,alNum,quotedString,space,spaces,eol,
  
  -- * Basic combinators
  many,many1,sepBy,sepBy1,skipMany,skipMany1,
  chainl,chainr,option                   
  ) where

import Definitive hiding (take)

import Data.Char
import Data.Containers.Sequence

newtype ParserT s m a = ParserT (StateT s (LogicT m) a)
                        deriving (Unit,Functor,Semigroup,Monoid,Applicative,
                                  MonadFix,MonadState s)
instance (Monad m,Stream Char s) => IsString (ParserT s m a) where
  fromString s = undefined <$ several s
instance Monad m => Monad (ParserT s m) where join = coerceJoin ParserT
type Parser c a = ParserT c Id a
instance MonadTrans (ParserT s) where
  lift = ParserT . lift . lift
instance ConcreteMonad (ParserT s) where
  generalize = parserT %%~ map (pure.yb i'Id)
i'ParserT :: Iso (ParserT s m a) (ParserT t n b) (StateT s (LogicT m) a) (StateT t (LogicT n) b)
i'ParserT = iso ParserT (\(ParserT p) -> p)
parserT :: (Monad n,Monad m) => Iso (ParserT s m a) (ParserT t n b) (s -> m [(s,a)]) (t -> n [(t,b)])
parserT = mapping listLogic.stateT.i'ParserT
parser :: Iso (Parser s a) (Parser t b) (s -> [(s,a)]) (t -> [(t,b)])
parser = mapping i'Id.parserT

ioParser :: Parser a b -> (a -> IO b)
ioParser p s = case (p^..parser) s of
  [] -> error "Error in parsing"
  (_,a):_ -> return a

-- |The @(+)@ operator with lower priority
(<+>) :: Semigroup m => m -> m -> m
(<+>) = (+)
(>*>) :: Monad m => ParserT a m b -> ParserT b m c -> ParserT a m c
(>*>) = (>>>)^..(i'ParserA<.>i'ParserA<.>i'ParserA)
(<*<) :: Monad m => ParserT b m c -> ParserT a m b -> ParserT a m c
(<*<) = flip (>*>)
cut :: Monad m => ParserT s m a -> ParserT s m a
cut = parserT %%~ map2 (take 1)

newtype ParserA m s a = ParserA (ParserT s m a)
i'ParserA :: Iso (ParserA m s a) (ParserA m' s' a') (ParserT s m a) (ParserT s' m' a')
i'ParserA = iso ParserA (\(ParserA p) -> p)
parserA :: Iso (ParserA m s a) (ParserA m' s' a') (StateA (LogicT m) s a) (StateA (LogicT m') s' a') 
parserA = from stateA.i'ParserT.i'ParserA
instance Monad m => Deductive (ParserA m) where
  (.) = (.)^.(parserA<.>parserA<.>parserA)
instance Monad m => Category (ParserA m) where
  id = ParserA get
instance Monad m => Split (ParserA m) where
  (<#>) = (<#>)^.(parserA<.>parserA<.>parserA)
instance Monad m => Choice (ParserA m) where
  (<|>) = (<|>)^.(parserA<.>parserA<.>parserA)
instance Monad m => Arrow (ParserA m) where
  arr f = arr f^.parserA

-- |The remaining Stream to parse
remaining :: Monad m => ParserT s m s
remaining = get
-- |Consume a token from the Stream
token :: (Monad m,Stream c s) => ParserT s m c
{-# SPECIALIZE token :: Monad m => ParserT [c] m c #-}
token = get >>= \s -> case uncons s of
  Nothing -> zero
  Just (c,t) -> c <$ put t

-- |Parse zero, one or more successive occurences of a parser.
many :: Monad m => ParserT c m a -> ParserT c m [a]
many p = many1 p <+> pure []
-- |Parse one or more successiveé occurences of a parser.
many1 :: Monad m => ParserT c m a -> ParserT c m [a]
many1 p = (:)<$>p<*>many p
-- |Skip many occurences of a parser
skipMany :: Monad m => ParserT c m a -> ParserT c m ()
skipMany p = skipMany1 p <+> pure () 
-- |Skip multiple occurences of a parser
skipMany1 :: Monad m => ParserT c m a -> ParserT c m ()
skipMany1 p = p >> skipMany p

-- |Consume a token and succeed if it verifies a predicate
satisfy :: (Monad m, Stream c s) => (c -> Bool) -> ParserT s m c
{-# SPECIALIZE satisfy :: Monad m => (c -> Bool) -> ParserT [c] m c #-}
satisfy p = token <*= guard . p
-- |Consume a single fixed token or fail.
single :: (Eq c, Monad m, Stream c s) => c -> ParserT s m ()
single = void . satisfy . (==)

-- |Consume a structure of characters or fail
several :: (Eq c, Monad m, Foldable t, Stream c s) => t c -> ParserT s m ()
{-# SPECIALIZE several :: (Eq c, Monad m) => [c] -> ParserT [c] m () #-}
several l = traverse_ single l

-- |Try to consume a parser. Return a default value when it fails.
option :: Monad m => a -> ParserT s m a -> ParserT s m a
option a p = p <+> pure a

-- |Succeed only at the End Of Input.
eoi :: (Monad m,Stream c s) => ParserT s m ()
eoi = remaining >>= guard.emptyStream
-- |The end of a line
eol :: (Monad m,Stream Char s) => ParserT s m ()
eol = single '\n'

-- |Parse one or more successive occurences of a parser separated by
-- occurences of a second parser.
sepBy1 ::Monad m => ParserT c m a -> ParserT c m b -> ParserT c m [a]
sepBy1 p sep = (:)<$>p<*>many (sep >> p)
-- |Parse zero or more successive occurences of a parser separated by
-- occurences of a second parser.
sepBy ::Monad m => ParserT c m a -> ParserT c m b -> ParserT c m [a]
sepBy p sep = option [] (sepBy1 p sep)

-- |Parse a member of a set of values
oneOf :: (Eq c, Monad m, Foldable t, Stream c s) => t c -> ParserT s m c
oneOf = satisfy . flip elem
-- |Parse anything but a member of a set
noneOf :: (Eq c, Monad m, Foldable t, Stream c s) => t c -> ParserT s m c
noneOf = satisfy . map not . flip elem

-- |Parse a litteral decimal number
number :: (Monad m,Stream Char s,Num n) => ParserT s m n
number = fromInteger.read <$> many1 digit
-- |Parse a single decimal digit
digit :: (Monad m,Stream Char s) => ParserT s m Char
digit = satisfy isDigit
alNum :: (Monad m,Stream Char s) => ParserT s m Char
alNum = satisfy isAlphaNum
letter :: (Monad m,Stream Char s) => ParserT s m Char
letter = satisfy isAlpha
-- |Parse a delimited string, using '\\' as the quoting character
quotedString :: (Monad m,Stream Char s) => Char -> ParserT s m String
quotedString d = between (single d) (single d) (many ch)
  where ch = single '\\' >> unquote<$>token
             <+> noneOf (d:"\\")
        unquote 'n' = '\n'
        unquote 't' = '\t'
        unquote c = c
-- |A single space
space :: (Monad m,Stream Char s) => ParserT s m Char
space = satisfy isSpace
-- |Many spaces
spaces :: (Monad m,Stream Char s) => ParserT s m String
spaces = many1 space

infixl 1 `sepBy`,`sepBy1`
infixr 0 <+>

-- |Chain an operator with an initial value and several tail values.
chainr :: (Stream c s,Monad m) => ParserT s m a -> ParserT s m (b -> a -> a) -> ParserT s m b -> ParserT s m a
chainr expr op e = compose<$>many (op<**>e)<*>expr
-- |Chain an operator with an initial value
chainl :: (Stream c s,Monad m) => ParserT s m a -> ParserT s m (a -> b -> a) -> ParserT s m b -> ParserT s m a
chainl expr op e = compose<$>many (flip<$>op<*>e)<**>expr

-- |Test if a Stream is empty
emptyStream :: Stream c s => s -> Bool
emptyStream = maybe True (const False) . uncons

readable :: (Monad m,Read a) => ParserT String m a 
readable = generalize $ map2 swap (readsPrec 0)^.parser