aboutsummaryrefslogtreecommitdiff
path: root/hsm-repl/Hsm/Repl.hs
diff options
context:
space:
mode:
Diffstat (limited to 'hsm-repl/Hsm/Repl.hs')
-rw-r--r--hsm-repl/Hsm/Repl.hs104
1 files changed, 104 insertions, 0 deletions
diff --git a/hsm-repl/Hsm/Repl.hs b/hsm-repl/Hsm/Repl.hs
new file mode 100644
index 0000000..5265e59
--- /dev/null
+++ b/hsm-repl/Hsm/Repl.hs
@@ -0,0 +1,104 @@
+{-# LANGUAGE TypeFamilies #-}
+
+module Hsm.Repl
+ ( Repl
+ , repl
+ , runRepl
+ )
+where
+
+import Control.Monad (forM_)
+import Data.Typeable (Proxy (Proxy), Typeable, typeRep)
+import Effectful (Dispatch (Static), DispatchOf, Eff, IOE, liftIO, (:>))
+import Effectful.Dispatch.Static
+ ( SideEffects (WithSideEffects)
+ , StaticRep
+ , evalStaticRep
+ , getStaticRep
+ , unsafeEff_
+ )
+import Effectful.Resource (Resource, allocateEff)
+import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
+import Generic.Data.Function.Common.Generic.Meta (KnownSymbols, symbolVals)
+import Hsm.Log (Log, Severity (Attention, Info, Trace), logMsg)
+import Language.Haskell.Interpreter
+ ( GhcError (errMsg)
+ , InterpreterError (WontCompile)
+ , as
+ , interpret
+ , runInterpreter
+ , setImports
+ )
+import System.Console.Haskeline
+ ( defaultSettings
+ , getInputLine
+ , handleInterrupt
+ , withInterrupt
+ )
+import System.Console.Haskeline.IO
+ ( InputState
+ , cancelInput
+ , initializeInput
+ , queryInput
+ )
+
+data Repl (p :: Symbol) (ms :: [Symbol]) (t :: *) (a :: * -> *) (b :: *)
+
+type instance DispatchOf (Repl p ms t) = Static WithSideEffects
+
+newtype instance StaticRep (Repl p ms t)
+ = Repl InputState
+
+repl
+ :: forall p ms t es
+ . ( KnownSymbol p
+ , KnownSymbols ms
+ , Log "repl" :> es
+ , Repl p ms t :> es
+ , Show t
+ , Typeable t
+ )
+ => Eff es (Maybe t)
+repl = query >>= maybe (return Nothing) parse
+ where
+ query = do
+ Repl inputState <- getStaticRep
+ logMsg Trace $ "Expecting a value of type: " <> show (typeRep $ Proxy @t)
+ unsafeEff_
+ . queryInput inputState
+ . handleInterrupt (return Nothing)
+ . withInterrupt
+ . getInputLine
+ $ symbolVal (Proxy @p)
+ parse string = do
+ logMsg Trace $ "Parsing string: " <> string
+ eitherValue <-
+ unsafeEff_ . runInterpreter $ do
+ setImports $ symbolVals @ms
+ interpret string as
+ case eitherValue of
+ Right value -> do
+ logMsg Trace $ "Parsed value: " <> show value
+ return $ Just value
+ Left (WontCompile errors) -> do
+ forM_ errors $ logMsg Attention . errMsg
+ repl
+ Left err -> do
+ logMsg Attention $ show err
+ repl
+
+runRepl
+ :: forall p ms t es a
+ . (IOE :> es, Log "repl" :> es, Resource :> es)
+ => Eff (Repl p ms t : es) a
+ -> Eff es a
+runRepl action = do
+ inputState <- snd <$> allocateEff inputStateAlloc inputStateDealloc
+ evalStaticRep (Repl inputState) action
+ where
+ inputStateAlloc = do
+ logMsg Info "Initializing input"
+ liftIO $ initializeInput defaultSettings
+ inputStateDealloc inputState = do
+ logMsg Info "Cancelling input"
+ liftIO $ cancelInput inputState