IHaskell/Main.hs

110 lines
3.3 KiB
Haskell
Raw Normal View History

import ClassyPrelude
import Control.Concurrent.Chan
import Data.Aeson
import qualified Data.Map as Map
import IHaskell.Types
2013-08-26 14:47:27 -07:00
import IHaskell.ZeroMQ
import qualified IHaskell.Message.UUID as UUID
2013-10-10 22:52:32 -07:00
import IHaskell.Eval.Evaluate
import qualified Data.ByteString.Char8 as Chars
2013-09-28 21:29:54 -07:00
data KernelState = KernelState
{ getExecutionCounter :: Int
2013-10-10 22:52:32 -07:00
, getInterpreter :: Interpreter
2013-09-28 21:29:54 -07:00
}
main :: IO ()
main = do
2013-08-26 14:47:27 -07:00
-- Read the profile JSON file from the argument list.
[profileSrc] <- getArgs
2013-08-26 14:47:27 -07:00
-- Parse the profile file.
Just profile <- liftM decode $ readFile $ fpFromText profileSrc
2013-08-26 14:47:27 -07:00
-- Serve on all sockets and ports defined in the profile.
interface <- serveProfile profile
2013-08-26 14:47:27 -07:00
state <- initialKernelState
2013-08-26 14:47:27 -07:00
-- Receive and reply to all messages on the shell socket.
forever $ do
-- Read the request from the request channel.
request <- readChan $ shellRequestChannel interface
2013-08-26 14:47:27 -07:00
-- Create a header for the reply.
replyHeader <- createReplyHeader (header request)
2013-08-26 14:47:27 -07:00
-- Create the reply, possibly modifying kernel state.
reply <- modifyMVar state $ replyTo interface request replyHeader
2013-08-26 14:47:27 -07:00
-- Write the reply to the reply channel.
writeChan (shellReplyChannel interface) reply
2013-08-26 14:47:27 -07:00
-- Initial kernel state.
initialKernelState :: IO (MVar KernelState)
2013-10-10 22:52:32 -07:00
initialKernelState = do
interpreter <- makeInterpreter
newMVar KernelState {
getExecutionCounter = 1,
getInterpreter = interpreter
2013-09-28 21:29:54 -07:00
}
2013-10-10 22:52:32 -07:00
-- | 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 }
2013-08-26 14:47:27 -07:00
-- | 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
2013-08-26 14:47:27 -07:00
return MessageHeader {
identifiers = identifiers parent,
parentHeader = Just parent,
metadata = Map.fromList [],
messageId = newMessageId,
sessionId = sessionId parent,
username = username parent,
msgType = replyType $ msgType parent
}
2013-08-26 14:47:27 -07:00
replyTo :: ZeroMQInterface -> Message -> MessageHeader -> KernelState -> IO (KernelState, Message)
replyTo _ KernelInfoRequest{} replyHeader state = return (state, KernelInfoReply { header = replyHeader })
2013-08-26 16:14:48 -07:00
2013-10-10 22:52:32 -07:00
replyTo interface ExecuteRequest{ getCode = code } replyHeader state = do
let execCount = getExecutionCounter state
interpreter = getInterpreter state
send = writeChan $ iopubChannel interface
2013-10-07 21:32:51 -07:00
2013-10-10 22:52:32 -07:00
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 }
2013-09-28 21:29:54 -07:00
return (newState, ExecuteReply {
2013-08-27 09:46:31 -07:00
header = replyHeader,
2013-10-10 22:52:32 -07:00
executionCounter = execCount,
status = Ok
2013-08-27 09:46:31 -07:00
})