module Text.Trifecta.Result
(
Result(..)
, AsResult(..)
, _Success
, _Failure
, Err(..), HasErr(..), Errable(..)
, ErrInfo(..)
, explain
, failed
) where
import Control.Applicative as Alternative
import Control.Lens hiding (snoc, cons)
import Control.Monad (guard)
import Data.Foldable
import Data.Maybe (fromMaybe, isJust)
import qualified Data.List as List
import Data.Semigroup
import Data.Set as Set hiding (empty, toList)
import Text.PrettyPrint.ANSI.Leijen as Pretty hiding (line, (<>), (<$>), empty)
import Text.Trifecta.Instances ()
import Text.Trifecta.Rendering
import Text.Trifecta.Delta as Delta
data ErrInfo = ErrInfo
{ _errDoc :: Doc
, _errDeltas :: [Delta]
} deriving(Show)
data Err = Err
{ _reason :: Maybe Doc
, _footnotes :: [Doc]
, _expected :: Set String
, _finalDeltas :: [Delta]
}
makeClassy ''Err
instance Semigroup Err where
Err md mds mes delta1 <> Err nd nds nes delta2
= Err (nd <|> md) (if isJust nd then nds else if isJust md then mds else nds ++ mds) (mes <> nes) (delta1 <> delta2)
instance Monoid Err where
mempty = Err Nothing [] mempty mempty
mappend = (<>)
failed :: String -> Err
failed m = Err (Just (fillSep (pretty <$> words m))) [] mempty mempty
explain :: Rendering -> Err -> Doc
explain r (Err mm as es _)
| Set.null es = report (withEx mempty)
| isJust mm = report $ withEx $ Pretty.char ',' <+> expecting
| otherwise = report expecting
where
now = spaceHack $ toList es
spaceHack [""] = ["space"]
spaceHack xs = List.filter (/= "") xs
withEx x = fromMaybe (fillSep $ text <$> words "unspecified error") mm <> x
expecting = text "expected:" <+> fillSep (punctuate (Pretty.char ',') (text <$> now))
report txt = vsep $ [pretty (delta r) <> Pretty.char ':' <+> red (text "error") <> Pretty.char ':' <+> nest 4 txt]
<|> pretty r <$ guard (not (nullRendering r))
<|> as
class Errable m where
raiseErr :: Err -> m a
instance Monoid ErrInfo where
mempty = ErrInfo mempty mempty
mappend (ErrInfo xs d1) (ErrInfo ys d2) = ErrInfo (vsep [xs, ys]) (max d1 d2)
data Result a
= Success a
| Failure ErrInfo
deriving (Show,Functor,Foldable,Traversable)
class AsResult s t a b | s -> a, t -> b, s b -> t, t a -> s where
_Result :: Prism s t (Result a) (Result b)
instance AsResult (Result a) (Result b) a b where
_Result = id
_Success :: AsResult s t a b => Prism s t a b
_Success = _Result . dimap seta (either id id) . right' . rmap (fmap Success) where
seta (Success a) = Right a
seta (Failure e) = Left (pure (Failure e))
_Failure :: AsResult s s a a => Prism' s ErrInfo
_Failure = _Result . dimap seta (either id id) . right' . rmap (fmap Failure) where
seta (Failure e) = Right e
seta (Success a) = Left (pure (Success a))
instance Show a => Pretty (Result a) where
pretty (Success a) = pretty (show a)
pretty (Failure xs) = pretty . _errDoc $ xs
instance Applicative Result where
pure = Success
Success f <*> Success a = Success (f a)
Success _ <*> Failure y = Failure y
Failure x <*> Success _ = Failure x
Failure x <*> Failure y =
Failure $ ErrInfo (vsep [_errDoc x, _errDoc y]) (_errDeltas x <> _errDeltas y)
instance Alternative Result where
Failure x <|> Failure y =
Failure $ ErrInfo (vsep [_errDoc x, _errDoc y]) (_errDeltas x <> _errDeltas y)
Success a <|> Success _ = Success a
Success a <|> Failure _ = Success a
Failure _ <|> Success a = Success a
empty = Failure mempty