{-# LANGUAGE ScopedTypeVariables #-}
module KleenePlugin.Matching where
import Control.Applicative ((<|>))
import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe (isJust, maybeToList)
import qualified Outputable as PP
import KleenePlugin.Types
leading :: RE a -> [a]
leading E = []
leading (V a) = [a]
leading (a :\/ b) = leading a <> leading b
leading (a :<> b)
| isJust (nullable a) = leading a <> leading b
| otherwise = leading a
leading (S a) = leading a
#ifdef KLEENE_TOP
leading T = []
#endif
data MatchingError b a
= UnexpectedEnd (RE a) [a]
| NonMatch (RE a) b [b] [Maybe a]
matches
:: (PP.Outputable a, PP.Outputable b)
=> (a -> b -> Either c Bool)
-> RE a
-> [b]
-> Either (MatchingError b a) (NonEmpty (Proof (Maybe c) b a))
matches _eq re [] = case nullable re of
Just pf -> Right (pure pf)
Nothing -> Left $ UnexpectedEnd re $ leading re
matches eq re (c : cs) = case derivate eq c re of
[] -> Left $ NonMatch re c cs $
maybe [] (const [Nothing]) (nullable re) ++
fmap Just (leading re)
(d:ds) ->
(d :| ds) >>>= \(re', endo) ->
matches eq re' cs <&&> \p ->
endo p
infixl 1 >>>=, <&&>
(>>>=) :: NonEmpty a -> (a -> Either e (NonEmpty b)) -> Either e (NonEmpty b)
xs >>>= f = acc $ fmap f xs where
acc :: NonEmpty (Either e (NonEmpty b)) -> Either e (NonEmpty b)
acc (x :| []) = x
acc (x :| y : zs) = comb x (acc (y :| zs))
comb :: Either e (NonEmpty b) -> Either e (NonEmpty b) -> Either e (NonEmpty b)
comb (Left e) (Left _) = Left e
comb (Left _) (Right y) = Right y
comb (Right x) (Left _) = Right x
comb (Right x) (Right y) = Right (x <> y)
(<&&>) :: (Functor f, Functor g) => f (g a) -> (a -> b) -> f (g b)
x <&&> f = fmap (fmap f) x
nullable :: RE a -> Maybe (Proof (Maybe c) b a)
nullable E = return ProofE
nullable (V _) = Nothing
nullable (a :<> b) = ProofA a b [] []
<$> nullable a
<*> nullable b
nullable (a :\/ b) =
ProofL a b [] <$> nullable a <|>
ProofR a b [] <$> nullable b
nullable (S re) = return (ProofN re)
#ifdef KLEENE_TOP
nullable T = return (ProofT [])
#endif
derivate
:: forall a b c. (PP.Outputable a, PP.Outputable b)
=> (a -> b -> Either c Bool)
-> b
-> RE a
-> [(RE a, Proof (Maybe c) b a -> Proof (Maybe c) b a)]
derivate _ _ E = []
derivate eq c (V b) = case eq b c of
Right False -> []
Right True -> return $ exists E $ \p -> case p of
ProofE -> ProofV Nothing c b
_ -> error $ "panic! Proof for E isn't ProofE, but "
++ PP.showSDocUnsafe (PP.ppr p)
++ "\n : " ++ PP.showSDocUnsafe (PP.ppr (proofTy p))
++ "\n : " ++ PP.showSDocUnsafe (PP.ppr (proofStr p))
Left x -> return $ exists E $ \p -> case p of
ProofE -> ProofV (Just x) c b
_ -> error $ "panic! Proof for E isn't ProofE, but "
++ PP.showSDocUnsafe (PP.ppr p)
++ "\n : " ++ PP.showSDocUnsafe (PP.ppr (proofTy p))
++ "\n : " ++ PP.showSDocUnsafe (PP.ppr (proofStr p))
derivate eq c (r :<> s) = dr <|> ds where
dr :: [(RE a, Proof (Maybe c) b a -> Proof (Maybe c) b a)]
dr = do
(r', endo) <- derivate eq c r
return $ exists (r' :<> s) $ \p -> appendLemma c r endo p
ds :: [(RE a, Proof (Maybe c) b a -> Proof (Maybe c) b a)]
ds = do
p <- maybeToList $ nullable r
(s', endo) <- derivate eq c s
return $ exists s' $ \q ->
ProofA r s [] (c : proofStr q) p (endo q)
derivate eq c (r :\/ s) = dr <|> ds where
dr :: [(RE a, Proof (Maybe c) b a -> Proof (Maybe c) b a)]
dr = do
(r', endo) <- derivate eq c r
return $ exists r' $ \p ->
ProofL r s (c : proofStr p) (endo p)
ds :: [(RE a, Proof (Maybe c) b a -> Proof (Maybe c) b a)]
ds = do
(s', endo) <- derivate eq c s
return $ exists s' $ \p ->
ProofR r s (c : proofStr p) (endo p)
derivate eq c (S r) = do
(r', endo) <- derivate eq c r
return $ exists (r' :<> S r) $ \p -> starLemma c r endo p
#ifdef KLEENE_TOP
derivate _eq c T = consume where
consume :: [(RE a, Proof (Maybe c) b a -> Proof (Maybe c) b a)]
consume = return $ exists T $ \p -> case p of
ProofT xs -> ProofT (c : xs)
_ -> error $ "panic! Proof for T isn't ProofT, but "
++ PP.showSDocUnsafe (PP.ppr p)
++ "\n : " ++ PP.showSDocUnsafe (PP.ppr (proofTy p))
++ "\n : " ++ PP.showSDocUnsafe (PP.ppr (proofStr p))
#endif
exists
:: RE a -> (Proof (Maybe c) b a -> Proof (Maybe c) b a)
-> (RE a, Proof (Maybe c) b a -> Proof (Maybe c) b a)
exists = (,)
appendLemma
:: (PP.Outputable a, PP.Outputable b)
=> b
-> RE a
-> (Proof (Maybe c) b a -> Proof (Maybe c) b a)
-> Proof (Maybe c) b a
-> Proof (Maybe c) b a
appendLemma c r f (ProofA _r' s xs ys p q) =
ProofA r s (c : xs) ys (f p) q
appendLemma c r _ p = error $ "panic! appendLemma called with not ProofA\n "
++ PP.showSDocUnsafe (PP.ppr p)
++ "\n : " ++ PP.showSDocUnsafe (PP.ppr (proofTy p))
++ "\n : " ++ PP.showSDocUnsafe (PP.ppr (proofStr p))
++ "\n arguments:"
++ "\n c: " ++ PP.showSDocUnsafe (PP.ppr c)
++ "\n r: " ++ PP.showSDocUnsafe (PP.ppr r)
starLemma
:: (PP.Outputable a, PP.Outputable b)
=> b
-> RE a
-> (Proof (Maybe c) b a -> Proof (Maybe c) b a)
-> Proof (Maybe c) b a
-> Proof (Maybe c) b a
starLemma c r f (ProofA _r' _s' xs ys p q) =
ProofC r (c : xs) ys (f p) q
starLemma c r _ p = error $ "panic! appendLemma called with not ProofA\n "
++ PP.showSDocUnsafe (PP.ppr p)
++ "\n : " ++ PP.showSDocUnsafe (PP.ppr (proofTy p))
++ "\n : " ++ PP.showSDocUnsafe (PP.ppr (proofStr p))
++ "\n arguments:"
++ "\n c: " ++ PP.showSDocUnsafe (PP.ppr c)
++ "\n r: " ++ PP.showSDocUnsafe (PP.ppr r)