1{- 2 Copyright 2012-2019 Vidar Holen 3 4 This file is part of ShellCheck. 5 https://www.shellcheck.net 6 7 ShellCheck is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation, either version 3 of the License, or 10 (at your option) any later version. 11 12 ShellCheck is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <https://www.gnu.org/licenses/>. 19-} 20{-# LANGUAGE DeriveGeneric, DeriveAnyClass, DeriveTraversable, PatternSynonyms #-} 21module ShellCheck.AST where 22 23import GHC.Generics (Generic) 24import Control.Monad.Identity 25import Control.DeepSeq 26import Text.Parsec 27import qualified ShellCheck.Regex as Re 28import Prelude hiding (id) 29 30newtype Id = Id Int deriving (Show, Eq, Ord, Generic, NFData) 31 32data Quoted = Quoted | Unquoted deriving (Show, Eq) 33data Dashed = Dashed | Undashed deriving (Show, Eq) 34data AssignmentMode = Assign | Append deriving (Show, Eq) 35newtype FunctionKeyword = FunctionKeyword Bool deriving (Show, Eq) 36newtype FunctionParentheses = FunctionParentheses Bool deriving (Show, Eq) 37data CaseType = CaseBreak | CaseFallThrough | CaseContinue deriving (Show, Eq) 38 39newtype Root = Root Token 40data Token = OuterToken Id (InnerToken Token) deriving (Show) 41 42data InnerToken t = 43 Inner_TA_Binary String t t 44 | Inner_TA_Assignment String t t 45 | Inner_TA_Variable String [t] 46 | Inner_TA_Expansion [t] 47 | Inner_TA_Sequence [t] 48 | Inner_TA_Trinary t t t 49 | Inner_TA_Unary String t 50 | Inner_TC_And ConditionType String t t 51 | Inner_TC_Binary ConditionType String t t 52 | Inner_TC_Group ConditionType t 53 | Inner_TC_Nullary ConditionType t 54 | Inner_TC_Or ConditionType String t t 55 | Inner_TC_Unary ConditionType String t 56 | Inner_TC_Empty ConditionType 57 | Inner_T_AND_IF 58 | Inner_T_AndIf t t 59 | Inner_T_Arithmetic t 60 | Inner_T_Array [t] 61 | Inner_T_IndexedElement [t] t 62 -- Store the index as string, and parse as arithmetic or string later 63 | Inner_T_UnparsedIndex SourcePos String 64 | Inner_T_Assignment AssignmentMode String [t] t 65 | Inner_T_Backgrounded t 66 | Inner_T_Backticked [t] 67 | Inner_T_Bang 68 | Inner_T_Banged t 69 | Inner_T_BraceExpansion [t] 70 | Inner_T_BraceGroup [t] 71 | Inner_T_CLOBBER 72 | Inner_T_Case 73 | Inner_T_CaseExpression t [(CaseType, [t], [t])] 74 | Inner_T_Condition ConditionType t 75 | Inner_T_DGREAT 76 | Inner_T_DLESS 77 | Inner_T_DLESSDASH 78 | Inner_T_DSEMI 79 | Inner_T_Do 80 | Inner_T_DollarArithmetic t 81 | Inner_T_DollarBraced Bool t 82 | Inner_T_DollarBracket t 83 | Inner_T_DollarDoubleQuoted [t] 84 | Inner_T_DollarExpansion [t] 85 | Inner_T_DollarSingleQuoted String 86 | Inner_T_DollarBraceCommandExpansion [t] 87 | Inner_T_Done 88 | Inner_T_DoubleQuoted [t] 89 | Inner_T_EOF 90 | Inner_T_Elif 91 | Inner_T_Else 92 | Inner_T_Esac 93 | Inner_T_Extglob String [t] 94 | Inner_T_FdRedirect String t 95 | Inner_T_Fi 96 | Inner_T_For 97 | Inner_T_ForArithmetic t t t [t] 98 | Inner_T_ForIn String [t] [t] 99 | Inner_T_Function FunctionKeyword FunctionParentheses String t 100 | Inner_T_GREATAND 101 | Inner_T_Glob String 102 | Inner_T_Greater 103 | Inner_T_HereDoc Dashed Quoted String [t] 104 | Inner_T_HereString t 105 | Inner_T_If 106 | Inner_T_IfExpression [([t],[t])] [t] 107 | Inner_T_In 108 | Inner_T_IoFile t t 109 | Inner_T_IoDuplicate t String 110 | Inner_T_LESSAND 111 | Inner_T_LESSGREAT 112 | Inner_T_Lbrace 113 | Inner_T_Less 114 | Inner_T_Literal String 115 | Inner_T_Lparen 116 | Inner_T_NEWLINE 117 | Inner_T_NormalWord [t] 118 | Inner_T_OR_IF 119 | Inner_T_OrIf t t 120 | Inner_T_ParamSubSpecialChar String -- e.g. '%' in ${foo%bar} or '/' in ${foo/bar/baz} 121 | Inner_T_Pipeline [t] [t] -- [Pipe separators] [Commands] 122 | Inner_T_ProcSub String [t] 123 | Inner_T_Rbrace 124 | Inner_T_Redirecting [t] t 125 | Inner_T_Rparen 126 | Inner_T_Script t [t] -- Shebang T_Literal, followed by script. 127 | Inner_T_Select 128 | Inner_T_SelectIn String [t] [t] 129 | Inner_T_Semi 130 | Inner_T_SimpleCommand [t] [t] 131 | Inner_T_SingleQuoted String 132 | Inner_T_Subshell [t] 133 | Inner_T_Then 134 | Inner_T_Until 135 | Inner_T_UntilExpression [t] [t] 136 | Inner_T_While 137 | Inner_T_WhileExpression [t] [t] 138 | Inner_T_Annotation [Annotation] t 139 | Inner_T_Pipe String 140 | Inner_T_CoProc (Maybe String) t 141 | Inner_T_CoProcBody t 142 | Inner_T_Include t 143 | Inner_T_SourceCommand t t 144 | Inner_T_BatsTest t t 145 deriving (Show, Eq, Functor, Foldable, Traversable) 146 147data Annotation = 148 DisableComment Integer Integer -- [from, to) 149 | EnableComment String 150 | SourceOverride String 151 | ShellOverride String 152 | SourcePath String 153 | ExternalSources Bool 154 deriving (Show, Eq) 155data ConditionType = DoubleBracket | SingleBracket deriving (Show, Eq) 156 157pattern T_AND_IF id = OuterToken id Inner_T_AND_IF 158pattern T_Bang id = OuterToken id Inner_T_Bang 159pattern T_Case id = OuterToken id Inner_T_Case 160pattern TC_Empty id typ = OuterToken id (Inner_TC_Empty typ) 161pattern T_CLOBBER id = OuterToken id Inner_T_CLOBBER 162pattern T_DGREAT id = OuterToken id Inner_T_DGREAT 163pattern T_DLESS id = OuterToken id Inner_T_DLESS 164pattern T_DLESSDASH id = OuterToken id Inner_T_DLESSDASH 165pattern T_Do id = OuterToken id Inner_T_Do 166pattern T_DollarSingleQuoted id str = OuterToken id (Inner_T_DollarSingleQuoted str) 167pattern T_Done id = OuterToken id Inner_T_Done 168pattern T_DSEMI id = OuterToken id Inner_T_DSEMI 169pattern T_Elif id = OuterToken id Inner_T_Elif 170pattern T_Else id = OuterToken id Inner_T_Else 171pattern T_EOF id = OuterToken id Inner_T_EOF 172pattern T_Esac id = OuterToken id Inner_T_Esac 173pattern T_Fi id = OuterToken id Inner_T_Fi 174pattern T_For id = OuterToken id Inner_T_For 175pattern T_Glob id str = OuterToken id (Inner_T_Glob str) 176pattern T_GREATAND id = OuterToken id Inner_T_GREATAND 177pattern T_Greater id = OuterToken id Inner_T_Greater 178pattern T_If id = OuterToken id Inner_T_If 179pattern T_In id = OuterToken id Inner_T_In 180pattern T_Lbrace id = OuterToken id Inner_T_Lbrace 181pattern T_Less id = OuterToken id Inner_T_Less 182pattern T_LESSAND id = OuterToken id Inner_T_LESSAND 183pattern T_LESSGREAT id = OuterToken id Inner_T_LESSGREAT 184pattern T_Literal id str = OuterToken id (Inner_T_Literal str) 185pattern T_Lparen id = OuterToken id Inner_T_Lparen 186pattern T_NEWLINE id = OuterToken id Inner_T_NEWLINE 187pattern T_OR_IF id = OuterToken id Inner_T_OR_IF 188pattern T_ParamSubSpecialChar id str = OuterToken id (Inner_T_ParamSubSpecialChar str) 189pattern T_Pipe id str = OuterToken id (Inner_T_Pipe str) 190pattern T_Rbrace id = OuterToken id Inner_T_Rbrace 191pattern T_Rparen id = OuterToken id Inner_T_Rparen 192pattern T_Select id = OuterToken id Inner_T_Select 193pattern T_Semi id = OuterToken id Inner_T_Semi 194pattern T_SingleQuoted id str = OuterToken id (Inner_T_SingleQuoted str) 195pattern T_Then id = OuterToken id Inner_T_Then 196pattern T_UnparsedIndex id pos str = OuterToken id (Inner_T_UnparsedIndex pos str) 197pattern T_Until id = OuterToken id Inner_T_Until 198pattern T_While id = OuterToken id Inner_T_While 199pattern TA_Assignment id op t1 t2 = OuterToken id (Inner_TA_Assignment op t1 t2) 200pattern TA_Binary id op t1 t2 = OuterToken id (Inner_TA_Binary op t1 t2) 201pattern TA_Expansion id t = OuterToken id (Inner_TA_Expansion t) 202pattern T_AndIf id t u = OuterToken id (Inner_T_AndIf t u) 203pattern T_Annotation id anns t = OuterToken id (Inner_T_Annotation anns t) 204pattern T_Arithmetic id c = OuterToken id (Inner_T_Arithmetic c) 205pattern T_Array id t = OuterToken id (Inner_T_Array t) 206pattern TA_Sequence id l = OuterToken id (Inner_TA_Sequence l) 207pattern T_Assignment id mode var indices value = OuterToken id (Inner_T_Assignment mode var indices value) 208pattern TA_Trinary id t1 t2 t3 = OuterToken id (Inner_TA_Trinary t1 t2 t3) 209pattern TA_Unary id op t1 = OuterToken id (Inner_TA_Unary op t1) 210pattern TA_Variable id str t = OuterToken id (Inner_TA_Variable str t) 211pattern T_Backgrounded id l = OuterToken id (Inner_T_Backgrounded l) 212pattern T_Backticked id list = OuterToken id (Inner_T_Backticked list) 213pattern T_Banged id l = OuterToken id (Inner_T_Banged l) 214pattern T_BatsTest id name t = OuterToken id (Inner_T_BatsTest name t) 215pattern T_BraceExpansion id list = OuterToken id (Inner_T_BraceExpansion list) 216pattern T_BraceGroup id l = OuterToken id (Inner_T_BraceGroup l) 217pattern TC_And id typ str t1 t2 = OuterToken id (Inner_TC_And typ str t1 t2) 218pattern T_CaseExpression id word cases = OuterToken id (Inner_T_CaseExpression word cases) 219pattern TC_Binary id typ op lhs rhs = OuterToken id (Inner_TC_Binary typ op lhs rhs) 220pattern TC_Group id typ token = OuterToken id (Inner_TC_Group typ token) 221pattern TC_Nullary id typ token = OuterToken id (Inner_TC_Nullary typ token) 222pattern T_Condition id typ token = OuterToken id (Inner_T_Condition typ token) 223pattern T_CoProcBody id t = OuterToken id (Inner_T_CoProcBody t) 224pattern T_CoProc id var body = OuterToken id (Inner_T_CoProc var body) 225pattern TC_Or id typ str t1 t2 = OuterToken id (Inner_TC_Or typ str t1 t2) 226pattern TC_Unary id typ op token = OuterToken id (Inner_TC_Unary typ op token) 227pattern T_DollarArithmetic id c = OuterToken id (Inner_T_DollarArithmetic c) 228pattern T_DollarBraceCommandExpansion id list = OuterToken id (Inner_T_DollarBraceCommandExpansion list) 229pattern T_DollarBraced id braced op = OuterToken id (Inner_T_DollarBraced braced op) 230pattern T_DollarBracket id c = OuterToken id (Inner_T_DollarBracket c) 231pattern T_DollarDoubleQuoted id list = OuterToken id (Inner_T_DollarDoubleQuoted list) 232pattern T_DollarExpansion id list = OuterToken id (Inner_T_DollarExpansion list) 233pattern T_DoubleQuoted id list = OuterToken id (Inner_T_DoubleQuoted list) 234pattern T_Extglob id str l = OuterToken id (Inner_T_Extglob str l) 235pattern T_FdRedirect id v t = OuterToken id (Inner_T_FdRedirect v t) 236pattern T_ForArithmetic id a b c group = OuterToken id (Inner_T_ForArithmetic a b c group) 237pattern T_ForIn id v w l = OuterToken id (Inner_T_ForIn v w l) 238pattern T_Function id a b name body = OuterToken id (Inner_T_Function a b name body) 239pattern T_HereDoc id d q str l = OuterToken id (Inner_T_HereDoc d q str l) 240pattern T_HereString id word = OuterToken id (Inner_T_HereString word) 241pattern T_IfExpression id conditions elses = OuterToken id (Inner_T_IfExpression conditions elses) 242pattern T_Include id script = OuterToken id (Inner_T_Include script) 243pattern T_IndexedElement id indices t = OuterToken id (Inner_T_IndexedElement indices t) 244pattern T_IoDuplicate id op num = OuterToken id (Inner_T_IoDuplicate op num) 245pattern T_IoFile id op file = OuterToken id (Inner_T_IoFile op file) 246pattern T_NormalWord id list = OuterToken id (Inner_T_NormalWord list) 247pattern T_OrIf id t u = OuterToken id (Inner_T_OrIf t u) 248pattern T_Pipeline id l1 l2 = OuterToken id (Inner_T_Pipeline l1 l2) 249pattern T_ProcSub id typ l = OuterToken id (Inner_T_ProcSub typ l) 250pattern T_Redirecting id redirs cmd = OuterToken id (Inner_T_Redirecting redirs cmd) 251pattern T_Script id shebang list = OuterToken id (Inner_T_Script shebang list) 252pattern T_SelectIn id v w l = OuterToken id (Inner_T_SelectIn v w l) 253pattern T_SimpleCommand id vars cmds = OuterToken id (Inner_T_SimpleCommand vars cmds) 254pattern T_SourceCommand id includer t_include = OuterToken id (Inner_T_SourceCommand includer t_include) 255pattern T_Subshell id l = OuterToken id (Inner_T_Subshell l) 256pattern T_UntilExpression id c l = OuterToken id (Inner_T_UntilExpression c l) 257pattern T_WhileExpression id c l = OuterToken id (Inner_T_WhileExpression c l) 258 259{-# COMPLETE T_AND_IF, T_Bang, T_Case, TC_Empty, T_CLOBBER, T_DGREAT, T_DLESS, T_DLESSDASH, T_Do, T_DollarSingleQuoted, T_Done, T_DSEMI, T_Elif, T_Else, T_EOF, T_Esac, T_Fi, T_For, T_Glob, T_GREATAND, T_Greater, T_If, T_In, T_Lbrace, T_Less, T_LESSAND, T_LESSGREAT, T_Literal, T_Lparen, T_NEWLINE, T_OR_IF, T_ParamSubSpecialChar, T_Pipe, T_Rbrace, T_Rparen, T_Select, T_Semi, T_SingleQuoted, T_Then, T_UnparsedIndex, T_Until, T_While, TA_Assignment, TA_Binary, TA_Expansion, T_AndIf, T_Annotation, T_Arithmetic, T_Array, TA_Sequence, T_Assignment, TA_Trinary, TA_Unary, TA_Variable, T_Backgrounded, T_Backticked, T_Banged, T_BatsTest, T_BraceExpansion, T_BraceGroup, TC_And, T_CaseExpression, TC_Binary, TC_Group, TC_Nullary, T_Condition, T_CoProcBody, T_CoProc, TC_Or, TC_Unary, T_DollarArithmetic, T_DollarBraceCommandExpansion, T_DollarBraced, T_DollarBracket, T_DollarDoubleQuoted, T_DollarExpansion, T_DoubleQuoted, T_Extglob, T_FdRedirect, T_ForArithmetic, T_ForIn, T_Function, T_HereDoc, T_HereString, T_IfExpression, T_Include, T_IndexedElement, T_IoDuplicate, T_IoFile, T_NormalWord, T_OrIf, T_Pipeline, T_ProcSub, T_Redirecting, T_Script, T_SelectIn, T_SimpleCommand, T_SourceCommand, T_Subshell, T_UntilExpression, T_WhileExpression #-} 260 261instance Eq Token where 262 OuterToken _ a == OuterToken _ b = a == b 263 264analyze :: Monad m => (Token -> m ()) -> (Token -> m ()) -> (Token -> m Token) -> Token -> m Token 265analyze f g i = 266 round 267 where 268 round t@(OuterToken id it) = do 269 f t 270 newIt <- traverse round it 271 g t 272 i (OuterToken id newIt) 273 274getId :: Token -> Id 275getId (OuterToken id _) = id 276 277blank :: Monad m => Token -> m () 278blank = const $ return () 279doAnalysis :: Monad m => (Token -> m ()) -> Token -> m Token 280doAnalysis f = analyze f blank return 281doStackAnalysis :: Monad m => (Token -> m ()) -> (Token -> m ()) -> Token -> m Token 282doStackAnalysis startToken endToken = analyze startToken endToken return 283doTransform :: (Token -> Token) -> Token -> Token 284doTransform i = runIdentity . analyze blank blank (return . i) 285 286