mirror of
https://github.com/mappu/miqt.git
synced 2025-04-01 11:20:23 +00:00
genbindings: add support for enums
This commit is contained in:
parent
25e6ea1698
commit
d62ec99cf1
@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -88,7 +89,14 @@ func parseHeader(topLevel []interface{}, addNamePrefix string) (*CppParsedHeader
|
|||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
case "EnumDecl":
|
case "EnumDecl":
|
||||||
// TODO e.g. qmetatype.h
|
// Child class enum
|
||||||
|
en, err := processEnum(node, addNamePrefix)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("processEnum: %w", err)) // A real problem
|
||||||
|
}
|
||||||
|
if len(en.Entries) > 0 { // e.g. qmetatype's version of QCborSimpleType (the real one is in qcborcommon)
|
||||||
|
ret.Enums = append(ret.Enums, en)
|
||||||
|
}
|
||||||
|
|
||||||
case "VarDecl":
|
case "VarDecl":
|
||||||
// TODO e.g. qmath.h
|
// TODO e.g. qmath.h
|
||||||
@ -312,6 +320,21 @@ nextMethod:
|
|||||||
}
|
}
|
||||||
ret.ChildTypedefs = append(ret.ChildTypedefs, td)
|
ret.ChildTypedefs = append(ret.ChildTypedefs, td)
|
||||||
|
|
||||||
|
case "EnumDecl":
|
||||||
|
// Child class enum
|
||||||
|
|
||||||
|
if !visibility {
|
||||||
|
continue // Skip private/protected
|
||||||
|
}
|
||||||
|
|
||||||
|
en, err := processEnum(node, nodename+"::")
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("processEnum: %w", err)) // A real problem
|
||||||
|
}
|
||||||
|
if len(en.Entries) > 0 { // e.g. qmetatype's version of QCborSimpleType (the real one is in qcborcommon)
|
||||||
|
ret.ChildEnums = append(ret.ChildEnums, en)
|
||||||
|
}
|
||||||
|
|
||||||
case "CXXConstructorDecl":
|
case "CXXConstructorDecl":
|
||||||
|
|
||||||
if isImplicit, ok := node["isImplicit"].(bool); ok && isImplicit {
|
if isImplicit, ok := node["isImplicit"].(bool); ok && isImplicit {
|
||||||
@ -455,6 +478,108 @@ var (
|
|||||||
ErrNoContent = errors.New("There's no content to include")
|
ErrNoContent = errors.New("There's no content to include")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func processEnum(node map[string]interface{}, addNamePrefix string) (CppEnum, error) {
|
||||||
|
var ret CppEnum
|
||||||
|
ret.UnderlyingType = "int" // FIXME
|
||||||
|
|
||||||
|
nodename, ok := node["name"].(string)
|
||||||
|
if !ok {
|
||||||
|
// An unnamed enum is possible (e.g. qcalendar.h)
|
||||||
|
// It defines integer constants just in the current scope
|
||||||
|
ret.EnumName = addNamePrefix
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ret.EnumName = addNamePrefix + nodename
|
||||||
|
}
|
||||||
|
|
||||||
|
inner, ok := node["inner"].([]interface{})
|
||||||
|
if !ok {
|
||||||
|
// An enum with no entries? We're done
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastImplicitValue int64 = -1
|
||||||
|
|
||||||
|
for _, entry := range inner {
|
||||||
|
entry, ok := entry.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return ret, errors.New("bad inner type")
|
||||||
|
}
|
||||||
|
|
||||||
|
kind, ok := entry["kind"].(string)
|
||||||
|
if !ok || kind != "EnumConstantDecl" {
|
||||||
|
return ret, fmt.Errorf("unexpected kind %q", kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cee CppEnumEntry
|
||||||
|
|
||||||
|
entryname, ok := entry["name"].(string)
|
||||||
|
if !ok {
|
||||||
|
return ret, errors.New("entry without name")
|
||||||
|
}
|
||||||
|
|
||||||
|
cee.EntryName = entryname
|
||||||
|
|
||||||
|
// Try to find the enum value
|
||||||
|
ei1, ok := entry["inner"].([]interface{})
|
||||||
|
if !ok || len(ei1) == 0 {
|
||||||
|
// Enum case without definition e.g. QCalendar::Gregorian
|
||||||
|
// This means one more than the last value
|
||||||
|
cee.EntryValue = fmt.Sprintf("%d", lastImplicitValue+1)
|
||||||
|
|
||||||
|
} else if len(ei1) == 1 {
|
||||||
|
|
||||||
|
ei1_0 := ei1[0].(map[string]interface{})
|
||||||
|
|
||||||
|
// Best case: .inner -> kind=ConstantExpr value=xx
|
||||||
|
// e.g. qabstractitemmodel
|
||||||
|
if ei1Kind, ok := ei1_0["kind"].(string); ok && ei1Kind == "ConstantExpr" {
|
||||||
|
log.Printf("Got ConstantExpr OK")
|
||||||
|
if ei1Value, ok := ei1_0["value"].(string); ok {
|
||||||
|
cee.EntryValue = ei1Value
|
||||||
|
goto afterParse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Best case: .inner -> kind=ImplicitCastExpr .inner -> kind=ConstantExpr value=xx
|
||||||
|
// e.g. QCalendar (when there is a int typecast)
|
||||||
|
if ei1Kind, ok := ei1_0["kind"].(string); ok && ei1Kind == "ImplicitCastExpr" {
|
||||||
|
log.Printf("Got ImplicitCastExpr OK")
|
||||||
|
if ei2, ok := ei1_0["inner"].([]interface{}); ok && len(ei2) > 0 {
|
||||||
|
ei2_0 := ei2[0].(map[string]interface{})
|
||||||
|
if ei2Kind, ok := ei2_0["kind"].(string); ok && ei2Kind == "ConstantExpr" {
|
||||||
|
log.Printf("Got ConstantExpr OK")
|
||||||
|
if ei2Value, ok := ei2_0["value"].(string); ok {
|
||||||
|
cee.EntryValue = ei2Value
|
||||||
|
goto afterParse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
afterParse:
|
||||||
|
if cee.EntryValue == "" {
|
||||||
|
return ret, fmt.Errorf("Complex enum %q entry %q", ret.EnumName, entryname)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if cee.EntryValue == "true" || cee.EntryValue == "false" {
|
||||||
|
ret.UnderlyingType = "bool"
|
||||||
|
|
||||||
|
} else {
|
||||||
|
lastImplicitValue, err = strconv.ParseInt(cee.EntryValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return ret, fmt.Errorf("Enum %q entry %q has non-parseable value %q: %w", ret.EnumName, entryname, cee.EntryValue, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Entries = append(ret.Entries, cee)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseMethod(node map[string]interface{}, mm *CppMethod) error {
|
func parseMethod(node map[string]interface{}, mm *CppMethod) error {
|
||||||
|
|
||||||
if typobj, ok := node["type"].(map[string]interface{}); ok {
|
if typobj, ok := node["type"].(map[string]interface{}); ok {
|
||||||
|
@ -379,6 +379,12 @@ func getReferencedTypes(src *CppParsedHeader) []string {
|
|||||||
// cabiClassName returns the Go / CABI class name for a Qt C++ class.
|
// cabiClassName returns the Go / CABI class name for a Qt C++ class.
|
||||||
// Normally this is the same, except for class types that are nested inside another class definition.
|
// Normally this is the same, except for class types that are nested inside another class definition.
|
||||||
func cabiClassName(className string) string {
|
func cabiClassName(className string) string {
|
||||||
|
|
||||||
|
// Many types are defined in qnamespace.h under Qt::
|
||||||
|
// The Go implementation is always called qt.Foo, and these names don't
|
||||||
|
// collide with anything, so strip the redundant prefix
|
||||||
|
className = strings.TrimPrefix(className, `Qt::`)
|
||||||
|
|
||||||
// Must use __ to avoid subclass/method name collision e.g. QPagedPaintDevice::Margins
|
// Must use __ to avoid subclass/method name collision e.g. QPagedPaintDevice::Margins
|
||||||
return strings.Replace(className, `::`, `__`, -1)
|
return strings.Replace(className, `::`, `__`, -1)
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,10 @@ func (p CppParameter) RenderTypeGo() string {
|
|||||||
ret += "int"
|
ret += "int"
|
||||||
|
|
||||||
} else if strings.Contains(p.ParameterType, `::`) {
|
} else if strings.Contains(p.ParameterType, `::`) {
|
||||||
if p.IsEnum() {
|
if p.IsKnownEnum() {
|
||||||
|
ret += cabiClassName(p.ParameterType)
|
||||||
|
|
||||||
|
} else if p.IsEnum() {
|
||||||
ret += "uintptr"
|
ret += "uintptr"
|
||||||
} else {
|
} else {
|
||||||
// Inner class
|
// Inner class
|
||||||
@ -291,6 +294,22 @@ import "C"
|
|||||||
imports: map[string]struct{}{},
|
imports: map[string]struct{}{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, e := range src.Enums {
|
||||||
|
goEnumName := cabiClassName(e.EnumName)
|
||||||
|
|
||||||
|
ret.WriteString(`
|
||||||
|
type ` + goEnumName + ` ` + parseSingleTypeString(e.UnderlyingType).RenderTypeGo() + `
|
||||||
|
|
||||||
|
const (
|
||||||
|
`)
|
||||||
|
|
||||||
|
for _, ee := range e.Entries {
|
||||||
|
ret.WriteString(cabiClassName(goEnumName+"::"+ee.EntryName) + " " + goEnumName + " = " + ee.EntryValue + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.WriteString("\n)\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
for _, c := range src.Classes {
|
for _, c := range src.Classes {
|
||||||
|
|
||||||
goClassName := cabiClassName(c.ClassName)
|
goClassName := cabiClassName(c.ClassName)
|
||||||
|
@ -8,11 +8,13 @@ import (
|
|||||||
var (
|
var (
|
||||||
KnownClassnames map[string]struct{} // Entries of the form QFoo::Bar if it is an inner class
|
KnownClassnames map[string]struct{} // Entries of the form QFoo::Bar if it is an inner class
|
||||||
KnownTypedefs map[string]CppTypedef
|
KnownTypedefs map[string]CppTypedef
|
||||||
|
KnownEnums map[string]CppEnum
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
KnownClassnames = make(map[string]struct{})
|
KnownClassnames = make(map[string]struct{})
|
||||||
KnownTypedefs = make(map[string]CppTypedef)
|
KnownTypedefs = make(map[string]CppTypedef)
|
||||||
|
KnownEnums = make(map[string]CppEnum)
|
||||||
|
|
||||||
// Seed well-known typedefs
|
// Seed well-known typedefs
|
||||||
|
|
||||||
@ -96,6 +98,11 @@ func (p CppParameter) QtClassType() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p CppParameter) IsKnownEnum() bool {
|
||||||
|
_, ok := KnownEnums[p.ParameterType]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func (p CppParameter) IsEnum() bool {
|
func (p CppParameter) IsEnum() bool {
|
||||||
if strings.Contains(p.ParameterType, `::`) {
|
if strings.Contains(p.ParameterType, `::`) {
|
||||||
if _, ok := KnownClassnames[p.ParameterType]; ok {
|
if _, ok := KnownClassnames[p.ParameterType]; ok {
|
||||||
@ -278,8 +285,9 @@ type CppEnumEntry struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CppEnum struct {
|
type CppEnum struct {
|
||||||
EnumName string
|
EnumName string
|
||||||
Entries []CppEnumEntry
|
UnderlyingType string
|
||||||
|
Entries []CppEnumEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
type CppClass struct {
|
type CppClass struct {
|
||||||
|
@ -144,6 +144,9 @@ func main() {
|
|||||||
for _, td := range parsed.Typedefs {
|
for _, td := range parsed.Typedefs {
|
||||||
KnownTypedefs[td.Alias] = td // copy
|
KnownTypedefs[td.Alias] = td // copy
|
||||||
}
|
}
|
||||||
|
for _, en := range parsed.Enums {
|
||||||
|
KnownEnums[en.EnumName] = en // copy
|
||||||
|
}
|
||||||
|
|
||||||
processHeaders = append(processHeaders, parsed)
|
processHeaders = append(processHeaders, parsed)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user