Last active
November 27, 2021 15:44
-
-
Save luochen1990/ad01e5cfbd43c714a66ccc1eb5e55bff to your computer and use it in GitHub Desktop.
Haskell eval & repl implemented via hint & conduit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env nix-shell | |
#! nix-shell -p "haskellPackages.ghcWithPackages (p: with p; [conduit conduit-extra bytestring text hint])" | |
#! nix-shell -i "ghci -ignore-dot-ghci -fdefer-type-errors -XTypeApplications" | |
{-# language ScopedTypeVariables, TypeApplications, PartialTypeSignatures #-} | |
{-# language OverloadedStrings #-} | |
import Conduit | |
import Data.Conduit | |
import Data.ByteString (ByteString) | |
import Data.Text (Text, pack, unpack) | |
import qualified Data.Conduit.Text as CT | |
import Data.Typeable (Typeable) | |
import qualified Language.Haskell.Interpreter as Hint | |
-- * eval | |
eval :: forall t. Typeable t => String -> IO t | |
eval s = do | |
mr <- Hint.runInterpreter $ do | |
Hint.setImports ["Prelude"] | |
Hint.interpret s (Hint.as :: t) | |
case mr of | |
Left err -> error (show err) | |
Right r -> pure r | |
evalS :: String -> IO String | |
evalS s = eval @String ("show (" ++ s ++ ")") | |
-- * Proc & LinedProc | |
type CliArg = [Text] | |
type Proc = ConduitT ByteString ByteString IO Int | |
type Command = CliArg -> Proc | |
type Line = Text -- ^ line is text without newline | |
type LinedProc = ConduitT Line Line IO Int | |
-- * run & runLined | |
unlined :: LinedProc -> Proc | |
unlined proc = CT.decode CT.utf8 .| CT.lines .| mapOutput (<> "\n") (proc `fuseUpstream` CT.encode CT.utf8) | |
run :: Proc -> IO Int | |
run proc = runConduit (stdinC .| proc `fuseUpstream` stdoutC) | |
runLined :: LinedProc -> IO Int | |
runLined proc = run (unlined proc) | |
-- * echoLine & repl | |
echoLine :: LinedProc | |
echoLine = do | |
ml <- await | |
case ml of | |
Just l -> if l == "exit" then pure 0 else yield l >> echoLine | |
Nothing -> pure 0 | |
repl :: LinedProc | |
repl = echoLine `fuseUpstream` mapMC (\l -> (pack . show) <$> evalS (unpack l)) | |
-- * main | |
main :: IO Int | |
main = runLined repl |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment