module Text.Earley.Grammar
( Prod(..)
, terminal
, (<?>)
, alts
, Grammar(..)
, rule
, runGrammar
) where
import Control.Applicative
import Control.Monad
import Control.Monad.Fix
import Data.String (IsString(..))
#if !MIN_VERSION_base(4,8,0)
import Data.Monoid
#endif
import Data.Semigroup
infixr 0 <?>
data Prod r e t a where
Terminal :: !(t -> Maybe a) -> !(Prod r e t (a -> b)) -> Prod r e t b
NonTerminal :: !(r e t a) -> !(Prod r e t (a -> b)) -> Prod r e t b
Pure :: a -> Prod r e t a
Alts :: ![Prod r e t a] -> !(Prod r e t (a -> b)) -> Prod r e t b
Many :: !(Prod r e t a) -> !(Prod r e t ([a] -> b)) -> Prod r e t b
Named :: !(Prod r e t a) -> e -> Prod r e t a
terminal :: (t -> Maybe a) -> Prod r e t a
terminal p = Terminal p $ Pure id
(<?>) :: Prod r e t a -> e -> Prod r e t a
(<?>) = Named
instance Semigroup (Prod r e t a) where
(<>) = (<|>)
instance Monoid (Prod r e t a) where
mempty = empty
mappend = (<|>)
instance Functor (Prod r e t) where
fmap f (Terminal b p) = Terminal b $ fmap (f .) p
fmap f (NonTerminal r p) = NonTerminal r $ fmap (f .) p
fmap f (Pure x) = Pure $ f x
fmap f (Alts as p) = Alts as $ fmap (f .) p
fmap f (Many p q) = Many p $ fmap (f .) q
fmap f (Named p n) = Named (fmap f p) n
alts :: [Prod r e t a] -> Prod r e t (a -> b) -> Prod r e t b
alts as p = case as >>= go of
[] -> empty
[a] -> a <**> p
as' -> Alts as' p
where
go (Alts [] _) = []
go (Alts as' (Pure f)) = fmap f <$> as'
go (Named p' n) = map (<?> n) $ go p'
go a = [a]
instance Applicative (Prod r e t) where
pure = Pure
Terminal b p <*> q = Terminal b $ flip <$> p <*> q
NonTerminal r p <*> q = NonTerminal r $ flip <$> p <*> q
Pure f <*> q = fmap f q
Alts as p <*> q = alts as $ flip <$> p <*> q
Many a p <*> q = Many a $ flip <$> p <*> q
Named p n <*> q = Named (p <*> q) n
instance Alternative (Prod r e t) where
empty = Alts [] $ pure id
Named p m <|> q = Named (p <|> q) m
p <|> Named q n = Named (p <|> q) n
p <|> q = alts [p, q] $ pure id
many (Alts [] _) = pure []
many p = Many p $ Pure id
some p = (:) <$> p <*> many p
instance (IsString t, Eq t, a ~ t) => IsString (Prod r e t a) where
fromString s = Terminal f $ Pure id
where
fs = fromString s
f t | t == fs = Just fs
f _ = Nothing
data Grammar r a where
RuleBind :: Prod r e t a -> (Prod r e t a -> Grammar r b) -> Grammar r b
FixBind :: (a -> Grammar r a) -> (a -> Grammar r b) -> Grammar r b
Return :: a -> Grammar r a
instance Functor (Grammar r) where
fmap f (RuleBind ps h) = RuleBind ps (fmap f . h)
fmap f (FixBind g h) = FixBind g (fmap f . h)
fmap f (Return x) = Return $ f x
instance Applicative (Grammar r) where
pure = return
(<*>) = ap
instance Monad (Grammar r) where
return = Return
RuleBind ps f >>= k = RuleBind ps (f >=> k)
FixBind f g >>= k = FixBind f (g >=> k)
Return x >>= k = k x
instance MonadFix (Grammar r) where
mfix f = FixBind f return
rule :: Prod r e t a -> Grammar r (Prod r e t a)
rule p = RuleBind p return
runGrammar :: MonadFix m
=> (forall e t a. Prod r e t a -> m (Prod r e t a))
-> Grammar r b -> m b
runGrammar r grammar = case grammar of
RuleBind p k -> do
nt <- r p
runGrammar r $ k nt
Return a -> return a
FixBind f k -> do
a <- mfix $ runGrammar r <$> f
runGrammar r $ k a