packages feed

language-bash 0.7.1 → 0.8.0

raw patch · 9 files changed

+177/−99 lines, 9 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Language.Bash.Cond: instance GHC.Generics.Generic (Language.Bash.Cond.CondExpr a)
+ Language.Bash.Cond: instance GHC.Generics.Generic Language.Bash.Cond.BinaryOp
+ Language.Bash.Cond: instance GHC.Generics.Generic Language.Bash.Cond.UnaryOp
+ Language.Bash.Parse.Word: functionName :: Stream s m Char => ParsecT s u m String
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.AndOr
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.Assign
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.AssignOp
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.CaseClause
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.CaseTerm
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.Command
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.HeredocOp
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.IODesc
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.List
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.ListTerm
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.Pipeline
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.RValue
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.Redir
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.RedirOp
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.ShellCommand
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.Statement
+ Language.Bash.Syntax: instance GHC.Generics.Generic Language.Bash.Syntax.WordList
+ Language.Bash.Word: instance GHC.Generics.Generic Language.Bash.Word.AltOp
+ Language.Bash.Word: instance GHC.Generics.Generic Language.Bash.Word.Direction
+ Language.Bash.Word: instance GHC.Generics.Generic Language.Bash.Word.LetterCaseOp
+ Language.Bash.Word: instance GHC.Generics.Generic Language.Bash.Word.ParamSubst
+ Language.Bash.Word: instance GHC.Generics.Generic Language.Bash.Word.Parameter
+ Language.Bash.Word: instance GHC.Generics.Generic Language.Bash.Word.ProcessSubstOp
+ Language.Bash.Word: instance GHC.Generics.Generic Language.Bash.Word.Span
+ Language.Bash.Word: wordToString :: Word -> Maybe String

Files

language-bash.cabal view
@@ -1,16 +1,16 @@ name:               language-bash-version:            0.7.1+version:            0.8.0 category:           Language license:            BSD3 license-file:       LICENSE author:             Kyle Raftogianis-maintainer:         Kyle Raftogianis <kylerafto@gmail.com>+maintainer:         Kyle Raftogianis <knrafto@gmail.com> copyright:          Copyright (c) 2013-2018 Kyle Raftogianis build-type:         Simple cabal-version:      >= 1.8 homepage:           http://github.com/knrafto/language-bash/ bug-reports:        http://github.com/knrafto/language-bash/issues-tested-with:        GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.1, GHC == 7.10.2, GHC == 8.0.2, GHC == 8.4.3+tested-with:        GHC == 7.10.1, GHC == 7.10.2, GHC == 8.0.2, GHC == 8.4.3 synopsis:           Parsing and pretty-printing Bash shell scripts description:     A library for parsing, pretty-printing, and manipulating
src/Language/Bash/Cond.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE     DeriveDataTypeable   , DeriveFoldable-  , CPP+  , DeriveGeneric   , DeriveFunctor   , DeriveTraversable   , OverloadedStrings@@ -19,15 +19,11 @@ import Control.Applicative import Data.Data              (Data) import Data.Typeable          (Typeable)+import GHC.Generics           (Generic) import Text.Parsec            hiding ((<|>), token) import Text.Parsec.Expr       hiding (Operator) import Text.PrettyPrint       hiding (parens) -#if __GLASGOW_HASKELL__ < 710-import Data.Foldable (Foldable)-import Data.Traversable (Traversable)-#endif- import Language.Bash.Operator import Language.Bash.Pretty @@ -38,7 +34,7 @@     | Not (CondExpr a)     | And (CondExpr a) (CondExpr a)     | Or (CondExpr a) (CondExpr a)-    deriving (Data, Eq, Read, Show, Typeable, Functor, Foldable, Traversable)+    deriving (Data, Eq, Read, Show, Typeable, Functor, Foldable, Traversable, Generic)  instance Pretty a => Pretty (CondExpr a) where     pretty = go (0 :: Int)@@ -77,7 +73,7 @@     | Varname        -- ^ @-v@     | ZeroString     -- ^ @-z@     | NonzeroString  -- ^ @-n /string/@ or @/string/@-    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded)+    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded, Generic)  instance Operator UnaryOp where     operatorTable =@@ -106,7 +102,7 @@     | ArithLE    -- ^ @-le@     | ArithGT    -- ^ @-gt@     | ArithGE    -- ^ @-ge@-    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded)+    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded, Generic)  instance Operator BinaryOp where     operatorTable =
src/Language/Bash/Expand.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverloadedStrings, PatternGuards, CPP #-}+{-# LANGUAGE OverloadedStrings, PatternGuards, DeriveGeneric #-} -- | Shell expansions. module Language.Bash.Expand     ( braceExpand@@ -7,11 +7,7 @@     , splitWord     ) where -#if __GLASGOW_HASKELL__ >= 710 import Prelude hiding ((<>), Word)-#else-import Data.Traversable (traverse)-#endif  import Control.Applicative import Control.Monad
src/Language/Bash/Parse.hs view
@@ -58,25 +58,27 @@     duck = do         u <- getState         case postHeredoc u of-            Nothing -> () <$ line+            Nothing -> () <$ line <* optional (char '\n')             Just s  -> () <$ setParserState s         h <- unlines <$> heredocLines         s <- getParserState         return (h, s) -    line = many (satisfy (/= '\n')) <* optional (char '\n')+    line = many (satisfy (/= '\n'))      heredocLines = [] <$ eof                <|> nextLine      nextLine = do         l <- process <$> line-        if l == end then return [] else (l :) <$> heredocLines+        if l == end+            then return []+            else (l :) <$ optional (char '\n') <*> heredocLines  -- | Parse a newline, skipping any here documents. newline :: Parser String newline = "newline" ?: do-    _ <- operator "\n"+    _ <- operator "\r\n" <|> operator "\n"     u <- getState     case postHeredoc u of         Nothing -> return ()@@ -126,7 +128,7 @@     redirOperator   = selectOperator operator <?> "redirection operator"     heredocOperator = selectOperator operator <?> "here document operator" --- | Skip a list of redirections.+-- | Parse a list of redirections. redirList :: Parser [Redir] redirList = many redir @@ -152,8 +154,9 @@     normalCommand = "simple command" ?: do         (as, rs1) <- commandParts assign         (ws, rs2) <- commandParts anyWord-        guard (not $ null as && null ws)-        return $ Command (SimpleCommand as ws) (rs1 ++ rs2)+        let rs = rs1 ++ rs2+        guard (not $ null as && null ws && null rs)+        return $ Command (SimpleCommand as ws) rs      assignArg = Left  <$> assign             <|> Right <$> anyWord@@ -338,11 +341,12 @@   where     expr = buildExpressionParser opTable term -    term = word "(" *> expr <* word ")"+    term = operator "(" *> expr <* operator ")"        <|> Cond.Unary <$> unaryOp <*> condWord        <|> (condWord >>= wordTerm) -    wordTerm w = Cond.Binary w <$> binaryOp <*> condWord+    wordTerm w = Cond.Binary w Cond.StrMatch <$ try (word "=~") <*> regexWord+             <|> Cond.Binary w <$> binaryOp <*> condWord              <|> pure (Cond.Unary Cond.NonzeroString w)      opTable =@@ -360,6 +364,39 @@     unaryOp  = selectOperator condOperator <?> "unary operator"     binaryOp = selectOperator condOperator <?> "binary operator" +    regexWord = stringToWord . concat <$> many1 (regexPart " \t\r\n") <* skipSpace+            <?> "regular expression"++    regexPart delims = regexParens+                   <|> regexSingleQuote+                   <|> regexDoubleQuote+                   <|> regexEscape+                   <|> regexChar delims++    regexDelimiters :: Char -> Parser String -> Char -> Parser String+    regexDelimiters begin middle end = do+        _ <- char begin+        parts <- many middle+        _ <- char end+        return $ [begin] ++ concat parts ++ [end]++    regexParens = regexDelimiters '(' (regexPart ")") ')'++    regexSingleQuote = regexDelimiters '\'' singleQuoteChar '\''+      where+        singleQuoteChar = sequence [char '\\', anyChar]+                      <|> (:[]) <$> noneOf "'"++    regexDoubleQuote = regexDelimiters '"' doubleQuoteChar '"'+      where+        doubleQuoteChar = sequence [char '\\', anyChar]+                      <|> (:[]) <$> noneOf "\""++    regexEscape = sequence [char '\\', anyChar]++    regexChar :: [Char] -> Parser String+    regexChar delims = (:[]) <$> noneOf delims+ ------------------------------------------------------------------------------- -- Coprocesses -------------------------------------------------------------------------------@@ -383,12 +420,12 @@           <?> "function definition"   where     functionDef1 = FunctionDef-               <$> try (word "function" *> name+               <$> try (word "function" *> functionName                         <* optional functionParens <* newlineList)                <*> functionBody      functionDef2 = FunctionDef-               <$> try (name <* functionParens <* newlineList)+               <$> try (functionName <* functionParens <* newlineList)                <*> functionBody      functionParens = operator "(" <* operator ")"
src/Language/Bash/Parse/Internal.hs view
@@ -2,7 +2,6 @@     FlexibleContexts   , FlexibleInstances   , LambdaCase-  , CPP   , MultiParamTypeClasses   , PatternGuards   , RecordWildCards@@ -21,10 +20,10 @@     , anyWord     , word     , reservedWord-    , unreservedWord     , assignBuiltin     , ioDesc     , name+    , functionName       -- * Operators     , anyOperator     , operator@@ -36,9 +35,7 @@     , heredocWord     ) where -#if __GLASGOW_HASKELL__ >= 710 import Prelude hiding (Word)-#endif  import           Control.Applicative import           Control.Monad@@ -155,7 +152,7 @@ operators :: [String] operators =     [ "(", ")", ";;", ";&", ";;&"-    , "|", "|&", "||", "&&", ";", "&", "\n"+    , "|", "|&", "||", "&&", ";", "&", "\r\n", "\n"     , "<", ">", ">|", ">>", "&>", "&>>", "<<<", "<&", ">&", "<>"     , "<<", "<<-"     ]@@ -181,11 +178,6 @@ reservedWord :: Monad m => ParsecT D u m Word reservedWord = anyWord `satisfying` (`elem` reservedWords) <?> "reserved word" --- | Parse a word that is not reserved.-unreservedWord :: Monad m => ParsecT D u m Word-unreservedWord = anyWord `satisfying` (`notElem` reservedWords)-    <?> "unreserved word"- -- | Parse an assignment builtin. assignBuiltin :: Monad m => ParsecT D u m Word assignBuiltin = anyWord `satisfying` (`elem` assignBuiltins)@@ -197,9 +189,18 @@  -- | Parse a variable name. name :: Monad m => ParsecT D u m String-name = (prettyText <$> unreservedWord) `satisfying` isName <?> "name"+name = (prettyText <$> anyWord) `satisfying` isName <?> "name"   where     isName s = case parse (I.name <* eof) "" (prettyText s) of+        Left _  -> False+        Right _ -> True++-- | Parse a function name.+functionName :: Monad m => ParsecT D u m String+functionName = (prettyText <$> anyWord) `satisfying` isFunctionName+   <?> "function name"+  where+    isFunctionName s = case parse (I.functionName <* eof) "" (prettyText s) of         Left _  -> False         Right _ -> True 
src/Language/Bash/Parse/Word.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE FlexibleContexts, RecordWildCards, CPP #-}+{-# LANGUAGE FlexibleContexts, RecordWildCards #-} -- | Word-level parsers. module Language.Bash.Parse.Word     ( skipSpace@@ -6,14 +6,13 @@     , word     , heredocWord     , name+    , functionName     , subscript     , assign     , operator     ) where -#if __GLASGOW_HASKELL__ >= 710 import Prelude hiding (Word)-#endif  import           Control.Applicative import           Control.Monad@@ -298,7 +297,7 @@  -- | Parse a word. word :: Stream s m Char => ParsecT s u m Word-word = spans " \t\n&|;()<>" True wordSpan+word = spans " \t\r\n&|;()<>" True wordSpan  -- | Parse a here document as a word. This parses substitutions, but not -- most quoting.@@ -315,6 +314,14 @@   where     nameStart  = letter   <|> char '_'     nameLetter = alphaNum <|> char '_'++-- | Parse a function name.+functionName :: Stream s m Char => ParsecT s u m String+functionName = (:) <$> nameStart <*> many nameLetter+  where+    nameStart  = letter   <|> specialChar+    nameLetter = alphaNum <|> specialChar+    specialChar = oneOf "_-.+!"  -- | Parse a special parameter name. specialName :: Stream s m Char => ParsecT s u m String
src/Language/Bash/Syntax.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE DeriveDataTypeable, OverloadedStrings, RecordWildCards, CPP #-}+{-# LANGUAGE DeriveDataTypeable, OverloadedStrings, RecordWildCards, DeriveGeneric #-} -- | Shell script types. module Language.Bash.Syntax     (@@ -25,12 +25,11 @@     , RValue(..)     ) where -#if __GLASGOW_HASKELL__ >= 710 import Prelude hiding ((<>), Word)-#endif  import Data.Data        (Data) import Data.Typeable    (Typeable)+import GHC.Generics     (Generic) import Text.PrettyPrint  import Language.Bash.Cond     (CondExpr)@@ -43,12 +42,12 @@ indent = nest 4 . pretty  -- | Render a @do...done@ block.-doDone :: Pretty a => a -> Doc-doDone a = "do" $+$ indent a $+$ "done"+doDone :: Pretty a => Doc -> a -> Doc+doDone header body = header <+> "do" $+$ indent body $+$ "done"  -- | A Bash command with redirections. data Command = Command ShellCommand [Redir]-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty Command where     pretty (Command c rs) = pretty c <+> pretty rs@@ -89,7 +88,7 @@     | Until List List       -- | A @while@ command.     | While List List-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty ShellCommand where     pretty (SimpleCommand as ws)  = pretty as <+> pretty ws@@ -107,11 +106,11 @@     pretty (Cond e) =         "[[" <+> pretty e <+> "]]"     pretty (For w ws l) =-        "for" <+> pretty w <+> pretty ws <> ";" $+$ doDone l+        doDone ("for" <+> pretty w <+> pretty ws <> ";") l     pretty (ArithFor s l) =-        "for" <+> "((" <> text s <> "))" $+$ doDone l+        doDone ("for" <+> "((" <> text s <> "))") l     pretty (Select w ws l) =-        "select" <+> pretty w <+> pretty ws <> ";" $+$ doDone l+        doDone ("select" <+> pretty w <+> pretty ws <> ";") l     pretty (Case w cs) =         "case" <+> pretty w <+> "in" $+$ (vcat $ map indent cs) $+$ "esac"     pretty (If p t f) =@@ -119,15 +118,15 @@         pretty (fmap (\l -> "else" $+$ indent l) f) $+$         "fi"     pretty (Until p l) =-        "until" <+> pretty p <+> doDone l+        doDone ("until" <+> pretty p) l     pretty (While p l) =-        "while" <+> pretty p <+> doDone l+        doDone ("while" <+> pretty p) l  -- | A word list or @\"$\@\"@. data WordList     = Args     | WordList [Word]-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty WordList where     pretty Args          = empty@@ -135,7 +134,7 @@  -- | A single case clause. data CaseClause = CaseClause [Word] List CaseTerm-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty CaseClause where     pretty (CaseClause ps l term) =@@ -148,7 +147,7 @@     = Break        -- ^ @;;@     | FallThrough  -- ^ @;&@     | Continue     -- ^ @;;&@-    deriving (Data, Eq, Ord, Read, Show, Typeable, Bounded, Enum)+    deriving (Data, Eq, Ord, Read, Show, Typeable, Bounded, Enum, Generic)  instance Operator CaseTerm where     operatorTable = zip [minBound .. maxBound] [";;", ";&", ";;&"]@@ -180,7 +179,7 @@           -- and command substitutions take place.         , hereDocument       :: Word         }-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty Redir where     pretty Redir{..} =@@ -203,7 +202,7 @@     = IONumber Int       -- | A variable @{/varname/}@ to allocate a file descriptor for.     | IOVar String-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty IODesc where     pretty (IONumber n) = int n@@ -221,7 +220,7 @@     | InAnd       -- ^ @\<&@     | OutAnd      -- ^ @\>&@     | InOut       -- ^ @\<\>@-    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded)+    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded, Generic)  instance Operator RedirOp where     operatorTable = zip [minBound .. maxBound]@@ -234,7 +233,7 @@ data HeredocOp     = Here       -- ^ @\<\<@     | HereStrip  -- ^ @\<\<-@-    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded)+    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded, Generic)  instance Operator HeredocOp where     operatorTable = zip [Here, HereStrip] ["<<", "<<-"]@@ -244,14 +243,14 @@  -- | A compound list of statements. newtype List = List [Statement]-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty List where     pretty (List as) = pretty as  -- | A single statement in a list. data Statement = Statement AndOr ListTerm-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty Statement where     pretty (Statement l Sequential)   = pretty l <> ";"@@ -266,7 +265,7 @@ data ListTerm     = Sequential    -- ^ @;@     | Asynchronous  -- ^ @&@-    deriving (Data, Eq, Ord, Read, Show, Typeable, Bounded, Enum)+    deriving (Data, Eq, Ord, Read, Show, Typeable, Bounded, Enum, Generic)  instance Operator ListTerm where     operatorTable =@@ -286,7 +285,7 @@     | And Pipeline AndOr       -- | A @||@ construct.     | Or Pipeline AndOr-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty AndOr where     pretty (Last p)  = pretty p@@ -305,7 +304,7 @@       -- @command1 |& command2@ is treated as a shorthand for       -- @command1 2>&1 | command2@.     , commands   :: [Command]-    } deriving (Data, Eq, Read, Show, Typeable)+    } deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty Pipeline where     pretty Pipeline{..} =@@ -316,7 +315,7 @@  -- | An assignment. data Assign = Assign Parameter AssignOp RValue-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty Assign where     pretty (Assign lhs op rhs) = pretty lhs <> pretty op <> pretty rhs@@ -325,7 +324,7 @@ data AssignOp     = Equals      -- ^ @=@     | PlusEquals  -- ^ @+=@-    deriving (Data, Eq, Ord, Read, Show, Typeable, Bounded, Enum)+    deriving (Data, Eq, Ord, Read, Show, Typeable, Bounded, Enum, Generic)  instance Operator AssignOp where     operatorTable = zip [Equals, PlusEquals] ["=", "+="]@@ -339,7 +338,7 @@     = RValue Word       -- | An array assignment, as @(subscript, word)@ pairs.     | RArray [(Maybe Word, Word)]-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty RValue where     pretty (RValue w)  = pretty w
src/Language/Bash/Word.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE     DeriveDataTypeable-  , CPP+  , DeriveGeneric   , OverloadedStrings   , RecordWildCards   , TypeSynonymInstances@@ -21,15 +21,15 @@     , ProcessSubstOp(..)       -- * Manipulation     , stringToWord+    , wordToString     , unquote     ) where -#if __GLASGOW_HASKELL__ >= 710 import Prelude hiding ((<>), Word)-#endif  import           Data.Data        (Data) import           Data.Typeable    (Typeable)+import           GHC.Generics     (Generic) import           Text.PrettyPrint  import           Language.Bash.Operator@@ -63,7 +63,7 @@     | CommandSubst String       -- | A process substitution.     | ProcessSubst ProcessSubstOp String-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty Span where     pretty (Char c)           = char c@@ -82,7 +82,7 @@  -- | A parameter name an optional subscript. data Parameter = Parameter String (Maybe Word)-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  instance Pretty Parameter where     pretty (Parameter s sub) = text s <> subscript sub@@ -161,7 +161,7 @@         , convertAll        :: Bool         , pattern           :: Word         }-    deriving (Data, Eq, Read, Show, Typeable)+    deriving (Data, Eq, Read, Show, Typeable, Generic)  prettyParameter :: Bool -> Parameter -> Doc -> Doc prettyParameter bang param suffix =@@ -204,7 +204,7 @@     | AltAssign   -- ^ '=', ':='     | AltError    -- ^ '?', ':?'     | AltReplace  -- ^ '+', ':+'-    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded)+    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded, Generic)  instance Operator AltOp where     operatorTable = zip [minBound .. maxBound] ["-", "=", "?", "+"]@@ -216,7 +216,7 @@ data LetterCaseOp     = ToLower     | ToUpper-    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded)+    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded, Generic)  instance Operator LetterCaseOp where     operatorTable = zip [ToLower, ToUpper] [",", "^"]@@ -228,7 +228,7 @@ data Direction     = Front     | Back-    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded)+    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded, Generic)  instance Pretty Direction where     pretty Front = "#"@@ -238,7 +238,7 @@ data ProcessSubstOp     = ProcessIn   -- ^ @\<@     | ProcessOut  -- ^ @\>@-    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded)+    deriving (Data, Eq, Ord, Read, Show, Typeable, Enum, Bounded, Generic)  instance Operator ProcessSubstOp where     operatorTable = zip [ProcessIn, ProcessOut] ["<", ">"]@@ -249,6 +249,14 @@ -- | Convert a string to an unquoted word. stringToWord :: String -> Word stringToWord = map Char++-- | If a word is a plain, unquoted string (e.g. the result of @stringToWord@),+-- returns @Just@ that string; otherwise, returns @Nothing@.+wordToString :: Word -> Maybe String+wordToString = traverse spanToChar+  where+    spanToChar (Char c) = Just c+    spanToChar _        = Nothing  -- | Remove all quoting characters from a word. unquote :: Word -> String
tests/Tests.hs view
@@ -64,16 +64,16 @@                Left err -> assertFailure $ "parseError: " ++ show err                Right ans -> expected @=? ans +wrapCommands :: [Command] -> List+wrapCommands = List . map (\c -> Statement (Last Pipeline {timed = False, timedPosix = False, inverted = False, commands = [c]}) Sequential)+ wrapCommand :: Command -> List-wrapCommand c = List [Statement (Last Pipeline {timed = False, timedPosix = False, inverted = False, commands = [c]}) Sequential]+wrapCommand c = wrapCommands [c] -tp :: TestName -> Command -> TestTree-tp source expected = testMatches (filter ((/=) '\n') source)+tp :: TestName -> List -> TestTree+tp source expected = testMatches (show source)                                  (Parse.parse "source" source)-                                 (wrapCommand expected)--expandString :: String -> [Span]-expandString = map Char+                                 expected  unittests :: TestTree unittests = testGroup "Unit tests"@@ -81,35 +81,69 @@     testMatches "testTest"       (Cond.parseTestExpr ["!", "-e", "\"asd\""])       (Cond.Not (Cond.Unary Cond.FileExists "\"asd\""))-  , tp "\"$(ls)\""+  , tp "\"$(ls)\"" $ wrapCommand       (Command (SimpleCommand [] [[Double [CommandSubst "ls"]]]) [])-  , tp "arguments=()"+  , tp "arguments=()" $ wrapCommand       (Command (SimpleCommand [Assign (Parameter "arguments" Nothing) Equals (RArray [])] []) [])-  , tp "cat <<EOF\nasd\\`\nEOF"+  , tp "cat <<EOF\nasd\\`\nEOF" $ wrapCommand        (Command-        (SimpleCommand [] [expandString "cat"])+        (SimpleCommand [] [stringToWord "cat"])         [Heredoc {heredocOp = Here,                   heredocDelim = "EOF",                   heredocDelimQuoted = False,                   hereDocument = [Char 'a',Char 's',Char 'd',Escape '`',                                   Char '\n']}])-  , tp "cat <<\"EOF\"\nasd\\`\nEOF"+  , tp "cat <<\"EOF\"\nasd\\`\nEOF" $ wrapCommand        (Command-        (SimpleCommand [] [expandString "cat"])+        (SimpleCommand [] [stringToWord "cat"])         [Heredoc {heredocOp = Here,                   heredocDelim = "EOF",                   heredocDelimQuoted = True,-                  hereDocument = expandString "asd\\`\n"}])-  , tp "echo $((2 + 2))"+                  hereDocument = stringToWord "asd\\`\n"}])+  , tp "echo $((2 + 2))" $ wrapCommand        (Command-        (SimpleCommand [] [expandString "echo", [ArithSubst "2 + 2"]])+        (SimpleCommand [] [stringToWord "echo", [ArithSubst "2 + 2"]])         [])-  , tp "((2 + 2))"+  , tp "((2 + 2))" $ wrapCommand        (Command (Arith "2 + 2") [])-  , tp "echo $(((2 + 2)))"+  , tp "echo $(((2 + 2)))" $ wrapCommand        (Command-        (SimpleCommand [] [expandString "echo", [ArithSubst "(2 + 2)"]])+        (SimpleCommand [] [stringToWord "echo", [ArithSubst "(2 + 2)"]])         [])+  , tp "function foo() { true; }" $ wrapCommand+       (Command+        (FunctionDef "foo"+          (wrapCommand (Command (SimpleCommand [] [stringToWord "true"]) []))) [])+  , tp "!awesome-function-name-2.0+() { true; }" $ wrapCommand+       (Command+        (FunctionDef "!awesome-function-name-2.0+"+          (wrapCommand (Command (SimpleCommand [] [stringToWord "true"]) []))) [])+  , tp "cat <<EOF\nasd\nEOF\ntrue" $ wrapCommands+       [Command+        (SimpleCommand [] [stringToWord "cat"])+        [Heredoc {heredocOp = Here,+                  heredocDelim = "EOF",+                  heredocDelimQuoted = False,+                  hereDocument = stringToWord "asd\n"}],+        Command (SimpleCommand [] [stringToWord "true"]) []]+  , tp "true\r\nfalse" $ wrapCommands+       [Command (SimpleCommand [] [stringToWord "true"]) [],+        Command (SimpleCommand [] [stringToWord "false"]) []]+  , tp "[[ ! ( -f foo ) ]]" $ wrapCommand+      (Command (Cond (Cond.Not (Cond.Unary Cond.RegularFile (stringToWord "foo")))) [])+  , tp "[[ foo =~ a( b )c\" d\\\"\"[e]*' f '\\ g ]]" $ wrapCommand+      (Command (Cond (Cond.Binary (stringToWord "foo") Cond.StrMatch (stringToWord "a( b )c\" d\\\"\"[e]*' f '\\ g"))) [])+  , tp "for function in foo; do true; done" $ wrapCommand+       (Command+        (For "function" (WordList [stringToWord "foo"])+          (wrapCommand (Command (SimpleCommand [] [stringToWord "true"]) []))) [])+  , tp "<<EOF\ncomment\nEOF" $ wrapCommand+       (Command+        (SimpleCommand [] [])+        [Heredoc {heredocOp = Here,+                  heredocDelim = "EOF",+                  heredocDelimQuoted = False,+                  hereDocument = stringToWord "comment\n"}])   ]  failingtests :: TestTree