package lang import ( "fmt" "strings" ) type Type interface { typeNode() String() string } type TypeRef struct { Source string Syntax Type } func ParseTypeRef(source string) (TypeRef, error) { typ, err := ParseType(source) if err != nil { return TypeRef{}, err } return TypeRef{Source: source, Syntax: typ}, nil } type UnknownType struct{} func (UnknownType) typeNode() {} func (UnknownType) String() string { return "" } type NamedType struct{ Name string } func (NamedType) typeNode() {} func (t NamedType) String() string { return t.Name } type TypeParameterType struct{ Name string } func (TypeParameterType) typeNode() {} func (t TypeParameterType) String() string { return t.Name } type ClassType struct{ Class *ClassSymbol } func (ClassType) typeNode() {} func (t ClassType) String() string { return t.Class.Name } type ImportedClassType struct { Package string Class *ClassSymbol } func (ImportedClassType) typeNode() {} func (t ImportedClassType) String() string { return t.Package + "." + t.Class.Name } type GoInterfaceType struct{ Name string } func (GoInterfaceType) typeNode() {} func (t GoInterfaceType) String() string { return t.Name } type NullableType struct{ Element Type } func (NullableType) typeNode() {} func (t NullableType) String() string { return t.Element.String() + "?" } type GoPointerType struct{ Element Type } func (GoPointerType) typeNode() {} func (t GoPointerType) String() string { return "*" + t.Element.String() } type FunctionType struct { TypeParams []string Params []Type Result Type Effects Effect Variadic bool } func (FunctionType) typeNode() {} func (t FunctionType) String() string { params := make([]string, 0, len(t.Params)) for _, param := range t.Params { params = append(params, param.String()) } return "(" + strings.Join(params, ", ") + ") -> " + t.Result.String() } type GenericType struct { Base Type Args []Type } type TupleType struct{ Elements []Type } func (TupleType) typeNode() {} func (t TupleType) String() string { elements := make([]string, len(t.Elements)) for index, element := range t.Elements { elements[index] = element.String() } return "(" + strings.Join(elements, ", ") + ")" } func (GenericType) typeNode() {} func (t GenericType) String() string { args := make([]string, 0, len(t.Args)) for _, arg := range t.Args { args = append(args, arg.String()) } return t.Base.String() + "<" + strings.Join(args, ", ") + ">" } func ParseType(text string) (Type, error) { text = strings.TrimSpace(text) if text == "" { return UnknownType{}, nil } if strings.HasSuffix(text, "?") { element, err := ParseType(strings.TrimSpace(strings.TrimSuffix(text, "?"))) if err != nil { return nil, err } return NullableType{Element: element}, nil } if strings.HasPrefix(text, "*") { element, err := ParseType(strings.TrimSpace(strings.TrimPrefix(text, "*"))) if err != nil { return nil, err } return GoPointerType{Element: element}, nil } if params, result, ok := parseFunctionType(text); ok { resolvedParams := make([]Type, 0, len(params)) for _, param := range params { resolved, err := ParseType(param) if err != nil { return nil, err } resolvedParams = append(resolvedParams, resolved) } resolvedResult, err := ParseType(result) if err != nil { return nil, err } return FunctionType{Params: resolvedParams, Result: resolvedResult}, nil } if base, args, ok := parseGenericType(text); ok { resolvedBase, err := ParseType(base) if err != nil { return nil, err } resolvedArgs := make([]Type, 0, len(args)) for _, arg := range args { resolved, err := ParseType(arg) if err != nil { return nil, err } resolvedArgs = append(resolvedArgs, resolved) } return GenericType{Base: resolvedBase, Args: resolvedArgs}, nil } if strings.ContainsAny(text, "<>?()") { return nil, fmt.Errorf("invalid type %q", text) } return NamedType{Name: text}, nil } func resolveClassTypes(typ Type, classes map[string]*ClassSymbol) Type { switch value := typ.(type) { case NamedType: if class, ok := classes[value.Name]; ok { return ClassType{Class: class} } return value case TypeParameterType: return value case NullableType: return NullableType{Element: resolveClassTypes(value.Element, classes)} case GoPointerType: return GoPointerType{Element: resolveClassTypes(value.Element, classes)} case FunctionType: params := make([]Type, len(value.Params)) for i, param := range value.Params { params[i] = resolveClassTypes(param, classes) } return FunctionType{TypeParams: value.TypeParams, Params: params, Result: resolveClassTypes(value.Result, classes), Effects: value.Effects, Variadic: value.Variadic} case GenericType: args := make([]Type, len(value.Args)) for i, arg := range value.Args { args[i] = resolveClassTypes(arg, classes) } return GenericType{Base: resolveClassTypes(value.Base, classes), Args: args} case TupleType: elements := make([]Type, len(value.Elements)) for index, element := range value.Elements { elements[index] = resolveClassTypes(element, classes) } return TupleType{Elements: elements} default: return typ } } func resolveImportedTypes(typ Type, packages map[string]*PackageSymbol) Type { switch value := typ.(type) { case NamedType: parts := strings.Split(value.Name, ".") if len(parts) == 2 { if pack := packages[parts[0]]; pack != nil { if class := pack.Classes[parts[1]]; class != nil { return ImportedClassType{Package: parts[0], Class: class} } } } return value case NullableType: return NullableType{Element: resolveImportedTypes(value.Element, packages)} case GoPointerType: return GoPointerType{Element: resolveImportedTypes(value.Element, packages)} case GenericType: args := make([]Type, len(value.Args)) for index, arg := range value.Args { args[index] = resolveImportedTypes(arg, packages) } return GenericType{Base: resolveImportedTypes(value.Base, packages), Args: args} case FunctionType: params := make([]Type, len(value.Params)) for index, param := range value.Params { params[index] = resolveImportedTypes(param, packages) } return FunctionType{TypeParams: value.TypeParams, Params: params, Result: resolveImportedTypes(value.Result, packages), Effects: value.Effects, Variadic: value.Variadic} default: return typ } } func resolveTypeParameters(typ Type, params map[string]bool) Type { switch value := typ.(type) { case NamedType: if params[value.Name] { return TypeParameterType{Name: value.Name} } return value case NullableType: return NullableType{Element: resolveTypeParameters(value.Element, params)} case GoPointerType: return GoPointerType{Element: resolveTypeParameters(value.Element, params)} case FunctionType: resolved := make([]Type, len(value.Params)) for index, param := range value.Params { resolved[index] = resolveTypeParameters(param, params) } return FunctionType{TypeParams: value.TypeParams, Params: resolved, Result: resolveTypeParameters(value.Result, params), Effects: value.Effects, Variadic: value.Variadic} case GenericType: args := make([]Type, len(value.Args)) for index, arg := range value.Args { args[index] = resolveTypeParameters(arg, params) } return GenericType{Base: resolveTypeParameters(value.Base, params), Args: args} case TupleType: elements := make([]Type, len(value.Elements)) for index, element := range value.Elements { elements[index] = resolveTypeParameters(element, params) } return TupleType{Elements: elements} default: return typ } } func typeEqual(left, right Type) bool { return left.String() == right.String() } func isImportedClassType(typ Type) bool { switch value := typ.(type) { case ImportedClassType: return true case NullableType: return isImportedClassType(value.Element) case GenericType: _, ok := value.Base.(ImportedClassType) return ok } return false } func substituteType(typ Type, bindings map[string]Type) Type { switch value := typ.(type) { case TypeParameterType: if bound, ok := bindings[value.Name]; ok { return bound } return value case NullableType: return NullableType{Element: substituteType(value.Element, bindings)} case GoPointerType: return GoPointerType{Element: substituteType(value.Element, bindings)} case GenericType: args := make([]Type, len(value.Args)) for index, arg := range value.Args { args[index] = substituteType(arg, bindings) } return GenericType{Base: substituteType(value.Base, bindings), Args: args} case TupleType: elements := make([]Type, len(value.Elements)) for index, element := range value.Elements { elements[index] = substituteType(element, bindings) } return TupleType{Elements: elements} case FunctionType: params := make([]Type, len(value.Params)) for index, param := range value.Params { params[index] = substituteType(param, bindings) } return FunctionType{TypeParams: value.TypeParams, Params: params, Result: substituteType(value.Result, bindings), Effects: value.Effects, Variadic: value.Variadic} default: return typ } } func inferTypeBindings(parameter, argument Type, bindings map[string]Type) { switch expected := parameter.(type) { case TypeParameterType: if _, exists := bindings[expected.Name]; !exists && !isUnknownType(argument) { bindings[expected.Name] = argument } case NullableType: if actual, ok := argument.(NullableType); ok { inferTypeBindings(expected.Element, actual.Element, bindings) } case GenericType: actual, ok := argument.(GenericType) if !ok || expected.Base.String() != actual.Base.String() { return } for index := range expected.Args { if index < len(actual.Args) { inferTypeBindings(expected.Args[index], actual.Args[index], bindings) } } case FunctionType: actual, ok := argument.(FunctionType) if !ok { return } for index := range expected.Params { if index < len(actual.Params) { inferTypeBindings(expected.Params[index], actual.Params[index], bindings) } } inferTypeBindings(expected.Result, actual.Result, bindings) } } func renderGoType(typ Type) string { switch value := typ.(type) { case UnknownType: return "" case ClassType: return "*" + value.Class.Name case ImportedClassType: return "*" + value.Package + "." + value.Class.Name case GoInterfaceType: return value.Name case NullableType: element := renderGoType(value.Element) switch value.Element.(type) { case ClassType, ImportedClassType, GoPointerType: return element } if element == "error" || element == "any" { return element } return "*" + element case GoPointerType: return "*" + renderGoType(value.Element) case FunctionType: params := make([]string, len(value.Params)) for i, param := range value.Params { params[i] = renderGoType(param) } result := renderGoType(value.Result) if result == "" { return "func(" + strings.Join(params, ", ") + ")" } return "func(" + strings.Join(params, ", ") + ") " + result case GenericType: base := value.Base.String() args := make([]string, len(value.Args)) for i, arg := range value.Args { args[i] = renderGoType(arg) } switch base { case "List", "MutableList": return "[]" + args[0] case "Map", "MutableMap": return "map[" + args[0] + "]" + args[1] case "Channel": return "chan " + args[0] case "Result": if args[0] == "" { args[0] = "struct{}" } return "GotlinResult[" + args[0] + "]" case "GotlinSQLIterator": argument := args[0] if class, ok := value.Args[0].(ClassType); ok { argument = class.Class.Name } return "*GotlinSQLIterator[" + argument + "]" } if class, ok := value.Base.(ClassType); ok { return "*" + class.Class.Name + "[" + strings.Join(args, ", ") + "]" } if class, ok := value.Base.(ImportedClassType); ok { return "*" + class.Package + "." + class.Class.Name + "[" + strings.Join(args, ", ") + "]" } return base + "[" + strings.Join(args, ", ") + "]" case TypeParameterType: return value.Name case NamedType: switch value.Name { case "Int": return "int" case "Long": return "int64" case "Float", "Double": return "float64" case "String": return "string" case "Any": return "any" case "ByteSlice": return "[]byte" case "Boolean": return "bool" case "Unit": return "" case "Error": return "error" default: return value.Name } case TupleType: elements := make([]string, len(value.Elements)) for index, element := range value.Elements { elements[index] = renderGoType(element) } return "(" + strings.Join(elements, ", ") + ")" default: return "" } }