Generate libvirt structs, unions, typedefs.

This commit is contained in:
Geoff Hickey 2017-11-07 16:05:12 -05:00
parent f88cbd7a8e
commit fb16117ff9
6 changed files with 2748 additions and 56 deletions

View File

@ -471,5 +471,4 @@ const (
DomainEventGraphicsIdentityMax = 20 DomainEventGraphicsIdentityMax = 20
Program = 0x20008086 Program = 0x20008086
ProtocolVersion = 1 ProtocolVersion = 1
) )

View File

@ -19,10 +19,10 @@ package constants
// REMOTE_PROC_DOMAIN_MIGRATE_SET_MAX_SPEED = 207, // REMOTE_PROC_DOMAIN_MIGRATE_SET_MAX_SPEED = 207,
const ( const (
// From enums: // From enums:
{{range .Enums}}{{.Name}} = {{.Val}} {{range .EnumVals}} {{.Name}} = {{.Val}}
{{end}} {{end}}
// From consts: // From consts:
{{range .Consts}}{{.Name}} = {{.Val}} {{range .Consts}} {{.Name}} = {{.Val}}
{{end}} {{end -}}
) )

View File

@ -62,10 +62,19 @@ type ConstItem struct {
// Generator holds all the information parsed out of the protocol file. // Generator holds all the information parsed out of the protocol file.
type Generator struct { type Generator struct {
// Enums holds the list of enums found by the parser. // Enums holds the enum declarations. The type of enums is always int32.
Enums []ConstItem Enums []Decl
// EnumVals holds the list of enum values found by the parser. In sunrpc as
// in go, these are not separately namespaced.
EnumVals []ConstItem
// Consts holds all the const items found by the parser. // Consts holds all the const items found by the parser.
Consts []ConstItem Consts []ConstItem
// Structs holds a list of all the structs found by the parser
Structs []Structure
// Typedefs hold all the type definitions from 'typedef ...' lines.
Typedefs []Typedef
// Unions hold all the discriminated unions
Unions []Union
} }
// Gen accumulates items as the parser runs, and is then used to produce the // Gen accumulates items as the parser runs, and is then used to produce the
@ -81,6 +90,60 @@ var CurrentEnumVal int64
// runes. // runes.
var oneRuneTokens = `{}[]<>(),=;:*` var oneRuneTokens = `{}[]<>(),=;:*`
var reservedIdentifiers = map[string]string{
"type": "lvtype",
"string": "lvstring",
"error": "lverror",
}
// Decl records a declaration, like 'int x' or 'remote_nonnull_string str'
type Decl struct {
Name, Type string
}
// Structure records the name and members of a struct definition.
type Structure struct {
Name string
Members []Decl
}
// Typedef holds the name and underlying type for a typedef.
type Typedef struct {
Name string
Type string
}
// Union holds a "discriminated union", which consists of a discriminant, which
// tells you what kind of thing you're looking at, and a number of encodings.
type Union struct {
Name string
DiscriminantType string
Cases []Case
}
// Case holds a single case of a discriminated union.
type Case struct {
DiscriminantVal string
Type Decl
}
// CurrentStruct will point to a struct record if we're in a struct declaration.
// When the parser adds a declaration, it will be added to the open struct if
// there is one.
var CurrentStruct *Structure
// CurrentTypedef will point to a typedef record if we're parsing one. Typedefs
// can define a struct or union type, but the preferred for is struct xxx{...},
// so we may never see the typedef form in practice.
var CurrentTypedef *Typedef
// CurrentUnion holds the current discriminated union record.
var CurrentUnion *Union
// CurrentCase holds the current case record while the parser is in a union and
// a case statement.
var CurrentCase *Case
// Generate will output go bindings for libvirt. The lvPath parameter should be // Generate will output go bindings for libvirt. The lvPath parameter should be
// the path to the root of the libvirt source directory to use for the // the path to the root of the libvirt source directory to use for the
// generation. // generation.
@ -100,35 +163,51 @@ func Generate(proto io.Reader) error {
} }
// Generate and write the output. // Generate and write the output.
wr, err := os.Create("../constants/constants.gen.go") constFile, err := os.Create("../constants/constants.gen.go")
if err != nil { if err != nil {
return err return err
} }
defer wr.Close() defer constFile.Close()
procFile, err := os.Create("../../libvirt.gen.go")
if err != nil {
return err
}
defer procFile.Close()
err = genGo(wr) err = genGo(constFile, procFile)
return err return err
} }
func genGo(wr io.Writer) error { func genGo(constFile, procFile io.Writer) error {
// Enums and consts from the protocol definition both become go consts in
// the generated code. We'll remove "REMOTE_" and then camel-case the
// name before making each one a go constant.
for ix, en := range Gen.Enums {
Gen.Enums[ix].Name = constNameTransform(en.Name)
}
for ix, en := range Gen.Consts {
Gen.Consts[ix].Name = constNameTransform(en.Name)
}
t, err := template.ParseFiles("constants.tmpl") t, err := template.ParseFiles("constants.tmpl")
if err != nil { if err != nil {
return err return err
} }
if err := t.Execute(wr, Gen); err != nil { if err = t.Execute(constFile, Gen); err != nil {
return err return err
} }
t, err = template.ParseFiles("procedures.tmpl")
if err != nil {
return err
}
if err := t.Execute(procFile, Gen); err != nil {
return err
}
// Now generate the wrappers for libvirt's various public API functions.
// for _, c := range Gen.Enums {
// This appears to be the name of a libvirt procedure, so sort it into
// the right list based on the next part of its name.
// segs := camelcase.Split(c.Name)
// if len(segs) < 3 || segs[0] != "Proc" {
// continue
// }
//category := segs[1]
//fmt.Println(segs)
// }
return nil return nil
} }
@ -137,20 +216,35 @@ func genGo(wr io.Writer) error {
// also tries to upcase abbreviations so a name like DOMAIN_GET_XML becomes // also tries to upcase abbreviations so a name like DOMAIN_GET_XML becomes
// DomainGetXML, not DomainGetXml. // DomainGetXML, not DomainGetXml.
func constNameTransform(name string) string { func constNameTransform(name string) string {
nn := fromSnakeToCamel(strings.TrimPrefix(name, "REMOTE_")) nn := fromSnakeToCamel(strings.TrimPrefix(name, "REMOTE_"), true)
nn = fixAbbrevs(nn) nn = fixAbbrevs(nn)
return nn return nn
} }
func identifierTransform(name string) string {
nn := strings.TrimPrefix(name, "remote_")
nn = fromSnakeToCamel(nn, false)
nn = fixAbbrevs(nn)
nn = checkIdentifier(nn)
return nn
}
func typeTransform(name string) string {
nn := strings.TrimLeft(name, "*")
diff := len(name) - len(nn)
nn = identifierTransform(nn)
return name[0:diff] + nn
}
// fromSnakeToCamel transmutes a snake-cased string to a camel-cased one. All // fromSnakeToCamel transmutes a snake-cased string to a camel-cased one. All
// runes that follow an underscore are up-cased, and the underscores themselves // runes that follow an underscore are up-cased, and the underscores themselves
// are omitted. // are omitted.
// //
// ex: "PROC_DOMAIN_GET_METADATA" -> "ProcDomainGetMetadata" // ex: "PROC_DOMAIN_GET_METADATA" -> "ProcDomainGetMetadata"
func fromSnakeToCamel(s string) string { func fromSnakeToCamel(s string, public bool) string {
buf := make([]rune, 0, len(s)) buf := make([]rune, 0, len(s))
// Start with an upper-cased rune // Start rune may be either upper or lower case.
hump := true hump := public
for _, r := range s { for _, r := range s {
if r == '_' { if r == '_' {
@ -203,19 +297,22 @@ func fixAbbrevs(s string) string {
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// StartEnum is called when the parser has found a valid enum. // StartEnum is called when the parser has found a valid enum.
func StartEnum() { func StartEnum(name string) {
// Enums are always signed 32-bit integers.
name = identifierTransform(name)
Gen.Enums = append(Gen.Enums, Decl{name, "int32"})
// Set the automatic value var to -1; it will be incremented before being // Set the automatic value var to -1; it will be incremented before being
// assigned to an enum value. // assigned to an enum value.
CurrentEnumVal = -1 CurrentEnumVal = -1
} }
// AddEnum will add a new enum value to the list. // AddEnumVal will add a new enum value to the list.
func AddEnum(name, val string) error { func AddEnumVal(name, val string) error {
ev, err := parseNumber(val) ev, err := parseNumber(val)
if err != nil { if err != nil {
return fmt.Errorf("invalid enum value %v = %v", name, val) return fmt.Errorf("invalid enum value %v = %v", name, val)
} }
return addEnum(name, ev) return addEnumVal(name, ev)
} }
// AddEnumAutoVal adds an enum to the list, using the automatically-incremented // AddEnumAutoVal adds an enum to the list, using the automatically-incremented
@ -223,11 +320,12 @@ func AddEnum(name, val string) error {
// explicit value. // explicit value.
func AddEnumAutoVal(name string) error { func AddEnumAutoVal(name string) error {
CurrentEnumVal++ CurrentEnumVal++
return addEnum(name, CurrentEnumVal) return addEnumVal(name, CurrentEnumVal)
} }
func addEnum(name string, val int64) error { func addEnumVal(name string, val int64) error {
Gen.Enums = append(Gen.Enums, ConstItem{name, fmt.Sprintf("%d", val)}) name = constNameTransform(name)
Gen.EnumVals = append(Gen.EnumVals, ConstItem{name, fmt.Sprintf("%d", val)})
CurrentEnumVal = val CurrentEnumVal = val
return nil return nil
} }
@ -238,6 +336,7 @@ func AddConst(name, val string) error {
if err != nil { if err != nil {
return fmt.Errorf("invalid const value %v = %v", name, val) return fmt.Errorf("invalid const value %v = %v", name, val)
} }
name = constNameTransform(name)
Gen.Consts = append(Gen.Consts, ConstItem{name, val}) Gen.Consts = append(Gen.Consts, ConstItem{name, val})
return nil return nil
} }
@ -253,3 +352,80 @@ func parseNumber(val string) (int64, error) {
n, err := strconv.ParseInt(val, base, 64) n, err := strconv.ParseInt(val, base, 64)
return n, err return n, err
} }
// StartStruct is called from the parser when a struct definition is found, but
// before the member declarations are processed.
func StartStruct(name string) {
name = identifierTransform(name)
CurrentStruct = &Structure{Name: name}
}
// AddStruct is called when the parser has finished parsing a struct. It adds
// the now-complete struct definition to the generator's list.
func AddStruct() {
Gen.Structs = append(Gen.Structs, *CurrentStruct)
CurrentStruct = nil
}
func StartTypedef() {
CurrentTypedef = &Typedef{}
}
// TODO: remove before flight
func Beacon(name string) {
fmt.Println(name)
}
// StartUnion is called by the parser when it finds a union declaraion.
func StartUnion(name string) {
name = identifierTransform(name)
CurrentUnion = &Union{Name: name}
}
// AddUnion is called by the parser when it has finished processing a union
// type. It adds the union to the generator's list and clears the CurrentUnion
// pointer.
func AddUnion() {
Gen.Unions = append(Gen.Unions, *CurrentUnion)
CurrentUnion = nil
}
func StartCase(dvalue string) {
CurrentCase = &Case{DiscriminantVal: dvalue}
}
func AddCase() {
CurrentUnion.Cases = append(CurrentUnion.Cases, *CurrentCase)
CurrentCase = nil
}
// AddDeclaration is called by the parser when it find a declaration (int x).
// The declaration will be added to any open container (such as a struct, if the
// parser is working through a struct definition.)
func AddDeclaration(identifier, itype string) {
// TODO: panic if not in a struct/union/typedef?
// If the name is a reserved word, transform it so it isn't.
identifier = identifierTransform(identifier)
itype = typeTransform(itype)
decl := &Decl{Name: identifier, Type: itype}
if CurrentStruct != nil {
CurrentStruct.Members = append(CurrentStruct.Members, *decl)
} else if CurrentTypedef != nil {
CurrentTypedef.Name = identifier
CurrentTypedef.Type = itype
Gen.Typedefs = append(Gen.Typedefs, *CurrentTypedef)
CurrentTypedef = nil
} else if CurrentCase != nil {
CurrentCase.Type = *decl
} else if CurrentUnion != nil {
CurrentUnion.DiscriminantType = itype
}
}
func checkIdentifier(i string) string {
nn, reserved := reservedIdentifiers[i]
if reserved {
return nn
}
return i
}

View File

@ -0,0 +1,25 @@
/*
* This file generated by internal/lvgen/generate.go. DO NOT EDIT BY HAND!
*
* To regenerate, run 'go generate' in internal/lvgen.
*/
package libvirt
// Typedefs:
{{range .Typedefs}}type {{.Name}} {{.Type}}
{{end}}
// Enums:
{{range .Enums}} type {{.Name}} {{.Type}}
{{end}}
// Structs:
{{range .Structs}}type {{.Name}} struct {
{{range .Members}} {{.Name}} {{.Type}}
{{end -}}
}
{{end}}
// Unions:
{{range .Unions}}type {{.Name}} struct {
discriminant {{.DiscriminantType}}
{{end -}}
}

View File

@ -46,7 +46,7 @@ definition
; ;
enum_definition enum_definition
: ENUM enum_ident '{' enum_value_list '}' { StartEnum() } : ENUM enum_ident '{' enum_value_list '}' { StartEnum($2.val) }
; ;
enum_value_list enum_value_list
@ -63,7 +63,7 @@ enum_value
} }
} }
| enum_value_ident '=' value { | enum_value_ident '=' value {
err := AddEnum($1.val, $3.val) err := AddEnumVal($1.val, $3.val)
if err != nil { if err != nil {
yylex.Error(err.Error()) yylex.Error(err.Error())
return 1 return 1
@ -98,7 +98,7 @@ const_ident
; ;
typedef_definition typedef_definition
: TYPEDEF declaration : TYPEDEF {StartTypedef()} declaration
; ;
declaration declaration
@ -109,17 +109,17 @@ declaration
; ;
simple_declaration simple_declaration
: type_specifier variable_ident : type_specifier variable_ident {AddDeclaration($2.val, $1.val)}
; ;
type_specifier type_specifier
: int_spec : int_spec
| UNSIGNED int_spec | UNSIGNED int_spec {$$.val = "u"+$2.val}
| FLOAT | FLOAT {$$.val = "float32"}
| DOUBLE | DOUBLE {$$.val = "float64"}
| BOOL | BOOL {$$.val = "bool"}
| STRING | STRING {$$.val = "string"}
| OPAQUE | OPAQUE {$$.val = "[]byte"}
| enum_definition | enum_definition
| struct_definition | struct_definition
| union_definition | union_definition
@ -127,10 +127,10 @@ type_specifier
; ;
int_spec int_spec
: HYPER : HYPER {$$.val = "int64"}
| INT | INT {$$.val = "int32"}
| SHORT | SHORT {$$.val = "int16"}
| CHAR | CHAR {$$.val = "int8"}
; ;
variable_ident variable_ident
@ -138,20 +138,20 @@ variable_ident
; ;
fixed_array_declaration fixed_array_declaration
: type_specifier variable_ident '[' value ']' : type_specifier variable_ident '[' value ']' { AddDeclaration($2.val, $1.val) } // FIXME: Handle the max size (value)?
; ;
variable_array_declaration variable_array_declaration
: type_specifier variable_ident '<' value '>' : type_specifier variable_ident '<' value '>' { AddDeclaration($2.val, $1.val) } // FIXME: Handle the max size (value)?
| type_specifier variable_ident '<' '>' | type_specifier variable_ident '<' '>' { AddDeclaration($2.val, $1.val) }
; ;
pointer_declaration pointer_declaration
: type_specifier '*' variable_ident : type_specifier '*' variable_ident { AddDeclaration($3.val, "*"+$1.val) }
; ;
struct_definition struct_definition
: STRUCT struct_ident '{' declaration_list '}' : STRUCT struct_ident '{' {StartStruct($2.val)} declaration_list '}' {AddStruct()}
; ;
struct_ident struct_ident
@ -164,7 +164,7 @@ declaration_list
; ;
union_definition union_definition
: UNION union_ident SWITCH '(' simple_declaration ')' '{' case_list '}' : UNION union_ident {StartUnion($2.val)} SWITCH '(' simple_declaration ')' '{' case_list '}' {AddUnion()}
; ;
union_ident union_ident
@ -177,8 +177,8 @@ case_list
; ;
case case
: CASE value ':' declaration : CASE value {StartCase($2.val)} ':' declaration {AddCase()}
| DEFAULT ':' declaration | DEFAULT {StartCase("default")} ':' declaration {AddCase()}
; ;
program_definition program_definition

2492
libvirt.gen.go Normal file

File diff suppressed because it is too large Load Diff