content: -- |
  -- Type declarations and associated basic functions for PSCI.
  --
  module Language.PureScript.Interactive.Types
    ( PSCiConfig(..)
    , psciEnvironment
    , PSCiState -- constructor is not exported, to prevent psciImports and psciExports from
                -- becoming inconsistent with importedModules, letBindings and loadedExterns
    , ImportedModule
    , psciExports
    , psciImports
    , psciLoadedExterns
    , psciInteractivePrint
    , psciImportedModules
    , psciLetBindings
    , initialPSCiState
    , initialInteractivePrint
    , updateImportedModules
    , updateLoadedExterns
    , updateLets
    , setInteractivePrint
    , Command(..)
    , ReplQuery(..)
    , replQueries
    , replQueryStrings
    , showReplQuery
    , parseReplQuery
    , Directive(..)
    ) where
  
  import Prelude
  
  import Language.PureScript qualified as P
  import Data.Map qualified as M
  import Data.List (foldl')
  import Language.PureScript.Sugar.Names.Env (nullImports, primExports)
  import Control.Monad (foldM)
  import Control.Monad.Trans.Except (runExceptT)
  import Control.Monad.Trans.State (execStateT)
  import Control.Monad.Writer.Strict (runWriterT)
  
  
  -- | The PSCI configuration.
  --
  -- These configuration values do not change during execution.
  --
  newtype PSCiConfig = PSCiConfig
    { psciFileGlobs :: [String]
    } deriving Show
  
  -- | The PSCI state.
  --
  -- Holds a list of imported modules, loaded files, and partial let bindings,
  -- plus the currently configured interactive printing function.
  --
  -- The let bindings are partial, because it makes more sense to apply the
  -- binding to the final evaluated expression.
  --
  -- The last two fields are derived from the first three via updateImportExports
  -- each time a module is imported, a let binding is added, or the session is
  -- cleared or reloaded
  data PSCiState = PSCiState
    [ImportedModule]
    [P.Declaration]
    [(P.Module, P.ExternsFile)]
    (P.ModuleName, P.Ident)
    P.Imports
    P.Exports
    deriving Show
  
  psciImportedModules :: PSCiState -> [ImportedModule]
  psciImportedModules (PSCiState x _ _ _ _ _) = x
  
  psciLetBindings :: PSCiState -> [P.Declaration]
  psciLetBindings (PSCiState _ x _ _ _ _) = x
  
  psciLoadedExterns :: PSCiState -> [(P.Module, P.ExternsFile)]
  psciLoadedExterns (PSCiState _ _ x _ _ _) = x
  
  psciInteractivePrint :: PSCiState -> (P.ModuleName, P.Ident)
  psciInteractivePrint (PSCiState _ _ _ x _ _) = x
  
  psciImports :: PSCiState -> P.Imports
  psciImports (PSCiState _ _ _ _ x _) = x
  
  psciExports :: PSCiState -> P.Exports
  psciExports (PSCiState _ _ _ _ _ x) = x
  
  initialPSCiState :: PSCiState
  initialPSCiState = PSCiState [] [] [] initialInteractivePrint nullImports primExports
  
  -- | The default interactive print function.
  initialInteractivePrint :: (P.ModuleName, P.Ident)
  initialInteractivePrint = (P.moduleNameFromString "PSCI.Support", P.Ident "eval")
  
  psciEnvironment :: PSCiState -> P.Environment
  psciEnvironment st = foldl' (flip P.applyExternsFileToEnvironment) P.initEnvironment externs
    where externs = map snd (psciLoadedExterns st)
  
  -- | All of the data that is contained by an ImportDeclaration in the AST.
  -- That is:
  --
  -- * A module name, the name of the module which is being imported
  -- * An ImportDeclarationType which specifies whether there is an explicit
  --   import list, a hiding list, or neither.
  -- * If the module is imported qualified, its qualified name in the importing
  --   module. Otherwise, Nothing.
  --
  type ImportedModule = (P.ModuleName, P.ImportDeclarationType, Maybe P.ModuleName)
  
  -- * State helpers
  
  -- This function updates the Imports and Exports values in the PSCiState, which are used for
  -- handling completions. This function must be called whenever the PSCiState is modified to
  -- ensure that completions remain accurate.
  updateImportExports :: PSCiState -> PSCiState
  updateImportExports st@(PSCiState modules lets externs iprint _ _) =
    case createEnv (map snd externs) >>= flip desugarModule temporaryModule of
      Left _          -> st -- TODO: can this fail and what should we do?
      Right env  ->
        case M.lookup temporaryName env of
          Just (_, is, es)  -> PSCiState modules lets externs iprint is es
          _                 -> st -- impossible
    where
  
    desugarModule :: P.Env -> P.Module -> Either P.MultipleErrors P.Env
    desugarModule e = runExceptT =<< fmap (fst . fst) . runWriterT . flip execStateT (e, mempty) . P.desugarImports
  
    createEnv :: [P.ExternsFile] -> Either P.MultipleErrors P.Env
    createEnv = runExceptT =<< fmap fst . runWriterT . foldM P.externsEnv P.primEnv
  
    temporaryName :: P.ModuleName
    temporaryName = P.ModuleName "$PSCI"
  
    temporaryModule :: P.Module
    temporaryModule =
      let
        prim = (P.ModuleName "Prim", P.Implicit, Nothing)
        decl = (importDecl `map` (prim : modules)) ++ lets
      in
        P.Module internalSpan [] temporaryName decl Nothing
  
    importDecl :: ImportedModule -> P.Declaration
    importDecl (mn, declType, asQ) = P.ImportDeclaration (internalSpan, []) mn declType asQ
  
    internalSpan :: P.SourceSpan
    internalSpan = P.internalModuleSourceSpan "<internal>"
  
  -- | Updates the imported modules in the state record.
  updateImportedModules :: ([ImportedModule] -> [ImportedModule]) -> PSCiState -> PSCiState
  updateImportedModules f (PSCiState x a b c d e) =
    updateImportExports (PSCiState (f x) a b c d e)
  
  -- | Updates the loaded externs files in the state record.
  updateLoadedExterns :: ([(P.Module, P.ExternsFile)] -> [(P.Module, P.ExternsFile)]) -> PSCiState -> PSCiState
  updateLoadedExterns f (PSCiState a b x c d e) =
    updateImportExports (PSCiState a b (f x) c d e)
  
  -- | Updates the let bindings in the state record.
  updateLets :: ([P.Declaration] -> [P.Declaration]) -> PSCiState -> PSCiState
  updateLets f (PSCiState a x b c d e) =
    updateImportExports (PSCiState a (f x) b c d e)
  
  -- | Replaces the interactive printing function in the state record with a new
  -- one.
  setInteractivePrint :: (P.ModuleName, P.Ident) -> PSCiState -> PSCiState
  setInteractivePrint iprint (PSCiState a b c _ d e) =
    PSCiState a b c iprint d e
  
  -- * Commands
  
  -- |
  -- Valid Meta-commands for PSCI
  --
  data Command
    -- | A purescript expression
    = Expression P.Expr
    -- | Show the help (ie, list of directives)
    | ShowHelp
    -- | Import a module from a loaded file
    | Import ImportedModule
    -- | Browse a module
    | BrowseModule P.ModuleName
    -- | Exit PSCI
    | QuitPSCi
    -- | Reload all the imported modules of the REPL
    | ReloadState
    -- | Clear the state of the REPL
    | ClearState
    -- | Add some declarations to the current evaluation context
    | Decls [P.Declaration]
    -- | Find the type of an expression
    | TypeOf P.Expr
    -- | Find the kind of an expression
    | KindOf P.SourceType
    -- | Shows information about the current state of the REPL
    | ShowInfo ReplQuery
    -- | Paste multiple lines
    | PasteLines
    -- | Return auto-completion output as if pressing <tab>
    | CompleteStr String
    -- | Set the interactive printing function
    | SetInteractivePrint (P.ModuleName, P.Ident)
    deriving Show
  
  data ReplQuery
    = QueryLoaded
    | QueryImport
    | QueryPrint
    deriving (Eq, Show)
  
  -- | A list of all ReplQuery values.
  replQueries :: [ReplQuery]
  replQueries = [QueryLoaded, QueryImport, QueryPrint]
  
  replQueryStrings :: [String]
  replQueryStrings = map showReplQuery replQueries
  
  showReplQuery :: ReplQuery -> String
  showReplQuery QueryLoaded = "loaded"
  showReplQuery QueryImport = "import"
  showReplQuery QueryPrint = "print"
  
  parseReplQuery :: String -> Maybe ReplQuery
  parseReplQuery "loaded" = Just QueryLoaded
  parseReplQuery "import" = Just QueryImport
  parseReplQuery "print" = Just QueryPrint
  parseReplQuery _ = Nothing
  
  data Directive
    = Help
    | Quit
    | Reload
    | Clear
    | Browse
    | Type
    | Kind
    | Show
    | Paste
    | Complete
    | Print
    deriving (Eq, Show)
  