mirror of
https://github.com/mappu/miqt.git
synced 2024-12-22 08:58:37 +00:00
genbindings: add support for enums
This commit is contained in:
parent
25e6ea1698
commit
d62ec99cf1
@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -88,7 +89,14 @@ func parseHeader(topLevel []interface{}, addNamePrefix string) (*CppParsedHeader
|
||||
// TODO
|
||||
|
||||
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":
|
||||
// TODO e.g. qmath.h
|
||||
@ -312,6 +320,21 @@ nextMethod:
|
||||
}
|
||||
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":
|
||||
|
||||
if isImplicit, ok := node["isImplicit"].(bool); ok && isImplicit {
|
||||
@ -455,6 +478,108 @@ var (
|
||||
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 {
|
||||
|
||||
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.
|
||||
// Normally this is the same, except for class types that are nested inside another class definition.
|
||||
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
|
||||
return strings.Replace(className, `::`, `__`, -1)
|
||||
}
|
||||
|
@ -85,7 +85,10 @@ func (p CppParameter) RenderTypeGo() string {
|
||||
ret += "int"
|
||||
|
||||
} else if strings.Contains(p.ParameterType, `::`) {
|
||||
if p.IsEnum() {
|
||||
if p.IsKnownEnum() {
|
||||
ret += cabiClassName(p.ParameterType)
|
||||
|
||||
} else if p.IsEnum() {
|
||||
ret += "uintptr"
|
||||
} else {
|
||||
// Inner class
|
||||
@ -291,6 +294,22 @@ import "C"
|
||||
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 {
|
||||
|
||||
goClassName := cabiClassName(c.ClassName)
|
||||
|
@ -8,11 +8,13 @@ import (
|
||||
var (
|
||||
KnownClassnames map[string]struct{} // Entries of the form QFoo::Bar if it is an inner class
|
||||
KnownTypedefs map[string]CppTypedef
|
||||
KnownEnums map[string]CppEnum
|
||||
)
|
||||
|
||||
func init() {
|
||||
KnownClassnames = make(map[string]struct{})
|
||||
KnownTypedefs = make(map[string]CppTypedef)
|
||||
KnownEnums = make(map[string]CppEnum)
|
||||
|
||||
// Seed well-known typedefs
|
||||
|
||||
@ -96,6 +98,11 @@ func (p CppParameter) QtClassType() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p CppParameter) IsKnownEnum() bool {
|
||||
_, ok := KnownEnums[p.ParameterType]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p CppParameter) IsEnum() bool {
|
||||
if strings.Contains(p.ParameterType, `::`) {
|
||||
if _, ok := KnownClassnames[p.ParameterType]; ok {
|
||||
@ -278,8 +285,9 @@ type CppEnumEntry struct {
|
||||
}
|
||||
|
||||
type CppEnum struct {
|
||||
EnumName string
|
||||
Entries []CppEnumEntry
|
||||
EnumName string
|
||||
UnderlyingType string
|
||||
Entries []CppEnumEntry
|
||||
}
|
||||
|
||||
type CppClass struct {
|
||||
|
@ -144,6 +144,9 @@ func main() {
|
||||
for _, td := range parsed.Typedefs {
|
||||
KnownTypedefs[td.Alias] = td // copy
|
||||
}
|
||||
for _, en := range parsed.Enums {
|
||||
KnownEnums[en.EnumName] = en // copy
|
||||
}
|
||||
|
||||
processHeaders = append(processHeaders, parsed)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user