This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch main
in repository ego.
View the commit online.
commit 8f4e93875fe9a7449635c0be39e36a9146a4d00b
Author: [email protected] <[email protected]>
AuthorDate: Sun Mar 8 23:04:15 2026 -0600
feat: add C-to-Go type mapping for ego-gen code generator
Implements type conversion logic to translate Eolian C type strings
into their Go equivalents. Supports standard C integer types, floating
point types, bool variants, string types (char* and Eina_Stringshare),
and unknown pointer types as unsafe.Pointer.
- CTypeToGo: maps C type strings to Go types
- NeedsStringConversion: identifies C string types requiring conversion
- IsPointerType: classifies unsafe.Pointer types
- 59 table-driven subtests covering integer, float, string, and pointer types
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
cmd/ego-gen/typemap.go | 74 +++++++++++++++++++++++
cmd/ego-gen/typemap_test.go | 141 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 215 insertions(+)
diff --git a/cmd/ego-gen/typemap.go b/cmd/ego-gen/typemap.go
new file mode 100644
index 0000000..d45889b
--- /dev/null
+++ b/cmd/ego-gen/typemap.go
@@ -0,0 +1,74 @@
+package main
+
+import "strings"
+
+// CTypeToGo maps a C type string (as reported by Eolian) to its Go equivalent.
+// String types (those containing "char *" or "Eina_Stringshare") map to "string".
+// Any unrecognised pointer type maps to "unsafe.Pointer", as does any completely
+// unknown type.
+func CTypeToGo(ctype string) string {
+ // String types must be checked before the generic pointer fallback because
+ // "char *" contains an asterisk and would otherwise match the pointer rule.
+ if NeedsStringConversion(ctype) {
+ return "string"
+ }
+
+ switch ctype {
+ case "int":
+ return "int"
+ case "unsigned int", "uint":
+ return "uint"
+ case "double":
+ return "float64"
+ case "float":
+ return "float32"
+ case "bool", "Eina_Bool":
+ return "bool"
+ case "char":
+ return "byte"
+ case "short":
+ return "int16"
+ case "unsigned short":
+ return "uint16"
+ case "long":
+ return "int64"
+ case "unsigned long":
+ return "uint64"
+ case "int64_t":
+ return "int64"
+ case "uint64_t":
+ return "uint64"
+ case "int32_t":
+ return "int32"
+ case "uint32_t":
+ return "uint32"
+ case "size_t":
+ return "uint"
+ case "ssize_t":
+ return "int"
+ case "void *", "Eo *", "const Eo *":
+ return "unsafe.Pointer"
+ }
+
+ // Any remaining pointer type (ends with "*") becomes an opaque pointer.
+ if strings.HasSuffix(ctype, "*") {
+ return "unsafe.Pointer"
+ }
+
+ return "unsafe.Pointer"
+}
+
+// NeedsStringConversion reports whether a C type requires a C string ↔ Go
+// string conversion at the call boundary. This is true for "char *" variants
+// and the Eina_Stringshare family.
+func NeedsStringConversion(ctype string) bool {
+ return strings.Contains(ctype, "char *") ||
+ strings.Contains(ctype, "char*") ||
+ strings.Contains(ctype, "Eina_Stringshare")
+}
+
+// IsPointerType reports whether the Go type is "unsafe.Pointer", which
+// indicates the value must be handled as an opaque C pointer.
+func IsPointerType(goType string) bool {
+ return goType == "unsafe.Pointer"
+}
diff --git a/cmd/ego-gen/typemap_test.go b/cmd/ego-gen/typemap_test.go
new file mode 100644
index 0000000..2ea820b
--- /dev/null
+++ b/cmd/ego-gen/typemap_test.go
@@ -0,0 +1,141 @@
+package main
+
+import (
+ "testing"
+)
+
+func TestCTypeToGo(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ want string
+ }{
+ // Integer types
+ {"int", "int", "int"},
+ {"unsigned int", "unsigned int", "uint"},
+ {"uint alias", "uint", "uint"},
+ {"short", "short", "int16"},
+ {"unsigned short", "unsigned short", "uint16"},
+ {"long", "long", "int64"},
+ {"unsigned long", "unsigned long", "uint64"},
+
+ // Exact-width integer types
+ {"int32_t", "int32_t", "int32"},
+ {"uint32_t", "uint32_t", "uint32"},
+ {"int64_t", "int64_t", "int64"},
+ {"uint64_t", "uint64_t", "uint64"},
+
+ // Size types
+ {"size_t", "size_t", "uint"},
+ {"ssize_t", "ssize_t", "int"},
+
+ // Floating point
+ {"double", "double", "float64"},
+ {"float", "float", "float32"},
+
+ // Boolean
+ {"bool", "bool", "bool"},
+ {"Eina_Bool", "Eina_Bool", "bool"},
+
+ // Character
+ {"char", "char", "byte"},
+
+ // String types (contain "char *" or "Eina_Stringshare")
+ {"char pointer", "char *", "string"},
+ {"const char pointer", "const char *", "string"},
+ {"char pointer space variant", "char*", "string"},
+ {"Eina_Stringshare", "Eina_Stringshare", "string"},
+ {"Eina_Stringshare pointer", "Eina_Stringshare *", "string"},
+
+ // Known void and Eo pointer types
+ {"void pointer", "void *", "unsafe.Pointer"},
+ {"Eo pointer", "Eo *", "unsafe.Pointer"},
+ {"const Eo pointer", "const Eo *", "unsafe.Pointer"},
+
+ // Generic pointer types (end with "*" but are not string types)
+ {"unknown struct pointer", "SomeStruct *", "unsafe.Pointer"},
+ {"Evas_Object pointer", "Evas_Object *", "unsafe.Pointer"},
+
+ // Fallback for completely unknown types
+ {"unknown type", "some_unknown_type", "unsafe.Pointer"},
+ {"empty string", "", "unsafe.Pointer"},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ got := CTypeToGo(tc.input)
+ if got != tc.want {
+ t.Errorf("CTypeToGo(%q) = %q, want %q", tc.input, got, tc.want)
+ }
+ })
+ }
+}
+
+func TestNeedsStringConversion(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ want bool
+ }{
+ // Types that require C↔Go string conversion
+ {"char pointer", "char *", true},
+ {"const char pointer", "const char *", true},
+ {"char pointer no space", "char*", true},
+ {"Eina_Stringshare", "Eina_Stringshare", true},
+ {"Eina_Stringshare pointer", "Eina_Stringshare *", true},
+
+ // Types that do not require string conversion
+ {"int", "int", false},
+ {"double", "double", false},
+ {"bool", "bool", false},
+ {"Eina_Bool", "Eina_Bool", false},
+ {"void pointer", "void *", false},
+ {"Eo pointer", "Eo *", false},
+ {"char", "char", false},
+ {"unknown type", "some_unknown_type", false},
+ {"empty string", "", false},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ got := NeedsStringConversion(tc.input)
+ if got != tc.want {
+ t.Errorf("NeedsStringConversion(%q) = %v, want %v", tc.input, got, tc.want)
+ }
+ })
+ }
+}
+
+func TestIsPointerType(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ want bool
+ }{
+ {"unsafe.Pointer", "unsafe.Pointer", true},
+
+ {"int", "int", false},
+ {"uint", "uint", false},
+ {"float64", "float64", false},
+ {"float32", "float32", false},
+ {"bool", "bool", false},
+ {"byte", "byte", false},
+ {"int16", "int16", false},
+ {"uint16", "uint16", false},
+ {"int64", "int64", false},
+ {"uint64", "uint64", false},
+ {"int32", "int32", false},
+ {"uint32", "uint32", false},
+ {"string", "string", false},
+ {"empty string", "", false},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ got := IsPointerType(tc.input)
+ if got != tc.want {
+ t.Errorf("IsPointerType(%q) = %v, want %v", tc.input, got, tc.want)
+ }
+ })
+ }
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.