packages feed

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