mello-0.2.1: src/Mello/Match.hs
{-# LANGUAGE UndecidableInstances #-}
module Mello.Match
( MatchErr (..)
, LocMatchErr (..)
, MatchT
, MatchM
, runMatchT
, runMatchM
, SeqMatchT
, SeqMatchM
, annoM
, memoM
, embedM
, matchM
, listM
, lookM
, elemM
, restM
, repeatM
, remainingM
, altM
, anySymM
, symM
, anyIntM
, intM
, anySciM
, sciM
, anyStrM
, strM
, anyCharM
, charM
, anyAtomM
, quoteM
, unquoteM
, docM
, MatchSexp (..)
, fromSexpT
, fromSexp
, fromAnnoSexpT
, fromAnnoSexp
, proxyM
)
where
import Bowtie (Anno (..), Memo (..), mkMemo, unMkMemo, pattern MemoP)
import Bowtie qualified as B
import Control.Exception (Exception)
import Control.Monad (ap, unless)
import Control.Monad.Except (ExceptT, MonadError (..), runExceptT)
import Control.Monad.Identity (Identity (..))
import Control.Monad.Reader (MonadReader (..), ReaderT (..), ask, asks, local)
import Control.Monad.State (MonadState (..), StateT, runStateT)
import Control.Monad.Trans (MonadTrans (..))
import Data.Proxy (Proxy)
import Data.Scientific (Scientific)
import Data.Sequence (Seq (..))
import Data.Sequence qualified as Seq
import Data.Text (Text)
import Data.Typeable (Typeable)
import Mello.Syntax (Atom (..), AtomType (..), Brace, Doc, Sexp (..), SexpF (..), SexpType (..), Sym (..))
data MatchErr e r
= MatchErrType !SexpType
| MatchErrTypeAtom
| MatchErrTypeQuote
| MatchErrTypeUnquote
| MatchErrTypeDoc
| MatchErrNotEq !Atom
| MatchErrListElem !Int
| MatchErrListRem
| MatchErrAlt !(Seq (Text, r))
| MatchErrEmbed !e
deriving stock (Eq, Ord, Show)
instance (Typeable e, Show e, Typeable r, Show r) => Exception (MatchErr e r)
newtype LocMatchErr e k = LocMatchErr
{ unLocMatchErr :: Anno k (MatchErr e (LocMatchErr e k))
}
deriving stock (Show)
deriving newtype (Eq, Ord)
instance (Typeable e, Show e, Typeable k, Show k) => Exception (LocMatchErr e k)
newtype MatchT e k m a = MatchT {unMatchT :: ReaderT (Memo SexpF k) (ExceptT (LocMatchErr e k) m) a}
deriving newtype (Functor, Applicative, Monad)
type MatchM e k = MatchT e k Identity
instance MonadTrans (MatchT e k) where
lift = MatchT . lift . lift
unlift :: (Monad m) => (Memo SexpF k -> m (Either (LocMatchErr e k) a)) -> MatchT e k m a
unlift f = MatchT $ do
s <- ask
ea <- lift (lift (f s))
either throwError pure ea
instance (MonadReader r m) => MonadReader r (MatchT e k m) where
ask = lift ask
local f m = unlift (local f . runMatchT m)
instance (MonadState s m) => MonadState s (MatchT e k m) where
get = lift get
put = lift . put
state = lift . state
instance (MonadError x m) => MonadError x (MatchT e k m) where
throwError = lift . throwError
catchError m f = unlift (\s -> catchError (runMatchT m s) (flip runMatchT s . f))
runMatchT :: MatchT e k m a -> Memo SexpF k -> m (Either (LocMatchErr e k) a)
runMatchT m r = runExceptT (runReaderT (unMatchT m) r)
runMatchM :: MatchM e k a -> Memo SexpF k -> Either (LocMatchErr e k) a
runMatchM m r = runIdentity (runMatchT m r)
data SeqMatchT e k m a where
SeqMatchPure :: a -> SeqMatchT e k m a
SeqMatchEmbed :: MatchT e k m (SeqMatchT e k m a) -> SeqMatchT e k m a
SeqMatchElem :: MatchT e k m x -> (x -> SeqMatchT e k m a) -> SeqMatchT e k m a
SeqMatchRepeat :: SeqMatchT e k m x -> (Seq x -> SeqMatchT e k m a) -> SeqMatchT e k m a
SeqMatchRemaining :: (Int -> SeqMatchT e k m a) -> SeqMatchT e k m a
type SeqMatchM e k = SeqMatchT e k Identity
instance (Functor m) => Functor (SeqMatchT e k m) where
fmap f = go
where
go = \case
SeqMatchPure a -> SeqMatchPure (f a)
SeqMatchEmbed mr -> SeqMatchEmbed (fmap go mr)
SeqMatchElem mx k -> SeqMatchElem mx (go . k)
SeqMatchRepeat mx k -> SeqMatchRepeat mx (go . k)
SeqMatchRemaining k -> SeqMatchRemaining (go . k)
instance (Monad m) => Applicative (SeqMatchT e k m) where
pure = SeqMatchPure
(<*>) = ap
instance (Monad m) => Monad (SeqMatchT e k m) where
return = pure
r0 >>= f = go r0
where
go = \case
SeqMatchPure a -> f a
SeqMatchEmbed mr -> SeqMatchEmbed (fmap go mr)
SeqMatchElem mx k -> SeqMatchElem mx (go . k)
SeqMatchRepeat mx k -> SeqMatchRepeat mx (go . k)
SeqMatchRemaining k -> SeqMatchRemaining (go . k)
annoM :: (Monad m) => MatchT e k m a -> MatchT e k m (Anno k a)
annoM m = MatchT (asks (Anno . B.memoKey)) <*> m
memoM :: (Monad m) => MatchT e k m (f (Memo f k)) -> MatchT e k m (Memo f k)
memoM m = MatchT (asks (MemoP . B.memoKey)) <*> m
errM :: (Monad m) => MatchErr e (LocMatchErr e k) -> MatchT e k m a
errM e = do
s <- MatchT ask
MatchT (throwError (LocMatchErr (Anno (B.memoKey s) e)))
embedM :: (Monad m) => e -> MatchT e k m a
embedM = errM . MatchErrEmbed
matchM :: (Monad m) => (SexpF (Memo SexpF k) -> Either (MatchErr e (LocMatchErr e k)) a) -> MatchT e k m a
matchM f = do
s <- MatchT ask
case f (B.memoVal s) of
Left e -> MatchT (throwError (LocMatchErr (Anno (B.memoKey s) e)))
Right a -> pure a
data S k = S !Int !(Seq (Memo SexpF k))
deriving stock (Eq, Ord, Show)
listM :: (Monad m) => Brace -> SeqMatchT e k m a -> MatchT e k m a
listM = listFromM 0
-- heuper for listFromM, but needs type sig
goSeqX :: (Monad m) => SeqMatchT e k m a -> StateT (S k) (MatchT e k m) a
goSeqX = \case
SeqMatchPure a -> pure a
SeqMatchEmbed mr -> lift mr >>= goSeqX
SeqMatchElem mx k -> do
S i cs <- get
let i' = i + 1
case cs of
Empty -> lift (errM (MatchErrListElem i'))
c :<| cs' -> do
put (S i' cs')
x <- lift (MatchT (local (const c) (unMatchT mx)))
goSeqX (k x)
SeqMatchRepeat mx k -> goRepeatX mx k
SeqMatchRemaining k -> do
S _ cs <- get
goSeqX (k (Seq.length cs))
-- TODO Better error for failure on repeat?
-- helper for listFromM, but needs type sig
goRepeatX :: (Monad m) => SeqMatchT e k m x -> (Seq x -> SeqMatchT e k m a) -> StateT (S k) (MatchT e k m) a
goRepeatX mx k = go Empty
where
go !acc = do
S _ cs <- get
case cs of
Empty -> goSeqX (k acc)
_ -> do
x <- goSeqX mx
go (acc :|> x)
listFromM :: (Monad m) => Int -> Brace -> SeqMatchT e k m a -> MatchT e k m a
listFromM i0 b0 r = goStart
where
goStart = do
s <- MatchT ask
case B.memoVal s of
SexpListF b cs0 | b == b0 -> do
let s0 = S i0 (Seq.drop i0 cs0)
(a, S _ cs1) <- runStateT (goSeqX r) s0
case cs1 of
Empty -> pure a
_ -> errM MatchErrListRem
_ -> errM (MatchErrType (SexpTypeList b0))
elemM :: MatchT e k m a -> SeqMatchT e k m a
elemM = (`SeqMatchElem` SeqMatchPure)
restM :: MatchT e k m a -> SeqMatchT e k m (Seq a)
restM = repeatM . elemM
repeatM :: SeqMatchT e k m a -> SeqMatchT e k m (Seq a)
repeatM = (`SeqMatchRepeat` SeqMatchPure)
remainingM :: SeqMatchT e k m Int
remainingM = SeqMatchRemaining SeqMatchPure
altM :: (Monad m) => [(Text, MatchT e k m a)] -> MatchT e k m a
altM = go Empty
where
go !acc = \case
[] -> errM (MatchErrAlt acc)
(l, m) : ms -> do
s <- MatchT ask
res <- lift (runMatchT m s)
case res of
Right a -> pure a
Left e -> go (acc :|> (l, e)) ms
lookM :: (Monad m) => Brace -> [(Text, MatchT e k m (), SeqMatchT e k m a)] -> MatchT e k m a
lookM b0 as0 = goRoot
where
goRoot = do
s <- MatchT ask
case B.memoVal s of
SexpListF b cs0 | b == b0 ->
case cs0 of
Empty -> errM (MatchErrListElem 0)
hd :<| _ -> goAlt hd Empty as0
_ -> errM (MatchErrType (SexpTypeList b0))
goAlt hd !acc = \case
[] -> errM (MatchErrAlt acc)
(l, m, r) : as -> do
resHd <- lift (runMatchT m hd)
case resHd of
Right _ -> do
s <- MatchT ask
resTl <- lift (runMatchT (listFromM 1 b0 r) s)
case resTl of
Right a -> pure a
Left e -> goAlt hd (acc :|> (l, e)) as
Left e -> goAlt hd (acc :|> (l, e)) as
anySymM :: (Monad m) => MatchT e k m Sym
anySymM = matchM $ \case
SexpAtomF (AtomSym y) -> Right y
_ -> Left (MatchErrType (SexpTypeAtom AtomTypeSym))
symM :: (Monad m) => Sym -> MatchT e k m ()
symM x =
anySymM >>= \y ->
unless (y == x) (errM (MatchErrNotEq (AtomSym x)))
anyIntM :: (Monad m) => MatchT e k m Integer
anyIntM = matchM $ \case
SexpAtomF (AtomInt y) -> Right y
_ -> Left (MatchErrType (SexpTypeAtom AtomTypeInt))
intM :: (Monad m) => Integer -> MatchT e k m ()
intM x =
anyIntM >>= \y ->
unless (y == x) (errM (MatchErrNotEq (AtomInt x)))
anySciM :: (Monad m) => MatchT e k m Scientific
anySciM = matchM $ \case
SexpAtomF (AtomSci y) -> Right y
_ -> Left (MatchErrType (SexpTypeAtom AtomTypeSci))
sciM :: (Monad m) => Scientific -> MatchT e k m ()
sciM x =
anySciM >>= \y ->
unless (y == x) (errM (MatchErrNotEq (AtomSci x)))
anyStrM :: (Monad m) => MatchT e k m Text
anyStrM = matchM $ \case
SexpAtomF (AtomStr y) -> Right y
_ -> Left (MatchErrType (SexpTypeAtom AtomTypeStr))
strM :: (Monad m) => Text -> MatchT e k m ()
strM x =
anyStrM >>= \y ->
unless (y == x) (errM (MatchErrNotEq (AtomStr x)))
anyCharM :: (Monad m) => MatchT e k m Char
anyCharM = matchM $ \case
SexpAtomF (AtomChar y) -> Right y
_ -> Left (MatchErrType (SexpTypeAtom AtomTypeChar))
charM :: (Monad m) => Char -> MatchT e k m ()
charM x =
anyCharM >>= \y ->
unless (y == x) (errM (MatchErrNotEq (AtomChar x)))
anyAtomM :: (Monad m) => MatchT e k m Atom
anyAtomM = matchM $ \case
SexpAtomF a -> Right a
_ -> Left MatchErrTypeAtom
quoteM :: (Monad m) => MatchT e k m (Memo SexpF k)
quoteM = matchM $ \case
SexpQuoteF x -> Right x
_ -> Left MatchErrTypeQuote
unquoteM :: (Monad m) => MatchT e k m (Memo SexpF k)
unquoteM = matchM $ \case
SexpUnquoteF x -> Right x
_ -> Left MatchErrTypeQuote
docM :: (Monad m) => MatchT e k m a -> MatchT e k m (Doc, a)
docM m = do
(d, x) <- matchM $ \case
SexpDocF d x -> Right (d, x)
_ -> Left MatchErrTypeDoc
a <- MatchT (local (const x) (unMatchT m))
pure (d, a)
class (Monad m) => MatchSexp e k m a where
matchSexp :: MatchT e k m a
instance (Monad m) => MatchSexp e k m Sexp where
matchSexp = matchM (Right . Sexp . fmap unMkMemo)
instance (Monad m) => MatchSexp e k m (Memo SexpF k) where
matchSexp = memoM (matchM Right)
instance (MatchSexp e k m s) => MatchSexp e k m (Anno k s) where
matchSexp = annoM matchSexp
instance (Monad m) => MatchSexp e k m Atom where
matchSexp = anyAtomM
instance (Monad m) => MatchSexp e k m Sym where
matchSexp = anySymM
instance (Monad m) => MatchSexp e k m Integer where
matchSexp = anyIntM
instance (Monad m) => MatchSexp e k m Scientific where
matchSexp = anySciM
instance (Monad m) => MatchSexp e k m Text where
matchSexp = anyStrM
instance (Monad m) => MatchSexp e k m Char where
matchSexp = anyCharM
fromSexpT :: (MatchSexp e () m a) => Sexp -> m (Either (LocMatchErr e ()) a)
fromSexpT = runMatchT matchSexp . mkMemo (const ())
fromSexp :: (MatchSexp e () Identity a) => Sexp -> Either (LocMatchErr e ()) a
fromSexp = runIdentity . fromSexpT
fromAnnoSexpT :: (MatchSexp e k m a) => Memo SexpF k -> m (Either (LocMatchErr e k) a)
fromAnnoSexpT = runMatchT matchSexp
fromAnnoSexp :: (MatchSexp e k Identity a) => Memo SexpF k -> Either (LocMatchErr e k) a
fromAnnoSexp = runIdentity . fromAnnoSexpT
proxyM :: (MatchSexp e k m a) => Proxy a -> MatchT e k m a
proxyM = const matchSexp