packages feed

language-bash 0.2.0 → 0.3.0

raw patch · 7 files changed

+359/−197 lines, 7 files

Files

language-bash.cabal view
@@ -1,5 +1,5 @@ name:               language-bash-version:            0.2.0+version:            0.3.0 category:           Language license:            BSD3 license-file:       LICENSE@@ -26,12 +26,14 @@   hs-source-dirs: src    exposed-modules:+    Language.Bash.Cond     Language.Bash.Parse     Language.Bash.Parse.Builder     Language.Bash.Pretty     Language.Bash.Syntax    other-modules:+    Language.Bash.Operator     Language.Bash.Parse.Internal     Language.Bash.Parse.Packrat 
+ src/Language/Bash/Cond.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE OverloadedStrings #-}+-- | Bash conditional commands.+module Language.Bash.Cond+    ( CondExpr(..)+    , UnaryOp(..)+    , BinaryOp(..)+    ) where++import Text.PrettyPrint++import Language.Bash.Operator+import Language.Bash.Pretty++-- | Bash conditional expressions.+data CondExpr+    = Unary UnaryOp String+    | Binary String BinaryOp String+    | Not CondExpr+    | And CondExpr CondExpr+    | Or CondExpr CondExpr+    deriving (Eq, Read, Show)++instance Pretty CondExpr where+    pretty = go (0 :: Int)+      where+        go _ (Unary op a)    = pretty op <+> text a+        go _ (Binary a op b) = text a <+> pretty op <+> text b+        go _ (Not e)         = "!" <+> go 2 e+        go p (And e1 e2)     = paren (p > 1) $ go 1 e1 <+> "&&" <+> go 1 e2+        go p (Or e1 e2)      = paren (p > 0) $ go 0 e1 <+> "||" <+> go 0 e2++        paren False d = d+        paren True d  = "(" <+> d <+> ")"++-- | Unary conditional operators.+data UnaryOp+    = BlockFile      -- ^ @-b@+    | CharacterFile  -- ^ @-c@+    | Directory      -- ^ @-d@+    | FileExists     -- ^ @-e@, @-a@+    | RegularFile    -- ^ @-f@+    | SetGID         -- ^ @-g@+    | Sticky         -- ^ @-k@+    | NamedPipe      -- ^ @-p@+    | Readable       -- ^ @-r@+    | FileSize       -- ^ @-s@+    | Terminal       -- ^ @-t@+    | SetUID         -- ^ @-u@+    | Writable       -- ^ @-w@+    | Executable     -- ^ @-x@+    | GroupOwned     -- ^ @-G@+    | SymbolicLink   -- ^ @-L@, @-h@+    | Modified       -- ^ @-N@+    | UserOwned      -- ^ @-O@+    | Socket         -- ^ @-S@+    | Optname        -- ^ @-o@+    | Varname        -- ^ @-v@+    | ZeroString     -- ^ @-z@+    | NonzeroString  -- ^ @-n /string/@ or @/string/@+    deriving (Eq, Ord, Read, Show, Enum, Bounded)++instance Operator UnaryOp where+    operatorTable =+        zip [minBound .. maxBound]+            (map (\c -> ['-', c]) "bcdefgkprstuwxGLNOSovzn") +++        [ (FileExists  , "-a")+        , (SymbolicLink, "-h")+        ]++instance Pretty UnaryOp where+    pretty = prettyOperator++-- | Binary conditional operators.+data BinaryOp+    = SameFile   -- ^ @-ef@+    | NewerThan  -- ^ @-nt@+    | OlderThan  -- ^ @-ot@+    | StrMatch   -- ^ @=~@+    | StrEQ      -- ^ @==@, @=@+    | StrNE      -- ^ @!=@+    | StrLT      -- ^ @<@+    | StrGT      -- ^ @>@+    | ArithEQ    -- ^ @-eq@+    | ArithNE    -- ^ @-ne@+    | ArithLT    -- ^ @-lt@+    | ArithLE    -- ^ @-le@+    | ArithGT    -- ^ @-gt@+    | ArithGE    -- ^ @-ge@+    deriving (Eq, Ord, Read, Show, Enum, Bounded)++instance Operator BinaryOp where+    operatorTable =+        zip [minBound .. maxBound]+            [ "-ef", "-nt", "-ot"+            , "=~", "==", "!=", "<", ">"+            , "-eq", "-ne", "-lt", "-le", "-gt", "-ge"+            ] +++        [ (StrEQ, "=") ]++instance Pretty BinaryOp where+    pretty = prettyOperator
+ src/Language/Bash/Operator.hs view
@@ -0,0 +1,28 @@+-- | Bash has a lot of operators.+module Language.Bash.Operator+    ( Operator(..)+    , select+    , selectOperator+    , prettyOperator+    ) where++import Control.Applicative+import Text.PrettyPrint     hiding (empty)++import Language.Bash.Pretty++-- | String operators.+class Eq a => Operator a where+    operatorTable :: [(a, String)]++-- | Select the first element that succeeds from a table.+select :: Alternative f => (b -> f c) -> [(a, b)] -> f a+select p = foldr (<|>) empty . map (\(a, b) -> a <$ p b)++-- | Select an operator from the 'String' it represents.+selectOperator :: (Alternative f, Operator a) => (String -> f c) -> f a+selectOperator p = select p operatorTable++-- | Render an operator.+prettyOperator :: Operator a => a -> Doc+prettyOperator = pretty . flip lookup operatorTable
src/Language/Bash/Parse.hs view
@@ -10,9 +10,12 @@ import           Text.Parsec.Char             hiding (newline) import           Text.Parsec.Combinator       hiding (optional) import           Text.Parsec.Error            (ParseError)+import           Text.Parsec.Expr import           Text.Parsec.Pos import           Text.Parsec.Prim             hiding (parse, (<|>)) +import qualified Language.Bash.Cond           as Cond+import           Language.Bash.Operator import qualified Language.Bash.Parse.Internal as I import           Language.Bash.Parse.Packrat import           Language.Bash.Syntax@@ -112,20 +115,24 @@             }      heredocRedir = do-        (strip, op) <- heredocOperator+        strip <- heredocOperator         w <- anyWord         let delim = I.unquote w         h <- heredoc strip delim         return Heredoc-            { redirOp            = op+            { heredocStrip       = strip             , heredocDelim       = delim             , heredocDelimQuoted = delim /= w-            , document           = h+            , hereDocument       = h             } -    heredocOperator = (,) False <$> operator "<<"-                  <|> (,) True  <$> operator "<<-"+    redirOperator = selectOperator word+                <?> "redirection operator" +    heredocOperator = False <$ operator "<<"+                  <|> True  <$ operator "<<-"+                  <?> "here document operator"+ -- | Skip a list of redirections. redirList :: Parser [Redir] redirList = many redir@@ -214,7 +221,7 @@      addRedir (Command c rs) = Command c (stderrRedir : rs) -    stderrRedir = Redir (Just (IONumber 2)) ">&" "1"+    stderrRedir = Redir (Just (IONumber 2)) OutAnd "1"  -- | Parse a compound list of commands. compoundList :: Parser List@@ -337,7 +344,29 @@  -- | Parse a conditional command. condCommand :: Parser ShellCommand-condCommand = Cond <$ word "[[" <*> many1 condWord <* word "]]"+condCommand = Cond <$ word "[[" <*> expr <* word "]]"+  where+    expr = buildExpressionParser opTable term++    term = word "(" *> expr <* word ")"+       <|> Cond.Unary <$> unaryOp <*> condWord+       <|> (condWord >>= wordTerm)++    wordTerm w = Cond.Binary w <$> binaryOp <*> condWord+             <|> pure (Cond.Unary Cond.NonzeroString w)++    opTable =+        [ [Prefix (Cond.Not <$ word "!")]+        , [Infix  (Cond.And <$ operator "&&") AssocLeft]+        , [Infix  (Cond.Or  <$ operator "||") AssocLeft]+        ]++    condWord = (anyWord <|> anyOperator) `satisfying` (/= "]]") <?> "word"++    condOperator op = condWord `satisfying` (== op) <?> op++    unaryOp  = selectOperator condOperator <?> "unary operator"+    binaryOp = selectOperator condOperator <?> "binary operator"  ------------------------------------------------------------------------------- -- Coprocesses
src/Language/Bash/Parse/Packrat.hs view
@@ -12,6 +12,8 @@     ( -- * Packrat parsing       D     , pack+      -- * Tokens+    , satisfying       -- * Whitespace     , I.skipSpace       -- * Words@@ -19,14 +21,12 @@     , word     , reservedWord     , unreservedWord-    , condWord     , assignBuiltin     , ioDesc     , name       -- * Operators     , anyOperator     , operator-    , redirOperator       -- * Assignments     , assign       -- * Arithmetic expressions@@ -99,7 +99,7 @@         _ioDesc      = result $ token >>= \case             TIODesc desc -> return desc             _            -> empty-        _anyOperator = result $ I.operator normalOps <* I.skipSpace+        _anyOperator = result $ I.operator operators <* I.skipSpace         _assign      = result $ I.assign <* I.skipSpace         _uncons      = case s of             []     -> Nothing@@ -116,6 +116,32 @@     t <- a     if p t then return t else unexpected (show t) +-- | Shell reserved words.+reservedWords :: [Word]+reservedWords =+    [ "!", "[[", "]]", "{", "}"+    , "if", "then", "else", "elif", "fi"+    , "case", "esac", "for", "select", "while", "until"+    , "in", "do", "done", "time", "function"+    ]++-- | Shell assignment builtins. These builtins can take assignments as+-- arguments.+assignBuiltins :: [Word]+assignBuiltins =+    [ "alias", "declare", "export", "eval"+    , "let", "local", "readonly", "typeset"+    ]++-- | All Bash operators.+operators :: [String]+operators =+    [ "(", ")", ";;", ";&", ";;&"+    , "|", "|&", "||", "&&", ";", "&", "\n"+    , "<", ">", ">|", ">>", "&>", "&>>", "<<<", "<&", ">&", "<>"+    , "<<", "<<-"+    ]+ -- | Parse a descriptor. descriptor :: Stream s m Char => ParsecT s u m IODesc descriptor = IONumber . read <$> many1 digit@@ -142,10 +168,6 @@ unreservedWord = anyWord `satisfying` (`notElem` reservedWords)     <?> "unreserved word" --- | Parse a word in a @[[...]]@ command.-condWord :: Monad m => ParsecT D u m Word-condWord = (anyWord <|> anyOperator) `satisfying` (/= "]]")- -- | Parse an assignment builtin. assignBuiltin :: Monad m => ParsecT D u m Word assignBuiltin = anyWord `satisfying` (`elem` assignBuiltins)@@ -170,11 +192,6 @@ -- | Parse a given operator. operator :: Monad m => String -> ParsecT D u m String operator op = anyOperator `satisfying` (== op) <?> op---- | Parse a non-heredoc redirection operator.-redirOperator :: Monad m => ParsecT D u m String-redirOperator = anyOperator `satisfying` (`elem` redirOps)-    <?> "redirection operator"  -- | Parse an assignment. assign :: Monad m => ParsecT D u m Assign
src/Language/Bash/Pretty.hs view
@@ -1,15 +1,12 @@-{-# LANGUAGE OverloadedStrings, RecordWildCards #-} -- | Pretty-printing of Bash scripts. This tries to stay close to the format -- used by the Bash builtin @declare -f@. module Language.Bash.Pretty     ( Pretty(..)-    , render+    , prettyText     ) where  import Text.PrettyPrint -import Language.Bash.Syntax- -- | A class of types which may be pretty-printed. class Pretty a where     -- | Pretty-print to a 'Doc'.@@ -36,120 +33,6 @@ instance (Pretty a, Pretty b) => Pretty (Either a b) where     pretty = either pretty pretty -instance Pretty Command where-    pretty (Command c rs) = pretty c <+> pretty rs--instance Pretty Redir where-    pretty Redir{..} =-        pretty redirDesc <> text redirOp <> text redirTarget-    pretty Heredoc{..} =-        text redirOp <>-        text (if heredocDelimQuoted-              then "'" ++ heredocDelim ++ "'"-              else heredocDelim) <> "\n" <>-        text document <> text heredocDelim <> "\n"--    prettyList = foldr f empty-      where-        f a@(Redir{})   b = pretty a <+> b-        f a@(Heredoc{}) b = pretty a <> b--instance Pretty IODesc where-    pretty (IONumber n) = int n-    pretty (IOVar n)    = "{" <> text n <> "}"--instance Pretty ShellCommand where-    pretty (SimpleCommand as ws)  = pretty as <+> pretty ws-    pretty (AssignBuiltin w args) = text w <+> pretty args-    pretty (FunctionDef name l) =-        text name <+> "()" $+$ pretty (Group l)-    pretty (Coproc name c) =-        "coproc" <+> text name <+> pretty c-    pretty (Subshell l) =-        "(" <+> pretty l <+> ")"-    pretty (Group l) =-        "{" $+$ indent l $+$ "}"-    pretty (Arith s) =-        "((" <> text s <> "))"-    pretty (Cond ws) =-        "[[" <+> pretty ws <+> "]]"-    pretty (For w ws l) =-        "for" <+> text w <+> "in" <+> pretty ws <> ";" $+$ doDone l-    pretty (ArithFor s l) =-        "for" <+> "((" <> text s <> "))" $+$ doDone l-    pretty (Select w ws l) =-        "select" <+> text w <+> "in" <+> pretty ws <> ";" $+$ doDone l-    pretty (Case w cs) =-        "case" <+> text w <+> "in" $+$ indent cs $+$ "esac"-    pretty (If p t f) =-        "if" <+> pretty p <+> "then" $+$ indent t $+$-        pretty (fmap (\l -> "else" $+$ indent l) f) $+$-        "fi"-    pretty (Until p l) =-        "until" <+> pretty p <+> doDone l-    pretty (While p l) =-        "while" <+> pretty p <+> doDone l--instance Pretty CaseClause where-    pretty (CaseClause ps l term) =-        hcat (punctuate " | " (map text ps)) <> ")" $+$-        indent l $+$-        pretty term--instance Pretty CaseTerm where-    pretty Break       = ";;"-    pretty FallThrough = ";&"-    pretty Continue    = ";;&"--instance Pretty List where-    pretty (List as) = pretty as--instance Pretty Statement where-    pretty (Statement l Sequential)   = pretty l <> ";"-    pretty (Statement l Asynchronous) = pretty l <+> "&"--    prettyList = foldr f empty-      where-        f a@(Statement _ Sequential)   b = pretty a $+$ b-        f a@(Statement _ Asynchronous) b = pretty a <+> b--instance Pretty ListTerm where-    pretty Sequential   = ";"-    pretty Asynchronous = "&"--instance Pretty AndOr where-    pretty (Last p)  = pretty p-    pretty (And p a) = pretty p <+> "&&" <+> pretty a-    pretty (Or p a)  = pretty p <+> "||" <+> pretty a--instance Pretty Pipeline where-    pretty Pipeline{..} =-        (if timed      then "time" else empty) <+>-        (if timedPosix then "-p"   else empty) <+>-        (if inverted   then "!"    else empty) <+>-        pretty commands--instance Pretty Assign where-    pretty (Assign lhs op rhs) = pretty lhs <> pretty op <> pretty rhs--instance Pretty LValue where-    pretty (LValue name sub) =-        text name <> pretty (fmap (\s -> "[" ++ s ++ "]") sub)--instance Pretty AssignOp where-    pretty Equals     = "="-    pretty PlusEquals = "+="--instance Pretty RValue where-    pretty (RValue w)  = text w-    pretty (RArray rs) = "(" <> hsep (map f rs) <> ")"-      where-        f (sub, w) = pretty (fmap (\s -> "[" ++ s ++ "]=") sub) <> text w---- | Indent by 4 columns.-indent :: Pretty a => a -> Doc-indent = nest 4 . pretty---- | Render a @do...done@ block.-doDone :: Pretty a => a -> Doc-doDone a = "do" $+$ indent a $+$ "done"+-- | Pretty-print to a 'String'.+prettyText :: Pretty a => a -> String+prettyText = render . pretty
src/Language/Bash/Syntax.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings, RecordWildCards #-} -- | Shell script types. module Language.Bash.Syntax     ( -- * Syntax@@ -8,6 +9,7 @@     , Command(..)     , Redir(..)     , IODesc(..)+    , RedirOp(..)     , ShellCommand(..)     , CaseClause(..)     , CaseTerm(..)@@ -22,15 +24,22 @@     , LValue(..)     , AssignOp(..)     , RValue(..)-      -- * Syntax elements-    , reservedWords-    , assignBuiltins-    , redirOps-    , heredocOps-    , controlOps-    , normalOps     ) where +import Text.PrettyPrint++import Language.Bash.Cond     (CondExpr)+import Language.Bash.Operator+import Language.Bash.Pretty++-- | Indent by 4 columns.+indent :: Pretty a => a -> Doc+indent = nest 4 . pretty++-- | Render a @do...done@ block.+doDone :: Pretty a => a -> Doc+doDone a = "do" $+$ indent a $+$ "done"+ -- | A Bash word. type Word = String @@ -41,6 +50,9 @@ data Command = Command ShellCommand [Redir]     deriving (Eq, Read, Show) +instance Pretty Command where+    pretty (Command c rs) = pretty c <+> pretty rs+ -- | A redirection. data Redir     -- | A redirection.@@ -48,22 +60,39 @@         { -- | An optional file descriptor.           redirDesc   :: Maybe IODesc           -- | The redirection operator.-        , redirOp     :: String+        , redirOp     :: RedirOp           -- | The redirection target.         , redirTarget :: Word         }     -- | A here document.     | Heredoc-        { redirOp            :: String+        { -- | 'True' if the here document was stripped of leading tabs using+          -- the @\<\<-@ operator.+          heredocStrip       :: Bool           -- | The here document delimiter.         , heredocDelim       :: String           -- | 'True' if the delimiter was quoted.         , heredocDelimQuoted :: Bool           -- | The document itself.-        , document           :: String+        , hereDocument       :: String         }     deriving (Eq, Read, Show) +instance Pretty Redir where+    pretty Redir{..} =+        pretty redirDesc <> pretty redirOp <> text redirTarget+    pretty Heredoc{..} =+        text (if heredocStrip then "<<-" else "<<") <>+        text (if heredocDelimQuoted+              then "'" ++ heredocDelim ++ "'"+              else heredocDelim) <> "\n" <>+        text hereDocument <> text heredocDelim <> "\n"++    prettyList = foldr f empty+      where+        f a@Redir{}   b = pretty a <+> b+        f a@Heredoc{} b = pretty a <> b+ -- | A redirection file descriptor. data IODesc     -- | A file descriptor number.@@ -72,6 +101,32 @@     | IOVar String     deriving (Eq, Read, Show) +instance Pretty IODesc where+    pretty (IONumber n) = int n+    pretty (IOVar n)    = "{" <> text n <> "}"++-- | A redirection operator.+data RedirOp+    = In          -- ^ @\<@+    | Out         -- ^ @\>@+    | OutOr       -- ^ @\>|@+    | Append      -- ^ @\>\>@+    | AndOut      -- ^ @&\>@+    | AndAppend   -- ^ @&\>\>@+    | HereString  -- ^ @\<\<\<@+    | InAnd       -- ^ @\<&@+    | OutAnd      -- ^ @\>&@+    | InOut       -- ^ @\<\>@+    deriving (Eq, Ord, Read, Show, Enum, Bounded)++instance Operator RedirOp where+    operatorTable = zip [minBound .. maxBound]+        ["<", ">", ">|", ">>", "&>", "&>>", "<<<", "<&", ">&", "<>"]++-- | A redirection operator.+instance Pretty RedirOp where+    pretty = prettyOperator+ -- | A Bash command. data ShellCommand     -- | A simple command consisting of assignments followed by words.@@ -90,7 +145,7 @@     -- | An arithmetic expression.     | Arith String     -- | A Bash @[[...]]@ conditional expression.-    | Cond [Word]+    | Cond CondExpr     -- | A @for /word/ in /words/@ command. If @in /words/@ is absent,     -- the word list defaults to @\"$\@\"@.     | For Word [Word] List@@ -110,28 +165,80 @@     | While List List     deriving (Eq, Read, Show) +instance Pretty ShellCommand where+    pretty (SimpleCommand as ws)  = pretty as <+> pretty ws+    pretty (AssignBuiltin w args) = text w <+> pretty args+    pretty (FunctionDef name l) =+        text name <+> "()" $+$ pretty (Group l)+    pretty (Coproc name c) =+        "coproc" <+> text name <+> pretty c+    pretty (Subshell l) =+        "(" <+> pretty l <+> ")"+    pretty (Group l) =+        "{" $+$ indent l $+$ "}"+    pretty (Arith s) =+        "((" <> text s <> "))"+    pretty (Cond e) =+        "[[" <+> pretty e <+> "]]"+    pretty (For w ws l) =+        "for" <+> text w <+> "in" <+> pretty ws <> ";" $+$ doDone l+    pretty (ArithFor s l) =+        "for" <+> "((" <> text s <> "))" $+$ doDone l+    pretty (Select w ws l) =+        "select" <+> text w <+> "in" <+> pretty ws <> ";" $+$ doDone l+    pretty (Case w cs) =+        "case" <+> text w <+> "in" $+$ indent cs $+$ "esac"+    pretty (If p t f) =+        "if" <+> pretty p <+> "then" $+$ indent t $+$+        pretty (fmap (\l -> "else" $+$ indent l) f) $+$+        "fi"+    pretty (Until p l) =+        "until" <+> pretty p <+> doDone l+    pretty (While p l) =+        "while" <+> pretty p <+> doDone l+ -- | A single case clause. data CaseClause = CaseClause [Word] List CaseTerm     deriving (Eq, Read, Show) +instance Pretty CaseClause where+    pretty (CaseClause ps l term) =+        hcat (punctuate " | " (map text ps)) <> ")" $+$+        indent l $+$+        pretty term+ -- | A case clause terminator. data CaseTerm-    -- | The @;;@ operator.-    = Break-    -- | The @;&@ operator.-    | FallThrough-    -- | The @;;&@ operator.-    | Continue+    = Break        -- ^ @;;@+    | FallThrough  -- ^ @;&@+    | Continue     -- ^ @;;&@     deriving (Eq, Ord, Read, Show, Bounded, Enum) +instance Pretty CaseTerm where+    pretty Break       = ";;"+    pretty FallThrough = ";&"+    pretty Continue    = ";;&"+ -- | A compound list of statements. newtype List = List [Statement]     deriving (Eq, Read, Show) +instance Pretty List where+    pretty (List as) = pretty as+ -- | A single statement in a list. data Statement = Statement AndOr ListTerm     deriving (Eq, Read, Show) +instance Pretty Statement where+    pretty (Statement l Sequential)   = pretty l <> ";"+    pretty (Statement l Asynchronous) = pretty l <+> "&"++    prettyList = foldr f empty+      where+        f a@(Statement _ Sequential)   b = pretty a $+$ b+        f a@(Statement _ Asynchronous) b = pretty a <+> b+ -- | A statement terminator. data ListTerm     -- | The @;@ operator.@@ -140,6 +247,10 @@     | Asynchronous     deriving (Eq, Ord, Read, Show, Bounded, Enum) +instance Pretty ListTerm where+    pretty Sequential   = ";"+    pretty Asynchronous = "&"+ -- | A right-associative list of pipelines. data AndOr     -- | The last pipeline of a list.@@ -150,6 +261,11 @@     | Or Pipeline AndOr     deriving (Eq, Read, Show) +instance Pretty AndOr where+    pretty (Last p)  = pretty p+    pretty (And p a) = pretty p <+> "&&" <+> pretty a+    pretty (Or p a)  = pretty p <+> "||" <+> pretty a+ -- | A (possibly timed or inverted) pipeline, linked with @|@ or @|&@. data Pipeline = Pipeline     { -- | 'True' if the pipeline is timed with @time@.@@ -164,22 +280,38 @@     , commands   :: [Command]     } deriving (Eq, Read, Show) +instance Pretty Pipeline where+    pretty Pipeline{..} =+        (if timed      then "time" else empty) <+>+        (if timedPosix then "-p"   else empty) <+>+        (if inverted   then "!"    else empty) <+>+        pretty commands+ -- | An assignment. data Assign = Assign LValue AssignOp RValue     deriving (Eq, Read, Show) +instance Pretty Assign where+    pretty (Assign lhs op rhs) = pretty lhs <> pretty op <> pretty rhs+ -- | The left side of an assignment. data LValue = LValue String (Maybe Subscript)     deriving (Eq, Read, Show) +instance Pretty LValue where+    pretty (LValue name sub) =+        text name <> pretty (fmap (\s -> "[" ++ s ++ "]") sub)+ -- | An assignment operator. data AssignOp-    -- | The @=@ operator.-    = Equals-    -- | The @+=@ operator.-    | PlusEquals+    = Equals      -- ^ @=@+    | PlusEquals  -- ^ @+=@     deriving (Eq, Ord, Read, Show, Bounded, Enum) +instance Pretty AssignOp where+    pretty Equals     = "="+    pretty PlusEquals = "+="+ -- | The right side of an assignment. data RValue     -- | A simple word.@@ -188,38 +320,8 @@     | RArray [(Maybe Subscript, Word)]     deriving (Eq, Read, Show) --- | Shell reserved words.-reservedWords :: [Word]-reservedWords =-    [ "!", "[[", "]]", "{", "}"-    , "if", "then", "else", "elif", "fi"-    , "case", "esac", "for", "select", "while", "until"-    , "in", "do", "done", "time", "function"-    ]---- | Shell assignment builtins. These builtins can take assignments as--- arguments.-assignBuiltins :: [Word]-assignBuiltins =-    [ "alias", "declare", "export", "eval"-    , "let", "local", "readonly", "typeset"-    ]---- | Redirection operators, not including here document operators.-redirOps :: [String]-redirOps = [">", "<", ">>", ">|", "<>", "<<<", "<&", ">&", "&>", "&>>"]---- | Here document operators.-heredocOps :: [String]-heredocOps = ["<<", "<<-"]---- | Shell control operators.-controlOps :: [String]-controlOps =-    [ "(", ")", ";;", ";&", ";;&"-    , "|", "|&", "||", "&&", ";", "&", "\n"-    ]---- | All normal operators.-normalOps :: [String]-normalOps = redirOps ++ heredocOps ++ controlOps+instance Pretty RValue where+    pretty (RValue w)  = text w+    pretty (RArray rs) = "(" <> hsep (map f rs) <> ")"+      where+        f (sub, w) = pretty (fmap (\s -> "[" ++ s ++ "]=") sub) <> text w