github.com/holoplot/go-evdev@v0.0.0-20220721205823-d31c64b9d636/build/gen-codes/parser/process.go (about) 1 package parser 2 3 import ( 4 "bufio" 5 "errors" 6 "io" 7 "regexp" 8 "strconv" 9 "strings" 10 ) 11 12 type EncodingType string 13 14 const ( 15 EncodingUnknown EncodingType = "" 16 EncodingHex EncodingType = "hex" 17 EncodingInteger EncodingType = "int" 18 EncodingText EncodingType = "text" // e.g. "SOME_OTHER_CONST_NAME" 19 EncodingEquation EncodingType = "equation" // e.g. "(CONST_NAME + 1)" 20 ) 21 22 const ( 23 stateNeutral = iota 24 stateReadingMultilineComment 25 stateReadingDefineWithMultilineComment 26 ) 27 28 var ( 29 regexHex = regexp.MustCompile(`^0x[0-9a-fA-F]+$`) 30 regexInteger = regexp.MustCompile(`^[0-9]+$`) 31 regexText = regexp.MustCompile(`^\w+$`) 32 regexEquation = regexp.MustCompile(`^\((\w+)\s*(\+)\s*(\d)+\)$`) 33 ) 34 35 func detectEncodingType(value string) (EncodingType, int) { 36 var convertedValue int64 37 var err error 38 39 switch { 40 case regexHex.MatchString(value): 41 convertedValue, err = strconv.ParseInt(value[2:], 16, 64) 42 if err != nil { 43 panic(err) 44 } 45 return EncodingHex, int(convertedValue) 46 case regexInteger.MatchString(value): 47 convertedValue, err = strconv.ParseInt(value, 10, 64) 48 if err != nil { 49 panic(err) 50 } 51 return EncodingInteger, int(convertedValue) 52 case regexEquation.MatchString(value): 53 return EncodingEquation, 0 // can't resolve value that points on different constant here 54 case regexText.MatchString(value): 55 return EncodingText, 0 // can't resolve value that points on different constant here 56 default: 57 return EncodingUnknown, 0 58 } 59 } 60 61 type separator string // one line 62 63 type comment string // may be bigger than one line 64 65 type constant struct { 66 name string 67 value string 68 decodedValue int 69 comment string // oneliner 70 encodingType EncodingType 71 } 72 73 func (c *constant) resolveEquation(group *Group) (EncodingType, int, error) { 74 out := regexEquation.FindStringSubmatch(c.value) 75 rawA, sign, rawB := out[1], out[2], out[3] 76 if sign != "+" { 77 return EncodingUnknown, 0, errors.New("e1") 78 } 79 i, err := strconv.ParseInt(rawB, 10, 64) 80 if err != nil { 81 return EncodingUnknown, 0, errors.New("e2") 82 } 83 84 valName, valDecoded := rawA, int(i) 85 86 var found, ok bool 87 var v2 constant 88 for _, e := range group.elements { 89 v2, ok = e.(constant) 90 if !ok { 91 continue 92 } 93 if v2.name == valName { 94 found = true 95 break 96 } 97 } 98 if !found { 99 return EncodingUnknown, 0, errors.New("value not found") 100 } 101 102 return v2.encodingType, v2.decodedValue + valDecoded, nil 103 } 104 105 func (c *constant) resolveText(group *Group) (EncodingType, int, error) { 106 var found, ok bool 107 var v2 constant 108 for _, e := range group.elements { 109 v2, ok = e.(constant) 110 if !ok { 111 continue 112 } 113 if v2.name == c.value { 114 found = true 115 break 116 } 117 } 118 if !found { 119 return EncodingUnknown, 0, errors.New("value not found") 120 } 121 122 return v2.encodingType, v2.decodedValue, nil 123 } 124 125 // Group contains list of elements (separator, comment or constant) 126 type Group struct { 127 comment string 128 elements []interface{} 129 } 130 131 func NewGroup() Group { 132 return Group{ 133 comment: "", 134 elements: make([]interface{}, 0), 135 } 136 } 137 138 var ( 139 reDefineWithComment = regexp.MustCompile(`^#define[ \t]+(?P<name>[A-Z0-9_]+)[ \t]+(?P<value>[0-9a-fA-Zx()_+ ]+)[ \t]+/\*(?P<comment>.*)\*/[ \t]*$`) 140 reDefineWithCommentStart = regexp.MustCompile(`^#define[ \t]+(?P<name>[A-Z0-9_]+)[ \t]+(?P<value>[0-9a-fA-Zx()_+ ]+)[ \t]+/\*(?P<comment>.*)[ \t]*$`) 141 reDefine = regexp.MustCompile(`^#define[ \t]+(?P<name>[A-Z0-9_]+)[ \t]+(?P<value>[0-9a-fA-Zx()_+ ]+)`) 142 reOnelineComment = regexp.MustCompile(`^[ \t]*/\*(?P<text>.*)\*/[ \t]*$`) 143 reMultilineCommentStart = regexp.MustCompile(`^[ \t]*/\*(?P<text>.*)[ \t]*$`) 144 reMultilineCommentEnd = regexp.MustCompile(`^[ \t](?:\*)?(?P<text>.*)\*/[ \t]*$`) 145 reMultilineCommentMid = regexp.MustCompile(`^[ \t]*(?:\*)?(?P<text>.*)[ \t]*$`) 146 ) 147 148 func hasPrefixInSlice(v string, s []string) bool { 149 for _, vs := range s { 150 if strings.HasPrefix(v, vs) { 151 return true 152 } 153 } 154 return false 155 } 156 157 type CodeProcessor struct { 158 prefixesGroups [][]string 159 160 elements []interface{} 161 lastComment string 162 state int 163 } 164 165 func NewCodeProcessor(prefixesGroups [][]string) CodeProcessor { 166 return CodeProcessor{ 167 prefixesGroups: prefixesGroups, 168 169 elements: make([]interface{}, 0), 170 lastComment: "", 171 state: stateNeutral, 172 } 173 } 174 175 func (p *CodeProcessor) ProcessFile(file io.Reader) ([]Group, error) { 176 var groups = make([]Group, 0) 177 var group = NewGroup() 178 179 var variable constant 180 var suffixCounter int 181 var lastComment string 182 var sepAfterComment bool 183 var groupCommentAdded bool 184 var seeking = true 185 var currentPrefixes = p.prefixesGroups[suffixCounter] 186 187 scanner := bufio.NewScanner(file) 188 189 scanning: 190 for scanner.Scan() { 191 s := scanner.Text() 192 193 switch p.state { 194 case stateNeutral: 195 if match := reDefine.FindStringSubmatch(s); match != nil { 196 if !hasPrefixInSlice(match[1], currentPrefixes) { // exiting type group 197 if seeking { 198 continue // seeking through #define elements and preserving last comment in the meantime 199 } 200 groups = append(groups, group) 201 suffixCounter++ 202 if suffixCounter == len(p.prefixesGroups) { 203 break scanning 204 } 205 group = NewGroup() 206 groupCommentAdded = false 207 currentPrefixes = p.prefixesGroups[suffixCounter] 208 } 209 seeking = false 210 211 if lastComment != "" && !groupCommentAdded { 212 group.comment = lastComment 213 groupCommentAdded = true 214 lastComment = "" 215 } 216 217 if lastComment != "" { 218 group.elements = append(group.elements, comment(lastComment)) 219 lastComment = "" 220 if sepAfterComment { 221 group.elements = append(group.elements, separator("")) 222 } 223 } 224 } 225 226 if match := reDefineWithComment.FindStringSubmatch(s); match != nil { 227 variable.name, variable.value, variable.comment = match[1], match[2], strings.Trim(match[3], " \t") 228 variable.value = strings.Trim(variable.value, " \t") 229 variable.encodingType, variable.decodedValue = detectEncodingType(variable.value) 230 group.elements = append(group.elements, variable) 231 continue 232 } 233 234 if match := reDefineWithCommentStart.FindStringSubmatch(s); match != nil { 235 variable.name, variable.value, variable.comment = match[1], match[2], strings.Trim(match[3], " \t") 236 variable.value = strings.Trim(variable.value, " \t") 237 variable.encodingType, variable.decodedValue = detectEncodingType(variable.value) 238 p.state = stateReadingDefineWithMultilineComment 239 continue 240 } 241 242 if match := reDefine.FindStringSubmatch(s); match != nil { 243 variable.name, variable.value, variable.comment = match[1], match[2], "" 244 variable.value = strings.Trim(variable.value, " \t") 245 variable.encodingType, variable.decodedValue = detectEncodingType(variable.value) 246 group.elements = append(group.elements, variable) 247 continue 248 } 249 250 if match := reOnelineComment.FindStringSubmatch(s); match != nil { 251 lastComment = strings.Trim(match[1], " \t") 252 continue 253 } 254 255 if match := reMultilineCommentStart.FindStringSubmatch(s); match != nil { 256 lastComment = "" 257 if len(match[1]) > 0 { 258 lastComment += strings.Trim(match[1], " \t") 259 } 260 p.state = stateReadingMultilineComment 261 continue 262 } 263 264 if s == "" { 265 sepAfterComment = true 266 group.elements = append(group.elements, separator("")) 267 continue 268 } else { 269 sepAfterComment = false 270 } 271 272 case stateReadingDefineWithMultilineComment: 273 if match := reMultilineCommentEnd.FindStringSubmatch(s); match != nil { 274 if len(match[1]) > 0 { 275 variable.comment += strings.Trim(match[1], " \t") + "\n" 276 } 277 group.elements = append(group.elements, variable) 278 p.state = stateNeutral 279 continue 280 } 281 282 if match := reMultilineCommentMid.FindStringSubmatch(s); match != nil { 283 if len(match[1]) > 0 { 284 variable.comment += strings.Trim(match[1], " \t") 285 } 286 continue 287 } 288 289 return groups, errors.New("processing should not reach this point: #1") 290 291 case stateReadingMultilineComment: 292 if match := reMultilineCommentEnd.FindStringSubmatch(s); match != nil { 293 if len(match[1]) > 0 { 294 lastComment += " " + strings.Trim(match[1], " \t") 295 } 296 lastComment += match[1] 297 p.state = stateNeutral 298 continue 299 } 300 301 if match := reMultilineCommentMid.FindStringSubmatch(s); match != nil { 302 if len(match[1]) > 0 { 303 lastComment += strings.Trim(match[1], " \t") + "\n" 304 } 305 continue 306 } 307 308 return groups, errors.New("processing should not reach this point: #2") 309 } 310 } 311 groups = append(groups, group) 312 return groups, nil 313 }