1module ProjectM36.ScriptSession where
5import Control.Exception
9import Control.Monad.IO.Class
10import System.FilePath.Glob
12import Data.Text (Text, unpack)
16import GHC.Paths (libdir)
17#if __GLASGOW_HASKELL__ >= 800
18import GHC.LanguageExtensions
20#if __GLASGOW_HASKELL__ >= 802
25import Outputable --hiding ((<>))
28#if __GLASGOW_HASKELL__ >= 802
30#elif __GLASGOW_HASKELL__ >= 710
31import Type hiding (pprTyThing)
35data ScriptSession = ScriptSession {
37 atomFunctionBodyType :: Type,
38 dbcFunctionBodyType :: Type
41newtype ScriptSessionError = ScriptSessionLoadError GhcException
44-- | Configure a GHC environment/session which we will use for all script compilation.
45initScriptSession :: [String] -> IO (Either ScriptSessionError ScriptSession)
46initScriptSession ghcPkgPaths = do
47 --for the sake of convenience, for developers' builds, include the local cabal sandbox package database and the cabal new-build package database
48 eHomeDir <- tryJust (guard . isDoesNotExistError) getHomeDirectory
49 let homeDir = either (const "/") id eHomeDir
50 let excHandler exc = pure $ Left (ScriptSessionLoadError exc)
51 handleGhcException excHandler $ runGhc (Just libdir) $ do
52 dflags <- getSessionDynFlags
53 let ghcVersion = projectVersion dflags
55 sandboxPkgPaths <- liftIO $ concat <$> mapM glob [
56 "./dist-newstyle/packagedb/ghc-" ++ ghcVersion,
57 ".cabal-sandbox/*ghc-" ++ ghcVersion ++ "-packages.conf.d",
58 ".stack-work/install/*/*/" ++ ghcVersion ++ "/pkgdb",
59 ".stack-work/install/*/pkgdb/", --windows stack build
60 "C:/sr/snapshots/b201cfe6/pkgdb", --windows stack build- ideally, we could run `stack path --snapshot-pkg-db, but this is sufficient to pass CI
61 homeDir </> ".stack/snapshots/*/*/" ++ ghcVersion ++ "/pkgdb",
62 homeDir </> ".cabal/store/ghc-" ++ ghcVersion ++ "/package.db"
65 let localPkgPaths = map PkgConfFile (ghcPkgPaths ++ sandboxPkgPaths)
67 let dflags' = applyGopts . applyXopts $ dflags { hscTarget = HscInterpreted ,
68 ghcLink = LinkInMemory,
69 safeHaskell = Sf_Trustworthy,
73#if __GLASGOW_HASKELL__ >= 800
74 trustFlags = map TrustPackage required_packages,
76 packageFlags = packageFlags dflags ++ packages,
77#if __GLASGOW_HASKELL__ >= 802
78 packageDBFlags = map PackageDB localPkgPaths
80 extraPkgConfs = const (localPkgPaths ++ [UserPkgConf, GlobalPkgConf])
84 applyGopts flags = foldl gopt_set flags gopts
85 applyXopts flags = foldl xopt_set flags xopts
86#if __GLASGOW_HASKELL__ >= 800
87 xopts = [OverloadedStrings, ExtendedDefaultRules, ImplicitPrelude, ScopedTypeVariables]
89 xopts = [Opt_OverloadedStrings, Opt_ExtendedDefaultRules, Opt_ImplicitPrelude, Opt_ScopedTypeVariables]
91 gopts = [] --[Opt_DistrustAllPackages, Opt_PackageTrust]
92 required_packages = ["base",
96 "unordered-containers",
102 "vector-binary-instances",
106#if __GLASGOW_HASKELL__ >= 800
107 packages = map (\m -> ExposePackage ("-package " ++ m) (PackageArg m) (ModRenaming True [])) required_packages
109 packages = map TrustPackage required_packages
111 --liftIO $ traceShowM (showSDoc dflags' (ppr packages))
112 _ <- setSessionDynFlags dflags'
113 let safeImportDecl mn mQual = ImportDecl {
114#if __GLASGOW_HASKELL__ >= 802
115 ideclSourceSrc = NoSourceText,
117 ideclSourceSrc = Nothing,
119 ideclName = noLoc mn,
120 ideclPkgQual = Nothing,
123 ideclImplicit = False,
124 ideclQualified = isJust mQual,
126 ideclHiding = Nothing
128 unqualifiedModules = map (\modn -> IIDecl $ safeImportDecl (mkModuleName modn) Nothing) [
132 "Data.Time.Calendar",
133 "Control.Monad.State",
135 "ProjectM36.Relation",
136 "ProjectM36.AtomFunctionError",
137 "ProjectM36.DatabaseContextFunctionError",
138 "ProjectM36.DatabaseContextFunctionUtils",
139 "ProjectM36.RelationalExpression"]
140#if __GLASGOW_HASKELL__ >= 802
141 mkModName = noLoc . mkModuleName
143 mkModName = mkModuleName
145 qualifiedModules = map (\(modn, qualNam) -> IIDecl $ safeImportDecl (mkModuleName modn) (Just (mkModName qualNam))) [
148 setContext (unqualifiedModules ++ qualifiedModules)
150 atomFuncType <- mkTypeForName "AtomFunctionBodyType"
151 dbcFuncType <- mkTypeForName "DatabaseContextFunctionBodyType"
152 pure (Right (ScriptSession env atomFuncType dbcFuncType))
154addImport :: String -> Ghc ()
155addImport moduleNam = do
157 setContext ( (IIDecl $ simpleImportDecl (mkModuleName moduleNam)) : ctx )
159showType :: DynFlags -> Type -> String
160showType dflags ty = showSDocForUser dflags alwaysQualify (pprTypeForUser ty)
162mkTypeForName :: String -> Ghc Type
163mkTypeForName name = do
164 lBodyName <- parseName name
166 [] -> error ("failed to parse " ++ name)
167 _:_:_ -> error "too many name matches"
169 mThing <- lookupName bodyName
171 Nothing -> error ("failed to find " ++ name)
172 Just (ATyCon tyCon) -> case synTyConRhs_maybe tyCon of
174 Nothing -> error (name ++ " is not a type synonym")
175 Just _ -> error ("failed to find type synonym " ++ name)
177compileScript :: Type -> Text -> Ghc (Either ScriptCompilationError a)
178compileScript funcType script = do
179 let sScript = unpack script
180 mErr <- typeCheckScript funcType script
182 Just err -> pure (Left err)
184 --catch exception here
185 --we could potentially wrap the script with Atom pattern matching so that the script doesn't have to do it, but the change to an Atom ADT should make it easier. Still, it would be nice if the script didn't have to handle a list of arguments, for example.
186 -- we can't use dynCompileExpr here because
187 func <- compileExpr sScript
188 pure $ Right (unsafeCoerce func)
190typeCheckScript :: Type -> Text -> Ghc (Maybe ScriptCompilationError)
191typeCheckScript expectedType inp = do
192 dflags <- getSessionDynFlags
193 --catch exception for SyntaxError
194#if __GLASGOW_HASKELL__ >= 802
195 funcType <- GHC.exprType TM_Inst (unpack inp)
197 funcType <- GHC.exprType (unpack inp)
199 --liftIO $ putStrLn $ showType dflags expectedType ++ ":::" ++ showType dflags funcType
200 if eqType funcType expectedType then
203 pure (Just (TypeCheckCompilationError (showType dflags expectedType) (showType dflags funcType)))
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list