module Text.Trifecta.Parser
( Parser(..)
, manyAccum
, Step(..)
, feed
, starve
, stepParser
, stepResult
, stepIt
, parseFromFile
, parseFromFileEx
, parseString
, parseByteString
, parseTest
) where
import Control.Applicative as Alternative
import Control.Monad (MonadPlus(..), ap, join)
import Control.Monad.IO.Class
import Data.ByteString as Strict hiding (empty, snoc)
import Data.ByteString.UTF8 as UTF8
import Data.Maybe (isJust)
import Data.Semigroup
import Data.Semigroup.Reducer
import Data.Set as Set hiding (empty, toList)
import System.IO
import Text.Parser.Combinators
import Text.Parser.Char
import Text.Parser.LookAhead
import Text.Parser.Token
import Text.PrettyPrint.ANSI.Leijen as Pretty hiding (line, (<>), (<$>), empty)
import Text.Trifecta.Combinators
import Text.Trifecta.Instances ()
import Text.Trifecta.Rendering
import Text.Trifecta.Result
import Text.Trifecta.Rope
import Text.Trifecta.Delta as Delta
import Text.Trifecta.Util.It
newtype Parser a = Parser
{ unparser :: forall r.
(a -> Err -> It Rope r) ->
(Err -> It Rope r) ->
(a -> Set String -> Delta -> ByteString -> It Rope r) ->
(Doc -> It Rope r) ->
Delta -> ByteString -> It Rope r
}
instance Functor Parser where
fmap f (Parser m) = Parser $ \ eo ee co -> m (eo . f) ee (co . f)
a <$ Parser m = Parser $ \ eo ee co -> m (\_ -> eo a) ee (\_ -> co a)
instance Applicative Parser where
pure a = Parser $ \ eo _ _ _ _ _ -> eo a mempty
(<*>) = ap
instance Alternative Parser where
empty = Parser $ \_ ee _ _ _ _ -> ee mempty
Parser m <|> Parser n = Parser $ \ eo ee co ce d bs ->
m eo (\e -> n (\a e' -> eo a (e <> e')) (\e' -> ee (e <> e')) co ce d bs) co ce d bs
many p = Prelude.reverse <$> manyAccum (:) p
some p = (:) <$> p <*> Alternative.many p
instance Semigroup a => Semigroup (Parser a) where
(<>) = liftA2 (<>)
instance Monoid a => Monoid (Parser a) where
mappend = liftA2 mappend
mempty = pure mempty
instance Monad Parser where
return a = Parser $ \ eo _ _ _ _ _ -> eo a mempty
Parser m >>= k = Parser $ \ eo ee co ce d bs ->
m (\a e -> unparser (k a) (\b e' -> eo b (e <> e')) (\e' -> ee (e <> e')) co ce d bs) ee
(\a es d' bs' -> unparser (k a)
(\b e' -> co b (es <> _expected e') d' bs')
(\e -> ce (explain (renderingCaret d' bs') e { _expected = _expected e <> es }))
co ce d' bs') ce d bs
(>>) = (*>)
fail s = Parser $ \ _ ee _ _ _ _ -> ee (failed s)
instance MonadPlus Parser where
mzero = empty
mplus = (<|>)
manyAccum :: (a -> [a] -> [a]) -> Parser a -> Parser [a]
manyAccum f (Parser p) = Parser $ \eo _ co ce d bs ->
let walk xs x es d' bs' = p (manyErr d' bs') (\e -> co (f x xs) (_expected e <> es) d' bs') (walk (f x xs)) ce d' bs'
manyErr d' bs' _ e = ce $ explain (renderingCaret d' bs') (e <> failed "'many' applied to a parser that accepted an empty string")
in p (manyErr d bs) (eo []) (walk []) ce d bs
liftIt :: It Rope a -> Parser a
liftIt m = Parser $ \ eo _ _ _ _ _ -> do
a <- m
eo a mempty
instance Parsing Parser where
try (Parser m) = Parser $ \ eo ee co _ -> m eo ee co (\_ -> ee mempty)
Parser m <?> nm = Parser $ \ eo ee -> m
(\a e -> eo a (if isJust (_reason e) then e { _expected = Set.singleton nm } else e))
(\e -> ee e { _expected = Set.singleton nm })
skipMany p = () <$ manyAccum (\_ _ -> []) p
unexpected s = Parser $ \ _ ee _ _ _ _ -> ee $ failed $ "unexpected " ++ s
eof = notFollowedBy anyChar <?> "end of input"
notFollowedBy p = try (optional p >>= maybe (pure ()) (unexpected . show))
instance Errable Parser where
raiseErr e = Parser $ \ _ ee _ _ _ _ -> ee e
instance LookAheadParsing Parser where
lookAhead (Parser m) = Parser $ \eo ee _ -> m eo ee (\a _ _ _ -> eo a mempty)
instance CharParsing Parser where
satisfy f = Parser $ \ _ ee co _ d bs ->
case UTF8.uncons $ Strict.drop (fromIntegral (columnByte d)) bs of
Nothing -> ee (failed "unexpected EOF")
Just (c, xs)
| not (f c) -> ee mempty
| Strict.null xs -> let !ddc = d <> delta c
in join $ fillIt (co c mempty ddc (if c == '\n' then mempty else bs))
(co c mempty)
ddc
| otherwise -> co c mempty (d <> delta c) bs
instance TokenParsing Parser
instance DeltaParsing Parser where
line = Parser $ \eo _ _ _ _ bs -> eo bs mempty
position = Parser $ \eo _ _ _ d _ -> eo d mempty
rend = Parser $ \eo _ _ _ d bs -> eo (rendered d bs) mempty
slicedWith f p = do
m <- position
a <- p
r <- position
f a <$> liftIt (sliceIt m r)
instance MarkParsing Delta Parser where
mark = position
release d' = Parser $ \_ ee co _ d bs -> do
mbs <- rewindIt d'
case mbs of
Just bs' -> co () mempty d' bs'
Nothing
| bytes d' == bytes (rewind d) + fromIntegral (Strict.length bs) -> if near d d'
then co () mempty d' bs
else co () mempty d' mempty
| otherwise -> ee mempty
data Step a
= StepDone !Rope a
| StepFail !Rope Doc
| StepCont !Rope (Result a) (Rope -> Step a)
instance Show a => Show (Step a) where
showsPrec d (StepDone r a) = showParen (d > 10) $
showString "StepDone " . showsPrec 11 r . showChar ' ' . showsPrec 11 a
showsPrec d (StepFail r xs) = showParen (d > 10) $
showString "StepFail " . showsPrec 11 r . showChar ' ' . showsPrec 11 xs
showsPrec d (StepCont r fin _) = showParen (d > 10) $
showString "StepCont " . showsPrec 11 r . showChar ' ' . showsPrec 11 fin . showString " ..."
instance Functor Step where
fmap f (StepDone r a) = StepDone r (f a)
fmap _ (StepFail r xs) = StepFail r xs
fmap f (StepCont r z k) = StepCont r (fmap f z) (fmap f . k)
feed :: Reducer t Rope => t -> Step r -> Step r
feed t (StepDone r a) = StepDone (snoc r t) a
feed t (StepFail r xs) = StepFail (snoc r t) xs
feed t (StepCont r _ k) = k (snoc r t)
starve :: Step a -> Result a
starve (StepDone _ a) = Success a
starve (StepFail _ xs) = Failure xs
starve (StepCont _ z _) = z
stepResult :: Rope -> Result a -> Step a
stepResult r (Success a) = StepDone r a
stepResult r (Failure xs) = StepFail r xs
stepIt :: It Rope a -> Step a
stepIt = go mempty where
go r (Pure a) = StepDone r a
go r (It a k) = StepCont r (pure a) $ \s -> go s (k s)
data Stepping a
= EO a Err
| EE Err
| CO a (Set String) Delta ByteString
| CE Doc
stepParser :: Parser a -> Delta -> ByteString -> Step a
stepParser (Parser p) d0 bs0 = go mempty $ p eo ee co ce d0 bs0 where
eo a e = Pure (EO a e)
ee e = Pure (EE e)
co a es d bs = Pure (CO a es d bs)
ce doc = Pure (CE doc)
go r (Pure (EO a _)) = StepDone r a
go r (Pure (EE e)) = StepFail r $ explain (renderingCaret d0 bs0) e
go r (Pure (CO a _ _ _)) = StepDone r a
go r (Pure (CE d)) = StepFail r d
go r (It ma k) = StepCont r (case ma of
EO a _ -> Success a
EE e -> Failure $ explain (renderingCaret d0 bs0) e
CO a _ _ _ -> Success a
CE d -> Failure d
) (go <*> k)
parseFromFile :: MonadIO m => Parser a -> String -> m (Maybe a)
parseFromFile p fn = do
result <- parseFromFileEx p fn
case result of
Success a -> return (Just a)
Failure xs -> do
liftIO $ displayIO stdout $ renderPretty 0.8 80 $ xs <> linebreak
return Nothing
parseFromFileEx :: MonadIO m => Parser a -> String -> m (Result a)
parseFromFileEx p fn = do
s <- liftIO $ Strict.readFile fn
return $ parseByteString p (Directed (UTF8.fromString fn) 0 0 0 0) s
parseByteString :: Parser a -> Delta -> UTF8.ByteString -> Result a
parseByteString p d inp = starve $ feed inp $ stepParser (release d *> p) mempty mempty
parseString :: Parser a -> Delta -> String -> Result a
parseString p d inp = starve $ feed inp $ stepParser (release d *> p) mempty mempty
parseTest :: (MonadIO m, Show a) => Parser a -> String -> m ()
parseTest p s = case parseByteString p mempty (UTF8.fromString s) of
Failure xs -> liftIO $ displayIO stdout $ renderPretty 0.8 80 $ xs <> linebreak
Success a -> liftIO (print a)