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.

Reply via email to