btrfs/cmd/hgen.go
2017-01-08 16:13:47 +02:00

212 lines
4.5 KiB
Go

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)
}
}
}