{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE ViewPatterns #-}
module Minilang.REPL.Types where
import Control.Exception (Exception)
import Data.Aeson (FromJSON, ToJSON)
import Data.Text as Text hiding (replicate)
import GHC.Generics
import Minilang.Lambda.Eval (Env, Term, newEnv)
import Minilang.Lambda.Infer (Context, newContext)
import Minilang.Lambda.Parser (AST)
import Minilang.Lambda.Unify (Type)
import Text.Read (readMaybe)
class Monad m => MonadREPL m where
-- | Returns the current environment for this session
-- Environment is made of an `Env`ironment binding names to `Decl`arations
-- and typing `Context` binding names to `Value`s denoting evaluated types.
getEnv :: m REPLEnv
-- | Updates the current environment for this session.
setEnv :: REPLEnv -> m ()
-- | Handle some input, parsing, typechecking and possibly evaluating it.
input :: m In
-- | Produces some output
output :: Out -> m ()
-- | Prompt for some input
prompt :: m ()
-- | Read given file
load :: Text -> m (Either REPLError Text)
-- | Possible error in REPL interactions
data REPLError
= FileError IOError
| REPLError Text
deriving (Eq, Show)
instance Exception REPLError
-- | REPL state
data REPLEnv = REPLEnv
{ env :: Env
, context :: Context
, curIndent :: Int
, stepTypeCheck :: Bool
, debugParser :: Bool
, language :: Language
, evaluator :: Evaluator
}
initialREPL :: REPLEnv
initialREPL = REPLEnv newEnv newContext 0 False False Untyped DeBruijn
-- | Input
data In
= EOF
| In Text
| Com Command
deriving (Eq, Show, Generic, ToJSON, FromJSON)
data Command
= ClearEnv
| DumpEnv
| Set Flag
| Load Content
| Help
deriving (Eq, Show, Generic, ToJSON, FromJSON)
data Content
= File Text
| Data Text
deriving (Eq, Show, Generic, ToJSON, FromJSON)
data Out
= Defined Text (Maybe Type)
| Evaluated AST Term (Maybe Type)
| CurrentEnv Env Context Language
| Msg Text
| Bye
deriving (Eq, Show, Generic, ToJSON, FromJSON)
data Flag
= StepTypeChecker Bool
| DebugParser Bool
| SelectLanguage Language
| SelectEvaluator Evaluator
deriving (Eq, Show, Generic, ToJSON, FromJSON)
data Language = Untyped | Typed
deriving (Eq, Show, Read, Generic, ToJSON, FromJSON)
-- | Which reduction strategy to use.
--
-- * 'DeBruijn' — call-by-value head reduction on De Bruijn-indexed terms
-- (the original evaluator).
-- * 'NbE' — Normalisation by Evaluation: eval to a 'Value' semantic domain,
-- then read back to a 'Term'. Reduces under lambda binders and therefore
-- produces fully-normalised terms.
data Evaluator = DeBruijn | NbE
deriving (Eq, Show, Read, Generic, ToJSON, FromJSON)
-- | "Parse" given `Text` into some input for the REPL
interpret ::
Text -> In
interpret ":q" = EOF
interpret ":quit" = EOF
interpret ":h" = Com Help
interpret ":help" = Com Help
interpret ":e" = Com DumpEnv
interpret ":env" = Com DumpEnv
interpret ":c" = Com ClearEnv
interpret ":clear" = Com ClearEnv
interpret ":set step" = Com $ Set $ StepTypeChecker True
interpret ":unset step" = Com $ Set $ StepTypeChecker False
interpret ":set debug" = Com $ Set $ DebugParser True
interpret ":unset debug" = Com $ Set $ DebugParser False
interpret (unpack -> (':' : 'l' : ' ' : file)) = Com $ Load $ File $ strip $ pack file
interpret (unpack -> (':' : 'l' : 'o' : 'a' : 'd' : ' ' : file)) = Com $ Load $ File $ strip $ pack file
interpret (unpack -> (':' : 'l' : 'a' : 'n' : 'g' : ' ' : lang)) =
case readMaybe lang of
Nothing -> Com Help
Just l -> Com $ Set $ SelectLanguage l
interpret (unpack -> (':' : 'e' : 'v' : 'a' : 'l' : ' ' : ev)) =
case readMaybe ev of
Nothing -> Com Help
Just e -> Com $ Set $ SelectEvaluator e
interpret t = In t
helpText :: Text
helpText =
Text.unlines
[ "REPL Commands Help:"
, " :h(elp) : Display this help"
, " :q(uit) : Exit the interpreter"
, " :e(nv) : Displays the current interpreter's environment"
, " This dumps both the Environment, where values are defined,"
, " and the typing Context, where types are defined. "
, " :c(lear) : Clear the current interpreter's environment"
, " :set