inventory-0.1.0.0: src/MatchSigs/Matching/Env.hs
{-# LANGUAGE MultiWayIf #-}
module MatchSigs.Matching.Env
( Env
, (/\)
, checkOr
, checkAnd
, introVars
, tryAssignVar
, initEnv
) where
import Control.Monad.State.Strict
import Data.List
import qualified Data.IntMap.Strict as IM
import MatchSigs.Sig (FreeVarIdx)
type Level = Int
type VarLevel = IM.IntMap Int
type VarAssign = IM.IntMap FreeVarIdx
-- | Context for matching the free vars in two 'Sigs'
data Env =
MkEnv { level :: !Level -- current var level
, vass :: !VarAssign -- map from B vars to A vars
, vlA :: !VarLevel -- the level at which an A var with introduced
, vlB :: !VarLevel
}
initEnv :: Env
initEnv =
MkEnv { level = 0
, vass = mempty
, vlA = mempty
, vlB = mempty
}
-- | Identify var from one sig with var in other sig
tryAssignVar :: FreeVarIdx
-> FreeVarIdx
-> State Env Bool
tryAssignVar ai bi = do
env <- get
let mb = IM.lookup bi $ vass env
if -- already assigned
| Just x <- mb
, x == ai -> pure True
-- not assigned and levels match
| Nothing <- mb
, Just lA <- IM.lookup ai $ vlA env
, Just lB <- IM.lookup bi $ vlB env
, lA == lB
-> do put env { vass = IM.insert bi ai $ vass env }
pure True
| otherwise -> pure False
-- | Add vars from both sigs to the context, accounting for level
introVars :: [FreeVarIdx]
-> [FreeVarIdx]
-> State Env Bool
introVars [] [] = pure True
introVars va vb
| length va == length vb
= (True <$) . modify' $ \env ->
let lvl = level env
in env { vlA = IM.fromList (zip va $ repeat lvl) <> vlA env
, vlB = IM.fromList (zip vb $ repeat lvl) <> vlB env
, level = lvl + 1
}
| otherwise = pure False
-- | Logical conjuction
(/\) :: State env Bool
-> State env Bool
-> State env Bool
a /\ b = do
r <- a
if r then b else pure False
checkAnd :: [State Env Bool]
-> State Env Bool
checkAnd = foldl' (/\) (pure True)
-- | Logical disjunction. Discards state if False
(\/) :: State env Bool
-> State env Bool
-> State env Bool
a \/ b = StateT $ \env ->
let (ar, as) = runState a env
~(br, bs) = runState b env
in if ar then pure (ar, as)
else if br then pure (br, bs)
else pure (False, env)
checkOr :: [State env Bool]
-> State env Bool
checkOr = foldl' (\/) (pure False)