{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- | Symantic instances for Megaparsec
module Grammar.Megaparsec where

import Control.Applicative (Applicative(..))
import Control.Monad (Monad(..))
import Data.Char (Char)
import Data.Either (Either(..))
import Data.Function (($), (.))
import Data.Functor (Functor(..), (<$>))
import Data.List.NonEmpty (NonEmpty(..))
import Data.Ord (Ord(..))
import Data.String (IsString(..))
import Data.Typeable ()
import Text.Show (Show(..))
import qualified Control.Applicative as Alt
import qualified Control.Monad.Classes as MC
import qualified Data.Char as Char
import qualified Data.Text as Text
import qualified Text.Megaparsec as P

import Language.Symantic.Grammar as Sym
import qualified Language.Symantic as Sym
import Language.Symantic.Lib ()

-- * Type 'ParsecC'
-- | Convenient alias for defining instances involving 'P.ParsecT'.
type ParsecC e s = (P.Token s ~ Char, P.Stream s, P.ErrorComponent e)
instance ParsecC e s => IsString (P.ParsecT e s m [Char]) where
	fromString = P.string

--
-- Readers
--

-- NonEmpty P.SourcePos
instance ParsecC e s => Sym.Gram_Reader (NonEmpty P.SourcePos) (P.ParsecT e s m) where
	askBefore g = do
		s <- P.statePos <$> P.getParserState
		($ s) <$> g
	askAfter g = do
		f <- g
		f . P.statePos <$> P.getParserState
type instance MC.CanDo (P.ParsecT e s m) (MC.EffReader (NonEmpty P.SourcePos)) = 'True
instance ParsecC e s => MC.MonadReaderN 'MC.Zero (NonEmpty P.SourcePos) (P.ParsecT e s m) where
	askN _n = P.statePos <$> P.getParserState
-- P.SourcePos
instance ParsecC e s => Sym.Gram_Reader P.SourcePos (P.ParsecT e s m) where
	askBefore g = do
		s <- P.getPosition
		($ s) <$> g
	askAfter g = do
		f <- g
		f <$> P.getPosition
type instance MC.CanDo (P.ParsecT e s m) (MC.EffReader P.SourcePos) = 'True
instance ParsecC e s => MC.MonadReaderN 'MC.Zero P.SourcePos (P.ParsecT e s m) where
	askN _n = P.getPosition
-- ()
instance ParsecC e s => Sym.Gram_Reader () (P.ParsecT e s m) where
	askBefore = fmap ($ ())
	askAfter  = fmap ($ ())

--
-- States
--

-- st
type instance MC.CanDo (P.ParsecT e s m) (MC.EffState st) = 'False
instance (Monad m, MC.MonadState st m) => Sym.Gram_State st m where
	stateBefore g = do
		s <- MC.get
		f <- g
		let (s', a) = f s
		MC.put s'
		return a
	stateAfter g = do
		f <- g
		s <- MC.get
		let (s_, a) = f s
		MC.put s_
		return a
	getBefore g = do
		s <- MC.get
		f <- g
		return (f s)
	getAfter g = do
		f <- g
		s <- MC.get
		return (f s)
	put g = do
		(s, a) <- g
		MC.put s
		return a

--
-- Sym instances
--
instance (ParsecC e s, Show err) => Sym.Gram_Error err (P.ParsecT e s m) where
	catch me {- if you can :-} = do
		e <- me
		case e of
		 Left err -> fail $ show err
		 Right a -> return a
instance ParsecC e s => Sym.Gram_Rule (P.ParsecT e s m) where
	rule = P.label . Text.unpack
instance ParsecC e s => Sym.Gram_Terminal (P.ParsecT e s m) where
	any          = P.anyChar
	eoi          = P.eof
	char         = P.char
	string       = P.string
	unicat cat   = P.satisfy $ (`elem` cats) . Char.generalCategory
		where cats = unicode_categories cat
	range (l, h) = P.satisfy $ \c -> l <= c && c <= h
	Terminal f `but` Terminal p = Terminal $ P.notFollowedBy (P.try p) *> f
instance ParsecC e s => Sym.Gram_Alt (P.ParsecT e s m) where
	empty  = Alt.empty
	(<+>)  = (Alt.<|>)
	choice = P.choice
instance ParsecC e s => Sym.Gram_Try (P.ParsecT e s m) where
	try = P.try
instance ParsecC e s => Sym.Gram_RegR (P.ParsecT e s m) where
	Terminal f .*> Reg x = Reg $ f <*> x
instance ParsecC e s => Sym.Gram_RegL (P.ParsecT e s m) where
	Reg f <*. Terminal x = Reg $ f <*> x
instance ParsecC e s => Sym.Gram_App (P.ParsecT e s m) where
	between = P.between
instance ParsecC e s => Sym.Gram_AltApp (P.ParsecT e s m) where
	option   = P.option
	optional = P.optional
	many     = P.many
	some     = P.some
	manySkip = P.skipMany
instance ParsecC e s => Sym.Gram_CF (P.ParsecT e s m) where
	CF f <& Reg p        = CF $ P.lookAhead f <*> p
	Reg f &> CF p        = CF $ P.lookAhead f <*> p
	minus (CF f) (Reg p) = CF $ P.notFollowedBy (P.try p) *> f
instance ParsecC e s => Sym.Gram_Comment (P.ParsecT e s m)
instance ParsecC e s => Sym.Gram_Op (P.ParsecT e s m)
instance ParsecC e s => Sym.Gram_Mod (P.ParsecT e s m)
instance ParsecC e s => Sym.Gram_Type_Name (P.ParsecT e s m)
instance ParsecC e s => Sym.Gram_Term_Name (P.ParsecT e s m)
instance -- Sym.Gram_Type
 ( ParsecC e s
 , Gram_Source src (P.ParsecT e s m)
 , Show src
 , MC.MonadState ( Sym.Imports Sym.NameTy
                 , Sym.ModulesTy src ) (P.ParsecT e s m)
 ) => Sym.Gram_Type src (P.ParsecT e s m)
instance -- Sym.Gram_Term_Type
 ( ParsecC e s
 , Show src
 , MC.MonadState ( Sym.Imports Sym.NameTy
                 , Sym.ModulesTy src ) (P.ParsecT e s m)
 , Gram_Source src           (P.ParsecT e s m)
 ) => Sym.Gram_Term_Type src (P.ParsecT e s m)
instance -- Sym.Gram_Term
 ( ParsecC e s
 , Show src
 , MC.MonadState ( Sym.Imports Sym.NameTy
                 , Sym.ModulesTy src )  (P.ParsecT e s m)
 , MC.MonadState ( Sym.Imports Sym.NameTe
                 , Sym.Modules src ss ) (P.ParsecT e s m)
 , Sym.Gram_Source src        (P.ParsecT e s m)
 , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
 ) => Sym.Gram_Term src ss    (P.ParsecT e s m)