packages feed

ihaskell-0.6.0.0: src/IHaskell/Eval/ParseShell.hs

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