module Data.Katydid.Relapse.MemDerive
( derive
, Mem
, newMem
, validate
)
where
import qualified Data.Map.Strict as M
import Control.Monad.State ( State
, runState
, lift
, state
)
import Control.Monad.Trans.Except ( ExceptT(..)
, runExceptT
)
import Data.Katydid.Parser.Parser
import qualified Data.Katydid.Relapse.Derive as Derive
import Data.Katydid.Relapse.Smart ( Grammar
, Pattern
, lookupRef
, nullable
, lookupMain
)
import Data.Katydid.Relapse.IfExprs
import Data.Katydid.Relapse.Expr
import Data.Katydid.Relapse.Zip
mem :: Ord k => (k -> v) -> k -> M.Map k v -> (v, M.Map k v)
mem f k m | M.member k m = (m M.! k, m)
| otherwise = let res = f k in (res, M.insert k res m)
type Calls = M.Map [Pattern] IfExprs
type Returns = M.Map ([Pattern], [Bool]) [Pattern]
newtype Mem = Mem (Calls, Returns)
newMem :: Mem
newMem = Mem (M.empty, M.empty)
calls :: Grammar -> [Pattern] -> State Mem IfExprs
calls g k = state $ \(Mem (c, r)) ->
let (v', c') = mem (Derive.calls g) k c in (v', Mem (c', r))
returns :: Grammar -> ([Pattern], [Bool]) -> State Mem [Pattern]
returns g k = state $ \(Mem (c, r)) ->
let (v', r') = mem (Derive.returns g) k r in (v', Mem (c, r'))
mderive
:: Tree t
=> Grammar
-> [Pattern]
-> [t]
-> ExceptT String (State Mem) [Pattern]
mderive _ ps [] = return ps
mderive g ps (tree : ts) = do
ifs <- lift $ calls g ps
childps <- hoistExcept $ evalIfExprs ifs (getLabel tree)
(zchildps, zipper) <- return $ zippy childps
childres <- mderive g zchildps (getChildren tree)
let nulls = map nullable childres
unzipns = unzipby zipper nulls
rs <- lift $ returns g (ps, unzipns)
mderive g rs ts
hoistExcept :: (Monad m) => Either e a -> ExceptT e m a
hoistExcept = ExceptT . return
derive :: Tree t => Grammar -> [t] -> Either String Pattern
derive g ts =
let start = [lookupMain g]
(res, _) = runState (runExceptT $ mderive g start ts) newMem
in case res of
(Left l ) -> Left l
(Right [r]) -> return r
(Right rs ) -> Left $ "not a single pattern: " ++ show rs
validate :: Tree t => Grammar -> Pattern -> [t] -> (State Mem) Bool
validate g start tree = do
rs <- runExceptT (mderive g [start] tree)
return $ case rs of
(Right [r]) -> nullable r
_ -> False