distributors-0.4.0.0: src/Data/Profunctor/Grammar.hs
{-|
Module : Data.Profunctor.Grammar
Description : grammar distributors
Copyright : (C) 2026 - Eitan Chatav
License : BSD-style (see the file LICENSE)
Maintainer : Eitan Chatav <eitan.chatav@gmail.com>
Stability : provisional
Portability : non-portable
-}
module Data.Profunctor.Grammar
( -- * Printor
Printor (..)
, printP
-- * Parsor
, Parsor (..)
, unparseP
, parseP
-- * Grammor
, Grammor (..)
) where
import Control.Applicative
import Control.Arrow
import Control.Category
import Control.Lens
import Control.Lens.Extras
import Control.Lens.Grammar.BackusNaur
import Control.Lens.Grammar.Kleene
import Control.Lens.Grammar.Symbol
import Control.Lens.Grammar.Token
import Control.Monad
import Control.Monad.Fail.Try
import Data.Coerce
import Data.Monoid
import Data.Profunctor
import Data.Profunctor.Distributor
import Data.Profunctor.Filtrator
import Data.Profunctor.Monoidal
import Data.Void
import Prelude hiding (id, (.))
import GHC.Exts
import Witherable
-- | `Printor` is a simple printer `Profunctor`.
newtype Printor s f a b = Printor {runPrintor :: a -> f (b, s -> s)}
-- | Run the printer on a value, returning a function
-- that `cons`es tokens at the beginning of an input string,
-- from right to left.
printP :: Functor f => Printor s f a b -> a -> f (s -> s)
printP (Printor f) = fmap snd . f
-- | `Parsor` is a simple invertible parser `Profunctor`.
newtype Parsor s f a b = Parsor {runParsor :: Maybe a -> s -> f (b,s)}
-- | Run the parser on an input string,
-- `uncons`ing tokens from the beginning of the string,
-- from left to right, returning a value and the remaining string.
parseP :: Parsor s f a b -> s -> f (b,s)
parseP (Parsor f) = f Nothing
-- | Run the parser in reverse on a value and an input string;
-- `snoc`ing tokens at the end of the string, from left to right,
-- and returning the new string.
unparseP :: Functor f => Parsor s f a b -> a -> s -> f s
unparseP (Parsor f) a = fmap snd . f (Just a)
-- | `Grammor` is a constant `Profunctor`.
newtype Grammor k a b = Grammor {runGrammor :: k}
-- Parsor instances
deriving stock instance Functor f => Functor (Parsor s f a)
instance Functor f => Profunctor (Parsor s f) where
dimap f g = Parsor . dimap (fmap f) (fmap (fmap (first' g))) . runParsor
instance Monad m => Applicative (Parsor s m a) where
pure b = Parsor (\_ s -> pure (b,s))
Parsor f <*> Parsor x = Parsor $ \ma s -> do
(g, s') <- f ma s
(a, s'') <- x ma s'
return (g a, s'')
instance (Alternative m, Monad m) => Strong (Parsor s m) where
first' p = p >*< id
second' p = id >*< p
instance Monad m => Monad (Parsor s m a) where
return = pure
Parsor p >>= f = Parsor $ \ma s -> do
(a, s') <- p ma s
runParsor (f a) ma s'
instance (Alternative m, Monad m) => Alternative (Parsor s m a) where
empty = Parsor (\_ _ -> empty)
Parsor p <|> Parsor q = Parsor $ \ma s -> p ma s <|> q ma s
instance (Alternative m, Monad m) => MonadPlus (Parsor s m a)
instance Filterable f => Filterable (Parsor s f a) where
mapMaybe f (Parsor p) = Parsor $ \fa s ->
mapMaybe (\(a,t) -> fmap (,t) (f a)) (p fa s)
instance Filterable f => Cochoice (Parsor s f) where
unleft = fst . filtrate
unright = snd . filtrate
instance Filterable f => Filtrator (Parsor s f) where
filtrate (Parsor p) =
( Parsor $ \ma s -> mapMaybe
(\case{(Left b,t) -> Just (b,t); _ -> Nothing})
(p (fmap Left ma) s)
, Parsor $ \ma s -> mapMaybe
(\case{(Right b,t) -> Just (b,t); _ -> Nothing})
(p (fmap Right ma) s)
)
instance (Alternative m, Monad m) => Distributor (Parsor s m)
instance (Alternative m, Monad m) => Choice (Parsor s m) where
left' = alternate . Left
right' = alternate . Right
instance (Alternative m, Monad m) => Alternator (Parsor s m) where
alternate = \case
Left (Parsor p) -> Parsor $ \ma s -> case ma of
Nothing -> fmap (first' Left) (p Nothing s)
Just (Left a) -> fmap (first' Left) (p (Just a) s)
Just (Right _) -> empty
Right (Parsor p) -> Parsor $ \ma s -> case ma of
Nothing -> fmap (first' Right) (p Nothing s)
Just (Right a) -> fmap (first' Right) (p (Just a) s)
Just (Left _) -> empty
optionP def p = Parsor $ \ma s -> case ma of
Nothing -> runParsor (p <|> pureP def) ma s
Just _ -> runParsor (pureP def <|> p) ma s
instance (Alternative m, Monad m) => Category (Parsor s m) where
id = Parsor $ \ma s -> case ma of
Nothing -> empty
Just a -> pure (a,s)
Parsor q . Parsor p = Parsor $ \ma s -> case ma of
Nothing -> empty
Just a -> do
(b, t) <- p (Just a) s
q (Just b) t
instance (Alternative m, Monad m) => Arrow (Parsor s m) where
arr f = Parsor $ \ma s -> case ma of
Nothing -> empty
Just a -> pure (f a, s)
(***) = (>*<)
first = first'
second = second'
instance (Alternative m, Monad m) => ArrowZero (Parsor s m) where
zeroArrow = empty
instance (Alternative m, Monad m) => ArrowPlus (Parsor s m) where
(<+>) = (<|>)
instance (Alternative m, Monad m) => ArrowChoice (Parsor s m) where
(+++) = (>+<)
left = left'
right = right'
instance
( Categorized a, a ~ Item s, IsList s
, Cons s s a a, Snoc s s a a
, Filterable m, Alternative m, Monad m
) => Tokenized a (Parsor s m a a) where
anyToken = Parsor $ maybe
(maybe empty pure . uncons)
(\a -> pure . (a,) . flip snoc a)
instance
( Categorized a, a ~ Item s, IsList s
, Cons s s a a, Snoc s s a a
, Filterable m, Alternative m, Monad m
) => TokenAlgebra a (Parsor s m a a)
instance
( Categorized a, a ~ Item s, IsList s
, Cons s s a a, Snoc s s a a
, Filterable m, Alternative m, Monad m
) => TerminalSymbol a (Parsor s m () ()) where
instance
( Char ~ Item s, IsList s
, Cons s s Char Char, Snoc s s Char Char
, Filterable m, Alternative m, Monad m
) => IsString (Parsor s m () ()) where
fromString = terminal
instance
( Char ~ Item s, IsList s
, Cons s s Char Char, Snoc s s Char Char, AsEmpty s
, Filterable m, Alternative m, Monad m
) => IsString (Parsor s m s s) where
fromString = tokens
instance BackusNaurForm (Parsor s m a b)
instance (Alternative m, Monad m) => MonadFail (Parsor s m a) where
fail _ = empty
instance (Alternative m, Monad m) => MonadTry (Parsor s m a)
instance AsEmpty s => Matching s (Parsor s [] a b) where
word =~ p = case
[ () | (_, remaining) <- runParsor p Nothing word
, is _Empty remaining
] of [] -> False; _:_ -> True
-- Printor instances
instance Functor f => Functor (Printor s f a) where
fmap f = Printor . fmap (fmap (first' f)) . runPrintor
instance Functor f => Profunctor (Printor s f) where
dimap f g = Printor . dimap f (fmap (first' g)) . runPrintor
instance Applicative f => Applicative (Printor s f a) where
pure b = Printor (\_ -> pure (b, id))
Printor f <*> Printor x = Printor $ \c ->
liftA2 (\(g, p) (a, q) -> (g a, p . q)) (f c) (x c)
instance Alternative f => Alternative (Printor s f a) where
empty = Printor (\_ -> empty)
Printor p <|> Printor q = Printor (\a -> p a <|> q a)
instance Filterable f => Filterable (Printor s f a) where
mapMaybe f (Printor p) = Printor $
mapMaybe (\(a,q) -> fmap (, q) (f a)) . p
instance Monad f => Monad (Printor s f a) where
return = pure
Printor mx >>= f = Printor $ \a -> do
(a1,g) <- mx a
(b,h) <- runPrintor (f a1) a
return (b, g . h)
instance (Alternative f, Monad f) => MonadPlus (Printor s f a)
instance Applicative f => Distributor (Printor s f) where
zeroP = Printor absurd
Printor p >+< Printor q = Printor $
either (fmap (first' Left) . p) (fmap (first' Right) . q)
instance Alternative f => Alternator (Printor s f) where
alternate = \case
Left (Printor p) -> Printor $
either (fmap (first' Left) . p) (\_ -> empty)
Right (Printor p) -> Printor $
either (\_ -> empty) (fmap (first' Right) . p)
optionP def p = pureP def <|> p
instance Filterable f => Filtrator (Printor s f) where
filtrate (Printor p) =
let
leftMaybe = \case
(Left b, q) -> Just (b, q)
_ -> Nothing
rightMaybe = \case
(Right b, q) -> Just (b, q)
_ -> Nothing
in
( Printor (mapMaybe leftMaybe . p . Left)
, Printor (mapMaybe rightMaybe . p . Right)
)
instance Alternative f => Choice (Printor s f) where
left' = alternate . Left
right' = alternate . Right
instance Filterable f => Cochoice (Printor s f) where
unleft = fst . filtrate
unright = snd . filtrate
instance Functor f => Strong (Printor s f) where
first' (Printor p) =
Printor (\(a,c) -> fmap (\(b,q) -> ((b,c),q)) (p a))
second' (Printor p) =
Printor (\(c,a) -> fmap (\(b,q) -> ((c,b),q)) (p a))
instance Monad f => Category (Printor s f) where
id = Printor $ \a -> return (a, id)
Printor q . Printor p = Printor $ \a -> do
(b, p') <- p a
(c, q') <- q b
return (c, q' . p')
instance Monad f => Arrow (Printor s f) where
arr f = Printor (return . (, id) . f)
(***) = (>*<)
first = first'
second = second'
instance (Alternative f, Monad f) => ArrowZero (Printor s f) where
zeroArrow = empty
instance (Alternative f, Monad f) => ArrowPlus (Printor s f) where
(<+>) = (<|>)
instance (Alternative f, Monad f) => ArrowChoice (Printor s f) where
(+++) = (>+<)
left = left'
right = right'
instance
( Categorized a, a ~ Item s, IsList s, Cons s s a a
, Filterable m, Alternative m, Monad m
) => Tokenized a (Printor s m a a) where
anyToken = Printor (\b -> pure (b, cons b))
instance
( Categorized a, a ~ Item s, IsList s, Cons s s a a
, Filterable m, Alternative m, Monad m
) => TokenAlgebra a (Printor s m a a)
instance
( Categorized a, a ~ Item s, IsList s, Cons s s a a
, Filterable m, Alternative m, Monad m
) => TerminalSymbol a (Printor s m () ()) where
instance
( Char ~ Item s, IsList s, Cons s s Char Char
, Filterable m, Alternative m, Monad m
) => IsString (Printor s m () ()) where
fromString = terminal
instance
( Char ~ Item s, IsList s, Cons s s Char Char, AsEmpty s
, Filterable m, Alternative m, Monad m
) => IsString (Printor s m s s) where
fromString = tokens
instance BackusNaurForm (Printor s m a b)
instance (Alternative m, Monad m) => MonadFail (Printor s m a) where
fail _ = empty
instance (Alternative m, Monad m) => MonadTry (Printor s m a)
-- Grammor instances
instance Functor (Grammor k a) where fmap _ = coerce
instance Contravariant (Grammor k a) where contramap _ = coerce
instance Profunctor (Grammor k) where dimap _ _ = coerce
instance Bifunctor (Grammor k) where bimap _ _ = coerce
instance Choice (Grammor k) where
left' = coerce
right' = coerce
instance Monoid k => Applicative (Grammor k a) where
pure _ = Grammor mempty
Grammor rex1 <*> Grammor rex2 = Grammor (rex1 <> rex2)
instance KleeneStarAlgebra k => Alternative (Grammor k a) where
empty = Grammor zeroK
Grammor rex1 <|> Grammor rex2 = Grammor (rex1 >|< rex2)
many (Grammor rex) = Grammor (starK rex)
some (Grammor rex) = Grammor (plusK rex)
instance KleeneStarAlgebra k => Distributor (Grammor k) where
zeroP = Grammor zeroK
Grammor rex1 >+< Grammor rex2 = Grammor (rex1 >|< rex2)
manyP (Grammor rex) = Grammor (starK rex)
optionalP (Grammor rex) = Grammor (optK rex)
instance KleeneStarAlgebra k => Alternator (Grammor k) where
alternate = either coerce coerce
someP (Grammor rex) = Grammor (plusK rex)
optionP _ (Grammor rex) = Grammor (optK rex)
instance Tokenized token k => Tokenized token (Grammor k a b) where
anyToken = Grammor anyToken
token = Grammor . token
oneOf = Grammor . oneOf
notOneOf = Grammor . notOneOf
asIn = Grammor . asIn
notAsIn = Grammor . notAsIn
instance TokenAlgebra a k => TokenAlgebra a (Grammor k a b) where
tokenClass = Grammor . tokenClass
instance TerminalSymbol token k
=> TerminalSymbol token (Grammor k a b) where
terminal = Grammor . terminal
instance BackusNaurForm k => BackusNaurForm (Grammor k a b) where
rule name = Grammor . rule name . runGrammor
ruleRec name = Grammor . ruleRec name . dimap Grammor runGrammor
instance Matching s k => Matching s (Grammor k a b) where
word =~ pattern = word =~ runGrammor pattern