package main import ( "bufio" "bytes" "flag" "fmt" "io" "log" "os" "regexp" "strings" "unicode" ) var ( f_pkg = flag.String("p", "main", "package name for generated file") f_out = flag.String("o", "-", "output file") f_unexport = flag.Bool("u", true, "make all definitions unexported") f_goname = flag.Bool("g", true, "rename symbols to follow Go conventions") f_trim = flag.String("t", "", "prefix to trim from names") f_constSuf = flag.String("cs", "", "comma-separated list of constant suffixes to create typed constants") f_constPref = flag.String("cp", "", "comma-separated list of constant prefixes to create typed constants") ) var ( reDefineIntConst = regexp.MustCompile(`#define\s+([A-Za-z_][A-Za-z\d_]*)\s+(\(?-?\d+(?:U?LL)?(?:\s*<<\s*\d+)?\)?)`) reNegULL = regexp.MustCompile(`-(\d+)ULL`) ) var ( constTypes []constType ) type constType struct { Name string Type string Suffix string Prefix string } func constName(s string) string { s = strings.TrimPrefix(s, *f_trim) typ := "" for _, t := range constTypes { if t.Suffix != "" && strings.HasSuffix(s, t.Suffix) { //s = strings.TrimSuffix(s, t.Suffix) typ = t.Name break } else if t.Prefix != "" && strings.HasPrefix(s, t.Prefix) { typ = t.Name break } } if *f_goname { buf := bytes.NewBuffer(nil) buf.Grow(len(s)) up := !*f_unexport for _, r := range s { if r == '_' { up = true continue } if up { up = false r = unicode.ToUpper(r) } else { r = unicode.ToLower(r) } buf.WriteRune(r) } s = buf.String() } else if *f_unexport { s = "_" + s } if typ != "" { s += " " + typ } return s } func process(w io.Writer, path string) error { file, err := os.Open(path) if err != nil { return err } defer file.Close() r := bufio.NewReader(file) var ( comment = false firstComment = true firstLineInComment = false ) nl := true defer fmt.Fprintln(w, ")") for { line, err := r.ReadBytes('\n') if err == io.EOF { return nil } else if err != nil { return err } line = bytes.TrimSpace(line) if len(line) == 0 { if !nl { nl = true w.Write([]byte("\n")) } continue } nl = false if bytes.HasPrefix(line, []byte("/*")) { comment = true firstLineInComment = true line = bytes.TrimPrefix(line, []byte("/*")) } if comment { ends := bytes.HasSuffix(line, []byte("*/")) if ends { comment = false line = bytes.TrimSuffix(line, []byte("*/")) } line = bytes.TrimLeft(line, " \t*") if len(line) > 0 { if !firstComment { w.Write([]byte("\t")) } w.Write([]byte("// ")) if firstLineInComment { line[0] = byte(unicode.ToUpper(rune(line[0]))) } line = bytes.Replace(line, []byte(" "), []byte(" "), -1) w.Write(line) w.Write([]byte("\n")) firstLineInComment = false } if ends && firstComment { firstComment = false fmt.Fprint(w, "\nconst (\n") nl = true } firstLineInComment = firstLineInComment && !ends continue } if bytes.HasPrefix(line, []byte("#define")) { sub := reDefineIntConst.FindStringSubmatch(string(line)) if len(sub) > 0 { name, val := sub[1], sub[2] if sub := reNegULL.FindAllStringSubmatch(val, -1); len(sub) > 0 { for _, s := range sub { val = strings.Replace(val, s[0], fmt.Sprintf("(1<<64 - %s)", s[1]), -1) } } val = strings.Replace(val, "ULL", "", -1) fmt.Fprintf(w, "\t%s = %s\n", constName(name), val) continue } } } } func regConstTypes(str string, fnc func(*constType, string)) { for _, s := range strings.Split(str, ",") { kv := strings.Split(s, "=") if len(kv) != 2 { continue } st := strings.Split(kv[0], ":") typ := "int" if len(st) > 1 { typ = st[1] } t := constType{Name: st[0], Type: typ} fnc(&t, kv[1]) constTypes = append(constTypes, t) } } func main() { flag.Parse() if suf := *f_constSuf; suf != "" { regConstTypes(suf, func(t *constType, v string) { t.Suffix = v }) } if pref := *f_constPref; pref != "" { regConstTypes(pref, func(t *constType, v string) { t.Prefix = v }) } var w io.Writer = os.Stdout if path := *f_out; path != "" && path != "-" { file, err := os.Create(path) if err != nil { log.Fatal(err) } defer file.Close() w = file } fmt.Fprintf(w, "package %s\n\n", *f_pkg) fmt.Fprint(w, "// This code was auto-generated; DO NOT EDIT!\n\n") for _, t := range constTypes { fmt.Fprintf(w, "type %s %s\n\n", t.Name, t.Type) } for _, path := range flag.Args() { if err := process(w, path); err != nil { log.Fatal(err) } } }