Skip to content

Commit

Permalink
greatly simplify prompt_; support more extensions in the initial parser
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeplus64 committed Feb 2, 2013
1 parent 67c943b commit de08f18
Showing 1 changed file with 74 additions and 61 deletions.
135 changes: 74 additions & 61 deletions src/Language/Haskell/Repl.hs
Expand Up @@ -16,8 +16,8 @@ module Language.Haskell.Repl
, Output(..) , Output(..)
, prompt , prompt
, prompt_ , prompt_
, input , send
, output , result
, prettyOutput , prettyOutput
, parseInput , parseInput
) where ) where
Expand All @@ -30,11 +30,12 @@ import Control.Arrow
import Data.Dynamic import Data.Dynamic
import Data.IORef import Data.IORef
import Data.Maybe import Data.Maybe
import qualified Data.DList as DL import Data.List
import Text.Parsec hiding (many,(<|>),newline) import Text.Parsec hiding (many,(<|>),newline)
import Text.Parsec.String import Text.Parsec.String
import qualified Language.Haskell.Exts.Parser as H import qualified Language.Haskell.Exts.Parser as H
import qualified Language.Haskell.Exts.Syntax as H import qualified Language.Haskell.Exts.Syntax as H
import qualified Language.Haskell.Exts.Extension as H


import GHC import GHC
import GHC.Paths import GHC.Paths
Expand Down Expand Up @@ -77,7 +78,7 @@ data Output
| Exception [String] String | Exception [String] String
| Errors [String] | Errors [String]
| Partial [String] | Partial [String]
| Timeout | Timeout [String]
deriving Show deriving Show


prefix :: Char -> Parser () prefix :: Char -> Parser ()
Expand All @@ -98,17 +99,32 @@ valid f x = case f x of
H.ParseOk _ -> True H.ParseOk _ -> True
_ -> False _ -> False


parseMode :: H.ParseMode
parseMode = H.defaultParseMode
{ H.extensions = H.knownExtensions \\
[ H.TemplateHaskell
, H.CPP
, H.ForeignFunctionInterface
, H.UnliftedFFITypes
, H.XmlSyntax
, H.MagicHash
, H.HereDocuments
, H.QuasiQuotes
, H.NPlusKPatterns
, H.UnboxedTuples ]
}

parseType, parseKind, parseInfo, parseDecl, parseStmt, parseExpr, parseUndefine, parseClear, parseInput :: Parser Input parseType, parseKind, parseInfo, parseDecl, parseStmt, parseExpr, parseUndefine, parseClear, parseInput :: Parser Input
parseType = simpl 't' Type parseType = simpl 't' Type
parseKind = simpl 'k' Kind parseKind = simpl 'k' Kind
parseInfo = simpl 'i' Info parseInfo = simpl 'i' Info
parseDecl = do parseDecl = do
decl <- getInput decl <- getInput
guard (valid H.parseDecl decl) guard (valid (H.parseDeclWithMode parseMode) decl)
return (Decl decl) return (Decl decl)
parseStmt = do parseStmt = do
stmt <- getInput stmt <- getInput
case H.parseStmt stmt of case H.parseStmtWithMode parseMode stmt of
H.ParseOk (H.LetStmt _) -> return (Stmt stmt) H.ParseOk (H.LetStmt _) -> return (Stmt stmt)
_ -> fail "Not a let binding." _ -> fail "Not a let binding."
parseExpr = Expr <$> getInput parseExpr = Expr <$> getInput
Expand All @@ -129,24 +145,25 @@ parseInput = foldr1 (\l r -> Text.Parsec.try l <|> r)
-- | Used by 'prompt'. -- | Used by 'prompt'.
prettyOutput :: Output -> [String] prettyOutput :: Output -> [String]
prettyOutput (OK s) = s prettyOutput (OK s) = s
prettyOutput (Exception s e) = overLast (++ ("*** Exception: " ++ e)) s prettyOutput (Partial s) = s
prettyOutput (Errors errs) = errs prettyOutput (Errors errs) = errs
prettyOutput (Partial s) = overLast (++ "*** Timed out") s prettyOutput (Exception s e) = overLast (++ ("*** Exception: " ++ e)) s
prettyOutput Timeout = ["*** Timed out"] prettyOutput (Timeout []) = ["*** Timed out"]
prettyOutput (Timeout s) = overLast (++ "*** Timed out") s


-- | Send input. -- | Send input.
input :: Repl a -> Input -> IO () send :: Repl a -> Input -> IO ()
input = writeChan . inputChan send = writeChan . inputChan


-- | Naiively get the next set of results. This /does not/ take into account -- | Naiively get the next set of results. This /does not/ take into account
-- 'patiences', 'patienceForErrors', or 'lineLength'. However, due -- 'patiences', 'patienceForErrors', or 'lineLength'. However, due
-- to laziness, this may not matter. -- to laziness, this may not matter.
output :: Repl a -> IO (ReplOutput a) result :: Repl a -> IO (ReplOutput a)
output = readChan . outputChan result = readChan . outputChan


{-# INLINE (!?) #-} {-# INLINE index #-}
(!?) :: [a] -> Int -> Maybe a index :: Int -> [a] -> Maybe a
ys !? i i `index` ys
| i >= 0 = go 0 ys | i >= 0 = go 0 ys
| otherwise = Nothing | otherwise = Nothing
where where
Expand All @@ -155,6 +172,12 @@ ys !? i
| j == i = Just x | j == i = Just x
| otherwise = go (j+1) xs | otherwise = go (j+1) xs


{-# INLINE overHead #-}
overHead :: (a -> a) -> [a] -> [a]
overHead f xs' = case xs' of
x:xs -> f x : xs
_ -> []

{-# INLINE overLast #-} {-# INLINE overLast #-}
overLast :: (a -> a) -> [a] -> [a] overLast :: (a -> a) -> [a] -> [a]
overLast f = go overLast f = go
Expand All @@ -163,6 +186,10 @@ overLast f = go
go [x] = [f x] go [x] = [f x]
go (x:xs) = x : go xs go (x:xs) = x : go xs


{-# INLINE lengthAt #-}
lengthAt :: Int -> [[a]] -> Int
lengthAt i = maybe 0 length . index i

-- | Same as 'prompt_', except it parses the input, and pretty prints the results. -- | Same as 'prompt_', except it parses the input, and pretty prints the results.
prompt prompt
:: Repl [String] :: Repl [String]
Expand All @@ -181,58 +208,44 @@ prompt_
-> Input -> Input
-> IO Output -> IO Output
prompt_ repl x = do prompt_ repl x = do
input repl x send repl x
results <- output repl results' <- result repl
threads <- newIORef []
final <- newEmptyMVar unlessRedundant results' $ \ results -> do

-- outputs is used iff an exception is raised by the compiled input.
-- outputs is used iff an exception is raised by the compiled input. -- This was a DList, but I didn't find any real advantage of it over
outputs <- newIORef [] :: IO (IORef [DL.DList Char]) -- [String] -- snoc was cheap but toList very expensive.

outputs :: IORef [String] <- newIORef []
let readOutputs = map DL.toList <$> readIORef outputs threads :: IORef [ThreadId] <- newIORef []
newline = modifyIORef outputs (++ [DL.empty]) final :: MVar Output <- newEmptyMVar
push char' = modifyIORef outputs (overLast (`DL.snoc` char')) let push c = do

output <- readIORef outputs
fork f = do if lengthAt (length output - 1) output > lineLength repl
t <- forkIO $ f `catch` \e@SomeException{} -> do then putMVar final (Partial output)
outs <- readOutputs else writeIORef outputs (overHead (c:) output)
putMVar final (Exception outs (show e)) newline = modifyIORef outputs ([]:)
modifyIORef threads (t:) readOutput = reverse . map reverse <$> readIORef outputs

fork f = do
-- Get the "progress" of a list. thread <- forkIO $ f `catch` \e@SomeException{} -> do
prog ys = do output <- readOutput
acc <- newIORef 0 putMVar final (Exception output (show e))
fork $ forM_ ys $ \_ -> modifyIORef acc (\i -> if i > lineLength repl then i else i+1) modifyIORef threads (thread:)
return acc

unlessRedundant results $ \ res -> do


-- Time out -- Time out
-- This can return only Timeout <what was consumed so far>
fork $ do fork $ do
threadDelay (floor (patience repl*1000000)) threadDelay (floor (patience repl * 1000000))
u <- readOutputs output <- readOutput
case res !? length u of putMVar final (Timeout output)
Nothing -> putMVar final (if null u then Timeout else Partial u)
Just h -> do -- Read characters off of results, and "push" them to outputs.
p <- prog h
i <- readIORef p
putMVar final $ case take i h of
[] -> case u of
[] -> Timeout
_ -> Partial u
xs -> Partial (u ++ [xs])

-- Return everything
fork $ do fork $ do
let r = map trim res forM_ results $ \l -> do
forM_ r $ \l -> do
newline newline
forM_ l push forM_ l push
putMVar final (OK r) putMVar final . OK =<< readIORef outputs


fin <- takeMVar final takeMVar final
mapM_ killThread =<< readIORef threads
return fin
where where
trim = take (lineLength repl) trim = take (lineLength repl)


Expand Down

0 comments on commit de08f18

Please sign in to comment.