{-# 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 -> ShowS
[EscapingMode] -> ShowS
EscapingMode -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [EscapingMode] -> ShowS
$cshowList :: [EscapingMode] -> ShowS
show :: EscapingMode -> String
$cshow :: EscapingMode -> String
showsPrec :: Int -> EscapingMode -> ShowS
$cshowsPrec :: Int -> EscapingMode -> ShowS
Show,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
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)

-- | Parse arguments using 'argsParser'.

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

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

parseArgsFromString :: EscapingMode -> String -> Either String [String]
parseArgsFromString :: EscapingMode -> String -> Either String [String]
parseArgsFromString EscapingMode
mode = forall a. Parser a -> Text -> Either String a
P.parseOnly (EscapingMode -> Parser [String]
argsParser EscapingMode
mode) forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> 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 [String]
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 String
quoted forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text String
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 -> String -> Parser i a
<?> String
"unterminated string")
  where
    unquoted :: Parser Text String
unquoted = forall (f :: * -> *) a. Alternative f => f a -> f [a]
P.many1 Parser Char
naked
    quoted :: Parser Text String
quoted = Char -> Parser Char
P.char Char
'"' forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser Text String
str forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Char -> Parser Char
P.char Char
'"'
    str :: Parser Text String
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 (String
"\" " :: String))