Generate libvirt structs, unions, typedefs.
This commit is contained in:
parent
f88cbd7a8e
commit
fb16117ff9
@ -471,5 +471,4 @@ const (
|
|||||||
DomainEventGraphicsIdentityMax = 20
|
DomainEventGraphicsIdentityMax = 20
|
||||||
Program = 0x20008086
|
Program = 0x20008086
|
||||||
ProtocolVersion = 1
|
ProtocolVersion = 1
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -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 -}}
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
25
internal/lvgen/procedures.tmpl
Normal file
25
internal/lvgen/procedures.tmpl
Normal 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 -}}
|
||||||
|
}
|
@ -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
2492
libvirt.gen.go
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user