packages feed

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

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleContexts, Rank2Types, FlexibleInstances #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Text.Trifecta.Parser.Prim
-- Copyright   :  (c) Edward Kmett 2011
-- License     :  BSD3
-- 
-- Maintainer  :  ekmett@gmail.com
-- Stability   :  experimental
-- Portability :  non-portable
-- 
-----------------------------------------------------------------------------
module Text.Trifecta.Parser.Prim 
  ( Parser(..)
  , why
  , stepParser
  , parseTest
  , manyAccum
  ) where

import Control.Applicative
import Control.Monad.Error.Class
import Control.Monad.Writer.Class
import Control.Monad
import qualified Data.Functor.Plus as Plus
import Data.Functor.Plus hiding (some, many)
import Data.Semigroup
import Data.Foldable
import Data.Monoid
import Data.Functor.Bind (Apply(..), Bind((>>-)))
import Data.Set as Set hiding (empty, toList)
import Data.ByteString as Strict hiding (empty)
import Data.Sequence as Seq hiding (empty)
import Data.ByteString.UTF8 as UTF8
import Text.PrettyPrint.Free hiding (line)
import Text.Trifecta.Rope.Delta
import Text.Trifecta.Rope.Prim
import Text.Trifecta.Rope.Bytes
import Text.Trifecta.Diagnostic.Class
import Text.Trifecta.Diagnostic.Prim
import Text.Trifecta.Diagnostic.Level
import Text.Trifecta.Diagnostic.Err
import Text.Trifecta.Diagnostic.Err.State
import Text.Trifecta.Diagnostic.Rendering.Prim
import Text.Trifecta.Diagnostic.Rendering.Caret
import Text.Trifecta.Parser.Class
import Text.Trifecta.Parser.It
import Text.Trifecta.Parser.Step
import Text.Trifecta.Parser.Result
import System.Console.Terminfo.PrettyPrint

type ErrLog e = Seq (Diagnostic e) -- use a fingertree to sort them?

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

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

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

instance Alt (Parser e) where 
  (<!>) = (<|>)
  many p = Prelude.reverse <$> manyAccum (:) p
  some p = p *> many p
instance Plus (Parser e) where zero = empty
instance Alternative (Parser e) where
  empty = Parser $ \_ ee _ _ -> ee mempty
  {-# INLINE empty #-}
  -- Parser m <|> Parser n = Parser $ \ eo ee co ce -> m eo (n eo ee co ce) co ce
  Parser m <|> Parser n = Parser $ \ eo ee co ce -> 
    m eo (\e -> n (\a e'-> eo a (e <> e')) (\e' -> ee (e <> e')) co ce) 
      co ce
  {-# INLINE (<|>) #-}
  many p = Prelude.reverse <$> manyAccum (:) p
  {-# INLINE many #-}
  some p = (:) <$> p <*> many p

instance Semigroup (Parser e a) where
  (<>) = (<|>) 

instance Monoid (Parser e a) where
  mappend = (<|>)
  mempty = empty

instance Bind (Parser e) where (>>-) = (>>=)
instance Monad (Parser e) where
  return a = Parser $ \ eo _ _ _ -> eo a mempty
  {-# INLINE return #-}
  Parser m >>= k = Parser $ \ eo ee co ce -> 
    m (\a e -> unparser (k a) (\b e' -> eo b (e <> e')) (\e' -> ee (e <> e')) co ce) ee 
      (\a e -> unparser (k a) (\b e' -> co b (e <> e')) (\e' -> ce (e <> e')) co ce) ce
  {-# INLINE (>>=) #-}
  (>>) = (*>) 
  {-# INLINE (>>) #-}
  fail s = Parser $ \ _ ee _ _ -> ee mempty { errMessage = FailErr s }
  {-# INLINE fail #-}

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

instance MonadWriter (Seq (Diagnostic e)) (Parser e) where
  tell w = Parser $ \eo _ _ _ l -> eo () mempty (l <> w)
  {-# INLINE tell #-}
  listen (Parser m) = Parser $ \eo ee co ce l -> 
    m (\ a e' l' -> eo (a,l') e' (l <> l')) 
      (\   e' l' -> ee        e' (l <> l'))
      (\ a e' l' -> co (a,l') e' (l <> l')) 
      (\   e' l' -> ce        e' (l <> l'))
      mempty
  {-# INLINE listen #-}
  pass (Parser m) = Parser $ \eo ee co ce l -> 
    m (\(a,p) e' l' -> eo a e' (l <> p l'))
      (\      e' l' -> ee   e' (l <>   l'))
      (\(a,p) e' l' -> co a e' (l <> p l'))
      (\      e' l' -> ce   e' (l <>   l'))
      mempty
  {-# INLINE pass #-}
manyAccum :: (a -> [a] -> [a]) -> Parser e a -> Parser e [a]
manyAccum acc (Parser p) = Parser $ \eo _ co ce -> 
  let walk xs x _ = p manyErr (\_ -> co (acc x xs) mempty) (walk (acc x xs)) ce
      -- NB: to squelch this error you can change your grammar to add a commit, if you are progressing
      -- in some other fashion
      manyErr _ e = ce e { errMessage = PanicErr "'many' applied to a parser that accepted an empty string" }
  in p manyErr (eo []) (walk []) ce
   
instance MonadDiagnostic e (Parser e) where
  fatalWith ds rs e = Parser $ \_ _ _ ce -> 
    ce mempty { errMessage = Err rs Fatal e ds }
  errWith ds rs e = Parser $ \_ ee _ _ -> 
    ee mempty { errMessage = Err rs Error e ds }
  logWith v ds rs e = Parser $ \eo _ _ _ l d bs -> 
    eo () mempty (l |> Diagnostic (addCaret d $ Prelude.foldr (<>) (rendering d bs) rs) v e ds) d bs
    
instance MonadError (ErrState e) (Parser e) where
  throwError m = Parser $ \_ ee _ _ -> ee m
  {-# INLINE throwError #-}
  catchError (Parser m) k = Parser $ \ eo ee co ce -> 
    m eo (\e -> unparser (k e) eo ee co ce) co ce
  {-# INLINE catchError #-}

instance MonadParser (Parser e) where
  -- commit (Parser m) = Parser $ \ _ _ co ce -> m co ce co ce
  try (Parser m) = Parser $ \ eo ee co ce -> m eo ee co $ 
    \e -> if fatalErr (errMessage e) then ce e else ee e
  {-# INLINE try #-}
  unexpected s = Parser $ \ _ ee _ _ -> ee mempty { errMessage = FailErr $ "unexpected " ++ s }

  labels (Parser p) msgs = Parser $ \ eo ee -> p
     (\a e -> eo a $ if knownErr (errMessage e)
                     then e { errExpected = msgs `union` errExpected e }
                     else e)
     (\e -> ee e { errExpected = msgs })
  {-# INLINE labels #-}
  liftIt m = Parser $ \ eo _ _ _ l d bs -> do 
     a <- m
     eo a mempty l d bs
  {-# INLINE liftIt #-}
  mark = Parser $ \eo _ _ _ l d -> eo d mempty l d
  {-# INLINE mark #-}
  release d' = Parser $ \_ ee co _ l d bs -> do
    mbs <- rewindIt d'
    case mbs of
      Just bs' -> co () mempty l d' bs'
      Nothing 
        | bytes d' == bytes (rewind d) + Strict.length bs -> co () mempty l d' $ 
                                                             if near d d' then bs else mempty
        | otherwise -> ee mempty l d bs
  skipping d' = Parser $ \_ ee co _ l d bs -> do
    let d'' = d <> d'
    mbs <- rewindIt d''
    case mbs of
      Just bs' -> co () mempty l d'' bs'
      Nothing 
        | bytes d'' == bytes (rewind d) + Strict.length bs -> co () mempty l d'' $
                                                              if near d d'' then bs else mempty
        | otherwise -> ee mempty l d bs
  {-# INLINE release #-}
  line = Parser $ \eo _ _ _ l d bs -> eo bs mempty l d bs
  {-# INLINE line #-}
  skipMany p = () <$ manyAccum (\_ _ -> []) p
  {-# INLINE skipMany #-}
  satisfy f = Parser $ \ _ ee co _ l d bs ->
    case UTF8.uncons $ Strict.drop (columnByte d) bs of
      Nothing             -> ee mempty { errMessage = FailErr "unexpected EOF" } l d bs
      Just (c, xs) 
        | not (f c)       -> ee mempty l d bs
        | Strict.null xs  -> let ddc = d <> delta c in
                             join $ fillIt (co c mempty l ddc bs) (co c mempty l) ddc
        | otherwise       -> co c mempty l (d <> delta c) bs 
  {-# INLINE satisfy #-}
  satisfyAscii f = Parser $ \ _ ee co _ l 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 mempty l d bs
        | b == Strict.length bs - 1 -> let ddc = d <> delta c in 
                                       join $ fillIt (co c mempty l ddc bs) (co c mempty l) ddc
        | otherwise                 -> co c mempty l (d <> delta c) bs
    else ee mempty { errMessage = FailErr "unexpected EOF" } l d bs
  {-# INLINE satisfyAscii #-}

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

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

why :: Pretty e => (e -> Doc t) -> ErrState e -> Delta -> ByteString -> Diagnostic (Doc t)
why pp (ErrState ss m) d bs 
  | Set.null ss = explicate m 
  | otherwise   = expected <$> explicate m
  where
    expected doc = doc <> text ", expected" <+> fillSep (punctuate (char ',') $ text <$> toList ss)
    r = addCaret d $ rendering d bs
    explicate EmptyErr        = Diagnostic r Error (text "unspecified error") []
    explicate (FailErr s)     = Diagnostic r Error (fillSep $ text <$> words s) []
    explicate (PanicErr s)    = Diagnostic r Fatal (fillSep $ text <$> words s) []
    explicate (Err rs l e es) = Diagnostic (addCaret d $ Prelude.foldr (<>) (rendering d bs) rs) l (pp e) (fmap (fmap pp) es)

parseTest :: Show a => Parser String a -> String -> IO ()
parseTest p s = case starve $ feed st $ UTF8.fromString s of
  Failure xs e -> displayLn $ prettyTerm $ toList (xs |> e)
  Success xs a -> do
    unless (Seq.null xs) $ displayLn $ prettyTerm $ toList xs
    print a
  where st = stepParser (fmap prettyTerm) (why prettyTerm) (release mempty *> p) mempty mempty mempty