~sircmpwn/go-bare

a037d68c8e43285cb698ae4f12f3998d3752f27b — Drew DeVault a month ago 3f998d9
cmd/gen: initial commit
4 files changed, 225 insertions(+), 1 deletions(-)

A cmd/gen/gen.go
A cmd/gen/main.go
M go.mod
M go.sum
A cmd/gen/gen.go => cmd/gen/gen.go +149 -0
@@ 0,0 1,149 @@
package main

import (
	"fmt"
	"io"
	"strings"

	"git.sr.ht/~sircmpwn/go-bare/schema"
)

func genTypes(w io.Writer, types []schema.SchemaType) {
	fmt.Fprintf(w, "\nimport \"git.sr.ht/~sircmpwn/go-bare\"\n")

	for _, ty := range types {
		switch ty := ty.(type) {
		case *schema.UserDefinedType:
			genUserType(w, ty)
		case *schema.UserDefinedEnum:
			genUserEnum(w, ty)
		}
	}
}

func genUserType(w io.Writer, udt *schema.UserDefinedType) {
	fmt.Fprintf(w, "\ntype %s ", udt.Name())
	genType(w, udt.Type(), 0)
	fmt.Fprintf(w, "\n")

	fmt.Fprintf(w, "\nfunc (t *%s) Decode(data []byte) {", udt.Name())
	fmt.Fprintf(w, "\n\tbare.Unmarshal(data, t)")
	fmt.Fprintf(w, "\n}\n")
}

func genUserEnum(w io.Writer, ude *schema.UserDefinedEnum) {
	// TODO: Disambiguate between enums with conflicting value names
	fmt.Fprintf(w, "\ntype %s %s\n", ude.Name(), primitiveType(ude.Kind()))
	fmt.Fprintf(w, "\nconst (")
	for i, val := range ude.Values() {
		if i == 0 {
			fmt.Fprintf(w, "\n\t%s %s = %d", val.Name(), ude.Name(), val.Value())
		} else {
			fmt.Fprintf(w, "\n\t%s = %d", val.Name(), val.Value())
		}
	}
	fmt.Fprintf(w, "\n)\n")
}

func genType(w io.Writer, ty schema.Type, indent int) {
	switch ty := ty.(type) {
	case *schema.PrimitiveType:
		fmt.Fprintf(w, "%s", primitiveType(ty.Kind()))
	case *schema.DataType:
		if ty.Kind() == schema.DataArray {
			fmt.Fprintf(w, "[%d]byte", ty.Length())
		} else {
			fmt.Fprintf(w, "[]byte")
		}
	case *schema.StructType:
		maxName := 0
		for _, field := range ty.Fields() {
			if len(field.Name()) > maxName {
				maxName = len(field.Name())
			}
		}

		fmt.Fprintf(w, "struct {\n")
		for _, field := range ty.Fields() {
			genIndent(w, indent + 1)
			n := fieldName(field.Name())
			fmt.Fprintf(w, "%s ", n)
			for i := len(n); i < maxName; i++ {
				fmt.Fprintf(w, " ")
			}
			genType(w, field.Type(), indent + 1)
			fmt.Fprintf(w, " `bare:\"%s\"`", field.Name())
			fmt.Fprintf(w, "\n")
		}
		genIndent(w, indent)
		fmt.Fprintf(w, "}")
	case *schema.NamedUserType:
		fmt.Fprintf(w, "%s", ty.Name())
	case *schema.MapType:
		fmt.Fprintf(w, "map[")
		genType(w, ty.Key(), indent)
		fmt.Fprintf(w, "]")
		genType(w, ty.Value(), indent)
	case *schema.ArrayType:
		if ty.Kind() == schema.Array {
			fmt.Fprintf(w, "[%d]", ty.Length())
		} else {
			fmt.Fprintf(w, "[]")
		}
		genType(w, ty.Member(), indent)
	case *schema.OptionalType:
		fmt.Fprintf(w, "*")
		genType(w, ty.Subtype(), indent)
	default:
		panic(fmt.Errorf("TODO: %T", ty))
	}
}

func primitiveType(kind schema.TypeKind) string {
	switch kind {
	case schema.U8:
		return "uint8"
	case schema.U16:
		return "uint16"
	case schema.U32:
		return "uint32"
	case schema.U64:
		return "uint64"
	case schema.I8:
		return "int8"
	case schema.I16:
		return "int16"
	case schema.I32:
		return "int32"
	case schema.I64:
		return "int64"
	case schema.E8:
		return "uint8"
	case schema.E16:
		return "uint16"
	case schema.E32:
		return "uint32"
	case schema.E64:
		return "uint64"
	case schema.F32:
		return "float32"
	case schema.F64:
		return "float64"
	case schema.Bool:
		return "bool"
	case schema.String:
		return "string"
	}
	panic(fmt.Errorf("Invalid primitive type %d", kind))
}

func genIndent(w io.Writer, indent int) {
	for ; indent > 0; indent-- {
		fmt.Fprintf(w, "\t")
	}
}

func fieldName(n string) string {
	// TODO: Correct initialisms
	return strings.ToUpper(n[:1]) + n[1:]
}

A cmd/gen/main.go => cmd/gen/main.go +69 -0
@@ 0,0 1,69 @@
package main

import (
	"fmt"
	"log"
	"os"

	"git.sr.ht/~sircmpwn/getopt"

	"git.sr.ht/~sircmpwn/go-bare/schema"
)

func main() {
	log.SetFlags(0)
	opts, optind, err := getopt.Getopts(os.Args, "hs:p:")
	if err != nil {
		log.Fatalf("error: %e", err)
	}
	pkg := "gen"
	skip := make(map[string]interface{})
	for _, opt := range opts {
		switch opt.Option {
		case 'p':
			pkg = opt.Value
		case 's':
			skip[opt.Value] = nil
		case 'h':
			log.Println("Usage: gen [-p <package>] [-s <skip type>] <input.bare> <output.go>")
			os.Exit(0)
		}
	}

	args := os.Args[optind:]
	if len(args) != 2 {
		log.Fatal("Usage: gen [-p <package>] <input.bare> <output.go>")
	}
	in := args[0]
	out := args[1]

	inf, err := os.Open(in)
	if err != nil {
		log.Fatalf("error opening %s: %e", in, err)
	}
	defer inf.Close()

	types, err := schema.Parse(inf)
	if err != nil {
		log.Fatalf("error parsing %s: %e", in, err)
	}

	outf, err := os.Create(out)
	if err != nil {
		log.Fatalf("error opening %s for writing: %e", out, err)
	}
	defer outf.Close()
	fmt.Fprintf(outf, "package %s\n", pkg)

	if len(skip) != 0 {
		var typesp []schema.SchemaType
		for _, ty := range types {
			if _, ok := skip[ty.Name()]; !ok {
				typesp = append(typesp, ty)
			}
		}
		types = typesp
	}

	genTypes(outf, types)
}

M go.mod => go.mod +4 -1
@@ 2,4 2,7 @@ module git.sr.ht/~sircmpwn/go-bare

go 1.14

require github.com/stretchr/testify v1.6.1
require (
	git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3
	github.com/stretchr/testify v1.6.1
)

M go.sum => go.sum +3 -0
@@ 1,8 1,11 @@
git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3 h1:4wDp4BKF7NQqoh73VXpZsB/t1OEhDpz/zEpmdQfbjDk=
git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=