IHaskell/Main.hs
2013-10-10 22:52:32 -07:00

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
})