-- | This module splits a shell command line into a list of strings, -- one for each command / filename module IHaskell.Eval.ParseShell (parseShell) where import Prelude hiding (words) import Text.ParserCombinators.Parsec hiding (manyTill) import Control.Applicative hiding ((<|>), many, optional) eol :: Parser Char eol = oneOf "\n\r" "end of line" quote :: Parser Char quote = char '\"' -- | @manyTill p end@ from hidden @manyTill@ in that it appends the result of @end@ manyTill :: Parser a -> Parser [a] -> Parser [a] manyTill p end = scan where scan = end <|> do x <- p xs <- scan return $ x:xs manyTill1 p end = do x <- p xs <- manyTill p end return $ x : xs unescapedChar :: Parser Char -> Parser String unescapedChar p = try $ do x <- noneOf "\\" lookAhead p return [x] quotedString = do quote "expected starting quote" (manyTill anyChar (unescapedChar quote) <* quote) "unexpected in quoted String " unquotedString = manyTill1 anyChar end where end = unescapedChar space <|> (lookAhead eol >> return []) word = quotedString <|> unquotedString "word" separator :: Parser String separator = many1 space "separator" -- | Input must terminate in a space character (like a \n) words :: Parser [String] words = try (eof *> return []) <|> do x <- word rest1 <- lookAhead (many anyToken) ss <- separator rest2 <- lookAhead (many anyToken) xs <- words return $ x : xs parseShell :: String -> Either ParseError [String] parseShell string = parse words "shell" (string ++ "\n")