github.com/holoplot/go-evdev@v0.0.0-20220721205823-d31c64b9d636/build/gen-codes/parser/generate.go (about) 1 package parser 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 ) 8 9 var typeNames = map[string]string{ 10 "EV_": "EvType", 11 "INPUT_PROP_": "EvProp", 12 } 13 14 func getType(in string) string { 15 for k, v := range typeNames { 16 if strings.HasPrefix(in, k) { 17 return v 18 } 19 } 20 return "EvCode" 21 } 22 23 var SelectedPrefixesGroups = map[string][][]string{ 24 // "Group" detection relies on the correct order of these set of prefixes 25 // this order should reflect exact order of groups in related source files 26 "input.h": { 27 {"ID_"}, 28 {"BUS_"}, 29 {"MT_TOOL_"}, 30 {"FF_"}, 31 }, 32 "input-event-codes.h": { 33 {"INPUT_PROP_"}, 34 {"EV_"}, 35 {"SYN_"}, 36 {"KEY_", "BTN_"}, // these have different prefixes but belongs to the same group type 37 {"REL_"}, 38 {"ABS_"}, 39 {"SW_"}, 40 {"MSC_"}, 41 {"LED_"}, 42 {"REP_"}, 43 {"SND_"}, 44 }, 45 } 46 47 // stripSeparators removes separators from the begging and the end 48 func stripSeparators(elements []interface{}) []interface{} { 49 var start, stop int 50 51 for i, e := range elements { 52 _, ok := e.(separator) 53 if !ok { 54 start = i 55 break 56 } 57 } 58 59 for i, e := range elements { 60 _, ok := e.(separator) 61 if !ok { 62 stop = i 63 } 64 } 65 66 return elements[start : stop+1] 67 } 68 69 type decodedValue struct { 70 value int 71 encodingType EncodingType 72 } 73 74 func removeRepeatedSeparators(groups []Group) []Group { 75 var newGroups = make([]Group, 0) 76 77 for _, group := range groups { 78 newGroup := Group{ 79 comment: group.comment, 80 elements: make([]interface{}, 0), 81 } 82 83 var sepPreviously bool 84 for _, element := range group.elements { 85 switch element.(type) { 86 case separator: 87 if !sepPreviously { 88 newGroup.elements = append(newGroup.elements, separator("")) 89 sepPreviously = true 90 } 91 default: 92 sepPreviously = false 93 newGroup.elements = append(newGroup.elements, element) 94 } 95 96 } 97 98 newGroups = append(newGroups, newGroup) 99 } 100 101 return newGroups 102 } 103 104 func generateSections(rawGroups []Group, disableComments bool) (codes, typeToString, typeFromString, typeNames string) { 105 groups := removeRepeatedSeparators(rawGroups) 106 107 var decodedValues = make(map[constant]decodedValue) 108 109 for _, group := range groups { 110 for _, element := range group.elements { 111 switch v := element.(type) { 112 case constant: 113 switch v.encodingType { 114 case EncodingEquation: 115 encType, valDecoded, err := v.resolveEquation(&group) 116 if err != nil { 117 panic(fmt.Errorf("failed to decode equation: %w", err)) 118 } 119 decodedValues[v] = decodedValue{valDecoded, encType} 120 case EncodingText: 121 encType, valDecoded, err := v.resolveText(&group) 122 if err != nil { 123 panic(fmt.Errorf("failed to decode text: %w", err)) 124 } 125 decodedValues[v] = decodedValue{valDecoded, encType} 126 case EncodingInteger: 127 i, err := strconv.ParseInt(v.value, 10, 64) 128 if err != nil { 129 panic(fmt.Errorf("integer conversion failed: %w", err)) 130 } 131 decodedValues[v] = decodedValue{int(i), EncodingInteger} 132 case EncodingHex: 133 i, err := strconv.ParseInt(v.value[2:], 16, 64) 134 if err != nil { 135 panic(fmt.Errorf("hex conversion failed: %w", err)) 136 } 137 decodedValues[v] = decodedValue{int(i), EncodingHex} 138 } 139 } 140 } 141 } 142 143 for i, group := range groups { 144 if !disableComments && group.comment != "" { 145 lines := strings.Split(group.comment, "\n") 146 for i := 0; i < len(lines); i++ { 147 cl := lines[i] 148 if cl != "" { 149 codes += "// " + cl + "\n" 150 } 151 } 152 } 153 154 codes += "const (\n" 155 for _, element := range stripSeparators(group.elements) { 156 switch v := element.(type) { 157 case separator: // space between blocks 158 codes += "\n" 159 case comment: // single comment entry 160 if disableComments { 161 continue 162 } 163 lines := strings.Split(string(v), "\n") 164 for i := 0; i < len(lines); i++ { 165 cl := lines[i] 166 if cl != "" { 167 codes += "\t// " + cl + "\n" 168 } 169 } 170 case constant: 171 if !disableComments && v.comment != "" { 172 codes += fmt.Sprintf("\t%s = %s // %s\n", v.name, v.value, v.comment) 173 } else { 174 codes += fmt.Sprintf("\t%s = %s\n", v.name, v.value) 175 } 176 default: 177 panic("") 178 } 179 } 180 codes += ")\n" 181 182 if i < len(groups) { 183 codes += "\n" 184 } 185 } 186 187 // generating additional mappings 188 for i, group := range groups { 189 var firstConstant constant 190 191 for _, element := range group.elements { 192 c, ok := element.(constant) 193 if !ok { 194 continue 195 } 196 firstConstant = c 197 break 198 } 199 200 parts := strings.Split(firstConstant.name, "_") 201 name := parts[0] 202 203 var valueRegistered = make(map[int]string) 204 205 typeToString += fmt.Sprintf("var %sToString = map[%s]string{\n", name, getType(firstConstant.name)) 206 for _, element := range stripSeparators(group.elements) { 207 switch v := element.(type) { 208 case separator: 209 typeToString += "\n" 210 case constant: 211 decoded, ok := decodedValues[v] 212 if !ok { 213 panic("decoded value not found") 214 } 215 if name, ok := valueRegistered[decoded.value]; ok { 216 typeToString += fmt.Sprintf("\t// %s: \"%s\", // (%s)\n", v.name, v.name, name) 217 continue 218 } 219 220 switch decoded.encodingType { 221 case EncodingHex, EncodingInteger: 222 typeToString += fmt.Sprintf("\t%s: \"%s\",\n", v.name, v.name) 223 valueRegistered[decoded.value] = v.name 224 default: 225 panic("unexpected encoding type") 226 } 227 } 228 } 229 230 typeFromString += fmt.Sprintf("var %sFromString = map[string]%s{\n", name, getType(firstConstant.name)) 231 for _, element := range stripSeparators(group.elements) { 232 switch v := element.(type) { 233 case separator: 234 typeFromString += "\n" 235 case constant: 236 typeFromString += fmt.Sprintf("\t\"%s\": %s,\n", v.name, v.name) 237 } 238 } 239 240 valueRegistered = make(map[int]string) 241 duplicates := make(map[int][]string) 242 for _, e := range group.elements { 243 re, ok := e.(constant) 244 if !ok { 245 continue 246 } 247 decoded, ok := decodedValues[re] 248 if !ok { 249 panic("decoded value not found") 250 } 251 252 duplicates[decoded.value] = append(duplicates[decoded.value], re.name) 253 } 254 255 typeNames += fmt.Sprintf("var %sNames = map[%s]string{\n", name, getType(firstConstant.name)) 256 for _, element := range stripSeparators(group.elements) { 257 switch v := element.(type) { 258 case separator: 259 typeNames += "\n" 260 case constant: 261 decoded, ok := decodedValues[v] 262 if !ok { 263 panic("decoded value not found") 264 } 265 266 if _, ok := valueRegistered[decoded.value]; ok { 267 continue 268 } 269 270 var names string 271 for i, name := range duplicates[decoded.value] { 272 names += name 273 if i != len(duplicates[decoded.value])-1 { 274 names += "/" 275 } 276 } 277 278 switch decoded.encodingType { 279 case EncodingHex, EncodingInteger: 280 typeNames += fmt.Sprintf("\t%s: \"%s\",\n", v.name, names) 281 valueRegistered[decoded.value] = v.name 282 default: 283 panic("unexpected encoding type") 284 } 285 } 286 } 287 288 typeToString += "}\n" 289 typeFromString += "}\n" 290 typeNames += "}\n" 291 292 if i < len(groups) { 293 typeToString += "\n" 294 typeFromString += "\n" 295 typeNames += "\n" 296 } 297 } 298 return 299 } 300 301 func GenerateFile(rawGroups []Group, disableComments bool, selectedTag, inputHURL, eventCodesURL string) string { 302 var file string 303 codes, typeToString, typeFromString, typeNames := generateSections(rawGroups, disableComments) 304 305 file += "// Code generated by build/gen-codes. DO NOT EDIT.\n" 306 file += fmt.Sprintf("// version tag: \"%s\", generated from files:\n", selectedTag) 307 file += fmt.Sprintf("// - %s\n", inputHURL) 308 file += fmt.Sprintf("// - %s\n\n", eventCodesURL) 309 file += "package evdev\n\n" 310 file += codes 311 312 file += "\n//\n// Type to String\n//\n\n" 313 file += typeToString 314 315 file += "\n//\n// Type from String\n//\n\n" 316 file += typeFromString 317 318 file += "\n//\n// Type Names (informative debug use only)\n//\n\n" 319 file += typeNames 320 321 return file 322 }