{-# LANGUAGE DefaultSignatures, FlexibleContexts, FlexibleInstances, TypeFamilies,
TypeSynonymInstances, UndecidableInstances #-}
module Construct.Classes where
import qualified Rank2
import qualified Text.ParserCombinators.Incremental as Incremental
import Control.Applicative (Alternative ((<|>), empty))
import Control.Monad (void)
import Data.String (IsString (fromString))
import Text.ParserCombinators.ReadP (ReadP)
import qualified Text.ParserCombinators.ReadP as ReadP
import Text.Parser.Char (CharParsing)
import Text.Parser.Combinators (count, eof, notFollowedBy, try, unexpected)
import Text.Parser.LookAhead (LookAheadParsing, lookAhead)
import qualified Text.Parser.Char as Char
import qualified Data.Monoid.Factorial as Factorial
import qualified Data.Monoid.Null as Null
import qualified Data.Monoid.Textual as Textual
import qualified Data.Semigroup.Cancellative as Cancellative
import Data.Monoid.Factorial (FactorialMonoid)
import Data.Monoid.Textual (TextualMonoid)
import Data.Semigroup.Cancellative (LeftReductive)
import Data.ByteString (ByteString)
import Data.Text (Text)
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Char8 as ByteString.Char8
import qualified Data.Text as Text
import qualified Data.Attoparsec.ByteString as Attoparsec
import qualified Data.Attoparsec.ByteString.Char8 as Attoparsec.Char8
import qualified Data.Attoparsec.Text as Attoparsec.Text
import Prelude hiding (take, takeWhile)
class Alternative m => AlternativeFail m where
failure :: String -> m a
expectedName :: String -> m a -> m a
failure = const empty
expectedName = const id
class LookAheadParsing m => InputParsing m where
type ParserInput m
getInput :: m (ParserInput m)
anyToken :: m (ParserInput m)
take :: Int -> m (ParserInput m)
satisfy :: (ParserInput m -> Bool) -> m (ParserInput m)
notSatisfy :: (ParserInput m -> Bool) -> m ()
scan :: state -> (state -> ParserInput m -> Maybe state) -> m (ParserInput m)
string :: ParserInput m -> m (ParserInput m)
takeWhile :: (ParserInput m -> Bool) -> m (ParserInput m)
takeWhile1 :: (ParserInput m -> Bool) -> m (ParserInput m)
concatMany :: Monoid a => m a -> m a
anyToken = take 1
notSatisfy predicate = try (void $ satisfy $ not . predicate) <|> eof
default concatMany :: (Monoid a, Alternative m) => m a -> m a
concatMany p = go
where go = mappend <$> try p <*> go <|> pure mempty
default string :: (Monad m, LeftReductive (ParserInput m), FactorialMonoid (ParserInput m), Show (ParserInput m))
=> ParserInput m -> m (ParserInput m)
string s = do i <- getInput
if s `Cancellative.isPrefixOf` i
then take (Factorial.length s)
else unexpected ("string " <> show s)
default scan :: (Monad m, FactorialMonoid (ParserInput m)) =>
state -> (state -> ParserInput m -> Maybe state) -> m (ParserInput m)
scan state f = do i <- getInput
let (prefix, _suffix, _state) = Factorial.spanMaybe' state f i
take (Factorial.length prefix)
default takeWhile :: (Monad m, FactorialMonoid (ParserInput m)) => (ParserInput m -> Bool) -> m (ParserInput m)
takeWhile predicate = do i <- getInput
take (Factorial.length $ Factorial.takeWhile predicate i)
default takeWhile1 :: (Monad m, FactorialMonoid (ParserInput m)) => (ParserInput m -> Bool) -> m (ParserInput m)
takeWhile1 predicate = do x <- takeWhile predicate
if Null.null x then unexpected "takeWhile1" else pure x
{-# INLINE concatMany #-}
class (CharParsing m, InputParsing m) => InputCharParsing m where
satisfyCharInput :: (Char -> Bool) -> m (ParserInput m)
notSatisfyChar :: (Char -> Bool) -> m ()
scanChars :: state -> (state -> Char -> Maybe state) -> m (ParserInput m)
takeCharsWhile :: (Char -> Bool) -> m (ParserInput m)
takeCharsWhile1 :: (Char -> Bool) -> m (ParserInput m)
default satisfyCharInput :: IsString (ParserInput m) => (Char -> Bool) -> m (ParserInput m)
satisfyCharInput = fmap (fromString . (:[])) . Char.satisfy
notSatisfyChar = notFollowedBy . Char.satisfy
default scanChars :: (Monad m, TextualMonoid (ParserInput m)) =>
state -> (state -> Char -> Maybe state) -> m (ParserInput m)
scanChars state f = do i <- getInput
let (prefix, _suffix, _state) = Textual.spanMaybe' state (const $ const Nothing) f i
take (Factorial.length prefix)
default takeCharsWhile :: (Monad m, TextualMonoid (ParserInput m)) => (Char -> Bool) -> m (ParserInput m)
takeCharsWhile predicate = do i <- getInput
take (Factorial.length $ Textual.takeWhile_ False predicate i)
default takeCharsWhile1 :: (Monad m, TextualMonoid (ParserInput m)) => (Char -> Bool) -> m (ParserInput m)
takeCharsWhile1 predicate = do x <- takeCharsWhile predicate
if Null.null x then unexpected "takeCharsWhile1" else pure x
class InputMappableParsing m where
mapParserInput :: (InputParsing (m s), s ~ ParserInput (m s), Monoid s, Monoid s') =>
(s -> s') -> (s' -> s) -> m s a -> m s' a
mapMaybeParserInput :: (InputParsing (m s), s ~ ParserInput (m s), Monoid s, Monoid s') =>
(s -> Maybe s') -> (s' -> Maybe s) -> m s a -> m s' a
class Monad m => FixTraversable m where
fixSequence :: (Rank2.Traversable g, Applicative n) => g m -> m (g n)
fixSequence = Rank2.traverse (pure <$>)
data Error = Error [String] (Maybe String) deriving (Eq, Show)
instance Semigroup Error where
Error expected1 encountered1 <> Error expected2 encountered2 =
Error (expected1 <> expected2) (maybe encountered2 Just encountered1)
instance AlternativeFail Maybe
instance AlternativeFail []
instance {-# OVERLAPS #-} Alternative (Either Error) where
empty = Left (Error [] Nothing)
Right a <|> _ = Right a
_ <|> Right a = Right a
Left e1 <|> Left e2 = Left (e1 <> e2)
instance AlternativeFail (Either Error) where
failure encountered = Left (Error [] (Just encountered))
expectedName expected (Left (Error _ encountered)) = Left (Error [expected] encountered)
expectedName _ success = success
errorString :: Error -> String
errorString (Error ex Nothing) = maybe "" ("expected " <>) (concatExpected ex)
errorString (Error [] (Just en)) = "encountered " <> en
errorString (Error ex (Just en)) = maybe "" ("expected " <>) (concatExpected ex) <> ", encountered " <> en
concatExpected :: [String] -> Maybe String
concatExpected [] = Nothing
concatExpected [e] = Just e
concatExpected [e1, e2] = Just (e1 <> " or " <> e2)
concatExpected (e:es) = Just (oxfordComma e es)
oxfordComma :: String -> [String] -> String
oxfordComma e [] = "or " <> e
oxfordComma e (e':es) = e <> ", " <> oxfordComma e' es
instance InputParsing ReadP where
type ParserInput ReadP = String
getInput = ReadP.look
take n = count n ReadP.get
anyToken = pure <$> ReadP.get
satisfy predicate = pure <$> ReadP.satisfy (predicate . pure)
string = ReadP.string
instance InputCharParsing ReadP where
satisfyCharInput predicate = pure <$> ReadP.satisfy predicate
instance InputParsing Attoparsec.Parser where
type ParserInput Attoparsec.Parser = ByteString
getInput = lookAhead Attoparsec.takeByteString
anyToken = Attoparsec.take 1
take = Attoparsec.take
satisfy predicate = Attoparsec.satisfyWith ByteString.singleton predicate
string = Attoparsec.string
instance InputCharParsing Attoparsec.Parser where
satisfyCharInput predicate = ByteString.Char8.singleton <$> Attoparsec.Char8.satisfy predicate
scanChars = Attoparsec.Char8.scan
takeCharsWhile = Attoparsec.Char8.takeWhile
takeCharsWhile1 = Attoparsec.Char8.takeWhile1
instance InputParsing Attoparsec.Text.Parser where
type ParserInput Attoparsec.Text.Parser = Text
getInput = lookAhead Attoparsec.Text.takeText
anyToken = Attoparsec.Text.take 1
take = Attoparsec.Text.take
satisfy predicate = Attoparsec.Text.satisfyWith Text.singleton predicate
string = Attoparsec.Text.string
instance InputCharParsing Attoparsec.Text.Parser where
satisfyCharInput predicate = Text.singleton <$> Attoparsec.Text.satisfy predicate
scanChars = Attoparsec.Text.scan
takeCharsWhile = Attoparsec.Text.takeWhile
takeCharsWhile1 = Attoparsec.Text.takeWhile1
instance (FactorialMonoid s, Cancellative.LeftReductive s, LookAheadParsing (Incremental.Parser t s)) =>
InputParsing (Incremental.Parser t s) where
type ParserInput (Incremental.Parser t s) = s
getInput = lookAhead Incremental.acceptAll
anyToken = Incremental.anyToken
take n = Incremental.count n Incremental.anyToken
satisfy = Incremental.satisfy
string = Incremental.string
takeWhile = Incremental.takeWhile
takeWhile1 = Incremental.takeWhile1
concatMany = Incremental.concatMany
instance (TextualMonoid s, Cancellative.LeftReductive s, LookAheadParsing (Incremental.Parser t s)) =>
InputCharParsing (Incremental.Parser t s) where
satisfyCharInput = Incremental.satisfyChar
takeCharsWhile = Incremental.takeCharsWhile
takeCharsWhile1 = Incremental.takeCharsWhile1
instance FixTraversable Attoparsec.Parser
instance Monoid s => FixTraversable (Incremental.Parser t s) where
fixSequence = Incremental.record
instance InputMappableParsing (Incremental.Parser t) where
mapParserInput = Incremental.mapInput
mapMaybeParserInput = Incremental.mapMaybeInput