From 6d45f6f66724ff3ef134d6a8be308127db015525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Mon, 5 Jun 2023 13:24:52 +0200 Subject: [PATCH] v0.0.129 --- bfcodegen/enum-generate.go | 318 +++++++++++++++++++++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 bfcodegen/enum-generate.go diff --git a/bfcodegen/enum-generate.go b/bfcodegen/enum-generate.go new file mode 100644 index 0000000..13363da --- /dev/null +++ b/bfcodegen/enum-generate.go @@ -0,0 +1,318 @@ +package main + +import ( + "errors" + "fmt" + "gogs.mikescher.com/BlackForestBytes/goext/cmdext" + "gogs.mikescher.com/BlackForestBytes/goext/langext" + "gogs.mikescher.com/BlackForestBytes/goext/rext" + "io" + "os" + "regexp" + "strings" + "time" +) + +type EnumDefVal struct { + VarName string + Value string + Description *string +} + +type EnumDef struct { + File string + EnumTypeName string + Type string + Values []EnumDefVal +} + +var rexPackage = rext.W(regexp.MustCompile("^package\\s+(?P[A-Za-z0-9_]+)\\s*$")) + +var rexEnumDef = rext.W(regexp.MustCompile("^\\s*type\\s+(?P[A-Za-z0-9_]+)\\s+(?P[A-Za-z0-9_]+)\\s*//\\s*(@enum:type).*$")) + +var rexValueDef = rext.W(regexp.MustCompile("^\\s*(?P[A-Za-z0-9_]+)\\s+(?P[A-Za-z0-9_]+)\\s*=\\s*(?P(\"[A-Za-z0-9_:]+\"|[0-9]+))\\s*(//(?P.*))?.*$")) + +func GenerateEnumSpecs(sourceDir string, destFile string) error { + + files, err := os.ReadDir(sourceDir) + if err != nil { + return err + } + + allEnums := make([]EnumDef, 0) + + pkgname := "" + + for _, f := range files { + if !strings.HasSuffix(f.Name(), ".go") { + continue + } + + fmt.Printf("========= %s =========\n\n", f.Name()) + fileEnums, pn, err := processFile(f.Name()) + if err != nil { + return err + } + + fmt.Printf("\n") + + allEnums = append(allEnums, fileEnums...) + + if pn != "" { + pkgname = pn + } + } + + if pkgname == "" { + return errors.New("no package name found in any file") + } + + err = os.WriteFile(destFile, []byte(fmtOutput(allEnums, pkgname)), 0o755) + if err != nil { + return err + } + + res, err := cmdext.RunCommand("go", []string{"fmt", destFile}, langext.Ptr(2*time.Second)) + if err != nil { + return err + } + + if res.CommandTimedOut { + fmt.Println(res.StdCombined) + return errors.New("go fmt timed out") + } + if res.ExitCode != 0 { + fmt.Println(res.StdCombined) + return errors.New("go fmt did not succeed") + } + + return nil +} + +func processFile(fn string) ([]EnumDef, string, error) { + file, err := os.Open(fn) + if err != nil { + return nil, "", err + } + + defer func() { _ = file.Close() }() + + bin, err := io.ReadAll(file) + if err != nil { + return nil, "", err + } + + lines := strings.Split(string(bin), "\n") + + enums := make([]EnumDef, 0) + + pkgname := "" + + for i, line := range lines { + if i == 0 && strings.HasPrefix(line, "// Code generated by") { + break + } + + if match, ok := rexPackage.MatchFirst(line); i == 0 && ok { + pkgname = match.GroupByName("name").Value() + continue + } + + if match, ok := rexEnumDef.MatchFirst(line); ok { + def := EnumDef{ + File: fn, + EnumTypeName: match.GroupByName("name").Value(), + Type: match.GroupByName("type").Value(), + Values: make([]EnumDefVal, 0), + } + enums = append(enums, def) + fmt.Printf("Found enum definition { '%s' -> '%s' }\n", def.EnumTypeName, def.Type) + } + + if match, ok := rexValueDef.MatchFirst(line); ok { + typename := match.GroupByName("type").Value() + def := EnumDefVal{ + VarName: match.GroupByName("name").Value(), + Value: match.GroupByName("value").Value(), + Description: match.GroupByNameOrEmpty("descr").ValueOrNil(), + } + + found := false + for i, v := range enums { + if v.EnumTypeName == typename { + enums[i].Values = append(enums[i].Values, def) + found = true + if def.Description != nil { + fmt.Printf("Found enum value [%s] for '%s' ('%s')\n", def.Value, def.VarName, *def.Description) + } else { + fmt.Printf("Found enum value [%s] for '%s'\n", def.Value, def.VarName) + } + break + } + } + if !found { + fmt.Printf("Found non-enum value [%s] for '%s' ( looks like enum value, but no matching @enum:type )\n", def.Value, def.VarName) + } + } + } + + return enums, pkgname, nil +} + +func fmtOutput(enums []EnumDef, pkgname string) string { + str := "// Code generated by permissions_gen.sh DO NOT EDIT.\n" + str += "\n" + str += "package " + pkgname + "\n" + str += "\n" + + str += "import \"gogs.mikescher.com/BlackForestBytes/goext/langext\"" + "\n" + str += "\n" + + str += "type Enum interface {" + "\n" + str += " Valid() bool" + "\n" + str += " ValuesAny() []any" + "\n" + str += " ValuesMeta() []EnumMetaValue" + "\n" + str += " VarName() string" + "\n" + str += "}" + "\n" + str += "" + "\n" + + str += "type StringEnum interface {" + "\n" + str += " Enum" + "\n" + str += " String() string" + "\n" + str += "}" + "\n" + str += "" + "\n" + + str += "type DescriptionEnum interface {" + "\n" + str += " Enum" + "\n" + str += " Description() string" + "\n" + str += "}" + "\n" + str += "\n" + + str += "type EnumMetaValue struct {" + "\n" + str += " VarName string `json:\"varName\"`" + "\n" + str += " Value any `json:\"value\"`" + "\n" + str += " Description *string `json:\"description\"`" + "\n" + str += "}" + "\n" + str += "\n" + + for _, enumdef := range enums { + + hasDescr := langext.ArrAll(enumdef.Values, func(val EnumDefVal) bool { return val.Description != nil }) + hasStr := enumdef.Type == "string" + + str += "// ================================ " + enumdef.EnumTypeName + " ================================" + "\n" + str += "//" + "\n" + str += "// File: " + enumdef.File + "\n" + str += "// StringEnum: " + langext.Conditional(hasStr, "true", "false") + "\n" + str += "// DescrEnum: " + langext.Conditional(hasDescr, "true", "false") + "\n" + str += "//" + "\n" + str += "" + "\n" + + str += "var __" + enumdef.EnumTypeName + "Values = []" + enumdef.EnumTypeName + "{" + "\n" + for _, v := range enumdef.Values { + str += " " + v.VarName + "," + "\n" + } + str += "}" + "\n" + str += "" + "\n" + + if hasDescr { + str += "var __" + enumdef.EnumTypeName + "Descriptions = map[" + enumdef.EnumTypeName + "]string{" + "\n" + for _, v := range enumdef.Values { + str += " " + v.VarName + ": \"" + strings.TrimSpace(*v.Description) + "\"," + "\n" + } + str += "}" + "\n" + str += "" + "\n" + } + + str += "var __" + enumdef.EnumTypeName + "Varnames = map[" + enumdef.EnumTypeName + "]string{" + "\n" + for _, v := range enumdef.Values { + str += " " + v.VarName + ": \"" + v.VarName + "\"," + "\n" + } + str += "}" + "\n" + str += "" + "\n" + + str += "func (e " + enumdef.EnumTypeName + ") Valid() bool {" + "\n" + str += " return langext.InArray(e, __" + enumdef.EnumTypeName + "Values)" + "\n" + str += "}" + "\n" + str += "" + "\n" + + str += "func (e " + enumdef.EnumTypeName + ") Values() []" + enumdef.EnumTypeName + " {" + "\n" + str += " return __" + enumdef.EnumTypeName + "Values" + "\n" + str += "}" + "\n" + str += "" + "\n" + + str += "func (e " + enumdef.EnumTypeName + ") ValuesAny() []any {" + "\n" + str += " return langext.ArrCastToAny(__" + enumdef.EnumTypeName + "Values)" + "\n" + str += "}" + "\n" + str += "" + "\n" + + str += "func (e " + enumdef.EnumTypeName + ") ValuesMeta() []EnumMetaValue {" + "\n" + str += " return []EnumMetaValue{" + "\n" + for _, v := range enumdef.Values { + if hasDescr { + str += " " + fmt.Sprintf("EnumMetaValue{VarName: \"%s\", Value: %s, Description: langext.Ptr(\"%s\")},", v.VarName, v.VarName, strings.TrimSpace(*v.Description)) + "\n" + } else { + str += " " + fmt.Sprintf("EnumMetaValue{VarName: \"%s\", Value: %s, Description: nil},", v.VarName, v.VarName) + "\n" + } + } + str += " }" + "\n" + str += "}" + "\n" + str += "" + "\n" + + if hasStr { + str += "func (e " + enumdef.EnumTypeName + ") String() string {" + "\n" + str += " return string(e)" + "\n" + str += "}" + "\n" + str += "" + "\n" + } + + if hasDescr { + str += "func (e " + enumdef.EnumTypeName + ") Description() string {" + "\n" + str += " if d, ok := __" + enumdef.EnumTypeName + "Descriptions[e]; ok {" + "\n" + str += " return d" + "\n" + str += " }" + "\n" + str += " return \"\"" + "\n" + str += "}" + "\n" + str += "" + "\n" + } + + str += "func (e " + enumdef.EnumTypeName + ") VarName() string {" + "\n" + str += " if d, ok := __" + enumdef.EnumTypeName + "Varnames[e]; ok {" + "\n" + str += " return d" + "\n" + str += " }" + "\n" + str += " return \"\"" + "\n" + str += "}" + "\n" + str += "" + "\n" + + str += "func Parse" + enumdef.EnumTypeName + "(vv string) (" + enumdef.EnumTypeName + ", bool) {" + "\n" + str += " for _, ev := range __" + enumdef.EnumTypeName + "Values {" + "\n" + str += " if string(ev) == vv {" + "\n" + str += " return ev, true" + "\n" + str += " }" + "\n" + str += " }" + "\n" + str += " return \"\", false" + "\n" + str += "}" + "\n" + str += "" + "\n" + + str += "func " + enumdef.EnumTypeName + "Values() []" + enumdef.EnumTypeName + " {" + "\n" + str += " return __" + enumdef.EnumTypeName + "Values" + "\n" + str += "}" + "\n" + str += "" + "\n" + + str += "func " + enumdef.EnumTypeName + "ValuesMeta() []EnumMetaValue {" + "\n" + str += " return []EnumMetaValue{" + "\n" + for _, v := range enumdef.Values { + if hasDescr { + str += " " + fmt.Sprintf("EnumMetaValue{VarName: \"%s\", Value: %s, Description: langext.Ptr(\"%s\")},", v.VarName, v.VarName, strings.TrimSpace(*v.Description)) + "\n" + } else { + str += " " + fmt.Sprintf("EnumMetaValue{VarName: \"%s\", Value: %s, Description: nil},", v.VarName, v.VarName) + "\n" + } + } + str += " }" + "\n" + str += "}" + "\n" + str += "" + "\n" + + } + + return str +}