aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/binapi-generator
diff options
context:
space:
mode:
authorRastislav Szabo <raszabo@cisco.com>2018-12-13 12:03:49 +0000
committerGerrit Code Review <gerrit@fd.io>2018-12-13 12:03:49 +0000
commit5f1917fd11e8562ef4094b7ce3a89af66ace5792 (patch)
tree02309976390883716e4a819d3b5969091069a750 /cmd/binapi-generator
parent6bfc1a27011d44cbf915607feb276a847148dd30 (diff)
parent868b541e296dc47748ad03b8f0174c828d996529 (diff)
Merge "Add support for aliases and boolean type"
Diffstat (limited to 'cmd/binapi-generator')
-rw-r--r--cmd/binapi-generator/definitions.go21
-rw-r--r--cmd/binapi-generator/generate.go157
-rw-r--r--cmd/binapi-generator/objects.go21
-rw-r--r--cmd/binapi-generator/parse.go136
4 files changed, 240 insertions, 95 deletions
diff --git a/cmd/binapi-generator/definitions.go b/cmd/binapi-generator/definitions.go
index 3ad782f..0176278 100644
--- a/cmd/binapi-generator/definitions.go
+++ b/cmd/binapi-generator/definitions.go
@@ -25,6 +25,8 @@ func getBinapiTypeSize(binapiType string) int {
b, err := strconv.Atoi(strings.TrimLeft(binapiType, "uif"))
if err == nil {
return b / 8
+ } else {
+ return 1
}
}
return -1
@@ -32,15 +34,16 @@ func getBinapiTypeSize(binapiType string) int {
// binapiTypes is a set of types used VPP binary API for translation to Go types
var binapiTypes = map[string]string{
- "u8": "uint8",
- "i8": "int8",
- "u16": "uint16",
- "i16": "int16",
- "u32": "uint32",
- "i32": "int32",
- "u64": "uint64",
- "i64": "int64",
- "f64": "float64",
+ "bool": "bool",
+ "u8": "uint8",
+ "i8": "int8",
+ "u16": "uint16",
+ "i16": "int16",
+ "u32": "uint32",
+ "i32": "int32",
+ "u64": "uint64",
+ "i64": "int64",
+ "f64": "float64",
}
func usesInitialism(s string) string {
diff --git a/cmd/binapi-generator/generate.go b/cmd/binapi-generator/generate.go
index 7cfe338..22b4af6 100644
--- a/cmd/binapi-generator/generate.go
+++ b/cmd/binapi-generator/generate.go
@@ -35,9 +35,7 @@ type context struct {
inputFile string // input file with VPP API in JSON
outputFile string // output file with generated Go package
- inputData []byte // contents of the input file
- inputBuff *bytes.Buffer // contents of the input file currently being read
- inputLine int // currently processed line in the input file
+ inputData []byte // contents of the input file
moduleName string // name of the source VPP module
packageName string // name of the Go package being generated
@@ -87,28 +85,40 @@ func generatePackage(ctx *context, w *bufio.Writer) error {
if *includeAPIVer {
const APIVerConstName = "VlAPIVersion"
- fmt.Fprintf(w, "// %s represents version of the API.\n", APIVerConstName)
+ fmt.Fprintf(w, "// %s represents version of the binary API module.\n", APIVerConstName)
fmt.Fprintf(w, "const %s = %v\n", APIVerConstName, ctx.packageData.APIVersion)
fmt.Fprintln(w)
}
+ // generate services
+ if len(ctx.packageData.Services) > 0 {
+ generateServices(ctx, w, ctx.packageData.Services)
+ }
+
+ // TODO: generate implementation for Services interface
+
// generate enums
if len(ctx.packageData.Enums) > 0 {
fmt.Fprintf(w, "/* Enums */\n\n")
- ctx.inputBuff = bytes.NewBuffer(ctx.inputData)
- ctx.inputLine = 0
for _, enum := range ctx.packageData.Enums {
generateEnum(ctx, w, &enum)
}
}
+ // generate aliases
+ if len(ctx.packageData.Aliases) > 0 {
+ fmt.Fprintf(w, "/* Aliases */\n\n")
+
+ for _, alias := range ctx.packageData.Aliases {
+ generateAlias(ctx, w, &alias)
+ }
+ }
+
// generate types
if len(ctx.packageData.Types) > 0 {
fmt.Fprintf(w, "/* Types */\n\n")
- ctx.inputBuff = bytes.NewBuffer(ctx.inputData)
- ctx.inputLine = 0
for _, typ := range ctx.packageData.Types {
generateType(ctx, w, &typ)
}
@@ -118,8 +128,6 @@ func generatePackage(ctx *context, w *bufio.Writer) error {
if len(ctx.packageData.Unions) > 0 {
fmt.Fprintf(w, "/* Unions */\n\n")
- ctx.inputBuff = bytes.NewBuffer(ctx.inputData)
- ctx.inputLine = 0
for _, union := range ctx.packageData.Unions {
generateUnion(ctx, w, &union)
}
@@ -129,28 +137,11 @@ func generatePackage(ctx *context, w *bufio.Writer) error {
if len(ctx.packageData.Messages) > 0 {
fmt.Fprintf(w, "/* Messages */\n\n")
- ctx.inputBuff = bytes.NewBuffer(ctx.inputData)
- ctx.inputLine = 0
for _, msg := range ctx.packageData.Messages {
generateMessage(ctx, w, &msg)
}
}
- // generate services
- if len(ctx.packageData.Services) > 0 {
- fmt.Fprintf(w, "/* Services */\n\n")
-
- ctx.inputBuff = bytes.NewBuffer(ctx.inputData)
- ctx.inputLine = 0
- fmt.Fprintf(w, "type %s interface {\n", "Services")
- for _, svc := range ctx.packageData.Services {
- generateService(ctx, w, &svc)
- }
- fmt.Fprintln(w, "}")
- }
-
- // TODO: generate implementation for Services interface
-
// generate message registrations
fmt.Fprintln(w)
fmt.Fprintln(w, "func init() {")
@@ -181,13 +172,18 @@ func generateHeader(ctx *context, w io.Writer) {
var printObjNum = func(obj string, num int) {
if num > 0 {
if num > 1 {
- obj += "s"
+ if strings.HasSuffix(obj, "s") {
+ obj += "es"
+ } else {
+ obj += "s"
+ }
}
fmt.Fprintf(w, "\t%3d %s\n", num, obj)
}
}
printObjNum("message", len(ctx.packageData.Messages))
printObjNum("type", len(ctx.packageData.Types))
+ printObjNum("alias", len(ctx.packageData.Aliases))
printObjNum("enum", len(ctx.packageData.Enums))
printObjNum("union", len(ctx.packageData.Unions))
printObjNum("service", len(ctx.packageData.Services))
@@ -213,44 +209,96 @@ func generateImports(ctx *context, w io.Writer) {
// generateComment writes generated comment for the object into w
func generateComment(ctx *context, w io.Writer, goName string, vppName string, objKind string) {
- fmt.Fprintf(w, "// %s represents the VPP binary API %s '%s'.\n", goName, objKind, vppName)
+ if objKind == "service" {
+ fmt.Fprintf(w, "// %s represents VPP binary API services:\n", goName)
+ } else {
+ fmt.Fprintf(w, "// %s represents VPP binary API %s '%s':\n", goName, objKind, vppName)
+ }
var isNotSpace = func(r rune) bool {
return !unicode.IsSpace(r)
}
// print out the source of the generated object
+ mapType := false
objFound := false
objTitle := fmt.Sprintf(`"%s",`, vppName)
+ switch objKind {
+ case "alias", "service":
+ objTitle = fmt.Sprintf(`"%s": {`, vppName)
+ mapType = true
+ }
+
+ inputBuff := bytes.NewBuffer(ctx.inputData)
+ inputLine := 0
+
+ var trimIndent string
var indent int
for {
- line, err := ctx.inputBuff.ReadString('\n')
+ line, err := inputBuff.ReadString('\n')
if err != nil {
break
}
- ctx.inputLine++
+ inputLine++
+ noSpaceAt := strings.IndexFunc(line, isNotSpace)
if !objFound {
indent = strings.Index(line, objTitle)
if indent == -1 {
continue
}
+ trimIndent = line[:indent]
// If no other non-whitespace character then we are at the message header.
if trimmed := strings.TrimSpace(line); trimmed == objTitle {
objFound = true
fmt.Fprintln(w, "//")
}
- } else {
- if strings.IndexFunc(line, isNotSpace) < indent {
- break // end of the object definition in JSON
- }
+ } else if noSpaceAt < indent {
+ break // end of the definition in JSON for array types
+ } else if objFound && mapType && noSpaceAt <= indent {
+ fmt.Fprintf(w, "//\t%s", strings.TrimPrefix(line, trimIndent))
+ break // end of the definition in JSON for map types (aliases, services)
}
- fmt.Fprint(w, "//", line)
+ fmt.Fprintf(w, "//\t%s", strings.TrimPrefix(line, trimIndent))
}
fmt.Fprintln(w, "//")
}
+// generateServices writes generated code for the Services interface into w
+func generateServices(ctx *context, w *bufio.Writer, services []Service) {
+ // generate services comment
+ generateComment(ctx, w, "Services", "services", "service")
+
+ // generate interface
+ fmt.Fprintf(w, "type %s interface {\n", "Services")
+ for _, svc := range ctx.packageData.Services {
+ generateService(ctx, w, &svc)
+ }
+ fmt.Fprintln(w, "}")
+
+ fmt.Fprintln(w)
+}
+
+// generateService writes generated code for the service into w
+func generateService(ctx *context, w io.Writer, svc *Service) {
+ reqTyp := camelCaseName(svc.RequestType)
+
+ // method name is same as parameter type name by default
+ method := svc.MethodName()
+ params := fmt.Sprintf("*%s", reqTyp)
+ returns := "error"
+ if replyType := camelCaseName(svc.ReplyType); replyType != "" {
+ repTyp := fmt.Sprintf("*%s", replyType)
+ if svc.Stream {
+ repTyp = fmt.Sprintf("[]%s", repTyp)
+ }
+ returns = fmt.Sprintf("(%s, error)", repTyp)
+ }
+
+ fmt.Fprintf(w, "\t%s(%s) %s\n", method, params, returns)
+}
+
// generateEnum writes generated code for the enum into w
func generateEnum(ctx *context, w io.Writer, enum *Enum) {
name := camelCaseName(enum.Name)
@@ -277,6 +325,28 @@ func generateEnum(ctx *context, w io.Writer, enum *Enum) {
fmt.Fprintln(w)
}
+// generateAlias writes generated code for the alias into w
+func generateAlias(ctx *context, w io.Writer, alias *Alias) {
+ name := camelCaseName(alias.Name)
+
+ logf(" writing type %q (%s), length: %d", alias.Name, name, alias.Length)
+
+ // generate struct comment
+ generateComment(ctx, w, name, alias.Name, "alias")
+
+ // generate struct definition
+ fmt.Fprintf(w, "type %s ", name)
+
+ if alias.Length > 0 {
+ fmt.Fprintf(w, "[%d]", alias.Length)
+ }
+
+ dataType := convertToGoType(ctx, alias.Type)
+ fmt.Fprintf(w, "%s\n", dataType)
+
+ fmt.Fprintln(w)
+}
+
// generateType writes generated code for the type into w
func generateType(ctx *context, w io.Writer, typ *Type) {
name := camelCaseName(typ.Name)
@@ -494,21 +564,6 @@ func generateField(ctx *context, w io.Writer, fields []Field, i int) {
fmt.Fprintln(w)
}
-// generateService writes generated code for the service into w
-func generateService(ctx *context, w io.Writer, svc *Service) {
- reqTyp := camelCaseName(svc.RequestType)
-
- // method name is same as parameter type name by default
- method := svc.MethodName()
- params := fmt.Sprintf("*%s", reqTyp)
- returns := "error"
- if replyTyp := camelCaseName(svc.ReplyType); replyTyp != "" {
- returns = fmt.Sprintf("(*%s, error)", replyTyp)
- }
-
- fmt.Fprintf(w, "\t%s(%s) %s\n", method, params, returns)
-}
-
// generateMessageNameGetter generates getter for original VPP message name into the provider writer
func generateMessageNameGetter(w io.Writer, structName, msgName string) {
fmt.Fprintf(w, `func (*%s) GetMessageName() string {
diff --git a/cmd/binapi-generator/objects.go b/cmd/binapi-generator/objects.go
index 2681085..97318cb 100644
--- a/cmd/binapi-generator/objects.go
+++ b/cmd/binapi-generator/objects.go
@@ -8,6 +8,7 @@ type Package struct {
Enums []Enum
Unions []Union
Types []Type
+ Aliases []Alias
Messages []Message
Services []Service
RefMap map[string]string
@@ -37,6 +38,13 @@ type Type struct {
Fields []Field
}
+// Alias represents VPP binary API alias
+type Alias struct {
+ Name string
+ Type string
+ Length int
+}
+
// Union represents VPP binary API union
type Union struct {
Name string
@@ -105,16 +113,3 @@ func (s Service) IsRequestService() bool {
// some binapi messages might have `null` reply (for example: memclnt)
return s.ReplyType != "" && s.ReplyType != "null" // not null
}
-
-func getSizeOfType(typ *Type) (size int) {
- for _, field := range typ.Fields {
- if n := getBinapiTypeSize(field.Type); n > 0 {
- if field.Length > 0 {
- size += n * field.Length
- } else {
- size += n
- }
- }
- }
- return size
-}
diff --git a/cmd/binapi-generator/parse.go b/cmd/binapi-generator/parse.go
index 2d6fdd4..5dfbe91 100644
--- a/cmd/binapi-generator/parse.go
+++ b/cmd/binapi-generator/parse.go
@@ -23,26 +23,6 @@ import (
"github.com/bennyscetbun/jsongo"
)
-func getTypeByRef(ctx *context, ref string) *Type {
- for _, typ := range ctx.packageData.Types {
- if ref == toApiType(typ.Name) {
- return &typ
- }
- }
- return nil
-}
-
-func getUnionSize(ctx *context, union *Union) (maxSize int) {
- for _, field := range union.Fields {
- if typ := getTypeByRef(ctx, field.Type); typ != nil {
- if size := getSizeOfType(typ); size > maxSize {
- maxSize = size
- }
- }
- }
- return
-}
-
// toApiType returns name that is used as type reference in VPP binary API
func toApiType(name string) string {
return fmt.Sprintf("vl_api_%s_t", name)
@@ -50,13 +30,14 @@ func toApiType(name string) string {
// parsePackage parses provided JSON data into objects prepared for code generation
func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) {
- logf(" %s contains: %d services, %d messages, %d types, %d enums, %d unions (version: %s)",
+ logf(" %s contains: %d services, %d messages, %d types, %d enums, %d unions, %d aliases (version: %s)",
ctx.packageName,
jsonRoot.Map("services").Len(),
jsonRoot.Map("messages").Len(),
jsonRoot.Map("types").Len(),
jsonRoot.Map("enums").Len(),
jsonRoot.Map("unions").Len(),
+ jsonRoot.Map("aliases").Len(),
jsonRoot.Map("vl_api_version").Get(),
)
@@ -79,6 +60,22 @@ func parsePackage(ctx *context, jsonRoot *jsongo.JSONNode) (*Package, error) {
pkg.RefMap[toApiType(enum.Name)] = enum.Name
}
+ // parse aliases
+ aliases := jsonRoot.Map("aliases")
+ if aliases.GetType() == jsongo.TypeMap {
+ pkg.Aliases = make([]Alias, aliases.Len())
+ for i, key := range aliases.GetKeys() {
+ aliasNode := aliases.At(key)
+
+ alias, err := parseAlias(ctx, key.(string), aliasNode)
+ if err != nil {
+ return nil, err
+ }
+ pkg.Aliases[i] = *alias
+ pkg.RefMap[toApiType(alias.Name)] = alias.Name
+ }
+ }
+
// parse types
types := jsonRoot.Map("types")
pkg.Types = make([]Type, types.Len())
@@ -308,6 +305,42 @@ func parseType(ctx *context, typeNode *jsongo.JSONNode) (*Type, error) {
return &typ, nil
}
+const (
+ aliasesLength = "length"
+ aliasesType = "type"
+)
+
+// parseAlias parses VPP binary API alias object from JSON node
+func parseAlias(ctx *context, aliasName string, aliasNode *jsongo.JSONNode) (*Alias, error) {
+ if aliasNode.Len() == 0 || aliasNode.At(aliasesType).GetType() != jsongo.TypeValue {
+ return nil, errors.New("invalid JSON for alias specified")
+ }
+
+ alias := Alias{
+ Name: aliasName,
+ }
+
+ if typeNode := aliasNode.At(aliasesType); typeNode.GetType() == jsongo.TypeValue {
+ typ, ok := typeNode.Get().(string)
+ if !ok {
+ return nil, fmt.Errorf("alias type is %T, not a string", typeNode.Get())
+ }
+ if typ != "null" {
+ alias.Type = typ
+ }
+ }
+
+ if lengthNode := aliasNode.At(aliasesLength); lengthNode.GetType() == jsongo.TypeValue {
+ length, ok := lengthNode.Get().(float64)
+ if !ok {
+ return nil, fmt.Errorf("alias length is %T, not a float64", lengthNode.Get())
+ }
+ alias.Length = int(length)
+ }
+
+ return &alias, nil
+}
+
// parseMessage parses VPP binary API message object from JSON node
func parseMessage(ctx *context, msgNode *jsongo.JSONNode) (*Message, error) {
if msgNode.Len() == 0 || msgNode.At(0).GetType() != jsongo.TypeValue {
@@ -364,7 +397,7 @@ func parseField(ctx *context, field *jsongo.JSONNode) (*Field, error) {
if field.Len() >= 3 {
fieldLength, ok = field.At(2).Get().(float64)
if !ok {
- return nil, fmt.Errorf("field length is %T, not an int", field.At(2).Get())
+ return nil, fmt.Errorf("field length is %T, not float64", field.At(2).Get())
}
}
var fieldLengthFrom string
@@ -461,3 +494,62 @@ func convertToGoType(ctx *context, binapiType string) (typ string) {
}
return typ
}
+
+func getSizeOfType(typ *Type) (size int) {
+ for _, field := range typ.Fields {
+ size += getSizeOfBinapiTypeLength(field.Type, field.Length)
+ }
+ return size
+}
+
+func getSizeOfBinapiTypeLength(typ string, length int) (size int) {
+ if n := getBinapiTypeSize(typ); n > 0 {
+ if length > 0 {
+ return n * length
+ } else {
+ return n
+ }
+ }
+ return
+}
+
+func getTypeByRef(ctx *context, ref string) *Type {
+ for _, typ := range ctx.packageData.Types {
+ if ref == toApiType(typ.Name) {
+ return &typ
+ }
+ }
+ return nil
+}
+
+func getAliasByRef(ctx *context, ref string) *Alias {
+ for _, alias := range ctx.packageData.Aliases {
+ if ref == toApiType(alias.Name) {
+ return &alias
+ }
+ }
+ return nil
+}
+
+func getUnionSize(ctx *context, union *Union) (maxSize int) {
+ for _, field := range union.Fields {
+ typ := getTypeByRef(ctx, field.Type)
+ if typ != nil {
+ if size := getSizeOfType(typ); size > maxSize {
+ maxSize = size
+ }
+ continue
+ }
+ alias := getAliasByRef(ctx, field.Type)
+ if alias != nil {
+ if size := getSizeOfBinapiTypeLength(alias.Type, alias.Length); size > maxSize {
+ maxSize = size
+ }
+ continue
+ } else {
+ logf("no type or alias found for union %s field type %q", union.Name, field.Type)
+ continue
+ }
+ }
+ return
+}