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  }