content: {-# LANGUAGE NoImplicitPrelude #-}
  
  -- | This module splits a shell command line into a list of strings,
  --   one for each command / filename
  module IHaskell.Eval.ParseShell (parseShell) where
  
  import           IHaskellPrelude
  
  import           Text.ParserCombinators.Parsec
  
  eol :: Parser Char
  eol = oneOf "\n\r" <?> "end of line"
  
  quote :: Parser Char
  quote = char '\"'
  
  -- | @manyTillEnd p end@ from normal @manyTill@ in that it appends the result of @end@
  manyTillEnd :: Parser a -> Parser [a] -> Parser [a]
  manyTillEnd p end = scan
    where
      scan = end <|> do
               x <- p
               xs <- scan
               return $ x : xs
  
  manyTillEnd1 :: Parser a -> Parser [a] -> Parser [a]
  manyTillEnd1 p end = do
    x <- p
    xs <- manyTillEnd p end
    return $ x : xs
  
  unescapedChar :: Parser Char -> Parser String
  unescapedChar p = try $ do
    x <- noneOf "\\"
    _ <- lookAhead p
    return [x]
  
  quotedString :: Parser [Char]
  quotedString = do
    _ <- quote <?> "expected starting quote"
    (manyTillEnd anyChar (unescapedChar quote) <* quote) <?> "unexpected in quoted String "
  
  unquotedString :: Parser [Char]
  unquotedString = manyTillEnd1 anyChar end
    where
      end = unescapedChar space
            <|> (lookAhead eol >> return [])
  
  word :: Parser [Char]
  word = quotedString <|> unquotedString <?> "word"
  
  separator :: Parser String
  separator = many1 space <?> "separator"
  
  -- | Input must terminate in a space character (like a \n)
  shellWords :: Parser [String]
  shellWords = try (eof *> return []) <|> do
                 x <- word
                 _rest1 <- lookAhead (many anyToken)
                 _ss <- separator
                 _rest2 <- lookAhead (many anyToken)
                 xs <- shellWords
                 return $ x : xs
  
  parseShell :: String -> Either ParseError [String]
  parseShell str = parse shellWords "shell" (str ++ "\n")
  