github.com/aavshr/aws-sdk-go@v1.41.3/internal/ini/ini_parser.go (about) 1 package ini 2 3 import ( 4 "fmt" 5 "io" 6 ) 7 8 // ParseState represents the current state of the parser. 9 type ParseState uint 10 11 // State enums for the parse table 12 const ( 13 InvalidState ParseState = iota 14 // stmt -> value stmt' 15 StatementState 16 // stmt' -> MarkComplete | op stmt 17 StatementPrimeState 18 // value -> number | string | boolean | quoted_string 19 ValueState 20 // section -> [ section' 21 OpenScopeState 22 // section' -> value section_close 23 SectionState 24 // section_close -> ] 25 CloseScopeState 26 // SkipState will skip (NL WS)+ 27 SkipState 28 // SkipTokenState will skip any token and push the previous 29 // state onto the stack. 30 SkipTokenState 31 // comment -> # comment' | ; comment' 32 // comment' -> MarkComplete | value 33 CommentState 34 // MarkComplete state will complete statements and move that 35 // to the completed AST list 36 MarkCompleteState 37 // TerminalState signifies that the tokens have been fully parsed 38 TerminalState 39 ) 40 41 // parseTable is a state machine to dictate the grammar above. 42 var parseTable = map[ASTKind]map[TokenType]ParseState{ 43 ASTKindStart: { 44 TokenLit: StatementState, 45 TokenSep: OpenScopeState, 46 TokenWS: SkipTokenState, 47 TokenNL: SkipTokenState, 48 TokenComment: CommentState, 49 TokenNone: TerminalState, 50 }, 51 ASTKindCommentStatement: { 52 TokenLit: StatementState, 53 TokenSep: OpenScopeState, 54 TokenWS: SkipTokenState, 55 TokenNL: SkipTokenState, 56 TokenComment: CommentState, 57 TokenNone: MarkCompleteState, 58 }, 59 ASTKindExpr: { 60 TokenOp: StatementPrimeState, 61 TokenLit: ValueState, 62 TokenSep: OpenScopeState, 63 TokenWS: ValueState, 64 TokenNL: SkipState, 65 TokenComment: CommentState, 66 TokenNone: MarkCompleteState, 67 }, 68 ASTKindEqualExpr: { 69 TokenLit: ValueState, 70 TokenSep: ValueState, 71 TokenOp: ValueState, 72 TokenWS: SkipTokenState, 73 TokenNL: SkipState, 74 TokenNone: SkipState, 75 }, 76 ASTKindStatement: { 77 TokenLit: SectionState, 78 TokenSep: CloseScopeState, 79 TokenWS: SkipTokenState, 80 TokenNL: SkipTokenState, 81 TokenComment: CommentState, 82 TokenNone: MarkCompleteState, 83 }, 84 ASTKindExprStatement: { 85 TokenLit: ValueState, 86 TokenSep: ValueState, 87 TokenOp: ValueState, 88 TokenWS: ValueState, 89 TokenNL: MarkCompleteState, 90 TokenComment: CommentState, 91 TokenNone: TerminalState, 92 TokenComma: SkipState, 93 }, 94 ASTKindSectionStatement: { 95 TokenLit: SectionState, 96 TokenOp: SectionState, 97 TokenSep: CloseScopeState, 98 TokenWS: SectionState, 99 TokenNL: SkipTokenState, 100 }, 101 ASTKindCompletedSectionStatement: { 102 TokenWS: SkipTokenState, 103 TokenNL: SkipTokenState, 104 TokenLit: StatementState, 105 TokenSep: OpenScopeState, 106 TokenComment: CommentState, 107 TokenNone: MarkCompleteState, 108 }, 109 ASTKindSkipStatement: { 110 TokenLit: StatementState, 111 TokenSep: OpenScopeState, 112 TokenWS: SkipTokenState, 113 TokenNL: SkipTokenState, 114 TokenComment: CommentState, 115 TokenNone: TerminalState, 116 }, 117 } 118 119 // ParseAST will parse input from an io.Reader using 120 // an LL(1) parser. 121 func ParseAST(r io.Reader) ([]AST, error) { 122 lexer := iniLexer{} 123 tokens, err := lexer.Tokenize(r) 124 if err != nil { 125 return []AST{}, err 126 } 127 128 return parse(tokens) 129 } 130 131 // ParseASTBytes will parse input from a byte slice using 132 // an LL(1) parser. 133 func ParseASTBytes(b []byte) ([]AST, error) { 134 lexer := iniLexer{} 135 tokens, err := lexer.tokenize(b) 136 if err != nil { 137 return []AST{}, err 138 } 139 140 return parse(tokens) 141 } 142 143 func parse(tokens []Token) ([]AST, error) { 144 start := Start 145 stack := newParseStack(3, len(tokens)) 146 147 stack.Push(start) 148 s := newSkipper() 149 150 loop: 151 for stack.Len() > 0 { 152 k := stack.Pop() 153 154 var tok Token 155 if len(tokens) == 0 { 156 // this occurs when all the tokens have been processed 157 // but reduction of what's left on the stack needs to 158 // occur. 159 tok = emptyToken 160 } else { 161 tok = tokens[0] 162 } 163 164 step := parseTable[k.Kind][tok.Type()] 165 if s.ShouldSkip(tok) { 166 // being in a skip state with no tokens will break out of 167 // the parse loop since there is nothing left to process. 168 if len(tokens) == 0 { 169 break loop 170 } 171 // if should skip is true, we skip the tokens until should skip is set to false. 172 step = SkipTokenState 173 } 174 175 switch step { 176 case TerminalState: 177 // Finished parsing. Push what should be the last 178 // statement to the stack. If there is anything left 179 // on the stack, an error in parsing has occurred. 180 if k.Kind != ASTKindStart { 181 stack.MarkComplete(k) 182 } 183 break loop 184 case SkipTokenState: 185 // When skipping a token, the previous state was popped off the stack. 186 // To maintain the correct state, the previous state will be pushed 187 // onto the stack. 188 stack.Push(k) 189 case StatementState: 190 if k.Kind != ASTKindStart { 191 stack.MarkComplete(k) 192 } 193 expr := newExpression(tok) 194 stack.Push(expr) 195 case StatementPrimeState: 196 if tok.Type() != TokenOp { 197 stack.MarkComplete(k) 198 continue 199 } 200 201 if k.Kind != ASTKindExpr { 202 return nil, NewParseError( 203 fmt.Sprintf("invalid expression: expected Expr type, but found %T type", k), 204 ) 205 } 206 207 k = trimSpaces(k) 208 expr := newEqualExpr(k, tok) 209 stack.Push(expr) 210 case ValueState: 211 // ValueState requires the previous state to either be an equal expression 212 // or an expression statement. 213 switch k.Kind { 214 case ASTKindEqualExpr: 215 // assigning a value to some key 216 k.AppendChild(newExpression(tok)) 217 stack.Push(newExprStatement(k)) 218 case ASTKindExpr: 219 k.Root.raw = append(k.Root.raw, tok.Raw()...) 220 stack.Push(k) 221 case ASTKindExprStatement: 222 root := k.GetRoot() 223 children := root.GetChildren() 224 if len(children) == 0 { 225 return nil, NewParseError( 226 fmt.Sprintf("invalid expression: AST contains no children %s", k.Kind), 227 ) 228 } 229 230 rhs := children[len(children)-1] 231 232 if rhs.Root.ValueType != QuotedStringType { 233 rhs.Root.ValueType = StringType 234 rhs.Root.raw = append(rhs.Root.raw, tok.Raw()...) 235 236 } 237 238 children[len(children)-1] = rhs 239 root.SetChildren(children) 240 241 stack.Push(k) 242 } 243 case OpenScopeState: 244 if !runeCompare(tok.Raw(), openBrace) { 245 return nil, NewParseError("expected '['") 246 } 247 // If OpenScopeState is not at the start, we must mark the previous ast as complete 248 // 249 // for example: if previous ast was a skip statement; 250 // we should mark it as complete before we create a new statement 251 if k.Kind != ASTKindStart { 252 stack.MarkComplete(k) 253 } 254 255 stmt := newStatement() 256 stack.Push(stmt) 257 case CloseScopeState: 258 if !runeCompare(tok.Raw(), closeBrace) { 259 return nil, NewParseError("expected ']'") 260 } 261 262 k = trimSpaces(k) 263 stack.Push(newCompletedSectionStatement(k)) 264 case SectionState: 265 var stmt AST 266 267 switch k.Kind { 268 case ASTKindStatement: 269 // If there are multiple literals inside of a scope declaration, 270 // then the current token's raw value will be appended to the Name. 271 // 272 // This handles cases like [ profile default ] 273 // 274 // k will represent a SectionStatement with the children representing 275 // the label of the section 276 stmt = newSectionStatement(tok) 277 case ASTKindSectionStatement: 278 k.Root.raw = append(k.Root.raw, tok.Raw()...) 279 stmt = k 280 default: 281 return nil, NewParseError( 282 fmt.Sprintf("invalid statement: expected statement: %v", k.Kind), 283 ) 284 } 285 286 stack.Push(stmt) 287 case MarkCompleteState: 288 if k.Kind != ASTKindStart { 289 stack.MarkComplete(k) 290 } 291 292 if stack.Len() == 0 { 293 stack.Push(start) 294 } 295 case SkipState: 296 stack.Push(newSkipStatement(k)) 297 s.Skip() 298 case CommentState: 299 if k.Kind == ASTKindStart { 300 stack.Push(k) 301 } else { 302 stack.MarkComplete(k) 303 } 304 305 stmt := newCommentStatement(tok) 306 stack.Push(stmt) 307 default: 308 return nil, NewParseError( 309 fmt.Sprintf("invalid state with ASTKind %v and TokenType %v", 310 k, tok.Type())) 311 } 312 313 if len(tokens) > 0 { 314 tokens = tokens[1:] 315 } 316 } 317 318 // this occurs when a statement has not been completed 319 if stack.top > 1 { 320 return nil, NewParseError(fmt.Sprintf("incomplete ini expression")) 321 } 322 323 // returns a sublist which excludes the start symbol 324 return stack.List(), nil 325 } 326 327 // trimSpaces will trim spaces on the left and right hand side of 328 // the literal. 329 func trimSpaces(k AST) AST { 330 // trim left hand side of spaces 331 for i := 0; i < len(k.Root.raw); i++ { 332 if !isWhitespace(k.Root.raw[i]) { 333 break 334 } 335 336 k.Root.raw = k.Root.raw[1:] 337 i-- 338 } 339 340 // trim right hand side of spaces 341 for i := len(k.Root.raw) - 1; i >= 0; i-- { 342 if !isWhitespace(k.Root.raw[i]) { 343 break 344 } 345 346 k.Root.raw = k.Root.raw[:len(k.Root.raw)-1] 347 } 348 349 return k 350 }