hydra-0.12.0: src/main/haskell/Hydra/Generation.hs
-- | Entry point for Hydra code generation utilities
module Hydra.Generation where
import Hydra.Kernel
import Hydra.Dsl.Annotations
import Hydra.Dsl.Bootstrap
import Hydra.Ext.Haskell.Coder
import Hydra.Ext.Org.Json.Coder
import Hydra.Staging.Yaml.Modules
import Hydra.Sources.Libraries
import qualified Control.Monad as CM
import qualified System.FilePath as FP
import qualified Data.List as L
import qualified Data.Map as M
import qualified System.Directory as SD
import qualified Data.Maybe as Y
-- TODO: deprecated
generateSources :: (Module -> Flow Graph (M.Map FilePath String)) -> FilePath -> [Module] -> IO ()
generateSources printModule basePath mods = do
mfiles <- runFlow bootstrapGraph generateFiles
case mfiles of
Nothing -> fail "Transformation failed"
Just files -> mapM_ writePair files
where
generateFiles = withTrace "generate files" $ withState (modulesToGraph mods) $ do
g <- getState
g1 <- inferGraphTypes g
withState g1 $ do
maps <- CM.mapM forModule $ refreshModule (graphElements g1) <$> mods
return $ L.concat (M.toList <$> maps)
where
refreshModule els mod = mod {
moduleElements = Y.catMaybes ((\e -> M.lookup (bindingName e) els) <$> moduleElements mod)}
writePair (path, s) = do
let fullPath = FP.combine basePath path
SD.createDirectoryIfMissing True $ FP.takeDirectory fullPath
writeFile fullPath withNewline
where
withNewline = if L.isSuffixOf "\n" s then s else s ++ "\n"
forModule mod = withTrace ("module " ++ unNamespace (moduleNamespace mod)) $ printModule mod
generateSourcesSimple :: (Module -> [Definition] -> Flow Graph (M.Map FilePath String)) -> Language -> Bool
-> FilePath -> [Module] -> IO ()
generateSourcesSimple printDefinitions lang doExpand basePath mods = do
mschemaFiles <- runFlow bootstrapGraph generateSchemaFiles
case mschemaFiles of
Nothing -> fail "Failed to generate schema files"
Just files -> mapM_ writePair files
mdataFiles <- runFlow bootstrapGraph generateDataFiles
case mdataFiles of
Nothing -> fail "Failed to generate data files"
Just files -> mapM_ writePair files
where
constraints = languageConstraints lang
isTypeElement el = case deannotateTerm (bindingTerm el) of
TermUnion inj -> injectionTypeName inj == _Type
_ -> False
-- Note: we assume that no module contains both type-level and term-level elements
isSchemaModule mod = not $ L.null $ L.filter isTypeElement $ moduleElements mod
(schemaModules, dataModules) = L.partition isSchemaModule mods
generateSchemaFiles = withTrace "generate schema files" $ do
(tmap, defLists) <- schemaGraphToDefinitions constraints g0 nameLists
withState g0 $ do
maps <- CM.zipWithM forEachModule schemaModules defLists
return $ L.concat (M.toList <$> maps)
where
g0 = modulesToGraph schemaModules
nameLists = fmap (fmap bindingName . moduleElements) schemaModules
forEachModule mod defs = withTrace ("schema module " ++ unNamespace (moduleNamespace mod)) $
printDefinitions mod (fmap DefinitionType defs)
generateDataFiles = withTrace "generate data files" $ do
(g1, defLists) <- dataGraphToDefinitions constraints doExpand g0 nameLists
withState g1 $ do
maps <- CM.zipWithM forEachModule dataModules defLists
return $ L.concat (M.toList <$> maps)
where
g0 = modulesToGraph dataModules
nameLists = fmap (fmap bindingName . moduleElements) dataModules
forEachModule mod defs = withTrace ("data module " ++ unNamespace (moduleNamespace mod)) $
printDefinitions mod (fmap DefinitionTerm defs)
writePair (path, s) = do
let fullPath = FP.combine basePath path
SD.createDirectoryIfMissing True $ FP.takeDirectory fullPath
writeFile fullPath withNewline
where
withNewline = if L.isSuffixOf "\n" s then s else s ++ "\n"
-- TODO: move into the kernel
modulesToGraph :: [Module] -> Graph
modulesToGraph mods = elementsToGraph parent (Just schemaGraph) dataElements
where
parent = bootstrapGraph
dataElements = L.concat (moduleElements <$> closedMods)
schemaElements = L.concat (moduleElements <$> (L.concat (moduleTypeDependencies <$> closedMods)))
schemaGraph = elementsToGraph bootstrapGraph Nothing schemaElements
closedMods = L.concat (close <$> mods)
close mod = mod:(L.concat (close <$> moduleTermDependencies mod))
printTrace :: Bool -> Trace -> IO ()
printTrace isError t = do
CM.unless (L.null $ traceMessages t) $ do
putStrLn $ if isError then "Flow failed. Messages:" else "Messages:"
putStrLn $ indentLines $ traceSummary t
runFlow :: s -> Flow s a -> IO (Maybe a)
runFlow s f = do
printTrace (Y.isNothing v) t
return v
where
FlowState v _ t = unFlow f s emptyTrace
writeHaskell :: FilePath -> [Module] -> IO ()
writeHaskell = generateSources moduleToHaskell
-- writeJson :: FP.FilePath -> [Module] -> IO ()
-- writeJson = generateSources Json.printModule
writeYaml :: FP.FilePath -> [Module] -> IO ()
writeYaml = generateSources moduleToYaml