mirror of
https://github.com/IHaskell/IHaskell.git
synced 2025-04-16 03:16:20 +00:00
110 lines
3.3 KiB
Haskell
110 lines
3.3 KiB
Haskell
import ClassyPrelude
|
|
import Control.Concurrent.Chan
|
|
import Data.Aeson
|
|
|
|
import qualified Data.Map as Map
|
|
|
|
import IHaskell.Types
|
|
import IHaskell.ZeroMQ
|
|
import qualified IHaskell.Message.UUID as UUID
|
|
import IHaskell.Eval.Evaluate
|
|
import qualified Data.ByteString.Char8 as Chars
|
|
|
|
data KernelState = KernelState
|
|
{ getExecutionCounter :: Int
|
|
, getInterpreter :: Interpreter
|
|
}
|
|
|
|
main :: IO ()
|
|
main = do
|
|
-- Read the profile JSON file from the argument list.
|
|
[profileSrc] <- getArgs
|
|
|
|
-- Parse the profile file.
|
|
Just profile <- liftM decode $ readFile $ fpFromText profileSrc
|
|
|
|
-- Serve on all sockets and ports defined in the profile.
|
|
interface <- serveProfile profile
|
|
|
|
state <- initialKernelState
|
|
|
|
-- Receive and reply to all messages on the shell socket.
|
|
forever $ do
|
|
-- Read the request from the request channel.
|
|
request <- readChan $ shellRequestChannel interface
|
|
|
|
-- Create a header for the reply.
|
|
replyHeader <- createReplyHeader (header request)
|
|
|
|
-- Create the reply, possibly modifying kernel state.
|
|
reply <- modifyMVar state $ replyTo interface request replyHeader
|
|
|
|
-- Write the reply to the reply channel.
|
|
writeChan (shellReplyChannel interface) reply
|
|
|
|
-- Initial kernel state.
|
|
initialKernelState :: IO (MVar KernelState)
|
|
initialKernelState = do
|
|
interpreter <- makeInterpreter
|
|
|
|
newMVar KernelState {
|
|
getExecutionCounter = 1,
|
|
getInterpreter = interpreter
|
|
}
|
|
|
|
-- | Duplicate a message header, giving it a new UUID and message type.
|
|
dupHeader :: MessageHeader -> MessageType -> IO MessageHeader
|
|
dupHeader header messageType = do
|
|
uuid <- UUID.random
|
|
|
|
return header { messageId = uuid, msgType = messageType }
|
|
|
|
-- | Create a new message header, given a parent message header.
|
|
createReplyHeader :: MessageHeader -> IO MessageHeader
|
|
createReplyHeader parent = do
|
|
-- Generate a new message UUID.
|
|
newMessageId <- UUID.random
|
|
|
|
return MessageHeader {
|
|
identifiers = identifiers parent,
|
|
parentHeader = Just parent,
|
|
metadata = Map.fromList [],
|
|
messageId = newMessageId,
|
|
sessionId = sessionId parent,
|
|
username = username parent,
|
|
msgType = replyType $ msgType parent
|
|
}
|
|
|
|
replyTo :: ZeroMQInterface -> Message -> MessageHeader -> KernelState -> IO (KernelState, Message)
|
|
replyTo _ KernelInfoRequest{} replyHeader state = return (state, KernelInfoReply { header = replyHeader })
|
|
|
|
replyTo interface ExecuteRequest{ getCode = code } replyHeader state = do
|
|
let execCount = getExecutionCounter state
|
|
interpreter = getInterpreter state
|
|
send = writeChan $ iopubChannel interface
|
|
|
|
idleHeader <- dupHeader replyHeader StatusMessage
|
|
send $ PublishStatus idleHeader Idle
|
|
|
|
busyHeader <- dupHeader replyHeader StatusMessage
|
|
send $ PublishStatus busyHeader Busy
|
|
|
|
outputs <- evaluate interpreter $ Chars.unpack code
|
|
|
|
let isPlain (Display mime _) = mime == PlainText
|
|
case find isPlain outputs of
|
|
Just (Display PlainText text) -> do
|
|
outHeader <- dupHeader replyHeader OutputMessage
|
|
send $ PublishOutput outHeader text execCount
|
|
Nothing -> return ()
|
|
|
|
displayHeader <- dupHeader replyHeader DisplayDataMessage
|
|
send $ PublishDisplayData displayHeader "haskell" $ filter (not . isPlain) outputs
|
|
|
|
let newState = state { getExecutionCounter = execCount + 1 }
|
|
return (newState, ExecuteReply {
|
|
header = replyHeader,
|
|
executionCounter = execCount,
|
|
status = Ok
|
|
})
|