{-# LANGUAGE OverloadedStrings #-}
module Language.Alloy.Functions (
getSingle, getDouble, getTriple,
lookupSig,
objectName,
relToMap,
scoped, unscoped,
) where
import qualified Data.Set as S (fromList, size, toList)
import qualified Data.Map as M (fromList, lookup)
import Control.Monad.Except (MonadError, throwError)
import Data.Function (on)
import Data.List (groupBy)
import Data.Map (Map)
import Data.Set (Set)
import Data.String (IsString, fromString)
import Language.Alloy.Types (
AlloyInstance,
AlloySig,
Entry (..),
Object (..),
Relation (..),
Signature (..),
)
scoped :: String -> String -> Signature
scoped = Signature . Just
unscoped :: String -> Signature
unscoped = Signature Nothing
getSingle
:: (IsString s, MonadError s m)
=> String
-> AlloySig
-> m (Set Object)
getSingle = lookupRel single
getDouble
:: (IsString s, MonadError s m)
=> String
-> AlloySig
-> m (Set (Object, Object))
getDouble = lookupRel double
getTriple
:: (IsString s, MonadError s m)
=> String
-> AlloySig
-> m (Set (Object, Object, Object))
getTriple = lookupRel triple
binaryToMap :: (Ord k, Ord v) => Set (k, v) -> Map k (Set v)
binaryToMap bin = M.fromList
[(fst (head gs), S.fromList $ snd <$> gs)
| gs <- groupBy ((==) `on` fst) $ S.toList bin]
relToMap
:: (IsString s, MonadError s m, Ord k, Ord v)
=> (a -> (k, v))
-> Set a
-> m (Map k (Set v))
relToMap f rel
| S.size map' == length rel' = return $ binaryToMap map'
| otherwise =
throwError "relToMap: The performed transformation is not injective."
where
rel' = S.toList rel
map' = S.fromList $ fmap f rel'
lookupRel
:: (IsString s, MonadError s m)
=> (Relation a -> m b)
-> String
-> Entry Map a
-> m b
lookupRel kind rel e = case M.lookup rel (relation e) of
Nothing -> throwError $ fromString $ "relation " ++ fromString rel
++ " is missing in the Alloy instance"
Just r -> kind r
lookupSig
:: (IsString s, MonadError s m)
=> Signature
-> AlloyInstance
-> m AlloySig
lookupSig s insta = case M.lookup s insta of
Nothing -> throwError $ fromString $ maybe "" (++ "/") (scope s) ++ sigName s
++ " is missing in the Alloy instance"
Just e -> return e
objectName :: Object -> String
objectName o = case o of
Object s n -> s ++ '$' : show n
NumberObject n -> show n
NamedObject n -> n
single
:: (IsString s, MonadError s m, Monoid (a Object))
=> Relation a
-> m (a Object)
single EmptyRelation = return mempty
single (Single r) = return r
single _ = throwError "Relation is (unexpectedly) a mapping"
double
:: (IsString s, MonadError s m, Monoid (a (Object, Object)))
=> Relation a
-> m (a (Object, Object))
double EmptyRelation = return mempty
double (Double r) = return r
double _ = throwError "Relation is not a binary mapping"
triple
:: (IsString s, MonadError s m, Monoid (a (Object, Object, Object)))
=> Relation a
-> m (a (Object, Object, Object))
triple EmptyRelation = return mempty
triple (Triple r) = return r
triple _ = throwError "Relation is not a ternary mapping"