{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}

-- | Parsing of Stack command line arguments

module Data.Attoparsec.Args
  ( EscapingMode (..)
  , argsParser
  , parseArgs
  , parseArgsFromString
  ) where

import           Data.Attoparsec.Text ( (<?>) )
import qualified Data.Attoparsec.Text as P
import qualified Data.Text as T
import           Stack.Prelude

-- | Mode for parsing escape characters.

data EscapingMode
  = Escaping
  | NoEscaping
  deriving (Int -> EscapingMode
EscapingMode -> Int
EscapingMode -> [EscapingMode]
EscapingMode -> EscapingMode
EscapingMode -> EscapingMode -> [EscapingMode]
EscapingMode -> EscapingMode -> EscapingMode -> [EscapingMode]
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: EscapingMode -> EscapingMode -> EscapingMode -> [EscapingMode]
$cenumFromThenTo :: EscapingMode -> EscapingMode -> EscapingMode -> [EscapingMode]
enumFromTo :: EscapingMode -> EscapingMode -> [EscapingMode]
$cenumFromTo :: EscapingMode -> EscapingMode -> [EscapingMode]
enumFromThen :: EscapingMode -> EscapingMode -> [EscapingMode]
$cenumFromThen :: EscapingMode -> EscapingMode -> [EscapingMode]
enumFrom :: EscapingMode -> [EscapingMode]
$cenumFrom :: EscapingMode -> [EscapingMode]
fromEnum :: EscapingMode -> Int
$cfromEnum :: EscapingMode -> Int
toEnum :: Int -> EscapingMode
$ctoEnum :: Int -> EscapingMode
pred :: EscapingMode -> EscapingMode
$cpred :: EscapingMode -> EscapingMode
succ :: EscapingMode -> EscapingMode
$csucc :: EscapingMode -> EscapingMode
Enum, EscapingMode -> EscapingMode -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: EscapingMode -> EscapingMode -> Bool
$c/= :: EscapingMode -> EscapingMode -> Bool
== :: EscapingMode -> EscapingMode -> Bool
$c== :: EscapingMode -> EscapingMode -> Bool
Eq, Int -> EscapingMode -> ShowS
[EscapingMode] -> ShowS
EscapingMode -> [Char]
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
showList :: [EscapingMode] -> ShowS
$cshowList :: [EscapingMode] -> ShowS
show :: EscapingMode -> [Char]
$cshow :: EscapingMode -> [Char]
showsPrec :: Int -> EscapingMode -> ShowS
$cshowsPrec :: Int -> EscapingMode -> ShowS
Show)

-- | Parse arguments using 'argsParser'.

parseArgs :: EscapingMode -> Text -> Either String [String]
parseArgs :: EscapingMode -> Text -> Either [Char] [[Char]]
parseArgs EscapingMode
mode = forall a. Parser a -> Text -> Either [Char] a
P.parseOnly (EscapingMode -> Parser [[Char]]
argsParser EscapingMode
mode)

-- | Parse using 'argsParser' from a string.

parseArgsFromString :: EscapingMode -> String -> Either String [String]
parseArgsFromString :: EscapingMode -> [Char] -> Either [Char] [[Char]]
parseArgsFromString EscapingMode
mode = forall a. Parser a -> Text -> Either [Char] a
P.parseOnly (EscapingMode -> Parser [[Char]]
argsParser EscapingMode
mode) forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> Text
T.pack

-- | A basic argument parser. It supports space-separated text, and

-- string quotation with identity escaping: \x -> x.

argsParser :: EscapingMode -> P.Parser [String]
argsParser :: EscapingMode -> Parser [[Char]]
argsParser EscapingMode
mode = forall (f :: * -> *) a. Alternative f => f a -> f [a]
many (Parser ()
P.skipSpace forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (Parser Text [Char]
quoted forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text [Char]
unquoted)) forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<*
                  Parser ()
P.skipSpace forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* (forall t. Chunk t => Parser t ()
P.endOfInput forall i a. Parser i a -> [Char] -> Parser i a
<?> [Char]
"unterminated string")
 where
  unquoted :: Parser Text [Char]
unquoted = forall (f :: * -> *) a. Alternative f => f a -> f [a]
P.many1 Parser Char
naked
  quoted :: Parser Text [Char]
quoted = Char -> Parser Char
P.char Char
'"' forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser Text [Char]
str forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Char -> Parser Char
P.char Char
'"'
  str :: Parser Text [Char]
str = forall (f :: * -> *) a. Alternative f => f a -> f [a]
many ( case EscapingMode
mode of
                 EscapingMode
Escaping -> Parser Char
escaped forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Char
nonquote
                 EscapingMode
NoEscaping -> Parser Char
nonquote
             )
  escaped :: Parser Char
escaped = Char -> Parser Char
P.char Char
'\\' forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser Char
P.anyChar
  nonquote :: Parser Char
nonquote = (Char -> Bool) -> Parser Char
P.satisfy (forall a. Eq a => a -> a -> Bool
/= Char
'"')
  naked :: Parser Char
naked = (Char -> Bool) -> Parser Char
P.satisfy (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b c. (a -> b -> c) -> b -> a -> c
flip forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem ([Char]
"\" " :: String))