packages feed

trifecta-0.12: Text/Trifecta/Parser/Prim.hs

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleContexts, Rank2Types, FlexibleInstances #-}
module Text.Trifecta.Parser.Prim 
  ( Parser(..)
  , why
-- , stepParser
  ) where

import Control.Applicative
import Control.Monad.Error.Class
import Control.Monad.Writer.Class
import Control.Monad
import Data.Functor.Plus
import Data.Semigroup
-- import Data.Monoid
import Data.Functor.Bind
import Data.Set as Set hiding (empty)
import Data.ByteString as Strict hiding (empty)
import Data.Sequence as Seq hiding (empty)
import Data.ByteString.UTF8 as UTF8
import Data.Bifunctor
import Text.PrettyPrint.Free
import Text.Trifecta.It
import Text.Trifecta.Delta
import Text.Trifecta.Diagnostic
import Text.Trifecta.Render.Prim
import Text.Trifecta.Parser.Class
import Text.Trifecta.Parser.Err
import Text.Trifecta.Parser.Err.State
--import Text.Trifecta.Parser.Step
--import Text.Trifecta.Parser.Result
import Text.Trifecta.Util.MaybePair
--import Text.PrettyPrint.Free
import System.Console.Terminfo.PrettyPrint

data Parser e a = Parser 
  { unparser :: forall r.
    (a -> ErrState e -> Delta -> ByteString -> It r) ->  -- uncommitted ok
    (ErrState e -> Delta -> ByteString -> It r) ->       -- uncommitted err
    (a ->  ErrState e -> Delta -> ByteString -> It r) -> -- committed ok
    (ErrState e -> Delta -> ByteString -> It r) ->       -- committed err
    ErrState e -> Delta -> ByteString -> It r
  } 

instance Functor (Parser e) where
  fmap f (Parser m) = Parser $ \ eo ee co -> m (eo . f) ee (co . f)
  {-# INLINE fmap #-}

instance Apply (Parser e) where (<.>) = (<*>)
instance Applicative (Parser e) where
  pure a = Parser $ \ eo _ _ _ -> eo a 
  {-# INLINE pure #-}
  Parser m <*> Parser n = Parser $ \ eo ee co ce -> 
    m (\f -> n (eo . f) ee (co . f) ce) ee
      (\f -> n (co . f) ce (co . f) ce) ce
  {-# INLINE (<*>) #-}

instance Alt (Parser e) where (<!>) = (<|>)
instance Plus (Parser e) where zero = empty
instance Alternative (Parser e) where
  empty = Parser $ \_ ee _ _ -> ee
  {-# INLINE empty #-}
  Parser m <|> Parser n = Parser $ \ eo ee co ce -> m eo (n eo ee co ce) co ce
  {-# INLINE (<|>) #-}

instance Bind (Parser e) where (>>-) = (>>=)
instance Monad (Parser e) where
  return a = Parser $ \ eo _ _ _ -> eo a 
  {-# INLINE return #-}
  Parser m >>= k = Parser $ \ eo ee co ce -> 
    m (\a -> unparser (k a) eo ee co ce) ee 
      (\a -> unparser (k a) co ce co ce) ce
  {-# INLINE (>>=) #-}
  fail = throwError . FailErr
  {-# INLINE fail #-}

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

instance MonadWriter (Seq (Diagnostic e)) (Parser e) where
  tell w = Parser $ \eo _ _ _ e -> eo () e { errLog = errLog e <> w }
  {-# INLINE tell #-}
  listen (Parser m) = Parser $ \eo ee co ce -> 
    m (\ a e -> eo (a,errLog e) e) ee 
      (\ a e -> co (a,errLog e) e) ce 
  pass (Parser m) = Parser $ \eo ee co ce -> 
    m (\(a,p) e -> eo a e { errLog = p $ errLog e }) ee
      (\(a,p) e -> co a e { errLog = p $ errLog e }) ce

instance MonadError (Err e) (Parser e) where
  throwError m = Parser $ \_ ee _ _ e -> ee e { errMessage = errMessage e <> m } 
  {-# INLINE throwError #-}
  catchError (Parser p) k = Parser $ \ eo ee co ce e -> p eo (\e' -> unparser (k (errMessage e')) eo ee co ce e) co ce e
  {-# INLINE catchError #-}

instance MonadParser (Parser e) where
  commit (Parser m) = Parser $ \ _ _ co ce -> m co ce co ce
  unexpected s = Parser $ \ _ ee _ _ e -> ee e { errMessage = UnexpectedErr s } 
  {-# INLINE commit #-}
  labels (Parser p) msgs = Parser $ \ eo ee -> p
     (\a e -> eo a (if knownErr (errMessage e) then e { errExpected = errExpected e `union` msgs } else e))
     (\e -> ee e { errExpected = errExpected e `union` msgs })
  {-# INLINE labels #-}
  it m = Parser $ \ eo _ _ _ e d bs -> do 
     a <- m
     eo a e d bs
  {-# INLINE it #-}
  mark = Parser $ \eo _ _ _ e d -> eo d e d
  {-# INLINE mark #-}
  release d' = Parser $ \eo ee _ _ e d bs -> do
    mbs <- lineIt d'
    case mbs of
      Just bs' -> eo () e d' bs'
      Nothing -> ee e d bs
  {-# INLINE release #-}
  line = Parser $ \eo _ _ _ e d bs -> eo bs e d bs
  {-# INLINE line #-}
  satisfy f = Parser $ \ eo ee _ _ e d bs ->
    case UTF8.uncons $ Strict.drop (columnByte d) bs of
      Nothing -> ee e { errMessage = EndOfFileErr } d bs
      Just (c, xs) 
        | not (f c) -> ee e d bs
        | Strict.null xs -> fillIt (d <> delta c) >>= \dbs -> case dbs of
          JustPair d' bs' -> eo c e d' bs'
          NothingPair -> eo c e (d <> delta c) bs -- END OF LINE
        | otherwise -> eo c e (d <> delta c) bs 
  {-# INLINE satisfy #-}
  satisfyAscii f = Parser $ \ eo ee _ _ e d bs ->
    let b = columnByte d in
    if b >= 0 && b < Strict.length bs 
    then case toEnum $ fromEnum $ Strict.index bs b of
      c | not (f c) -> ee e d bs
        | b == Strict.length bs - 1 -> fillIt (d <> delta c) >>= \dbs -> case dbs of
          JustPair d' bs' -> eo c e d' bs'
          NothingPair -> eo c e (d <> delta c) bs
        | otherwise -> eo c e (d <> delta c) bs
    else ee e { errMessage = EndOfFileErr } d bs
  {-# INLINE satisfyAscii #-}

data St e a = JuSt a !(ErrState e) !Delta !ByteString
            | NoSt !(ErrState e) !Delta !ByteString

instance Bifunctor St where
  bimap f g (JuSt a e d bs) = JuSt (g a) (fmap f e) d bs
  bimap f _ (NoSt e d bs) = NoSt (fmap f e) d bs

{-
stepParser :: (Diagnostic e -> Diagnostic t) -> 
              (ErrState e -> Delta -> ByteString -> Diagnostic t) ->
              Parser e a -> ErrState e -> Delta -> ByteString -> Step e a
stepParser yl y (Parser p) e0 d0 bs0 = 
  go mempty $ p ju no ju no e0 d0 bs0
  where
    ju a e d bs = Pure (JuSt a e d bs)
    no e d bs = Pure (NoSt e d bs)
    go r (Pure (JuSt a e _ _)) = StepDone r (yl <$> errLog e) a
    go r (Pure (NoSt e d bs))  = StepFail r (yl <$> errLog e) $ y e d bs
    go r (It ma k) = StepCont r (case ma of
                                   JuSt a e _ _ -> Success (yl <$> errLog e) a
                                   NoSt e d bs  -> Failure (yl <$> errLog e) (y e d bs)) 
                                (go <*> k)
-}

why :: PrettyTerm e => ErrState e -> Delta -> ByteString -> Diagnostic TermDoc
why (ErrState ss m _) d bs 
  | Set.null ss = diagnose prettyTerm (surface d bs) m
  | otherwise   = expected <$> diagnose prettyTerm (surface d bs) m 
  where
    expected doc = doc <> text ", expected" <+> fillSep (punctuate (char ',') $ text <$> toList ss)