-- | This module defines symantics
-- for regular grammars.
module Language.Symantic.Grammar.Regular where

import Data.String (IsString(..))
import qualified Data.Text as Text
import Data.Semigroup (Semigroup(..))

import Language.Symantic.Grammar.EBNF
import Language.Symantic.Grammar.Terminal

-- * Type 'Reg'
-- | Left or right regular grammar.
newtype Reg (lr::LR) g a = Reg { unReg :: g a }
 deriving (IsString, Functor, Gram_Terminal)
deriving instance Alter g => Alter (Reg lr g)
deriving instance Try g => Try (Reg lr g)
deriving instance Gram_Rule g => Gram_Rule (Reg lr g)
deriving instance (Functor g, Alter g, Gram_RegL g) => Gram_RegL (RegL g)
deriving instance (Functor g, Alter g, Gram_RegR g) => Gram_RegR (RegR g)
deriving instance Gram_RegL RuleDef
deriving instance Gram_RegR RuleDef
deriving instance Gram_RuleDef g => Gram_RuleDef (RegR g)
deriving instance Gram_RuleDef g => Gram_RuleDef (RegL g)

reg_of_Terminal :: Terminal g a -> Reg lr g a
reg_of_Terminal (Terminal g) = Reg g

type RegL = Reg 'L
type RegR = Reg 'R

-- ** Class 'Alter'
-- | Like 'Alternative' but without the 'Applicative' super-class,
-- because a regular grammar is not closed under 'Applicative'.
-- And also because the alternative operator has to backtrack
-- when the first alternative fails.
class Alter g where
	empty  :: g a
	(<+>)  :: g a -> g a -> g a
	infixl 3 <+>
	choice :: [g a] -> g a
	choice = foldr (<+>) empty
deriving instance Alter p => Alter (Terminal p)
deriving instance Alter RuleDef
instance Alter EBNF where
	empty = ebnf_const $ "empty"
	EBNF g <+> EBNF q = EBNF $ \bo po -> infix_paren po op $
		g bo (op, L) <> " | " <> q bo (op, R)
		where op = infixB L 2
	choice [] = empty
	choice [g] = g
	choice l@(_:_) = EBNF $ \bo po -> infix_paren po op $
		Text.intercalate " | " $
		(unEBNF <$> l) <*> pure bo <*> pure (op, L)
		where op = infixB L 2

-- ** Class 'Gram_RegR'
-- | Symantics for right regular grammars.
class (Functor g, Alter g) => Gram_RegR g where
	(.*>) :: Terminal g (a -> b) -> RegR g a -> RegR g b
	infixl 4 .*>
	manyR :: Terminal g a -> RegR g [a]
	manyR g = (:) <$> g .*> manyR g <+> empty
	someR :: Terminal g a -> RegR g [a]
	someR g = (:) <$> g .*> manyR g
instance Gram_RegR EBNF where
	Terminal f .*> Reg x = Reg $ f <*> x
	manyR (Terminal (EBNF g)) = Reg $ EBNF $ \rm _po -> "{" <> g rm (op, L) <> "}"  where op = infixN0
	someR (Terminal (EBNF g)) = Reg $ EBNF $ \rm _po -> "{" <> g rm (op, L) <> "}-" where op = infixN0

-- ** Class 'Gram_RegL'
-- | Symantics for left regular grammars.
class (Functor g, Alter g) => Gram_RegL g where
	(<*.) :: RegL g (a -> b) -> Terminal g a -> RegL g b
	infixl 4 <*.
	manyL :: Terminal g a -> RegL g [a]
	manyL g' = reverse <$> go g'
		where go g = flip (:) <$> go g <*. g <+> empty
	someL :: Terminal g a -> RegL g [a]
	someL g = (\cs c -> cs ++ [c]) <$> manyL g <*. g
instance Gram_RegL EBNF where
	Reg f <*. Terminal x = Reg $ f <*> x
	manyL (Terminal (EBNF g)) = Reg $ EBNF $ \rm _po -> "{" <> g rm (op, L) <> "}"  where op = infixN0
	someL (Terminal (EBNF g)) = Reg $ EBNF $ \rm _po -> "{" <> g rm (op, L) <> "}-" where op = infixN0