{-# LANGUAGE AllowAmbiguousTypes, ConstraintKinds, DefaultSignatures, OverloadedStrings, RankNTypes,
ScopedTypeVariables, TypeApplications, TypeFamilies, DeriveDataTypeable #-}
module Text.Grampa.Class (MultiParsing(..), GrammarParsing(..), AmbiguousParsing(..), MonoidParsing(..), Lexical(..),
ParseResults, ParseFailure(..), Ambiguous(..), Position, positionOffset, completeParser) where
import Control.Applicative (Alternative(empty), liftA2, (<|>))
import Data.Char (isAlphaNum, isLetter, isSpace)
import Data.Functor.Classes (Show1(..))
import Data.Functor.Compose (Compose(..))
import Data.List.NonEmpty (NonEmpty((:|)))
import Data.Data (Data)
import Data.Typeable (Typeable)
import Data.Monoid (Monoid(mempty, mappend))
import Data.Monoid.Cancellative (LeftReductiveMonoid)
import qualified Data.Monoid.Null as Null
import Data.Monoid.Null (MonoidNull)
import qualified Data.Monoid.Factorial as Factorial
import Data.Monoid.Factorial (FactorialMonoid)
import Data.Monoid.Textual (TextualMonoid)
import Data.Semigroup (Semigroup((<>)))
import Text.Parser.Combinators (Parsing((<?>)), skipMany)
import Text.Parser.Char (CharParsing(char))
import GHC.Exts (Constraint)
import qualified Rank2
type ParseResults = Either ParseFailure
data ParseFailure = ParseFailure Int [String] deriving (Eq, Show)
newtype Position s = Position{
remainderLength :: Int}
positionOffset :: FactorialMonoid s => s -> Position s -> Int
positionOffset wholeInput = (wholeLength -) . remainderLength
where wholeLength = Factorial.length wholeInput
{-# INLINE positionOffset #-}
newtype Ambiguous a = Ambiguous (NonEmpty a) deriving (Data, Eq, Ord, Show, Typeable)
instance Show1 Ambiguous where
liftShowsPrec sp sl d (Ambiguous (h :| l)) t
| d > 5 = "(Ambiguous $ " <> sp 0 h (" :| " <> sl l (')' : t))
| otherwise = "Ambiguous (" <> sp 0 h (" :| " <> sl l (')' : t))
instance Functor Ambiguous where
fmap f (Ambiguous a) = Ambiguous (fmap f a)
instance Applicative Ambiguous where
pure a = Ambiguous (pure a)
Ambiguous f <*> Ambiguous a = Ambiguous (f <*> a)
instance Foldable Ambiguous where
foldMap f (Ambiguous a) = foldMap f a
instance Traversable Ambiguous where
traverse f (Ambiguous a) = Ambiguous <$> traverse f a
instance Semigroup a => Semigroup (Ambiguous a) where
Ambiguous xs <> Ambiguous ys = Ambiguous (liftA2 (<>) xs ys)
instance Monoid a => Monoid (Ambiguous a) where
mempty = Ambiguous (mempty :| [])
Ambiguous xs `mappend` Ambiguous ys = Ambiguous (liftA2 mappend xs ys)
completeParser :: MonoidNull s => Compose ParseResults (Compose [] ((,) s)) r -> Compose ParseResults [] r
completeParser (Compose (Left failure)) = Compose (Left failure)
completeParser (Compose (Right (Compose results))) =
case filter (Null.null . fst) results
of [] -> Compose (Left $ ParseFailure 0 ["complete parse"])
completeResults -> Compose (Right $ snd <$> completeResults)
class MultiParsing m where
type ResultFunctor m :: * -> *
type GrammarConstraint m (g :: (* -> *) -> *) :: Constraint
type GrammarConstraint m g = Rank2.Functor g
parseComplete :: (GrammarConstraint m g, FactorialMonoid s) => g (m g s) -> s -> g (ResultFunctor m)
parsePrefix :: (GrammarConstraint m g, FactorialMonoid s) =>
g (m g s) -> s -> g (Compose (ResultFunctor m) ((,) s))
class MultiParsing m => GrammarParsing m where
type GrammarFunctor m :: ((* -> *) -> *) -> * -> * -> *
nonTerminal :: GrammarConstraint m g => (g (GrammarFunctor m g s) -> GrammarFunctor m g s a) -> m g s a
selfReferring :: (GrammarConstraint m g, Rank2.Distributive g) => g (m g s)
fixGrammar :: forall g s. (GrammarConstraint m g, Rank2.Distributive g) => (g (m g s) -> g (m g s)) -> g (m g s)
recursive :: m g s a -> m g s a
selfReferring = Rank2.cotraverse nonTerminal id
{-# INLINE selfReferring #-}
fixGrammar = ($ selfReferring)
{-# INLINE fixGrammar #-}
recursive = id
class MonoidParsing m where
endOfInput :: FactorialMonoid s => m s ()
getInput :: FactorialMonoid s => m s s
getSourcePos :: FactorialMonoid s => m s (Position s)
anyToken :: FactorialMonoid s => m s s
satisfy :: FactorialMonoid s => (s -> Bool) -> m s s
satisfyChar :: TextualMonoid s => (Char -> Bool) -> m s Char
satisfyCharInput :: TextualMonoid s => (Char -> Bool) -> m s s
notSatisfy :: FactorialMonoid s => (s -> Bool) -> m s ()
notSatisfyChar :: TextualMonoid s => (Char -> Bool) -> m s ()
scan :: FactorialMonoid t => s -> (s -> t -> Maybe s) -> m t t
scanChars :: TextualMonoid t => s -> (s -> Char -> Maybe s) -> m t t
string :: (FactorialMonoid s, LeftReductiveMonoid s, Show s) => s -> m s s
takeWhile :: FactorialMonoid s => (s -> Bool) -> m s s
takeWhile1 :: FactorialMonoid s => (s -> Bool) -> m s s
takeCharsWhile :: TextualMonoid s => (Char -> Bool) -> m s s
takeCharsWhile1 :: TextualMonoid s => (Char -> Bool) -> m s s
concatMany :: Monoid a => m s a -> m s a
default concatMany :: (Monoid a, Alternative (m s)) => m s a -> m s a
concatMany p = go
where go = mappend <$> p <*> go <|> pure mempty
default getSourcePos :: (FactorialMonoid s, Functor (m s)) => m s (Position s)
getSourcePos = Position . Factorial.length <$> getInput
{-# INLINE concatMany #-}
{-# INLINE getSourcePos #-}
class AmbiguousParsing m where
ambiguous :: m a -> m (Ambiguous a)
class Lexical (g :: (* -> *) -> *) where
type LexicalConstraint (m :: ((* -> *) -> *) -> * -> * -> *) g s :: Constraint
lexicalWhiteSpace :: LexicalConstraint m g s => m g s ()
someLexicalSpace :: LexicalConstraint m g s => m g s ()
lexicalComment :: LexicalConstraint m g s => m g s ()
lexicalSemicolon :: LexicalConstraint m g s => m g s Char
lexicalToken :: LexicalConstraint m g s => m g s a -> m g s a
identifierToken :: LexicalConstraint m g s => m g s s -> m g s s
isIdentifierStartChar :: Char -> Bool
isIdentifierFollowChar :: Char -> Bool
identifier :: LexicalConstraint m g s => m g s s
keyword :: LexicalConstraint m g s => s -> m g s ()
type instance LexicalConstraint m g s = (Applicative (m g ()), Monad (m g s),
CharParsing (m g s), MonoidParsing (m g),
Show s, TextualMonoid s)
default lexicalComment :: Alternative (m g s) => m g s ()
default lexicalWhiteSpace :: (LexicalConstraint m g s, Parsing (m g s), MonoidParsing (m g), TextualMonoid s)
=> m g s ()
default someLexicalSpace :: (LexicalConstraint m g s, Parsing (m g s), MonoidParsing (m g), TextualMonoid s)
=> m g s ()
default lexicalSemicolon :: (LexicalConstraint m g s, CharParsing (m g s), MonoidParsing (m g), TextualMonoid s)
=> m g s Char
default lexicalToken :: (LexicalConstraint m g s, Parsing (m g s), MonoidParsing (m g), TextualMonoid s)
=> m g s a -> m g s a
default identifierToken :: (LexicalConstraint m g s, Parsing (m g s), MonoidParsing (m g), TextualMonoid s)
=> m g s s -> m g s s
default identifier :: (LexicalConstraint m g s, Monad (m g s), Alternative (m g s),
Parsing (m g s), MonoidParsing (m g), TextualMonoid s) => m g s s
default keyword :: (LexicalConstraint m g s, Parsing (m g s), MonoidParsing (m g), Show s, TextualMonoid s)
=> s -> m g s ()
lexicalWhiteSpace = takeCharsWhile isSpace *> skipMany (lexicalComment *> takeCharsWhile isSpace)
someLexicalSpace = takeCharsWhile1 isSpace *> skipMany (lexicalComment *> takeCharsWhile isSpace)
<|> lexicalComment *> skipMany (takeCharsWhile isSpace *> lexicalComment)
lexicalComment = empty
lexicalSemicolon = lexicalToken (char ';')
lexicalToken p = p <* lexicalWhiteSpace
isIdentifierStartChar c = isLetter c || c == '_'
isIdentifierFollowChar c = isAlphaNum c || c == '_'
identifier = identifierToken (liftA2 mappend (satisfyCharInput (isIdentifierStartChar @g))
(takeCharsWhile (isIdentifierFollowChar @g))) <?> "an identifier"
identifierToken = lexicalToken
keyword s = lexicalToken (string s *> notSatisfyChar (isIdentifierFollowChar @g)) <?> ("keyword " <> show s)