github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/unstructured/lexer.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package unstructured 21 22 import ( 23 "bufio" 24 "reflect" 25 "sort" 26 "strings" 27 ) 28 29 type Item struct { 30 LineNo int 31 Comments []string 32 Values []string 33 } 34 35 type Lexer struct { 36 lines []string 37 dict map[string][]Item 38 39 isUpdated bool 40 } 41 42 const trimChars = " \r\n\t" 43 44 func (i *Item) addToken(str string) { 45 if i.Values == nil { 46 i.Values = make([]string, 0) 47 } 48 i.Values = append(i.Values, str) 49 } 50 51 func (l *Lexer) getItem(key string) []Item { 52 return l.dict[key] 53 } 54 55 func (l *Lexer) parseParameter(paramLine string, paramID int) (Item, error) { 56 paramItem := Item{LineNo: paramID} 57 itemWrap := fsm{ 58 param: ¶mItem, 59 splitCharacters: trimChars, 60 } 61 return paramItem, itemWrap.parse(paramLine) 62 } 63 64 func (l *Lexer) appendConfigLine(parameterLine string) { 65 l.lines = append(l.lines, parameterLine) 66 } 67 68 func (l *Lexer) appendValidParameter(param Item, fromNo int) { 69 newItem := param 70 key := newItem.Values[0] 71 l.addParameterComments(&newItem, fromNo+1, param.LineNo) 72 if _, ok := l.dict[key]; !ok { 73 l.dict[key] = make([]Item, 0) 74 } 75 l.dict[key] = append(l.dict[key], newItem) 76 l.isUpdated = true 77 } 78 79 func (l *Lexer) addParameterComments(param *Item, start, end int) { 80 if start+1 >= end { 81 return 82 } 83 param.Comments = l.lines[start:end] 84 } 85 86 func (l *Lexer) Load(str string) error { 87 var err error 88 89 param := Item{LineNo: -1} 90 scanner := bufio.NewScanner(strings.NewReader(str)) 91 l.dict = make(map[string][]Item) 92 for scanner.Scan() { 93 parameterLine := strings.Trim(scanner.Text(), trimChars) 94 lineNo := len(l.lines) 95 l.appendConfigLine(parameterLine) 96 if parameterLine == "" || parameterLine[0] == '#' { 97 continue 98 } 99 lastScanNo := param.LineNo 100 if param, err = l.parseParameter(parameterLine, lineNo); err != nil { 101 return err 102 } 103 l.appendValidParameter(param, lastScanNo) 104 } 105 106 l.isUpdated = false 107 return nil 108 } 109 110 func (l *Lexer) removeParameter(it *Item) { 111 v, ok := l.dict[it.Values[0]] 112 if !ok { 113 return 114 } 115 116 index := -1 117 for i := range v { 118 if reflect.DeepEqual(&v[i], it) { 119 index = i 120 break 121 } 122 } 123 124 if index >= 0 { 125 l.dict[it.Values[0]] = append(v[:index], v[index+1:]...) 126 } 127 l.isUpdated = true 128 } 129 130 func (l *Lexer) sortParameters() []Item { 131 items := make([]Item, 0) 132 for _, v := range l.dict { 133 items = append(items, v...) 134 } 135 sort.SliceStable(items, func(i, j int) bool { 136 no1 := items[i].LineNo 137 no2 := items[j].LineNo 138 if no1 == no2 { 139 return strings.Compare(items[i].Values[0], items[j].Values[0]) < 0 140 } 141 return no1 < no2 142 }) 143 return items 144 } 145 146 func (l *Lexer) empty() bool { 147 return len(l.dict) == 0 148 } 149 150 func (l *Lexer) getAllParams() map[string][]Item { 151 return l.dict 152 } 153 154 func (l *Lexer) toString() string { 155 return strings.Join(l.lines, "\n") 156 }