-- | Symantics for context-free grammars.
module Language.Symantic.Grammar.ContextFree where

import Control.Applicative (Applicative(..))
import Control.Monad
import Data.Semigroup (Semigroup(..))
import Data.String (IsString(..))
import Prelude hiding (any)

import Language.Symantic.Grammar.Meta
import Language.Symantic.Grammar.Fixity
import Language.Symantic.Grammar.EBNF
import Language.Symantic.Grammar.Terminal
import Language.Symantic.Grammar.Regular

-- * Type 'CF'
-- | Context-free grammar.
newtype CF g a = CF { unCF :: g a }
 deriving (IsString, Functor, Gram_Terminal, Applicative, Gram_App)
deriving instance Gram_Error err g => Gram_Error err (CF g)
deriving instance Gram_Reader st g => Gram_Reader st (CF g)
deriving instance Gram_State st g => Gram_State st (CF g)
deriving instance Gram_Alt g => Gram_Alt (CF g)
deriving instance Gram_Try g => Gram_Try (CF g)
deriving instance Gram_AltApp g => Gram_AltApp (CF g)
deriving instance Gram_Rule g => Gram_Rule (CF g)
deriving instance Gram_RegL g => Gram_RegL (CF g)
deriving instance Gram_RegR g => Gram_RegR (CF g)
deriving instance Gram_CF g => Gram_CF (CF g)
deriving instance Gram_CF RuleEBNF
deriving instance Gram_RuleEBNF g => Gram_RuleEBNF (CF g)
instance Gram_CF EBNF where
	CF (EBNF f) <& Reg (EBNF g) =
		CF $ EBNF $ \bo po -> parenInfix po op $
		f bo (op, SideL) <> " & " <> g bo (op, SideR)
		where op = infixB SideL 4
	Reg (EBNF f) &> CF (EBNF g) =
		CF $ EBNF $ \bo po -> parenInfix po op $
		f bo (op, SideL) <> " & " <> g bo (op, SideR)
		where op = infixB SideL 4
	CF (EBNF f) `minus` Reg (EBNF g) =
		CF $ EBNF $ \bo po -> parenInfix po op $
		f bo (op, SideL) <> " - " <> g bo (op, SideR)
		where op = infixL 6

class ContextFreeOf gram where
	cfOf :: gram g a -> CF g a
instance ContextFreeOf Terminal where
	cfOf (Terminal g) = CF g
instance ContextFreeOf (Reg lr) where
	cfOf (Reg g) = CF g

-- ** Class 'Gram_CF'
-- | Symantics for context-free grammars.
class Gram_CF g where
	-- | NOTE: CFL ∩ RL is a CFL.
	-- See ISBN 81-7808-347-7, Theorem 7.27, p.286
	(<&) :: CF g (a -> b) -> Reg lr g a -> CF g b
	infixl 4 <&
	(&>) :: Reg lr g (a -> b) -> CF g a -> CF g b
	infixl 4  &>
	-- | NOTE: CFL - RL is a CFL.
	-- See ISBN 81-7808-347-7, Theorem 7.29, p.289
	minus :: CF g a -> Reg lr g b -> CF g a

-- ** Class 'Gram_App'
class Applicative g => Gram_App g where
	between :: g open -> g close -> g a -> g a
	between open close g = open *> g <* close
deriving instance Gram_App RuleEBNF
instance Gram_App EBNF

-- ** Class 'Gram_AltApp'
-- | Symantics when 'Gram_Alt' and 'Gram_App' are allowed by the grammar.
class (Gram_Alt g, Gram_App g) => Gram_AltApp g where
	option :: a -> g a -> g a
	option x g = g <+> pure x
	optional :: g a -> g (Maybe a)
	optional v = Just <$> v <+> pure Nothing
	many :: g a -> g [a]
	many a = some a <+> pure []
	some :: g a -> g [a]
	some a = (:) <$> a <*> many a
	skipMany :: g a -> g ()
	skipMany = void . many
	--manyTill :: g a -> g end -> g [a]
	--manyTill g end = go where go = ([] <$ end) <|> ((:) <$> g <*> go)
	inside
	 :: (in_ -> next)
	 -> CF g begin
	 -> CF g in_
	 -> CF g end
	 -> CF g next
	 -> CF g next
	inside f begin in_ end next =
		(f <$ begin <*> in_ <* end) <+> next
deriving instance Gram_AltApp RuleEBNF
instance Gram_AltApp EBNF where
	many (EBNF g) = EBNF $ \rm _po -> "{" <> g rm (op, SideL) <> "}"  where op = infixN0
	some (EBNF g) = EBNF $ \rm _po -> "{" <> g rm (op, SideL) <> "}-" where op = infixN0
	option _x (EBNF g) = EBNF $ \rm _po ->
		"[" <> g rm (op, SideL) <> "]" where op = infixN0

-- * Class 'Gram_Comment'
-- | Symantics for handling comments after each 'lexeme'.
class
 ( Gram_Terminal g
 , Gram_Rule g
 , Gram_Alt g
 , Gram_App g
 , Gram_AltApp g
 , Gram_CF g
 ) => Gram_Comment g where
	commentable :: g () -> g () -> g () -> g ()
	commentable = rule3 "commentable" $ \space line block ->
		skipMany $ choice [space, line, block]
	comment_line :: CF g String -> CF g String
	comment_line prefix = rule "comment_line" $
		prefix *> many (any `minus` (void (char '\n') <+> eoi))
	comment_block :: CF g String -> Reg lr g String -> CF g String
	comment_block begin end = rule "comment_block" $
		begin *> many (any `minus` end) <* cfOf end
	lexeme :: CF g a -> CF g a
	lexeme = rule1 "lexeme" $ \g ->
		g <* commentable
		 (void $ string " " <+> string "\n ")
		 (void $ comment_line (string "--"))
		 (void $ comment_block (string "{-") (string "-}"))
	parens :: CF g a -> CF g a
	parens = rule1 "parens" $
		between
		 (lexeme $ char '(')
		 (lexeme $ char ')')
	symbol :: String -> CF g String
	symbol = lexeme . string
deriving instance Gram_Comment g => Gram_Comment (CF g)
instance Gram_Comment RuleEBNF
instance Gram_Comment EBNF

gram_comment :: forall g. (Gram_Comment g, Gram_RuleEBNF g) => [CF g ()]
gram_comment =
 [ void $ commentable (void $ argEBNF "space") (void $ argEBNF "line") (void $ argEBNF "block")
 , void $ comment_line (argEBNF "prefix")
 , void $ comment_block (argEBNF "begin") (argEBNF "end" :: RegL g String)
 , void $ lexeme (argEBNF "g")
 , void $ parens (argEBNF "g")
 , void $ inside id (argEBNF "begin") (argEBNF "in") (argEBNF "end") (argEBNF "next")
 ]