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
31import Type hiding (pprTyThing)
34data ScriptSession = ScriptSession {
36 atomFunctionBodyType :: Type,
37 dbcFunctionBodyType :: Type
40newtype ScriptSessionError = ScriptSessionLoadError GhcException
43-- | Configure a GHC environment/session which we will use for all script compilation.
44initScriptSession :: [String] -> IO (Either ScriptSessionError ScriptSession)
45initScriptSession ghcPkgPaths = do
46 --for the sake of convenience, for developers' builds, include the local cabal sandbox package database and the cabal new-build package database
47 eHomeDir <- tryJust (guard . isDoesNotExistError) getHomeDirectory
48 let homeDir = either (const "/") id eHomeDir
49 let excHandler exc = pure $ Left (ScriptSessionLoadError exc)
50 handleGhcException excHandler $ runGhc (Just libdir) $ do
51 dflags <- getSessionDynFlags
52 let ghcVersion = projectVersion dflags
54 sandboxPkgPaths <- liftIO $ concat <$> mapM glob [
55 "./dist-newstyle/packagedb/ghc-" ++ ghcVersion,
56 ".cabal-sandbox/*ghc-" ++ ghcVersion ++ "-packages.conf.d",
57 ".stack-work/install/*/*/" ++ ghcVersion ++ "/pkgdb",
58 ".stack-work/install/*/pkgdb/", --windows stack build
59 "C:/sr/snapshots/b201cfe6/pkgdb", --windows stack build- ideally, we could run `stack path --snapshot-pkg-db, but this is sufficient to pass CI
60 homeDir </> ".stack/snapshots/*/*/" ++ ghcVersion ++ "/pkgdb",
61 homeDir </> ".cabal/store/ghc-" ++ ghcVersion ++ "/package.db"
64 let localPkgPaths = map PkgConfFile (ghcPkgPaths ++ sandboxPkgPaths)
66 let dflags' = applyGopts . applyXopts $ dflags { hscTarget = HscInterpreted ,
67 ghcLink = LinkInMemory,
68 safeHaskell = Sf_Trustworthy,
72#if __GLASGOW_HASKELL__ >= 800
73 trustFlags = map TrustPackage required_packages,
75 packageFlags = packageFlags dflags ++ packages,
76#if __GLASGOW_HASKELL__ >= 802
77 packageDBFlags = map PackageDB localPkgPaths
79 extraPkgConfs = const (localPkgPaths ++ [UserPkgConf, GlobalPkgConf])
83 applyGopts flags = foldl gopt_set flags gopts
84 applyXopts flags = foldl xopt_set flags xopts
85#if __GLASGOW_HASKELL__ >= 800
86 xopts = [OverloadedStrings, ExtendedDefaultRules, ImplicitPrelude, ScopedTypeVariables]
88 xopts = [Opt_OverloadedStrings, Opt_ExtendedDefaultRules, Opt_ImplicitPrelude, Opt_ScopedTypeVariables]
90 gopts = [] --[Opt_DistrustAllPackages, Opt_PackageTrust]
91 required_packages = ["base",
95 "unordered-containers",
101 "vector-binary-instances",
105#if __GLASGOW_HASKELL__ >= 800
106 packages = map (\m -> ExposePackage ("-package " ++ m) (PackageArg m) (ModRenaming True [])) required_packages
108 packages = map TrustPackage required_packages
110 --liftIO $ traceShowM (showSDoc dflags' (ppr packages))
111 _ <- setSessionDynFlags dflags'
112 let safeImportDecl mn mQual = ImportDecl {
113#if __GLASGOW_HASKELL__ >= 802
114 ideclSourceSrc = NoSourceText,
116 ideclSourceSrc = Nothing,
118 ideclName = noLoc mn,
119 ideclPkgQual = Nothing,
122 ideclImplicit = False,
123 ideclQualified = isJust mQual,
125 ideclHiding = Nothing
127 unqualifiedModules = map (\modn -> IIDecl $ safeImportDecl (mkModuleName modn) Nothing) [
131 "Data.Time.Calendar",
132 "Control.Monad.State",
134 "ProjectM36.Relation",
135 "ProjectM36.AtomFunctionError",
136 "ProjectM36.DatabaseContextFunctionError",
137 "ProjectM36.DatabaseContextFunctionUtils",
138 "ProjectM36.RelationalExpression"]
139#if __GLASGOW_HASKELL__ >= 802
140 mkModName = noLoc . mkModuleName
142 mkModName = mkModuleName
144 qualifiedModules = map (\(modn, qualNam) -> IIDecl $ safeImportDecl (mkModuleName modn) (Just (mkModName qualNam))) [
147 setContext (unqualifiedModules ++ qualifiedModules)
149 atomFuncType <- mkTypeForName "AtomFunctionBodyType"
150 dbcFuncType <- mkTypeForName "DatabaseContextFunctionBodyType"
151 pure (Right (ScriptSession env atomFuncType dbcFuncType))
153addImport :: String -> Ghc ()
154addImport moduleNam = do
156 setContext ( (IIDecl $ simpleImportDecl (mkModuleName moduleNam)) : ctx )
158showType :: DynFlags -> Type -> String
159showType dflags ty = showSDocForUser dflags alwaysQualify (pprTypeForUser ty)
161mkTypeForName :: String -> Ghc Type
162mkTypeForName name = do
163 lBodyName <- parseName name
165 [] -> error ("failed to parse " ++ name)
166 _:_:_ -> error "too many name matches"
168 mThing <- lookupName bodyName
170 Nothing -> error ("failed to find " ++ name)
171 Just (ATyCon tyCon) -> case synTyConRhs_maybe tyCon of
173 Nothing -> error (name ++ " is not a type synonym")
174 Just _ -> error ("failed to find type synonym " ++ name)
176compileScript :: Type -> Text -> Ghc (Either ScriptCompilationError a)
177compileScript funcType script = do
178 let sScript = unpack script
179 mErr <- typeCheckScript funcType script
181 Just err -> pure (Left err)
183 --catch exception here
184 --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.
185 -- we can't use dynCompileExpr here because
186 func <- compileExpr sScript
187 pure $ Right (unsafeCoerce func)
189typeCheckScript :: Type -> Text -> Ghc (Maybe ScriptCompilationError)
190typeCheckScript expectedType inp = do
191 dflags <- getSessionDynFlags
192 --catch exception for SyntaxError
193#if __GLASGOW_HASKELL__ >= 802
194 funcType <- GHC.exprType TM_Inst (unpack inp)
196 funcType <- GHC.exprType (unpack inp)
198 --liftIO $ putStrLn $ showType dflags expectedType ++ ":::" ++ showType dflags funcType
199 if eqType funcType expectedType then
202 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