ampersand-3.0.3: src/Database/Design/Ampersand/FSpec/ToFSpec/CreateFspec.hs
module Database.Design.Ampersand.FSpec.ToFSpec.CreateFspec
(createFSpec,getPopulationsFrom)
where
import Prelude hiding (putStrLn, writeFile) -- make sure everything is UTF8
import Database.Design.Ampersand.Basics
import Database.Design.Ampersand.Misc
import Database.Design.Ampersand.ADL1
import Database.Design.Ampersand.ADL1.P2A_Converters
import Database.Design.Ampersand.FSpec.FSpec
import Database.Design.Ampersand.FSpec.ShowMeatGrinder
import Database.Design.Ampersand.Input
import Database.Design.Ampersand.FSpec.ToFSpec.ADL2FSpec
import System.Directory
import System.FilePath
import Data.Traversable (sequenceA)
import Control.Applicative
import Database.Design.Ampersand.Core.ToMeta
import Control.Monad
fatal :: Int -> String -> a
fatal = fatalMsg "CreateFspec"
-- | create an FSpec, based on the provided command-line options.
createFSpec :: Options -- ^The options derived from the command line
-> IO(Guarded FSpec)
createFSpec opts =
do userP_Ctx <- parseADL opts (fileName opts) -- the P_Context of the user's sourceFile
genFiles userP_Ctx >> genTables userP_Ctx
where
genFiles :: Guarded P_Context -> IO(Guarded ())
genFiles uCtx
= case pCtx2Fspec uCtx of
Errors es -> return(Errors es)
Checked uFspec
-> when (genASTFile opts) (doGenMetaFile AST uFspec)
>> when (genGenericsFile opts) (doGenMetaFile Generics uFspec)
>> return (Checked ())
genTables :: Guarded P_Context -> IO(Guarded FSpec)
genTables uCtx= case whatTablesToCreateExtra of
Nothing
-> return (pCtx2Fspec uCtx)
Just mType
-> do rapP_Ctx <- getFormalFile mType -- the P_Context of the
let populationPctx = unguard ( grind mType <$> pCtx2Fspec uCtx)
populatedRapPctx = merge.sequenceA $ [rapP_Ctx,populationPctx]
metaPopulatedRapPctx = toMeta opts <$> populatedRapPctx
allCombinedPctx = merge.sequenceA $ [uCtx, metaPopulatedRapPctx]
return $ pCtx2Fspec allCombinedPctx -- the RAP specification that is populated with the user's 'things' is returned.
whatTablesToCreateExtra :: Maybe MetaType
whatTablesToCreateExtra
| genASTTables opts = Just AST
| genGenericTables opts = Just Generics
| otherwise = Nothing
getFormalFile :: MetaType -> IO(Guarded P_Context)
getFormalFile mType
= do let file = ampersandDataDir opts
</> "FormalAmpersand"
</> (case mType of
Generics -> "Generics.adl"
AST -> "FormalAmpersand.adl")
exists <- doesFileExist file
if exists then parseADL opts file
else fatal 98 $ unlines
[ "Ampersand isn't installed properly. Couldn't read:"
, " "++show file
, " (Make sure you have the latest content of Ampersand data. You might need to re-install ampersand...)"
]
toFspec :: A_Context -> Guarded FSpec
toFspec = pure . makeFSpec opts
pCtx2Fspec :: Guarded P_Context -> Guarded FSpec
pCtx2Fspec c = unguard $ toFspec <$> (unguard $ pCtx2aCtx opts <$> c)
merge :: Guarded [P_Context] -> Guarded P_Context
merge ctxs = fmap f ctxs
where
f [] = fatal 77 $ "merge must not be applied to an empty list"
f (c:cs) = foldr mergeContexts c cs
grind :: MetaType -> FSpec -> Guarded P_Context
grind mType fSpec
= fmap fstIfNoIncludes $ parseCtx f c
where (f,c) = makeMetaPopulationFile mType fSpec
fstIfNoIncludes (a,includes)
= case includes of
[] -> a
_ -> fatal 83 "Meatgrinder returns included file. That isn't anticipated."
getPopulationsFrom :: Options -> FilePath -> IO (Guarded [Population])
getPopulationsFrom opts filePath =
do gpCtx <- parseADL opts filePath
return (unguard $ f <$> gpCtx)
where
f :: P_Context -> Guarded [Population]
f pCtx = unguard $
pure . initialPops . makeFSpec opts
<$> pCtx2aCtx opts pCtx
doGenMetaFile :: MetaType -> FSpec -> IO()
doGenMetaFile mType fSpec =
do { verboseLn (getOpts fSpec) $ "Generating "++show mType++" meta file for "++name fSpec
; writeFile outputFile contents
; verboseLn (getOpts fSpec) $ show mType++" written into " ++ outputFile ++ ""
}
where outputFile = combine (dirOutput (getOpts fSpec)) $ fpath
(fpath,contents) = makeMetaPopulationFile mType fSpec