language-bash 0.2.0 → 0.3.0
raw patch · 7 files changed
+359/−197 lines, 7 files
Files
- language-bash.cabal +3/−1
- src/Language/Bash/Cond.hs +101/−0
- src/Language/Bash/Operator.hs +28/−0
- src/Language/Bash/Parse.hs +36/−7
- src/Language/Bash/Parse/Packrat.hs +29/−12
- src/Language/Bash/Pretty.hs +4/−121
- src/Language/Bash/Syntax.hs +158/−56
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