module Language.Clafer.IG.ClaferIG (
claferIGVersion,
IGArgs(..),
ClaferIGEnv(..),
getClaferEnv,
getClaferIGArgs,
getConstraints,
getClaferModel,
getInfo,
getStrMap,
ClaferIGT(..),
Instance(..),
Counterexample(..),
runClaferIGT,
getAlloyModel,
solve,
getClafers,
getGlobalScope,
getBitwidth,
setGlobalScope,
getScopes,
getScope,
getQNameMaps,
valueOfScope,
increaseScope,
setScope,
setAlloyScope,
next,
setUnsatCoreMinimization,
setBitwidth,
quit,
reload,
findRemovable,
fst3,
getlineNumMap,
strictReadFile,
sigToClaferName) where
import Language.Clafer
import Language.Clafer.QNameUID
import Language.ClaferT
import Language.Clafer.Intermediate.Intclafer
import qualified Language.Clafer.Intermediate.Analysis as Analysis
import Language.Clafer.IG.AlloyIGInterface (AlloyIGT)
import qualified Language.Clafer.IG.AlloyIGInterface as AlloyIG
import Language.Clafer.IG.ClaferModel
import Language.Clafer.IG.Constraints
import Language.Clafer.IG.Solution
import Language.Clafer.IG.Sugarer
import Control.Applicative
import Control.Monad
import Control.Monad.Error
import Control.Monad.Trans.State.Strict
import Data.List
import Data.Monoid
import Data.Tuple (swap)
import Data.Map as Map hiding (map, null)
import qualified Data.Sequence as Seq
import Data.Maybe
import Data.Data
import System.Console.Haskeline.MonadException
import Paths_claferIG (version)
import Data.Version (showVersion)
claferIGVersion :: String
claferIGVersion = "ClaferIG " ++ showVersion Paths_claferIG.version
data IGArgs = IGArgs {
all :: Maybe Integer,
saveDir :: Maybe FilePath,
claferModelFile :: FilePath,
alloySolution :: Bool,
bitwidth :: Integer,
maxInt :: Integer,
useUids :: Bool,
addTypes :: Bool,
json :: Bool,
flatten_inheritance_comp :: Bool,
no_layout_comp :: Bool,
check_duplicates_comp :: Bool,
skip_resolver_comp :: Bool,
scope_strategy_comp :: ScopeStrategy
} deriving (Show, Data, Typeable)
newtype ClaferIGT m a = ClaferIGT (StateT ClaferIGEnv (AlloyIGT m) a)
deriving (Applicative, Functor, Monad, MonadIO)
deriving instance MonadException m => MonadException (ClaferIGT m)
instance MonadTrans ClaferIGT where
lift = ClaferIGT . lift . lift
fetch :: Monad m => ClaferIGT m ClaferIGEnv
fetch = ClaferIGT get
fetches :: Monad m => (ClaferIGEnv -> a) -> ClaferIGT m a
fetches = ClaferIGT . gets
set :: Monad m => ClaferIGEnv -> ClaferIGT m ()
set = ClaferIGT . put
runClaferIGT :: MonadIO m => IGArgs -> ClaferIGT m a -> m (Either ClaferErrs a)
runClaferIGT args run =
AlloyIG.runAlloyIGT $ runErrorT $ do
env <- (ErrorT $ load args) `catchError` (\x -> lift AlloyIG.sendQuitCommand >> throwError x)
lift $ evalStateT (unwrap run) env
where
unwrap (ClaferIGT c) = c
data ClaferIGEnv = ClaferIGEnv{
claferEnv'::ClaferEnv,
claferIGArgs :: IGArgs,
constraints:: [Constraint],
claferModel:: String,
qNameMaps :: QNameMaps,
info :: Analysis.Info,
strMap :: Map Int String,
lineNumMap :: Map Integer String
}
data Instance =
Instance {modelInstance::ClaferModel, alloyModelInstance::String} |
UnsatCore {unsatConstraints::[Constraint], counterexample::Maybe Counterexample} |
NoInstance
data Counterexample = Counterexample {removedConstraints::[Constraint], counterexampleInstance::ClaferModel, counterexampleAlloyInstance::String}
getClaferEnv :: Monad m => ClaferIGT m ClaferEnv
getClaferEnv = fetches claferEnv'
getlineNumMap :: Monad m => ClaferIGT m (Map Integer String)
getlineNumMap = fetches lineNumMap
getClaferIGArgs :: Monad m => ClaferIGT m IGArgs
getClaferIGArgs = fetches claferIGArgs
getConstraints :: Monad m => ClaferIGT m [ Constraint ]
getConstraints = fetches constraints
getClaferModel :: Monad m => ClaferIGT m String
getClaferModel = fetches claferModel
getStrMap :: Monad m => ClaferIGT m (Map Int String)
getStrMap = fetches strMap
getInfo :: Monad m => ClaferIGT m Analysis.Info
getInfo = fetches info
load :: MonadIO m => IGArgs -> AlloyIGT m (Either ClaferErrs ClaferIGEnv)
load igArgs =
runErrorT $ do
claferModel <- liftIO $ strictReadFile claferFile'
(claferEnv', alloyModel, mapping, sMap) <- ErrorT $ return $ callClaferTranslator claferModel
let ir = fst3 $ fromJust $ cIr claferEnv'
let constraints = parseConstraints claferModel ir mapping
lift $ AlloyIG.sendLoadCommand alloyModel
lift $ AlloyIG.sendSetBitwidthCommand bitwidth'
let qNameMaps = deriveQNameMaps ir
let info = Analysis.gatherInfo ir
let irTrace = editMap $ irModuleTrace claferEnv'
return $ ClaferIGEnv claferEnv' igArgs constraints claferModel qNameMaps info sMap irTrace
where
editMap :: (Map.Map Span [Ir]) -> (Map.Map Integer String)
editMap =
fromList . removeConstraints . Data.List.foldr (\(num, ir) acc -> case (getIClafer ir) of
Just (IClafer _ _ _ _ uid' _ _ _ _) -> (num, uid') : acc
_ -> acc) [] . tail . (Data.List.foldr (\((Span (Pos l1 _) (Pos l2 _)), irs) acc -> (zip [l1..l2] (replicate (fromIntegral $ l2 l1 + 1) irs)) ++ acc) []) . toList
getIClafer :: [Ir] -> Maybe IClafer
getIClafer [] = Nothing
getIClafer ((IRClafer c):_) = Just c
getIClafer (_:rs) = getIClafer rs
removeConstraints :: [(Integer, String)] -> [(Integer, String)]
removeConstraints = map swap . reverse . toList . fromList . reverse . map swap
callClaferTranslator code =
mapLeft ClaferErrs $ runClafer claferArgs $ do
addModuleFragment code
parse
compile
results <- generate
let (Just alloyResult) = Map.lookup Alloy42 results
return (claferEnv alloyResult, outputCode alloyResult, mappingToAlloy alloyResult, stringMap alloyResult)
mapLeft f (Left l) = Left $ f l
mapLeft _ (Right r) = Right r
claferArgs = defaultClaferArgs{mode = [Alloy42], keep_unused = True, no_stats = True, skip_goals = True ,flatten_inheritance = flatten_inheritance_comp igArgs, no_layout = no_layout_comp igArgs, check_duplicates = check_duplicates_comp igArgs, skip_resolver = skip_resolver_comp igArgs, scope_strategy = scope_strategy_comp igArgs}
claferFile' = claferModelFile igArgs
bitwidth' = bitwidth igArgs
strictReadFile :: FilePath -> IO String
strictReadFile filePath =
do
contents <- readFile filePath
mapM_ return contents
return contents
getAlloyModel :: MonadIO m => ClaferIGT m String
getAlloyModel = ClaferIGT $ lift AlloyIG.getAlloyModel
solve :: MonadIO m => ClaferIGT m ()
solve = ClaferIGT $ lift AlloyIG.sendResolveCommand
getClafers :: MonadIO m => ClaferIGT m [String]
getClafers =
do
sigs <- ClaferIGT $ lift AlloyIG.getSigs
return $ map sigToClaferName sigs
getGlobalScope :: MonadIO m => ClaferIGT m Integer
getGlobalScope = ClaferIGT $ lift AlloyIG.getGlobalScope
getBitwidth :: MonadIO m => ClaferIGT m Integer
getBitwidth =
do
claferIGArgs' <- getClaferIGArgs
return $ bitwidth claferIGArgs'
setGlobalScope :: MonadIO m => Integer -> ClaferIGT m ()
setGlobalScope scope = ClaferIGT $ lift $ AlloyIG.sendSetGlobalScopeCommand scope
getScopes :: MonadIO m => ClaferIGT m [ (String, Integer) ]
getScopes = ClaferIGT $ lift AlloyIG.getScopes
getScope :: MonadIO m => QName -> ClaferIGT m ([String])
getScope qName = do
qNameMaps' <- fetches qNameMaps
return $ getUIDs qNameMaps' qName
getQNameMaps :: MonadIO m => ClaferIGT m (QNameMaps)
getQNameMaps = fetches qNameMaps
valueOfScope :: MonadIO m => String -> ClaferIGT m Integer
valueOfScope sigName = ClaferIGT $ lift $ AlloyIG.getScope sigName
increaseScope :: MonadIO m => Integer -> (String, Integer) -> ClaferIGT m (Either String ())
increaseScope increment (sigName, value) = setAlloyScope (value + increment) sigName
setScope :: MonadIO m => Integer -> (String, Integer) -> ClaferIGT m (Either String ())
setScope value (sigName, _) = setAlloyScope value sigName
setAlloyScope :: MonadIO m => Integer -> String -> ClaferIGT m (Either String ())
setAlloyScope value sigName =
do
subset <- ClaferIGT $ lift $ AlloyIG.sendSetScopeCommand sigName value
runErrorT $ maybe (return ()) throwError $ sigToClaferName <$> subset
next :: MonadIO m => ClaferIGT m Instance
next = do
env <- getClaferEnv
claferIGArgs' <- getClaferIGArgs
let
useUids' = useUids claferIGArgs'
addTypes' = addTypes claferIGArgs'
constraints' <- getConstraints
info' <- getInfo
xmlSolution <- ClaferIGT $ lift AlloyIG.sendNextCommand
sMap <- getStrMap
case xmlSolution of
Just xml -> return $ Instance (xmlToModel useUids' addTypes' info' xml sMap) xml
Nothing -> do
ClaferIGT $ lift AlloyIG.sendSaveStateCommand
AlloyIG.UnsatCore core <- ClaferIGT $ lift AlloyIG.sendUnsatCoreCommand
c <- counterexample env core useUids' addTypes' info' constraints' sMap
ClaferIGT $ lift AlloyIG.sendRestoreStateCommand
return c
where
counterexample env' originalCore useUids'' addTypes'' info'' constraints' sMap = counterexample' env' originalCore [] useUids'' addTypes'' info'' constraints' sMap
where
counterexample' env'' core removed useUids''' addTypes''' info''' constraints'' sMap' =
case msum $ findRemovable env'' core constraints'' of
Just remove -> do
ClaferIGT $ lift $ AlloyIG.sendRemoveConstraintCommand $ range remove
ClaferIGT $ lift AlloyIG.sendResolveCommand
xmlSolution <- ClaferIGT $ lift AlloyIG.sendNextCommand
case xmlSolution of
Just xml -> return $
UnsatCore (catMaybes $ findRemovable env'' core constraints'') (Just $ Counterexample (reverse $ remove : removed) (xmlToModel useUids''' addTypes''' info''' xml sMap') xml)
Nothing ->
do
AlloyIG.UnsatCore core' <- ClaferIGT $ lift AlloyIG.sendUnsatCoreCommand
counterexample' env'' core' (remove : removed) useUids''' addTypes''' info''' constraints' sMap'
Nothing ->
return NoInstance
xmlToModel :: Bool -> Bool -> Analysis.Info -> String -> (Map Int String) -> ClaferModel
xmlToModel useUids' addTypes' info' xml sMap = (sugarClaferModel useUids' addTypes' (Just info') $ buildClaferModel $ parseSolution xml) sMap
reload :: MonadIO m => ClaferIGT m (Either ClaferErrs ())
reload =
runErrorT $ do
globalScope <- lift $ getGlobalScope
claferIGArgs' <- lift $ getClaferIGArgs
env <- ErrorT $ ClaferIGT $ lift $ load claferIGArgs'
lift $ set env
lift $ setGlobalScope globalScope
setUnsatCoreMinimization :: MonadIO m => Integer -> ClaferIGT m ()
setUnsatCoreMinimization level = ClaferIGT $ lift $ AlloyIG.sendSetUnsatCoreMinimizationCommand level
setBitwidth :: MonadIO m => Integer -> ClaferIGT m ()
setBitwidth bitwidth' = do
claferIGArgs' <- getClaferIGArgs
igEnv <- fetch
set igEnv{claferIGArgs = claferIGArgs'{bitwidth=bitwidth'}}
ClaferIGT $ lift $ AlloyIG.sendSetBitwidthCommand bitwidth'
quit :: MonadIO m => ClaferIGT m ()
quit = ClaferIGT $ lift AlloyIG.sendQuitCommand
sigToClaferName :: String -> String
sigToClaferName n =
case snd $ break ('_' ==) n of
[] -> n
x -> tail x
findRemovable :: ClaferEnv -> [Span] -> [Constraint] -> [Maybe Constraint]
findRemovable env core constraints' =
let absIDs = foldMapIR getId $ fst3 $ fromJust $ cIr env
in Data.List.filter (removeAbsZero absIDs ) $ map (\c -> find ((== c) . range) constraints') core
where
removeAbsZero :: (Seq.Seq String) -> Maybe Constraint -> Bool
removeAbsZero absIDs (Just (UpperCardinalityConstraint _ (ClaferInfo uID (Cardinality 0 (Just 0))))) = ((Seq.elemIndexL uID absIDs)==Nothing)
removeAbsZero _ _ = True
getId :: Ir -> (Seq.Seq String)
getId (IRClafer (IClafer _ True _ uID _ _ _ _ _)) = Seq.singleton uID
getId _ = mempty
fst3 :: (IModule, GEnv, Bool) -> IModule
fst3 (imod, _, _) = imod