{-| Module : MetaHS.EDSL.MetaModel Description : The MetaHS EDSL MetaModel part Copyright : Copyright (C) 2017-2019 H.H.R.F. Vos, S. Kamps License : -- This file is distributed under the terms of the Apache License 2.0. For more information, see the file "LICENSE", which is included in the distribution. Stability : experimental MetaHS EDSL MetaModel part -} module MetaHS.EDSL.MetaModel ( generateMetaModel , writeMetaModel , readMetaModel , writeMetaModelPretty , pretty , numberOfItems , getPrograms , getModules , programContains , moduleContains , moduleImports , elementContains , elementImports , elementSource , elementUses , getRelation , setRelation , domain , range , RelationKey ) where import qualified Data.Map.Strict as Map import Data.Maybe (listToMaybe) import qualified Data.Set as Set import qualified MetaHS.DataModel.Extractor.Module.Contains as ModuleContains import qualified MetaHS.DataModel.Extractor.Module.Imports as ModuleImports import qualified MetaHS.DataModel.Extractor.Module.Source as ModuleSource import qualified MetaHS.DataModel.Extractor.Module.Uses as ModuleUses import qualified MetaHS.DataModel.Extractor.Program.Contains as ProgramContains import qualified MetaHS.DataModel.MetaModel as MetaModel import MetaHS.DataModel.Utils.File.FileUtils import Text.PrettyPrint -- | convenience type that represents the metamodel aggregation relation type RelationKey = String keyContains :: RelationKey keyContains = "_contains" keyUses :: RelationKey keyUses = "_uses" keySource :: RelationKey keySource = "_source" keyImports :: RelationKey keyImports = "_imports" -- | Generates a meta-model. generateMetaModel :: String -- ^ The program name. -> String -- ^ The program path. -> String -- ^ The parse error output path. -> IO MetaModel.MetaModel -- ^ The resulting meta-model. generateMetaModel programName programPath parseErrorsPath = do (mods, failed) <- modulesInHierarchy programPath -- mods = modules, failed = ParseFailed writeFile parseErrorsPath $ show $ map show failed let pc = ProgramContains.contains programName mods -- pc = program contains let mc = Set.unions [ModuleContains.contains m | m <- mods] -- mc = module contains let mu = Set.unions [ModuleUses.uses m mc | m <- mods] -- mu = module uses let ms = Set.unions [ModuleSource.source m | m <- mods] -- ms = modules source let im = Set.unions [ModuleImports.imports m | m <- mods] -- im = module imports return . MetaModel.MetaModel $ Map.insert keyContains (Set.union pc mc) $ Map.insert keyUses mu $ Map.insert keySource ms $ Map.insert keyImports im Map.empty -- | Write a meta-model to a file. writeMetaModel :: MetaModel.MetaModel -- ^ The meta-model to write. -> String -- ^ The path of the file to write. -> IO () -- ^ No result. writeMetaModel mm path = writeFile path $ show mm -- | Read a meta-model from a file. readMetaModel :: String -- ^ The path of the file to read. -> IO MetaModel.MetaModel -- ^ The meta-model read. readMetaModel path = do s <- readFile path return $ read s -- | Write a meta-model to a file. writeMetaModelPretty :: MetaModel.MetaModel -- ^ The meta-model to write. -> String -- ^ The path of the file to write. -> IO () -- ^ No result. writeMetaModelPretty mm path = writeFile path $ renderStyle s $ pretty mm where s = style { mode = LeftMode } -- | Returns a pretty printed representation of the meta-model. pretty :: MetaModel.MetaModel -- ^ The meta-model to pretty print. -> Doc -- ^ The resulting Doc. pretty = MetaModel.pPrint -- | Returns the number of relation items in the meta-model. numberOfItems :: MetaModel.MetaModel -- ^ The meta-model. -> Int -- ^ The number of relation items in the meta-model. numberOfItems mm = Map.foldr f 0 $ MetaModel.getMetaModelImpl mm where f :: MetaModel.Relation -> Int -> Int f r t = t + Set.size r -- | Returns a list of Programs contained in the metamodel. getPrograms :: MetaModel.MetaModel -- ^ The metamodel. -> [MetaModel.Element] -- ^ The Programs contained in the metamodel. getPrograms mm = Set.elems $ Set.foldr f Set.empty pcs where f (p@MetaModel.Program{},_) es = Set.insert p es f _ es = es pcs = getRelation keyContains mm -- | Returns a list of Modules contained in the metamodel. getModules :: MetaModel.MetaModel -- ^ The metamodel. -> [MetaModel.Element] -- ^ The Modules contained by the specified Program. getModules mm = Set.elems $ Set.foldr f Set.empty pcs where f (p,c) ms | isProgram p && isModule c = Set.insert c ms | otherwise = ms pcs = getRelation keyContains mm -- | Returns a list of Elements contained by the specified Program. programContains :: MetaModel.MetaModel -- ^ The meta-model. -> MetaModel.Element -- ^ The specified Program. -> [MetaModel.Element] -- ^ The Elements contained by the specified Program. programContains mm e | isProgram e = elementContains mm e | otherwise = error "Function MetaHS.EDSL.MetaModel.programContains only works for MetaHS.DataModel.MetaModel.Program Elements!" -- | Returns a list of Elements contained by the specified Module. moduleContains :: MetaModel.MetaModel -- ^ The meta-model. -> MetaModel.Element -- ^ The specified Module. -> [MetaModel.Element] -- ^ The Elements contained by the specified Module. moduleContains mm e | isModule e = elementContains mm e | otherwise = error "Function MetaHS.EDSL.MetaModel.moduleContains only works for MetaHS.DataModel.MetaModel.Module Elements!" -- | Returns a list of Elements imported by the specified Module. moduleImports :: MetaModel.MetaModel -- ^ The meta-model. -> MetaModel.Element -- ^ The specified Module. -> [MetaModel.Element] -- ^ The Elements contained by the specified Module. moduleImports mm e | isModule e = elementImports mm e | otherwise = error "Function MetaHS.EDSL.MetaModel.moduleContains only works for MetaHS.DataModel.MetaModel.Module Elements!" -- | Returns a list of Elements contained by the specified Element. elementContains :: MetaModel.MetaModel -- ^ The meta-model. -> MetaModel.Element -- ^ The specified Element. -> [MetaModel.Element] -- ^ The Elements contained by the specified Element. elementContains mm e = Set.foldr f [] pcs where f (p,c) es | p == e = c : es | otherwise = es pcs = getRelation keyContains mm -- | Returns a list of Elements imported by the specified Element. elementImports :: MetaModel.MetaModel -- ^ The meta-model. -> MetaModel.Element -- ^ The specified Element. -> [MetaModel.Element] -- ^ The Elements contained by the specified Element. elementImports mm e = Set.foldr f [] pcs where f (p,c) es | p == e = c : es | otherwise = es pcs = getRelation keyImports mm -- | Returns a the source location for the specified Element. elementSource :: MetaModel.MetaModel -- ^ The meta-model. -> MetaModel.Element -- ^ The specified Element. -> Maybe MetaModel.Element -- ^ The source location for the specified Element. elementSource mm e = listToMaybe $ Set.foldr f [] pcs where f (p,c) es | p == e = c : es | otherwise = es pcs = getRelation keySource mm -- | Returns a list of Elements used by the specified Element. elementUses :: MetaModel.MetaModel -- ^ The meta-model. -> MetaModel.Element -- ^ The specified Element. -> [MetaModel.Element] -- ^ The Elements used by the specified Element. elementUses mm e = Set.foldr f [] pcs where f (p,c) es | p == e = c : es | otherwise = es pcs = getRelation keyUses mm -- | Returns the relation corresponding to the supplied key or an empty Set. getRelation :: String -- ^ The metamodel key string. -> MetaModel.MetaModel -- ^ The metamodel. -> MetaModel.Relation -- ^ The corresponding Relation or an empty set. getRelation k mm = Map.findWithDefault Set.empty k $ MetaModel.getMetaModelImpl mm -- | Returns a MetaModel that is appended with a new or replaced Relation set. setRelation :: String -- ^ The metamodel key string. -> MetaModel.Relation -- ^ The Relation to add. -> MetaModel.MetaModel -- ^ The metamodel. -> MetaModel.MetaModel -- ^ The resulting metamodel. setRelation k rs mm = MetaModel.MetaModel $ Map.insert k rs mmi where mmi = MetaModel.getMetaModelImpl mm -- | Returns the domain for a specified relation in the metamodel. domain :: String -- ^ The metamodel key string. -> MetaModel.MetaModel -- ^ The metamodel. -> Set.Set MetaModel.Element -- ^ Set of Elements that form the domain of the binary relation. domain k mm = foldr f Set.empty r where f a = Set.insert (fst a) r = getRelation k mm -- | Returns the range for a specified relation in the metamodel. range :: String -- ^ The metamodel key string. -> MetaModel.MetaModel -- ^ The metamodel. -> Set.Set MetaModel.Element -- ^ Set of Elements that form the range of the binary relation. range k mm = foldr f Set.empty r where f a = Set.insert (snd a) r = getRelation k mm -- | Checks that the provided MetaModel.Element is a MetaModel.Program isProgram :: MetaModel.Element -> Bool isProgram MetaModel.Program{} = True isProgram _ = False -- | Checks that the provided MetaModel.Element is a MetaModel.Module isModule :: MetaModel.Element -> Bool isModule MetaModel.Module{} = True isModule _ = False