package bindgen import "core:fmt" // Prevent keywords clashes and other tricky cases clean_identifier :: proc(name : string) -> string { name := name; if name == "" { return name; } // Starting with _? Try removing that. for true { if name[0] == '_' { name = name[1:]; } else { break; } } // Number if name[0] >= '0' && name[0] <= '9' { return tcat("_", name); } // Keywords clash else if name == "map" || name == "proc" || name == "opaque" || name == "in" { return tcat("_", name); } // Jai keywords clash else if name == "context" || name == "float32" || name == "float64" || name == "s8" || name == "s16" || name == "s32" || name == "s64" || name == "u8" || name == "u16" || name == "u32" || name == "u64" { return tcat("_", name); } return name; } clean_variable_name :: proc(name : string, options : ^GeneratorOptions) -> string { name := name; name = change_case(name, options.variableCase); return clean_identifier(name); } clean_pseudo_type_name :: proc(structName : string, options : ^GeneratorOptions) -> string { structName := structName; structName = remove_postfixes(structName, options.pseudoTypePostfixes, options.pseudoTypeTransparentPostfixes); structName = remove_prefixes(structName, options.pseudoTypePrefixes, options.pseudoTypeTransparentPrefixes); structName = change_case(structName, options.pseudoTypeCase); return structName; } // Clean up the enum name so that it can be used to remove the prefix from enum values. clean_enum_name_for_prefix_removal :: proc(enumName : string, options : ^GeneratorOptions) -> (string, [dynamic]string) { enumName := enumName; if !options.enumValueNameRemove { return enumName, nil; } // Remove postfix and use same case convention as the enum values removedPostfixes : [dynamic]string; enumName, removedPostfixes = remove_postfixes_with_removed(enumName, options.enumValueNameRemovePostfixes); enumName = change_case(enumName, options.enumValueCase); return enumName, removedPostfixes; } clean_enum_value_name :: proc(valueName : string, enumName : string, postfixes : []string, options : ^GeneratorOptions) -> string { valueName := valueName; valueName = remove_prefixes(valueName, options.enumValuePrefixes, options.enumValueTransparentPrefixes); valueName = remove_postfixes(valueName, postfixes, options.enumValueTransparentPostfixes); if options.enumValueNameRemove { valueName = remove_prefixes(valueName, []string{enumName}); } valueName = change_case(valueName, options.enumValueCase); return clean_identifier(valueName); } clean_function_name :: proc(functionName : string, options : ^GeneratorOptions) -> string { functionName := functionName; functionName = remove_prefixes(functionName, options.functionPrefixes, options.functionTransparentPrefixes); functionName = remove_postfixes(functionName, options.definePostfixes, options.defineTransparentPostfixes); functionName = change_case(functionName, options.functionCase); return functionName; } clean_define_name :: proc(defineName : string, options : ^GeneratorOptions) -> string { defineName := defineName; defineName = remove_prefixes(defineName, options.definePrefixes, options.defineTransparentPrefixes); defineName = remove_postfixes(defineName, options.definePostfixes, options.defineTransparentPostfixes); defineName = change_case(defineName, options.defineCase); return defineName; } // Convert to Odin's types clean_type :: proc(data : ^GeneratorData, type : Type, baseTab : string = "", explicitSharpType := true) -> string { output := ""; for dimension in type.dimensions { output = tcat(output, "[", dimension, "]"); } output = tcat(output, clean_base_type(data, type.base, baseTab, explicitSharpType)); return output; } clean_base_type :: proc(data : ^GeneratorData, baseType : BaseType, baseTab : string = "", explicitSharpType := true) -> string { options := data.options; if _type, ok := baseType.(BuiltinType); ok { if _type == BuiltinType.Void do return options.mode == "jai" ? "void" : ""; else if _type == BuiltinType.Int do return options.mode == "jai" ? "s64" : "_c.int"; else if _type == BuiltinType.UInt do return options.mode == "jai" ? "u64" :"_c.uint"; else if _type == BuiltinType.LongInt do return options.mode == "jai" ? "s64" :"_c.long"; else if _type == BuiltinType.ULongInt do return options.mode == "jai" ? "u64" :"_c.ulong"; else if _type == BuiltinType.LongLongInt do return options.mode == "jai" ? "s64" :"_c.longlong"; else if _type == BuiltinType.ULongLongInt do return options.mode == "jai" ? "u64" :"_c.ulonglong"; else if _type == BuiltinType.ShortInt do return options.mode == "jai" ? "s16" :"_c.short"; else if _type == BuiltinType.UShortInt do return options.mode == "jai" ? "u16" :"_c.ushort"; else if _type == BuiltinType.Char do return options.mode == "jai" ? "u8" :"_c.char"; else if _type == BuiltinType.SChar do return options.mode == "jai" ? "s8" :"_c.schar"; else if _type == BuiltinType.UChar do return options.mode == "jai" ? "u8" :"_c.uchar"; else if _type == BuiltinType.Float do return options.mode == "jai" ? "float32" :"_c.float"; else if _type == BuiltinType.Double do return options.mode == "jai" ? "float64" :"_c.double"; else if _type == BuiltinType.LongDouble { print_warning("Found long double which is currently not supported. Fallback to double in generated code."); return options.mode == "jai" ? "double" :"_c.double"; } else if _type == BuiltinType.Int8 do return options.mode == "jai" ? "s8" :"i8"; else if _type == BuiltinType.Int16 do return options.mode == "jai" ? "s16" :"i16"; else if _type == BuiltinType.Int32 do return options.mode == "jai" ? "s32" :"i32"; else if _type == BuiltinType.Int64 do return options.mode == "jai" ? "s64" :"i64"; else if _type == BuiltinType.UInt8 do return options.mode == "jai" ? "u8" :"u8"; else if _type == BuiltinType.UInt16 do return options.mode == "jai" ? "u16" :"u16"; else if _type == BuiltinType.UInt32 do return options.mode == "jai" ? "u32" :"u32"; else if _type == BuiltinType.UInt64 do return options.mode == "jai" ? "u64" :"u64"; else if _type == BuiltinType.Size do return options.mode == "jai" ? "u64" :"_c.size_t"; else if _type == BuiltinType.SSize do return options.mode == "jai" ? "u64" :"_c.ssize_t"; else if _type == BuiltinType.PtrDiff do return options.mode == "jai" ? "s64" :"_c.ptrdiff_t"; else if _type == BuiltinType.UIntPtr do return options.mode == "jai" ? "u64" :"_c.uintptr_t"; else if _type == BuiltinType.IntPtr do return options.mode == "jai" ? "s64" :"_c.intptr_t"; } else if _type, ok := baseType.(PointerType); ok { if options.mode == "jai" { // Hide pointers to types that were not declared. if !is_known_base_type(data, _type.type.base) { print_warning("*", _type.type.base.(IdentifierType).name, " replaced by *void as the pointed type is unknown."); return "*void"; } } else { if __type, ok := _type.type.base.(BuiltinType); ok { if __type == BuiltinType.Void do return "rawptr"; else if __type == BuiltinType.Char do return "cstring"; } } name := clean_type(data, _type.type^, baseTab); return tcat(options.mode == "jai" ? "*" :"^", name); } else if _type, ok := baseType.(IdentifierType); ok { return clean_pseudo_type_name(_type.name, options); } else if _type, ok := baseType.(FunctionType); ok { output : string; if explicitSharpType { output = "#type "; } output = tcat(output, options.mode == "jai" ? "(" :"proc("); parameters := clean_function_parameters(data, _type.parameters, baseTab); output = tcat(output, parameters, ")"); returnType := clean_type(data, _type.returnType^); if len(returnType) > 0 && returnType != "void" { output = tcat(output, " -> ", returnType); } return output; } else if _type, ok := baseType.(FunctionPointerType); ok { output : string; if explicitSharpType { output = "#type "; } output = tcat(output, options.mode == "jai" ? "(" :"proc("); parameters := clean_function_parameters(data, _type.parameters, baseTab); output = tcat(output, parameters, ")"); returnType := clean_type(data, _type.returnType^); if len(returnType) > 0 && returnType != "void" { output = tcat(output, " -> ", returnType); } if options.mode == "jai" { output = tcat(output, " #foreign"); } return output; } return ""; } clean_function_parameters :: proc(data : ^GeneratorData, parameters : [dynamic]FunctionParameter, baseTab : string) -> string { output := ""; options := data.options; // Special case: function(void) does not really have a parameter if len(parameters) == 1 { if _type, ok := parameters[0].type.base.(BuiltinType); ok { if _type == BuiltinType.Void { return ""; } } } tab := ""; if options.mode == "jai" { // @note :OdinCodingStyle Odin forces a coding style, now. Ugh. if (len(parameters) > 1) { output = tcat(output, "\n"); tab = tcat(baseTab, " "); } } unamedParametersCount := 0; for parameter, i in parameters { type := clean_type(data, parameter.type); name : string; if len(parameter.name) != 0 { name = clean_variable_name(parameter.name, options); } else { name = tcat("unamed", unamedParametersCount); unamedParametersCount += 1; } output = tcat(output, tab, name, " : ", type); if i != len(parameters) - 1 { if options.mode == "jai" { // @note :OdinCodingStyle output = tcat(output, ",\n"); } else { output = tcat(output, ", "); } } } if (len(parameters) > 1) { if options.mode == "jai" { // @note :OdinCodingStyle output = tcat(output, "\n", baseTab); } } return output; } is_known_base_type :: proc(data : ^GeneratorData, baseType : BaseType) -> bool { if _type, ok := baseType.(IdentifierType); ok { for it in data.nodes.typedefs { if _type.name == it.name { return true; } } for it in data.nodes.structDefinitions { if _type.name == it.name { return true; } } for it in data.nodes.enumDefinitions { if _type.name == it.name { return true; } } for it in data.nodes.unionDefinitions { if _type.name == it.name { return true; } } return false; } return true; }