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

-- | Parse arguments using 'argsParser'.

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