bindgen.odin (69628B)
1 /* 2 Generates Odin bindings from C code. 3 4 Usage: 5 bindgen folder_with_headers_inside 6 7 The folder can contain a `bindgen.sjson` file tha can be used to do overrides 8 and configure the generation. See the examples folder for how to do that. 9 */ 10 11 #+feature dynamic-literals 12 13 package bindgen 14 15 import "core:fmt" 16 import "core:os" 17 import "core:os/os2" 18 import "core:strings" 19 import "core:strconv" 20 import "core:path/filepath" 21 import "core:math/bits" 22 import "core:encoding/json" 23 import "core:unicode" 24 import "core:unicode/utf8" 25 import "base:runtime" 26 import "core:c" 27 import "core:slice" 28 import vmem "core:mem/virtual" 29 import clang "../libclang" 30 31 Struct_Field :: struct { 32 names: [dynamic]string, 33 type: clang.Type, 34 anon_using: bool, 35 comment: string, 36 comment_before: bool, 37 original_line: int, 38 } 39 40 Struct :: struct { 41 original_name: string, 42 name: string, 43 id: string, 44 fields: []Struct_Field, 45 comment: string, 46 is_union: bool, 47 is_anon: bool, 48 is_forward_declare: bool, 49 } 50 51 Function_Parameter :: struct { 52 name: string, 53 cursor: clang.Cursor, 54 } 55 56 Function :: struct { 57 original_name: string, 58 name: string, 59 cursor: clang.Cursor, 60 61 // if non-empty, then use this will be the link name used in bindings 62 link_name: string, 63 parameters: []clang.Cursor, 64 comment: string, 65 comment_before: bool, 66 variadic: bool, 67 post_comment: string, 68 } 69 70 Enum_Member :: struct { 71 name: string, 72 value: int, 73 comment: string, 74 comment_before: bool, 75 } 76 77 Enum :: struct { 78 original_name: string, 79 name: string, 80 id: string, 81 members: []Enum_Member, 82 comment: string, 83 backing_type: clang.Type, 84 } 85 86 Typedef :: struct { 87 original_name: string, 88 name: string, 89 type: clang.Type, 90 pre_comment: string, 91 side_comment: string, 92 } 93 94 Macro :: struct { 95 original_name: string, 96 name: string, 97 tokens: []clang.Token, 98 is_function: bool, 99 has_been_evaluated: bool, 100 should_not_output: bool, 101 val: string, 102 comment: string, 103 side_comment: string, 104 whitespace_after_name: int, 105 whitespace_before_side_comment: int, 106 } 107 108 Declaration_Variant :: union { 109 Struct, 110 Function, 111 Enum, 112 Typedef, 113 Macro, 114 } 115 116 Declaration :: struct { 117 // Used for sorting the declarations. They may be added out-of-order due to macros 118 // coming in from a separate code path. 119 cursor: clang.Cursor, 120 121 // The original idx in `s.decls`. This is for tie-breaking when line is the same. 122 original_idx: int, 123 variant: Declaration_Variant, 124 } 125 126 trim_prefix :: proc(s: string, p: string) -> string { 127 return strings.trim_prefix(strings.trim_prefix(s, p), "_") 128 } 129 130 // NOTE: This function disposes of the clang String after converting it to an Odin string. 131 // Be sure not to attempt to use the clang String after calling this function. 132 clang_string_to_string :: proc(str: clang.String) -> string { 133 ret := strings.clone_from_cstring(clang.getCString(str)) 134 clang.disposeString(str) 135 return ret 136 } 137 138 cursor_spelling :: proc(cursor: clang.Cursor) -> string { 139 return clang_string_to_string(clang.getCursorSpelling(cursor)) 140 } 141 142 cursor_usr :: proc(cursor: clang.Cursor) -> string { 143 return clang_string_to_string(clang.getCursorUSR(cursor)) 144 } 145 146 comment_text :: proc(cursor: clang.Cursor) -> string { 147 return clang_string_to_string(clang.Cursor_getRawCommentText(cursor)) 148 } 149 150 type_spelling :: proc(type: clang.Type) -> string { 151 return clang_string_to_string(clang.getTypeSpelling(type)) 152 } 153 154 token_string :: proc(translation_unit: clang.Translation_Unit, token: clang.Token) -> string { 155 return clang_string_to_string(clang.getTokenSpelling(translation_unit, token)) 156 } 157 158 // Put any built in c typedefs into here to have them converted properly. 159 c_typedef_types := map[string]string { 160 "uint8_t" = "u8", 161 "int8_t" = "i8", 162 "uint16_t" = "u16", 163 "int16_t" = "i16", 164 "uint32_t" = "u32", 165 "int32_t" = "i32", 166 "uint64_t" = "u64", 167 "int64_t" = "i64", 168 169 "int_least8_t" = "i8", 170 "uint_least8_t" = "u8", 171 "int_least16_t" = "i16", 172 "uint_least16_t" = "u16", 173 "int_least32_t" = "i32", 174 "uint_least32_t" = "u32", 175 "int_least64_t" = "i64", 176 "uint_least64_t" = "u64", 177 178 "int_fast8_t" = "i8", 179 "uint_fast8_t" = "u8", 180 "int_fast32_t" = "i32", 181 "uint_fast32_t" = "u32", 182 "int_fast64_t" = "i64", 183 "uint_fast64_t" = "u64", 184 } 185 186 // These types are either platform dependent or the type provides the developer with extra context for its use. 187 c_type_mapping := map[string]string { 188 // Platform dependent 189 "long" = "c.long", 190 "unsigned long" = "c.ulong", 191 "int_fast16_t" = "c.int_fast16_t", 192 "uint_fast16_t" = "c.uint_fast16_t", 193 194 // Size & wchar 195 "size_t" = "c.size_t", 196 "ssize_t" = "c.ssize_t", 197 "wchar_t" = "c.wchar_t", 198 199 // ptr types 200 "intptr_t" = "c.intptr_t", 201 "uintptr_t" = "c.uintptr_t", 202 "ptrdiff_t" = "c.ptrdiff_t", 203 204 // intmax types 205 "intmax_t" = "c.intmax_t", 206 "uintmax_t" = "c.uintmax_t", 207 208 // va_list 209 "va_list" = "c.va_list", 210 } 211 212 is_c_type :: proc(type: clang.Type) -> bool { 213 return type_spelling(type) in c_type_mapping 214 } 215 216 // Types that would need "import 'core:sys/posix'". 217 // Please add and send in a Pull Request if you needed to add anything here! 218 posix_type_mapping := map[string]string { 219 "dev_t" = "posix.dev_t", 220 "blkcnt_t" = "posix.blkcnt_t", 221 "blksize_t" = "posix.blksize_t", 222 "clock_t" = "posix.clock_t", 223 "clockid_t" = "posix.clockid_t", 224 "fsblkcnt_t" = "posix.fsblkcnt_t", 225 "off_t" = "posix.off_t", 226 "gid_t" = "posix.gid_t", 227 "pid_t" = "posix.pid_t", 228 "timespec" = "posix.timespec", 229 } 230 231 is_posix_type :: proc(type: clang.Type) -> bool { 232 return type_spelling(type) in posix_type_mapping 233 } 234 235 // Types that would need `import "core:c/libc"`. 236 // Please add and send in a Pull Request if you needed to add anything here! 237 libc_type_mapping := map[string]string { 238 "time_t" = "libc.time_t", 239 } 240 241 is_libc_type :: proc(type: clang.Type) -> bool { 242 return type_spelling(type) in libc_type_mapping 243 } 244 245 translate_name :: proc(s: ^Gen_State, name: string) -> string { 246 ret: string 247 if replacement, has_replacement := s.rename[name]; has_replacement { 248 ret = replacement 249 } else { 250 ret = trim_prefix(name, s.remove_type_prefix) 251 252 if s.force_ada_case_types { 253 ret = strings.to_ada_case(ret) 254 } 255 } 256 return ret 257 } 258 259 parse_nonfunction_type :: proc(s: ^Gen_State, type: clang.Type, opts: Type_Parsing_Options) -> (string, bool) { 260 type_string := type_spelling(type) 261 if c_type, exists := c_type_mapping[type_string]; exists { 262 return c_type, false 263 } 264 if posix_type, exists := posix_type_mapping[type_string]; exists { 265 return posix_type, false 266 } 267 if libc_type, exists := libc_type_mapping[type_string]; exists { 268 return libc_type, false 269 } 270 271 #partial switch type.kind { 272 case .Invalid, .Unexposed, .Void: 273 return "", false 274 case .Long, .ULong, .WChar: 275 // We handle these with c_type_mapping 276 return "", false 277 case .Bool: 278 return "bool", false 279 case .Char_U, .UChar: 280 return "u8", false 281 case .UShort: 282 return "u16", false 283 case .UInt: 284 return "u32", false 285 case .ULongLong: 286 return "u64", false 287 case .UInt128: 288 return "u128", false 289 case .Char_S, .SChar: 290 return "i8", false 291 case .Short: 292 return "i16", false 293 case .Int: 294 return "i32", false 295 case .LongLong: 296 return "i64", false 297 case .Int128: 298 return "i128", false 299 case .Float: 300 return "f32", false 301 case .Double, .LongDouble: 302 return "f64", false 303 case .NullPtr: 304 return "rawptr", false 305 case .Complex: 306 #partial switch clang.getElementType(type).kind { 307 case .Float: 308 return "complex64", false 309 case .Double, .LongDouble: 310 return "complex128", false 311 } 312 case .Pointer: 313 pointee_string, _ := parse_type(s, clang.getPointeeType(type), opts - {.Pointer_To_Array, .By_Pointer}) 314 if pointee_string == "" { 315 return "rawptr", false 316 } 317 318 builder := strings.builder_make() 319 320 if .Pointer_To_Array in opts { 321 strings.write_string(&builder, "[^]") 322 } else if .By_Pointer in opts { 323 // We need to handle this outside of the type parsing because it needs to go infront of the parameter name. 324 // strings.write_string(&builder, "#by_ptr ") 325 } else { 326 if pointee_string == "i8" { 327 return "cstring", false 328 } else if pointee_string == "cstring" { 329 return "[^]cstring", false 330 } 331 strings.write_byte(&builder, '^') 332 } 333 334 strings.write_string(&builder, pointee_string) 335 return strings.to_string(builder), .By_Pointer in opts 336 case .Record, .Enum, .Typedef: 337 return translate_name(s, cursor_spelling(clang.getTypeDeclaration(type))), false 338 case .ConstantArray: 339 builder := strings.builder_make() 340 341 strings.write_byte(&builder, '[') 342 343 str_conv_buf: [20]byte // 20 == base_10_digit_count(c.SIZE_MAX) 344 strings.write_string(&builder, strconv.write_int(str_conv_buf[:], i64(clang.getArraySize(type)), 10)) 345 346 strings.write_byte(&builder, ']') 347 str, _ := parse_type(s, clang.getArrayElementType(type), opts) 348 strings.write_string(&builder, str) 349 return strings.to_string(builder), true 350 case .IncompleteArray, .VariableArray: 351 builder := strings.builder_make() 352 strings.write_string(&builder, "[^]") 353 str, _ := parse_type(s, clang.getArrayElementType(type), opts) 354 strings.write_string(&builder, str) 355 return strings.to_string(builder), false 356 case .Elaborated: 357 elaborated_type := clang.Type_getNamedType(type) 358 #partial switch elaborated_type.kind { 359 case .Record, .Enum, .FunctionNoProto, .FunctionProto: 360 return translate_name(s, cursor_spelling(clang.getTypeDeclaration(type))), false 361 case .Typedef: 362 cursor_decl := clang.getTypeDeclaration(elaborated_type) 363 cursor_name := cursor_spelling(cursor_decl) 364 if replacement, exists := c_typedef_types[cursor_name]; exists { 365 return replacement, false 366 } 367 if clang.getTypedefDeclUnderlyingType(cursor_decl).kind == .ConstantArray { 368 return translate_name(s, cursor_name), true 369 } 370 return translate_name(s, cursor_name), false 371 } 372 return parse_type(s, elaborated_type, opts) 373 } 374 // If we get here then we need to add a new case. 375 panic("Unreachable!") 376 } 377 378 parse_function_type :: proc(s: ^Gen_State, type: clang.Type, opts: Type_Parsing_Options) -> (string, bool) { 379 builder := strings.builder_make() 380 strings.write_string(&builder, "proc ") 381 #partial switch clang.getFunctionTypeCallingConv(type) { 382 case .X86StdCall: 383 strings.write_string(&builder, "\"stdcall\" (") 384 case .X86FastCall: 385 strings.write_string(&builder, "\"fastcall\" (") 386 case: 387 strings.write_string(&builder, "\"c\" (") 388 } 389 390 for i: u32 = 0; i < u32(clang.getNumArgTypes(type)); i += 1 { 391 if i != 0 { 392 strings.write_string(&builder, ", ") 393 } 394 type_string, by_ptr := parse_type(s, clang.getArgType(type, i), nil) 395 if by_ptr { 396 strings.write_string(&builder, "#by_ptr ") 397 } 398 strings.write_string(&builder, type_string) 399 } 400 401 if bool(clang.isFunctionTypeVariadic(type)) { 402 if clang.getNumArgTypes(type) > 0 { 403 strings.write_string(&builder, ", ") 404 } 405 406 strings.write_string(&builder, "#c_vararg ..any") 407 } 408 409 strings.write_byte(&builder, ')') 410 411 if return_type := clang.getResultType(type); return_type.kind != .Void { 412 strings.write_string(&builder, " -> ") 413 str, _ := parse_type(s, return_type, nil) 414 strings.write_string(&builder, str) 415 } 416 417 return strings.to_string(builder), false 418 } 419 420 Type_Parsing_Option :: enum { 421 Pointer_To_Array, 422 By_Pointer, 423 } 424 425 Type_Parsing_Options :: bit_set[Type_Parsing_Option] 426 427 parse_type :: proc(s: ^Gen_State, type: clang.Type, opts: Type_Parsing_Options) -> (string, bool) { 428 #partial switch type.kind { 429 case .FunctionProto, .FunctionNoProto: 430 return parse_function_type(s, type, opts) 431 case .Pointer: 432 #partial switch pointee_type := clang.getPointeeType(type); pointee_type.kind { 433 case .FunctionProto, .FunctionNoProto: 434 return parse_function_type(s, pointee_type, opts) 435 case .Elaborated: 436 if elaborated_type := clang.Type_getNamedType(pointee_type); elaborated_type.kind == .Typedef { 437 #partial switch clang.getTypedefDeclUnderlyingType(clang.getTypeDeclaration(elaborated_type)).kind { 438 case .FunctionNoProto, .FunctionProto: 439 return translate_name(s, cursor_spelling(clang.getTypeDeclaration(elaborated_type))), false 440 } 441 } 442 } 443 } 444 return parse_nonfunction_type(s, type, opts) 445 } 446 447 // Only used for parsing types in macros 448 translate_type_string :: proc(s: ^Gen_State, t: string) -> string { 449 if type, exists := c_type_mapping[t]; exists { 450 return type 451 } 452 453 if replacement, exists := c_typedef_types[t]; exists { 454 return replacement 455 } 456 457 c_types := map[string]string { 458 "char" = "i8", 459 "short" = "i16", 460 "int" = "i32", 461 "long long" = "i64", 462 463 "unsigned char" = "u8", 464 "unsigned short" = "u16", 465 "unsigned int" = "u32", 466 "unsigned long long" = "u64", 467 468 "float" = "f32", 469 "double" = "f64", 470 471 "bool" = "bool", 472 } 473 if type, exists := c_types[t]; exists { 474 return type 475 } 476 477 // Tokenize the type and skip over some parameter type keywords that have no meaning in Odin. 478 type_tokens: [dynamic]string 479 token_start := 0 480 481 t := t 482 for s, idx in t { 483 tok: string 484 485 if strings.is_space(s) { 486 tok = t[token_start:idx] 487 token_start = idx + utf8.rune_size(s) 488 } else if s == '*' || s == '(' || s == ')' { 489 // Any type with a *, ( or ) is non trivial and shouldn't be used in a macro. 490 return "" 491 } else if idx == len(t) - 1{ 492 tok = t[token_start:idx + 1] 493 } 494 495 if len(tok) > 0 { 496 if tok == "const" { 497 continue 498 } 499 500 if tok == "struct" || tok == "enum" { 501 return "" 502 } 503 504 append(&type_tokens, tok) 505 } 506 } 507 508 t = strings.join(type_tokens[:], " ") 509 510 // A hack to check if something is an array of arrays. Then it will appear as `(*)[3] etc. But 511 // the code above removes the `*`, so we check for `( )[` 512 t_original := t 513 array_start := strings.index(t_original, "[") 514 array_end := strings.last_index(t_original, "]") 515 516 if array_start != -1 { 517 t = t[:array_start] 518 } 519 520 // check maps against this in case the header has a type which is exactly [prefix][mapped c type] 521 t_prefixed := strings.trim_space(t) 522 if t != s.remove_type_prefix { 523 t = trim_prefix(t, s.remove_type_prefix) 524 } 525 526 t = strings.trim_space(t) 527 528 if name_c, exists_c := c_type_mapping[t_prefixed]; exists_c { 529 t = name_c 530 } else if name_c_2, exists_c_2 := c_types[t_prefixed]; exists_c_2 { 531 t = name_c_2 532 } else if name_libc, exists_libc := libc_type_mapping[t_prefixed]; exists_libc { 533 t = name_libc 534 } else if name_posix, exists_posix := posix_type_mapping[t_prefixed]; exists_posix { 535 t = name_posix 536 } else if rename, exists := s.rename[t_prefixed]; exists { 537 t = vet_name(rename) 538 } else { 539 t = translate_name(s, t) 540 if t not_in s.created_types { 541 return "" 542 } 543 } 544 545 b := strings.builder_make() 546 547 if array_start != -1 { 548 strings.write_string(&b, t_original[array_start:array_end + 1]) 549 } 550 551 strings.write_string(&b, t) 552 return strings.to_string(b) 553 } 554 555 // Keywords in Odin that don't exist in C. The `_` is there so we can return it 556 // without allocating memory (we compare to the slice [1:]) 557 VET_NAMES :: [?]string { 558 "_rune", 559 "_import", 560 "_foreign", 561 "_package", 562 "_typeid", 563 "_when", 564 "_where", 565 "_in", 566 "_not_in", 567 "_fallthrough", 568 "_defer", 569 "_proc", 570 "_bit_set", 571 "_bit_field", 572 "_map", 573 "_dynamic", 574 "_auto_cast", 575 "_cast", 576 "_transmute", 577 "_distinct", 578 "_using", 579 "_context", 580 "_or_else", 581 "_or_return", 582 "_or_break", 583 "_or_continue", 584 "_asm", 585 "_inline", 586 "_no_inline", 587 "_matrix", 588 "_string", 589 590 // Because we import these three 591 "_c", 592 "_libc", 593 "_posix", 594 } 595 596 vet_name :: proc(s: string) -> string { 597 for v in VET_NAMES { 598 if s == v[1:] { 599 return v 600 } 601 } 602 603 return s 604 } 605 606 add_to_set :: proc(s: ^map[$T]struct{}, v: T) { 607 s[v] = {} 608 } 609 610 find_comment_at_line_end :: proc(str: string) -> (string, int) { 611 space_before_comment: int 612 comment_start: int 613 block_comment: bool 614 615 for c, i in str { 616 if c == ' ' { 617 space_before_comment += 1 618 } else if c == '/' && i + 1 < len(str) && str[i + 1] == '/' { 619 comment_start = i 620 break 621 } else if c == '/' && i + 1 < len(str) && str[i + 1] == '*' { 622 comment_start = i 623 block_comment = true 624 break 625 } else if c == '\n' { 626 break 627 } else { 628 space_before_comment = 0 629 } 630 } 631 632 if comment_start == 0 { 633 return "", 0 634 } 635 636 if block_comment { 637 from_start := str[comment_start:] 638 639 for c, i in from_start { 640 if c == '*' && i < len(from_start) - 1 && from_start[i + 1] == '/' { 641 return from_start[:i+2], space_before_comment 642 } 643 } 644 } else { 645 from_start := str[comment_start:] 646 647 for c, i in from_start { 648 if c == '\n' { 649 return from_start[:i], space_before_comment 650 } 651 } 652 } 653 654 return "", 0 655 } 656 657 dump_ast :: proc(root_cursor: clang.Cursor, source_file: clang.File, out_file: string) { 658 indent :: proc(file: ^os2.File, indent_level: u32) { 659 for _ in 0 ..< indent_level { 660 os2.write_string(file, " ") 661 } 662 } 663 664 visitor_proc: clang.Cursor_Visitor : proc "c" ( 665 cursor, parent: clang.Cursor, 666 state: clang.Client_Data, 667 ) -> clang.Child_Visit_Result { 668 context = runtime.default_context() 669 data := (^Data)(state) 670 671 file: clang.File 672 clang.getExpansionLocation(clang.getCursorLocation(cursor), &file, nil, nil, nil) 673 if !bool(clang.File_isEqual(file, data.clang_file)) { 674 return .Continue 675 } 676 677 indent(data.file, data.indent - 1) 678 os2.write_string(data.file, fmt.tprintln("- Visiting:", cursor_spelling(cursor))) 679 680 indent(data.file, data.indent) 681 os2.write_string(data.file, fmt.tprintln("Parent:", cursor_spelling(parent))) 682 683 indent(data.file, data.indent) 684 os2.write_string(data.file, fmt.tprintln("Kind:", cursor.kind)) 685 686 indent(data.file, data.indent) 687 os2.write_string(data.file, fmt.tprintln("TypeKind:", clang.getCursorType(cursor).kind)) 688 689 indent(data.file, data.indent) 690 os2.write_string(data.file, "Children:\n") 691 692 new_state := Data { 693 file = data.file, 694 clang_file = data.clang_file, 695 indent = data.indent + 1, 696 } 697 clang.visitChildren(cursor, visitor_proc, &new_state) 698 699 return .Continue 700 } 701 702 file, _ := os2.open(out_file, flags = {.Create, .Write, .Trunc}) 703 os2.write_string(file, fmt.tprintln("File:", clang_string_to_string(clang.getFileName(source_file)))) 704 os2.write_string(file, "Cursors:\n") 705 706 Data :: struct { 707 file: ^os2.File, 708 clang_file: clang.File, 709 indent: u32, 710 } 711 userData := Data { 712 file = file, 713 clang_file = source_file, 714 indent = 1, 715 } 716 717 clang.visitChildren(root_cursor, visitor_proc, &userData) 718 } 719 720 fp :: fmt.fprint 721 fpln :: fmt.fprintln 722 fpf :: fmt.fprintf 723 fpfln :: fmt.fprintfln 724 725 Config :: struct { 726 inputs: []string, 727 ignore_inputs: []string, 728 output_folder: string, 729 package_name: string, 730 required_prefix: string, 731 732 // deprecated: use remove_xxx_prefix 733 remove_prefix: string, 734 remove_type_prefix: string, 735 remove_function_prefix: string, 736 remove_macro_prefix: string, 737 import_lib: string, 738 imports_file: string, 739 clang_include_paths: []string, 740 clang_defines: map[string]string, 741 force_ada_case_types: bool, 742 opaque_types: []string, 743 rename: map[string]string, 744 remove_macros: []string, 745 debug_dump_ast: bool, 746 747 // deprecated: use rename 748 rename_types: map[string]string, 749 type_overrides: map[string]string, 750 struct_field_overrides: map[string]string, 751 procedure_type_overrides: map[string]string, 752 bit_setify: map[string]string, 753 inject_before: map[string]string, 754 } 755 756 Gen_State :: struct { 757 using config: Config, 758 file: clang.File, 759 source: string, 760 decls: [dynamic]Declaration, 761 macro_defines: map[string]int, 762 symbol_indices: map[string]int, 763 typedefs: map[string]string, 764 created_symbols: map[string]struct {}, 765 type_is_proc: map[string]struct {}, 766 opaque_type_lookup: map[string]struct {}, 767 remove_macros_lookup: map[string]struct {}, 768 created_types: map[string]struct {}, 769 needs_import_c: bool, 770 needs_import_libc: bool, 771 needs_import_posix: bool, 772 } 773 774 gen :: proc(input: string, c: Config) { 775 // Everything allocated within this call to `gen` is allocated on a single 776 // arena, which is destroyed when this procedure ends. 777 778 gen_arena: vmem.Arena 779 defer vmem.arena_destroy(&gen_arena) 780 context.allocator = vmem.arena_allocator(&gen_arena) 781 context.temp_allocator = vmem.arena_allocator(&gen_arena) 782 783 s := Gen_State { 784 config = c, 785 } 786 787 for ot in c.opaque_types { 788 // For quick lookup 789 add_to_set(&s.opaque_type_lookup, ot) 790 } 791 792 for m in c.remove_macros { 793 // For quick lookup 794 add_to_set(&s.remove_macros_lookup, m) 795 } 796 797 // 798 // Parse file using libclang and produce an AST. 799 // 800 801 clang_args := make([]cstring, 1 + len(c.clang_include_paths) + len(c.clang_defines)) 802 clang_args[0] = "-fparse-all-comments" 803 804 { 805 index := 1 806 for &include in c.clang_include_paths { 807 clang_args[index] = fmt.ctprintf("-I%v", include) 808 index += 1 809 } 810 811 for k, v in c.clang_defines { 812 clang_args[index] = fmt.ctprintf("-D%s=%s", k, v) 813 index += 1 814 } 815 } 816 817 idx := clang.createIndex(1, 0) 818 unit: clang.Translation_Unit 819 820 input_cstring := strings.clone_to_cstring(input) 821 822 // Keep macros, skip function bodies, and keep going on errors. 823 options: clang.Translation_Unit_Flags = { 824 .DetailedPreprocessingRecord, 825 .SkipFunctionBodies, 826 .KeepGoing, 827 } 828 err := clang.parseTranslationUnit2( 829 idx, 830 input_cstring, 831 raw_data(clang_args), 832 i32(len(clang_args)), 833 nil, 834 0, 835 options, 836 &unit, 837 ) 838 if err != .Success { 839 fmt.panicf("Failed to parse translation unit for %s. Error code: %i", input, err) 840 } 841 842 s.file = clang.getFile(unit, input_cstring) 843 844 source_data, source_data_ok := os.read_entire_file(input) 845 fmt.ensuref(source_data_ok, "Failed reading source file: %v", input) 846 s.source = string(source_data) 847 848 cursor_location :: proc(cursor: clang.Cursor, file: ^clang.File = nil, offset: ^u32 = nil) -> (line: u32) { 849 clang.getExpansionLocation(clang.getCursorLocation(cursor), file, &line, nil, offset) 850 return 851 } 852 853 comment_location :: proc(cursor: clang.Cursor) -> (line: u32) { 854 clang.getExpansionLocation(clang.getRangeStart(clang.Cursor_getCommentRange(cursor)), nil, &line, nil, nil) 855 return 856 } 857 858 vet_type :: proc(s: ^Gen_State, type: clang.Type) { 859 type := type 860 for type.kind == .Pointer { 861 type = clang.getPointeeType(type) 862 } 863 864 if is_c_type(type) { 865 s.needs_import_c = true 866 } else if is_libc_type(type) { 867 s.needs_import_libc = true 868 } else if is_posix_type(type) { 869 s.needs_import_posix = true 870 } 871 } 872 873 parse_function_decl :: proc(state: ^Gen_State, cursor: clang.Cursor) -> Function { 874 // We could probably make use of `clang.Type` here and not store a string. 875 // This is easier to implement for now. We can make improvments later. 876 return_type := clang.getCursorResultType(cursor) 877 vet_type(state, return_type) 878 879 out_params: [dynamic]clang.Cursor 880 881 for i in 0 ..< clang.Cursor_getNumArguments(cursor) { 882 param_cursor := clang.Cursor_getArgument(cursor, u32(i)) 883 #partial switch param_kind := clang.getCursorKind(param_cursor); param_kind { 884 case .ParmDecl: 885 vet_type(state, clang.getCursorType(param_cursor)) 886 append(&out_params, param_cursor) 887 case: 888 // For debugging purposes. 889 fmt.printfln("Unexpected cursor kind for parameter: %v", param_kind) 890 } 891 } 892 893 offset: u32 894 line := cursor_location(cursor, nil, &offset) 895 side_comment: string 896 translation_unit := clang.Cursor_getTranslationUnit(cursor) 897 for true { 898 token := clang.getToken(translation_unit, clang.getLocationForOffset(translation_unit, state.file, offset)) 899 if token == nil { 900 break 901 } 902 903 defer clang.disposeTokens(translation_unit, token, 1) 904 tline: u32 905 clang.getFileLocation(clang.getTokenLocation(translation_unit, token[0]), nil, &tline, nil, &offset) 906 if tline != line { 907 break 908 } 909 910 token_string := token_string(translation_unit, token[0]) 911 if clang.getTokenKind(token[0]) == .Comment { 912 side_comment = token_string 913 break 914 } 915 916 offset += u32(len(token_string)) 917 } 918 919 comment := comment_text(cursor) 920 cline := comment_location(cursor) 921 922 return Function { 923 original_name = cursor_spelling(cursor), 924 parameters = out_params[:], 925 cursor = cursor, 926 comment = comment, 927 comment_before = comment == "" ? false : cline != line, 928 post_comment = side_comment, 929 variadic = bool(clang.Cursor_isVariadic(cursor)), 930 } 931 } 932 933 parse_record_decl :: proc(state: ^Gen_State, cursor: clang.Cursor) -> Struct { 934 child_proc: clang.Cursor_Visitor : proc "c" ( 935 cursor, parent: clang.Cursor, 936 data: clang.Client_Data, 937 ) -> clang.Child_Visit_Result { 938 context = runtime.default_context() 939 data := (^Data)(data) 940 941 line: u32 942 clang.getExpansionLocation(clang.getCursorLocation(cursor), nil, &line, nil, nil) 943 944 cline := comment_location(cursor) 945 946 comment := comment_text(cursor) 947 comment_before := comment == "" ? false : cline != line 948 949 #partial switch kind := clang.getCursorKind(cursor); kind { 950 case .FieldDecl: 951 type := clang.getCursorType(cursor) 952 field_name := cursor_spelling(cursor) 953 if field_name == "" { 954 field_name = "_" 955 } 956 957 if prev_idx := len(data.out_fields) - 1; prev_idx >= 0 && bool(clang.equalTypes(data.out_fields[prev_idx].type, type)) \ 958 && data.out_fields[prev_idx].original_line == int(line) { 959 append(&data.out_fields[len(data.out_fields) - 1].names, field_name) 960 } else { 961 vet_type(data.state, type) 962 append(&data.out_fields, Struct_Field { 963 names = [dynamic]string {field_name}, 964 type = type, 965 anon_using = false, 966 comment = comment, 967 comment_before = comment_before, 968 original_line = int(line), 969 }) 970 } 971 case .StructDecl, .UnionDecl: 972 // This is a "forward declaration" of a struct directly on a field. We output a 973 // named opaque type for it. Not sure if it is the best idea, but it seems to "just work". 974 append(&data.state.decls, Declaration { 975 cursor = cursor, 976 original_idx = len(data.state.decls), 977 variant = parse_record_decl(data.state, cursor), 978 }) 979 980 data.state.opaque_type_lookup[cursor_spelling(cursor)] = {} 981 982 if bool(clang.Cursor_isAnonymousRecordDecl(cursor)) { 983 append(&data.out_fields, Struct_Field { 984 names = [dynamic]string {cursor_spelling(cursor)}, 985 type = clang.getCursorType(cursor), 986 anon_using = true, 987 comment = comment, 988 comment_before = comment_before, 989 original_line = int(line), 990 }) 991 } 992 case: 993 // For debugging purposes. 994 fmt.printf("Unexpected cursor kind for field: %v, name: %s\n", kind, cursor_spelling(cursor)) 995 } 996 return .Continue 997 } 998 999 Data :: struct { 1000 state: ^Gen_State, 1001 out_fields: [dynamic]Struct_Field, 1002 } 1003 1004 data: Data = { 1005 state = state, 1006 out_fields = {}, 1007 } 1008 1009 clang.visitChildren(cursor, child_proc, &data) 1010 1011 return { 1012 original_name = cursor_spelling(cursor), 1013 id = cursor_usr(cursor), 1014 fields = data.out_fields[:], 1015 comment = comment_text(cursor), 1016 is_union = clang.getCursorKind(cursor) == .UnionDecl, 1017 is_anon = bool(clang.Cursor_isAnonymous(cursor)), 1018 is_forward_declare = !bool(clang.isCursorDefinition(cursor)), 1019 } 1020 } 1021 1022 parse_typedef_decl :: proc(state: ^Gen_State, cursor: clang.Cursor) -> Typedef { 1023 type := clang.getTypedefDeclUnderlyingType(cursor) 1024 vet_type(state, type) 1025 1026 source_range := clang.getCursorExtent(cursor) 1027 start := clang.getRangeStart(source_range) 1028 start_offset: c.uint 1029 clang.getExpansionLocation(start, &state.file, nil, nil, &start_offset) 1030 side_comment, _ := find_comment_at_line_end(state.source[start_offset:]) 1031 1032 return { 1033 original_name = cursor_spelling(cursor), 1034 type = type, 1035 pre_comment = comment_text(cursor), 1036 side_comment = side_comment, 1037 } 1038 } 1039 1040 parse_enum_decl :: proc(state: ^Gen_State, cursor: clang.Cursor) -> Enum { 1041 out_members: [dynamic]Enum_Member 1042 1043 backing_type := clang.getEnumDeclIntegerType(cursor) 1044 vet_type(state, backing_type) 1045 1046 child_proc: clang.Cursor_Visitor : proc "c" ( 1047 cursor, parent: clang.Cursor, 1048 data: clang.Client_Data, 1049 ) -> clang.Child_Visit_Result { 1050 context = runtime.default_context() 1051 data := (^Data)(data) 1052 1053 #partial switch kind := clang.getCursorKind(cursor); kind { 1054 case .EnumConstantDecl: 1055 comment := comment_text(cursor) 1056 comment_before := comment == "" ? false : comment_location(cursor) != cursor_location(cursor) 1057 1058 append(data.out_members, Enum_Member { 1059 name = cursor_spelling(cursor), 1060 value = data.is_unsigned_type ? (int)(clang.getEnumConstantDeclUnsignedValue(cursor)) : (int)(clang.getEnumConstantDeclValue(cursor)), 1061 comment = comment, 1062 comment_before = comment_before, 1063 }) 1064 case: 1065 // For debugging purposes. 1066 fmt.println("Unexpected cursor kind for enum member:", kind) 1067 } 1068 1069 return .Continue 1070 } 1071 1072 Data :: struct { 1073 is_unsigned_type: bool, 1074 out_members: ^[dynamic]Enum_Member, 1075 } 1076 1077 clang.visitChildren(cursor, child_proc, &Data { 1078 is_unsigned_type = backing_type.kind >= .Char_U && backing_type.kind <= .UInt128, 1079 out_members = &out_members, 1080 }) 1081 1082 return { 1083 original_name = bool(clang.Cursor_isAnonymous(cursor)) ? "" : cursor_spelling(cursor), 1084 id = cursor_usr(cursor), 1085 comment = comment_text(cursor), 1086 members = out_members[:], 1087 backing_type = backing_type, 1088 } 1089 } 1090 1091 parse_macro_decl :: proc(state: ^Gen_State, cursor: clang.Cursor) -> Macro { 1092 translation_unit := clang.Cursor_getTranslationUnit(cursor) 1093 source_range := clang.getCursorExtent(cursor) 1094 1095 whitespace_after_name: int 1096 comment: string 1097 side_comment: string 1098 side_comment_align_whitespace: int 1099 { 1100 start := clang.getRangeStart(source_range) 1101 start_offset: c.uint 1102 clang.getExpansionLocation(start, &state.file, nil, nil, &start_offset) 1103 end := clang.getRangeEnd(source_range) 1104 end_offset: c.uint 1105 clang.getExpansionLocation(end, &state.file, nil, nil, &end_offset) 1106 macro_source := state.source[start_offset:end_offset] 1107 1108 // 1109 // Figure out spacing between name and value 1110 // 1111 first_space_seen := false 1112 1113 for c in macro_source { 1114 if unicode.is_white_space(c) { 1115 if !first_space_seen { 1116 first_space_seen = true 1117 } 1118 1119 whitespace_after_name += 1 1120 } else { 1121 if first_space_seen { 1122 break 1123 } 1124 } 1125 } 1126 1127 // 1128 // Figure out comments at the end of line 1129 // 1130 side_comment, side_comment_align_whitespace = find_comment_at_line_end(state.source[start_offset:]) 1131 1132 // 1133 // Figure out comments before the macro 1134 // 1135 1136 { 1137 Find_Comment_State :: enum { 1138 Looking_For_Start, 1139 Looking_For_Comment, 1140 Looking_For_Single_Line_Start, 1141 Verifying_Single_Line, 1142 Inside_Block_Comment, 1143 } 1144 src := state.source 1145 find_state: Find_Comment_State 1146 comment_start := -1 1147 comment_end: int 1148 1149 comment_loop: for i := int(start_offset); i >= 0; { 1150 c := utf8.rune_at(src, i) 1151 defer i -= utf8.rune_size(c) 1152 switch find_state { 1153 case .Looking_For_Start: 1154 if c == '#' { 1155 comment_end = i 1156 find_state = .Looking_For_Comment 1157 break 1158 } 1159 1160 if c == '\n' { 1161 break comment_loop 1162 } 1163 case .Looking_For_Comment: 1164 if unicode.is_white_space(c) { 1165 break 1166 } 1167 1168 if c == '/' && i > 1 && src[i - 1] == '*' { 1169 find_state = .Inside_Block_Comment 1170 break 1171 } 1172 1173 // TODO: Special case when line only is `//` 1174 1175 find_state = .Looking_For_Single_Line_Start 1176 case .Looking_For_Single_Line_Start: 1177 if c == '\n' { 1178 break comment_loop 1179 } 1180 1181 if c == '/' && i < len(src) - 1 && src[i + 1] == '/' { 1182 find_state = .Verifying_Single_Line 1183 break 1184 } 1185 1186 case .Verifying_Single_Line: 1187 if c == '\n' { 1188 comment_start = i 1189 find_state = .Looking_For_Comment 1190 break 1191 } 1192 1193 if !unicode.is_white_space(c) { 1194 break comment_loop 1195 } 1196 case .Inside_Block_Comment: 1197 if c == '/' && i < len(src) - 1 && src[i + 1] == '*' { 1198 comment_start = i 1199 find_state = .Looking_For_Comment 1200 break 1201 } 1202 } 1203 } 1204 1205 if comment_start != -1 && comment_end > comment_start { 1206 comment = strings.trim_space(src[comment_start:comment_end]) 1207 } 1208 } 1209 } 1210 1211 tokens: [^]clang.Token 1212 token_count: u32 1213 clang.tokenize(translation_unit, source_range, &tokens, &token_count) 1214 1215 return { 1216 original_name = cursor_spelling(cursor), 1217 tokens = tokens[:token_count], 1218 has_been_evaluated = false, 1219 is_function = bool(clang.Cursor_isMacroFunctionLike(cursor)), 1220 comment = comment, 1221 side_comment = side_comment, 1222 whitespace_before_side_comment = side_comment_align_whitespace, 1223 whitespace_after_name = whitespace_after_name, 1224 } 1225 } 1226 1227 root_cursor_visitor_proc: clang.Cursor_Visitor : proc "c" ( 1228 cursor, parent: clang.Cursor, 1229 state: clang.Client_Data, 1230 ) -> clang.Child_Visit_Result { 1231 context = runtime.default_context() 1232 state := (^Gen_State)(state) 1233 1234 file: clang.File 1235 _ = cursor_location(cursor, &file) 1236 if !bool(clang.File_isEqual(file, state.file)) { 1237 return .Continue // This cursor is not in the file we are interested in. 1238 } 1239 1240 kind := clang.getCursorKind(cursor) 1241 #partial switch kind { 1242 case .MacroDefinition: 1243 if bool(clang.Cursor_isMacroBuiltin(cursor)) { 1244 return .Continue 1245 } 1246 1247 append(&state.decls, Declaration { 1248 cursor = cursor, 1249 original_idx = len(state.decls), 1250 variant = parse_macro_decl(state, cursor), 1251 }) 1252 return .Continue 1253 case .FunctionDecl: 1254 if clang.Cursor_isFunctionInlined(cursor) != 0 { 1255 return .Continue 1256 } 1257 1258 append(&state.decls, Declaration { 1259 cursor = cursor, 1260 original_idx = len(state.decls), 1261 variant = parse_function_decl(state, cursor), 1262 }) 1263 return .Continue 1264 } 1265 1266 def: Declaration_Variant 1267 #partial switch kind { 1268 case .StructDecl, .UnionDecl: 1269 def = parse_record_decl(state, cursor) 1270 case .TypedefDecl: 1271 def = parse_typedef_decl(state, cursor) 1272 case .EnumDecl: 1273 def = parse_enum_decl(state, cursor) 1274 } 1275 1276 append(&state.decls, Declaration { 1277 cursor = cursor, 1278 original_idx = len(state.decls), 1279 variant = def, 1280 }) 1281 1282 return .Continue 1283 } 1284 1285 root_cursor := clang.getTranslationUnitCursor(unit) 1286 1287 input_filename := filepath.base(input) 1288 output_stem := filepath.stem(input_filename) 1289 output_filename := fmt.tprintf("%v/%v.odin", s.output_folder, output_stem) 1290 1291 if c.debug_dump_ast { 1292 dump_ast(root_cursor, s.file, fmt.tprintf("%v/%v.yml", s.output_folder, output_stem)) 1293 } 1294 clang.visitChildren(root_cursor, root_cursor_visitor_proc, &s) 1295 1296 slice.sort_by(s.decls[:], proc(i, j: Declaration) -> bool { 1297 // This should work but I get a linker error. Is the version of libclang from VS dev tools outdated? 1298 // return bool(clang.isBeforeInTranslationUnit(clang.getCursorLocation(i.cursor), clang.getCursorLocation(j.cursor))) 1299 1300 // This should be fine for now. 1301 return cursor_location(i.cursor) < cursor_location(j.cursor) 1302 }) 1303 1304 // 1305 // Use the stuff in `s` and `s.decl` to write out the bindings. 1306 // 1307 1308 f, f_err := os.open(output_filename, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0o644) 1309 1310 fmt.ensuref(f_err == nil, "Failed opening %v", output_filename) 1311 defer os.close(f) 1312 1313 // Extract any big comment at top of file (clang doesn't see these) 1314 { 1315 source := strings.trim_space(s.source) 1316 in_block := false 1317 top_comment_loop: for ll in strings.split_lines_iterator(&source) { 1318 l := strings.trim_space(ll) 1319 1320 if in_block { 1321 fpln(f, l) 1322 if strings.contains(l, "*/") { 1323 in_block = false 1324 } 1325 } else { 1326 if len(l) < 2 { 1327 continue 1328 } 1329 1330 switch l[:2] { 1331 case "//": 1332 fpln(f, l) 1333 case "/*": 1334 in_block = !strings.contains(l, "*/") 1335 fpln(f, l) 1336 case: 1337 break top_comment_loop 1338 } 1339 } 1340 } 1341 } 1342 1343 fpf(f, "package %v\n\n", s.package_name) 1344 1345 if s.needs_import_c { 1346 fpln(f, `import "core:c"`) 1347 } 1348 1349 if s.needs_import_libc { 1350 fpln(f, `import "core:c/libc"`) 1351 } 1352 1353 if s.needs_import_posix { 1354 fpln(f, `import "core:sys/posix"`) 1355 } 1356 1357 fp(f, "\n") 1358 1359 if s.needs_import_c { 1360 fpln(f, "_ :: c") 1361 } 1362 if s.needs_import_libc { 1363 fpln(f, "_ :: libc") 1364 } 1365 if s.needs_import_posix { 1366 fpln(f, "_ :: posix") 1367 } 1368 1369 fp(f, "\n") 1370 1371 if s.imports_file != "" { 1372 top_code, top_code_ok := os.read_entire_file(s.imports_file) 1373 fmt.ensuref(top_code_ok, "Failed to load %v", s.imports_file) 1374 fp(f, string(top_code)) 1375 } else if s.import_lib != "" { 1376 fpf(f, `foreign import lib "%v"`, s.import_lib) 1377 } 1378 1379 fp(f, "\n\n") 1380 1381 output_comment :: proc(f: os.Handle, c: string, indent := "") { 1382 ci := c 1383 for l in strings.split_lines_iterator(&ci) { 1384 fp(f, indent) 1385 fpln(f, strings.trim_space(l)) 1386 } 1387 } 1388 1389 // 1390 // Figure out all type names 1391 // 1392 1393 for &decl, i in s.decls { 1394 du := &decl.variant 1395 switch &d in du { 1396 case Struct: 1397 if d.is_anon { 1398 s.symbol_indices[d.original_name] = i 1399 continue // Skip anonymous structs. 1400 } 1401 1402 name := d.original_name 1403 if typedef, has_typedef := s.typedefs[d.id]; has_typedef { 1404 d.original_name = typedef 1405 name = typedef 1406 } 1407 name = translate_name(&s, name) 1408 1409 d.name = vet_name(name) 1410 add_to_set(&s.created_types, d.name) 1411 add_to_set(&s.created_symbols, name) 1412 case Function: 1413 name := d.original_name 1414 1415 if replacement, has_replacement := s.rename[name]; has_replacement { 1416 d.link_name = d.original_name 1417 name = replacement 1418 } else { 1419 name = trim_prefix(name, s.remove_function_prefix) 1420 } 1421 1422 d.name = vet_name(name) 1423 case Enum: 1424 name := d.original_name 1425 1426 if typedef, has_typedef := s.typedefs[d.id]; has_typedef { 1427 d.original_name = typedef 1428 name = typedef 1429 } 1430 name = translate_name(&s, name) 1431 1432 d.name = vet_name(name) 1433 add_to_set(&s.created_symbols, d.name) 1434 add_to_set(&s.created_types, d.name) 1435 case Typedef: 1436 name := d.original_name 1437 1438 if name in c_type_mapping { 1439 continue 1440 } 1441 1442 name = translate_name(&s, name) 1443 d.name = vet_name(name) 1444 add_to_set(&s.created_types, d.name) 1445 case Macro: 1446 name := d.original_name 1447 s.macro_defines[name] = i 1448 1449 if replacement, has_replacement := s.rename[name]; has_replacement { 1450 name = replacement 1451 } else { 1452 name = trim_prefix(name, s.remove_macro_prefix) 1453 } 1454 1455 d.name = vet_name(name) 1456 } 1457 } 1458 1459 for _, b in s.bit_setify { 1460 add_to_set(&s.created_types, b) 1461 } 1462 1463 for &decl, decl_idx in s.decls { 1464 output_struct :: proc(s: ^Gen_State, d: Struct, indent: int, n: string) -> string { 1465 w := strings.builder_make() 1466 ws :: strings.write_string 1467 ws(&w, "struct ") 1468 1469 if d.is_union { 1470 ws(&w, "#raw_union ") 1471 } 1472 1473 if len(d.fields) == 0 { 1474 ws(&w, "{}") 1475 return strings.to_string(w) 1476 } 1477 1478 ws(&w, "{\n") 1479 1480 longest_field_name_with_side_comment: int 1481 1482 for &field in d.fields { 1483 if bool(clang.Cursor_isAnonymous(clang.getTypeDeclaration(field.type))) { 1484 continue 1485 } 1486 1487 field_len: int 1488 for fn, nidx in field.names { 1489 if nidx != 0 { 1490 field_len += 2 // for comma and space 1491 } 1492 1493 field_len += len(vet_name(fn)) 1494 } 1495 if (field.comment == "" || !field.comment_before) && field_len > longest_field_name_with_side_comment { 1496 longest_field_name_with_side_comment = field_len 1497 } 1498 } 1499 1500 Formatted_Field :: struct { 1501 field: string, 1502 comment: string, 1503 comment_before: bool, 1504 } 1505 1506 fields: [dynamic]Formatted_Field 1507 1508 for &field in d.fields { 1509 b := strings.builder_make() 1510 1511 override_key: string 1512 1513 if field.anon_using { 1514 strings.write_string(&b, "using _: ") 1515 } else { 1516 for fn, nidx in field.names { 1517 if nidx != 0 { 1518 strings.write_string(&b, ", ") 1519 } 1520 strings.write_string(&b, vet_name(fn)) 1521 } 1522 1523 names_len := strings.builder_len(b) 1524 override_key = fmt.tprintf("%s.%s", d.original_name, strings.to_string(b)) 1525 strings.write_string(&b, ": ") 1526 1527 if !field.comment_before { 1528 // Padding between name and = 1529 for _ in 0..<longest_field_name_with_side_comment-names_len { 1530 strings.write_rune(&b, ' ') 1531 } 1532 } 1533 } 1534 1535 field_type: string 1536 1537 if field_type_override, has_field_type_override := s.struct_field_overrides[override_key]; override_key != "" && has_field_type_override { 1538 if field_type_override == "[^]" { 1539 field_type, _ = parse_type(s, field.type, {.Pointer_To_Array}) 1540 } else { 1541 field_type = field_type_override 1542 } 1543 } else { 1544 field_type, _ = parse_type(s, field.type, nil) 1545 } 1546 1547 comment := field.comment 1548 comment_before := field.comment_before 1549 1550 if bool(clang.Cursor_isAnonymous(clang.getTypeDeclaration(field.type))) { 1551 decl_index, exists := s.symbol_indices[cursor_spelling(clang.getTypeDeclaration(field.type))] 1552 if exists { 1553 anon_struct := s.decls[decl_index].variant.(Struct) 1554 if anon_struct.comment != "" { 1555 comment = anon_struct.comment 1556 comment_before = true 1557 } 1558 1559 field_type = output_struct(s, anon_struct, indent + 1, n) 1560 } 1561 } 1562 1563 strings.write_string(&b, field_type) 1564 1565 append(&fields, Formatted_Field { 1566 field = strings.to_string(b), 1567 comment = comment, 1568 comment_before = comment_before, 1569 }) 1570 } 1571 1572 longest_field_with_side_comment: int 1573 1574 for &field in fields { 1575 if field.comment != "" && !field.comment_before { 1576 longest_field_with_side_comment = max(len(field.field), longest_field_with_side_comment) 1577 } 1578 } 1579 1580 for &field, field_idx in fields { 1581 has_comment := field.comment != "" 1582 comment_before := field.comment_before 1583 1584 if has_comment && comment_before { 1585 if field_idx != 0 { 1586 ws(&w, "\n") 1587 } 1588 1589 ci := field.comment 1590 for l in strings.split_lines_iterator(&ci) { 1591 for _ in 0..<indent+1 { 1592 ws(&w, "\t") 1593 } 1594 ws(&w, strings.trim_space(l)) 1595 ws(&w, "\n") 1596 } 1597 } 1598 1599 for _ in 0..<indent+1 { 1600 ws(&w, "\t") 1601 } 1602 ws(&w, field.field) 1603 ws(&w, ",") 1604 1605 if has_comment && !comment_before { 1606 // Padding in front of comment 1607 for _ in 0..<(longest_field_with_side_comment - len(field.field)) { 1608 ws(&w, " ") 1609 } 1610 1611 ws(&w, " ") 1612 ws(&w, field.comment) 1613 } 1614 1615 ws(&w, "\n") 1616 } 1617 1618 for _ in 0..<indent { 1619 ws(&w, "\t") 1620 } 1621 ws(&w, "}") 1622 return strings.to_string(w) 1623 } 1624 1625 du := &decl.variant 1626 switch &d in du { 1627 case Struct: 1628 if d.is_anon { 1629 continue // Skip anonymous structs. 1630 } 1631 1632 n := d.name 1633 1634 if d.is_forward_declare { 1635 if d.original_name in s.opaque_type_lookup && d.id not_in s.typedefs { 1636 output_comment(f, d.comment) 1637 fpf(f, "%v :: struct {{}}\n\n", n) 1638 } 1639 1640 break 1641 } 1642 1643 output_comment(f, d.comment) 1644 1645 if inject, has_injection := s.inject_before[d.original_name]; has_injection { 1646 fpf(f, "%v\n\n", inject) 1647 } 1648 1649 fp(f, n) 1650 fp(f, " :: ") 1651 1652 if override, override_ok := s.type_overrides[d.original_name]; override_ok { 1653 fp(f, override) 1654 fp(f, "\n\n") 1655 break 1656 } 1657 1658 fp(f, output_struct(&s, d, 0, n)) 1659 fp(f, "\n\n") 1660 case Enum: 1661 output_comment(f, d.comment) 1662 1663 name := d.name 1664 1665 // It has no name, turn it into a bunch of constants 1666 if name == "" { 1667 for &m in d.members { 1668 mn := m.name 1669 1670 if strings.has_prefix(strings.to_lower(mn), strings.to_lower(s.remove_type_prefix)) { 1671 mn = mn[len(s.remove_type_prefix):] 1672 1673 if strings.has_prefix(mn, "_") { 1674 mn = mn[1:] 1675 } 1676 } 1677 1678 fpf(f, "%v :: %v\n\n", mn, m.value) 1679 } 1680 1681 break 1682 } 1683 1684 fp(f, name) 1685 { 1686 str, _ := parse_type(&s, d.backing_type, nil) 1687 fpf(f, " :: enum %v {{\n", str) 1688 } 1689 1690 bit_set_name, bit_setify := s.bit_setify[d.original_name] 1691 make_constant: map[string]int 1692 1693 if bit_setify { 1694 for &m in d.members { 1695 if bits.count_ones(m.value) != 1 { // Not a power of two, so not part of a bit_set. 1696 make_constant[m.name] = m.value 1697 continue 1698 } 1699 m.value = (int)(bits.log2((uint)(m.value))) 1700 } 1701 } 1702 1703 overlap_length := 0 1704 longest_name := 0 1705 1706 all_has_default_value := true 1707 counter := 0 1708 for &m in d.members { 1709 if _, skip := make_constant[m.name]; skip { 1710 continue 1711 } 1712 1713 if m.value != counter { 1714 all_has_default_value = false 1715 break 1716 } 1717 counter += 1 1718 } 1719 1720 if len(d.members) > 1 { 1721 overlap_length_source := d.members[0].name 1722 overlap_length = len(overlap_length_source) 1723 longest_name = overlap_length 1724 1725 for idx in 1..<len(d.members) { 1726 if _, skip := make_constant[d.members[idx].name]; skip { 1727 continue 1728 } 1729 1730 mn := d.members[idx].name 1731 length := strings.prefix_length(mn, overlap_length_source) 1732 1733 if length < overlap_length { 1734 overlap_length = length 1735 overlap_length_source = mn 1736 } 1737 1738 longest_name = max(len(mn), longest_name) 1739 } 1740 } 1741 1742 Formatted_Member :: struct { 1743 name: string, 1744 member: string, 1745 enum_member: ^Enum_Member, 1746 } 1747 1748 members: [dynamic]Formatted_Member 1749 1750 for &m in d.members { 1751 if _, skip := make_constant[m.name]; skip { 1752 continue 1753 } 1754 1755 b := strings.builder_make() 1756 1757 name_without_overlap := m.name[overlap_length:] 1758 1759 // I added this to fix something but I dont think we actually need it anymore. 1760 // If you see any enum members that start with an underscore uncomment this. 1761 // Remove any leading underscores. 1762 // for ; name_without_overlap[0] == '_'; name_without_overlap = name_without_overlap[1:] {} 1763 1764 // First letter is number... Can't have that! 1765 if len(name_without_overlap) > 0 && unicode.is_number(utf8.rune_at(name_without_overlap, 0)) { 1766 name_without_overlap = fmt.tprintf("_%v", name_without_overlap) 1767 } 1768 1769 strings.write_string(&b, name_without_overlap) 1770 1771 suffix_pad := longest_name - len(name_without_overlap) - overlap_length 1772 1773 if !all_has_default_value { 1774 if !m.comment_before { 1775 for _ in 0..<suffix_pad { 1776 // Padding between name and `=` 1777 strings.write_rune(&b, ' ') 1778 } 1779 } 1780 1781 strings.write_string(&b, fmt.tprintf(" = %v", m.value)) 1782 } 1783 1784 append(&members, Formatted_Member { 1785 name = name_without_overlap, 1786 member = strings.to_string(b), 1787 enum_member = &m, 1788 }) 1789 } 1790 1791 longest_member_name_with_side_comment: int 1792 1793 for &m in members { 1794 if m.enum_member.comment != "" && !m.enum_member.comment_before && len(m.member) > longest_member_name_with_side_comment { 1795 longest_member_name_with_side_comment = len(m.member) 1796 } 1797 } 1798 1799 for &m, m_idx in members { 1800 has_comment := m.enum_member.comment != "" 1801 comment_before := m.enum_member.comment_before 1802 1803 if has_comment && comment_before { 1804 if m_idx != 0 { 1805 fp(f, "\n") 1806 } 1807 output_comment(f, m.enum_member.comment, "\t") 1808 } 1809 1810 fp(f, "\t") 1811 fp(f, m.member) 1812 fp(f, ",") 1813 1814 if has_comment && !comment_before { 1815 for _ in 0..<(longest_member_name_with_side_comment - len(m.member)) { 1816 // Padding in front of comment 1817 fp(f, " ") 1818 } 1819 1820 fpf(f, " %v", m.enum_member.comment) 1821 } 1822 1823 fp(f, '\n') 1824 } 1825 1826 fp(f, "}\n\n") 1827 1828 if bit_setify { 1829 str, _ := parse_type(&s, d.backing_type, nil) 1830 fpf(f, "%v :: distinct bit_set[%v; %v]\n\n", bit_set_name, name, str) 1831 1832 // In case there is a typedef for this in the code. 1833 add_to_set(&s.created_symbols, bit_set_name) 1834 1835 // There was a member with a compound value, so we need to 1836 // decompose it into a constant bit set 1837 for constant_name, constant_val in make_constant { 1838 all_constant := strings.to_screaming_snake_case(trim_prefix(strings.to_lower(constant_name), strings.to_lower(s.remove_type_prefix))) 1839 1840 if constant_val == 0 { 1841 // If the value is 0, we don't need to output it. 1842 // This is because the zero value of a bit set is an empty set. 1843 continue 1844 } 1845 1846 fpf(f, "%v :: %v {{ ", all_constant, bit_set_name) 1847 1848 for &m, i in members { 1849 if (1 << uint(m.enum_member.value)) & constant_val != 0 { 1850 fpf(f, ".%v", m.name) 1851 1852 if i != len(members) - 1 { 1853 fp(f, ", ") 1854 } 1855 } 1856 } 1857 1858 fp(f, " }\n\n") 1859 } 1860 } 1861 1862 case Function: 1863 // handled later. This makes all procs end up at bottom, after types. 1864 case Typedef: 1865 n := d.name 1866 1867 if n == "" { 1868 // The name was a C type, so we don't need to output it. 1869 continue 1870 } 1871 1872 if d.original_name in s.opaque_type_lookup { 1873 if d.pre_comment != "" { 1874 output_comment(f, d.pre_comment) 1875 } 1876 fpf(f, "%v :: struct {{}}", n) 1877 1878 if d.side_comment != "" { 1879 fp(f, ' ') 1880 fp(f, d.side_comment) 1881 } 1882 1883 fp(f, "\n\n") 1884 continue 1885 } 1886 1887 type_string := type_spelling(d.type) 1888 if n in s.created_symbols || strings.has_prefix(type_string, "0x") { 1889 continue 1890 } 1891 1892 parsed_type, _ := parse_type(&s, d.type, nil) 1893 if parsed_type == d.name { 1894 continue 1895 } 1896 1897 if d.pre_comment != "" { 1898 output_comment(f, d.pre_comment) 1899 } 1900 1901 fp(f, n) 1902 1903 fp(f, " :: ") 1904 1905 if override, override_ok := s.type_overrides[d.original_name]; override_ok { 1906 fp(f, override) 1907 1908 if d.side_comment != "" { 1909 output_comment(f, d.side_comment) 1910 } 1911 1912 fp(f, "\n\n") 1913 continue 1914 } 1915 1916 if strings.has_prefix(type_string, "struct ") { 1917 // This is a weird case -- I used this for opaque types in the 1918 // beginning, but opaque types are now handled by 1919 // `s.opaque_type_lookup`, so perhaps this isn't needed anymore? 1920 fp(f, "struct {}") 1921 } else if strings.contains(type_string, "(") && strings.contains(type_string, ")") { 1922 // function pointer typedef 1923 fp(f, parsed_type) 1924 add_to_set(&s.type_is_proc, n) 1925 } else { 1926 fpf(f, "%v", parsed_type) 1927 } 1928 1929 if d.side_comment != "" { 1930 fp(f, ' ') 1931 fp(f, d.side_comment) 1932 } 1933 1934 fp(f, "\n\n") 1935 case Macro: 1936 // I'm not particularly proud of this implementation. 1937 // It could probably be massively simplified and improved. 1938 1939 parse_literal :: proc(token_str: string) -> string { 1940 switch token_str[0] { 1941 case '0'..='9': 1942 token_str := token_str 1943 if len(token_str) == 1 { 1944 return token_str 1945 } 1946 1947 hex := false 1948 if token_str[1] == 'x' { 1949 hex = true 1950 } else if token_str[1] == 'X' { 1951 hex = true 1952 // Odin requires hex x to be lowercase. 1953 tmp := transmute([]u8)(token_str) 1954 tmp[1] = 'x' 1955 } 1956 1957 index := len(token_str) - 1 1958 LOOP: for ; index > 0; index -= 1 { 1959 switch token_str[index] { 1960 case 'L', 'l', 'U', 'u': 1961 // These are suffixes for long and unsigned literals. 1962 continue LOOP 1963 case 'F', 'f': 1964 if hex { 1965 break LOOP 1966 } 1967 // Floating point literals can have 'F' or 'f' suffixes. 1968 continue LOOP 1969 case: 1970 // Not a suffix char. 1971 break LOOP 1972 } 1973 } 1974 return token_str[:index + 1] 1975 case '"': 1976 // String literal 1977 // We'll need to make some considerations here when we want to handle '#' operations. 1978 return token_str 1979 } 1980 return token_str 1981 } 1982 1983 parse_identifier :: proc(state: ^Gen_State, cursor: clang.Cursor, macro: ^Macro, index: int) -> (string, int) { 1984 // Could be a type or macro name. Could also be the name of a function or variable. 1985 tu := clang.Cursor_getTranslationUnit(cursor) 1986 token := macro.tokens[index] 1987 token_str := token_string(tu, token) 1988 1989 if token_str == "true" || token_str == "false" { 1990 return token_str, 0 1991 } 1992 1993 if token_str in state.created_types { 1994 return token_str, 0 1995 } 1996 1997 if decl_index, exists := state.macro_defines[token_str]; exists { 1998 val, offset := expand_inner_macro(state, cursor, macro, &state.decls[decl_index], index) 1999 if !state.decls[decl_index].variant.(Macro).should_not_output { 2000 val = state.decls[decl_index].variant.(Macro).name 2001 } 2002 return val, offset 2003 } 2004 2005 return translate_type_string(state, token_str), 0 2006 } 2007 2008 parse_format_string :: proc(str: string, args: []string) -> string { 2009 // Replaces ${0}, ${1}, etc. with the corresponding argument. 2010 builder := strings.builder_make() 2011 for i := 0; i < len(str); i += 1 { 2012 if i + 1 < len(str) && str[i] == '$' && str[i + 1] == '{' { 2013 i += 2 2014 for j := i; j < len(str); j += 1 { 2015 if str[j] == '}' { 2016 if num, ok := strconv.parse_int(str[i:j], 10); ok { 2017 strings.write_string(&builder, args[num]) 2018 i = j 2019 break 2020 } 2021 } 2022 } 2023 } else { 2024 strings.write_byte(&builder, str[i]) 2025 } 2026 } 2027 return strings.to_string(builder) 2028 } 2029 2030 get_fn_macro_params :: proc(state: ^Gen_State, cursor: clang.Cursor, macro: ^Macro, index: int) -> ([]string, int) { 2031 if index >= len(macro.tokens) { 2032 return nil, 0 // No parameters. 2033 } 2034 2035 tu := clang.Cursor_getTranslationUnit(cursor) 2036 2037 { 2038 token_str := token_string(tu, macro.tokens[index]) 2039 if token_str[0] != '(' { 2040 return nil, 0 // No parameters. 2041 } 2042 } 2043 2044 params: [dynamic]string 2045 builder := strings.builder_make() 2046 for loop_index := index; loop_index < len(macro.tokens); loop_index += 1 { 2047 token := macro.tokens[loop_index] 2048 2049 paren_count := 1 2050 token_str := token_string(tu, token) 2051 #partial switch clang.getTokenKind(token) { 2052 case .Punctuation: 2053 switch token_str[0] { 2054 case '(': 2055 paren_count += 1 2056 case ')': 2057 paren_count -= 1 2058 if paren_count == 0 { 2059 append(¶ms, strings.to_string(builder)) 2060 return params[:], loop_index - index + 1 2061 } 2062 case ',': 2063 if paren_count == 1 { 2064 append(¶ms, strings.to_string(builder)) 2065 builder = strings.builder_make() // Reset the builder for the next parameter. 2066 } 2067 } 2068 case .Keyword: 2069 tokens_str := token_str 2070 tokens_count := 0 2071 for t in macro.tokens[index + 1:] { 2072 if clang.getTokenKind(t) == .Keyword { 2073 tokens_str = fmt.tprint(tokens_str, token_string(tu, t)) 2074 tokens_count += 1 2075 } else { 2076 break 2077 } 2078 } 2079 2080 if keyword_string := translate_type_string(state, tokens_str); keyword_string != "" { 2081 strings.write_string(&builder, keyword_string) 2082 loop_index += tokens_count 2083 } else { 2084 if keyword_string = translate_type_string(state, token_str); keyword_string != "" { 2085 strings.write_string(&builder, keyword_string) 2086 } 2087 } 2088 case .Identifier: 2089 val, offset := parse_identifier(state, cursor, macro, loop_index) 2090 loop_index += offset 2091 if val == "" { 2092 // macro.should_not_output = true 2093 val = token_str // Fallback to the original token string. 2094 } 2095 2096 if strings.contains_rune(val, ',') { 2097 encapsulation := 0 2098 for r in val { 2099 switch r { 2100 case '(': 2101 encapsulation += 1 2102 case ')': 2103 encapsulation -= 1 2104 case ',': 2105 if encapsulation == 0 { 2106 // We found a comma at the top level, so we need to split this parameter. 2107 append(¶ms, strings.to_string(builder)) 2108 builder = strings.builder_make() // Reset the builder for the next parameter. 2109 continue 2110 } 2111 case: 2112 strings.write_rune(&builder, r) 2113 } 2114 } 2115 } else { 2116 strings.write_string(&builder, val) 2117 } 2118 case .Literal: 2119 strings.write_string(&builder, parse_literal(token_str)) 2120 } 2121 } 2122 return nil, 0 // We didn't find the closing parenthesis. 2123 } 2124 2125 expand_inner_macro :: proc(state: ^Gen_State, cursor: clang.Cursor, macro: ^Macro, decl: ^Declaration, index: int) -> (val: string, offset: int) { 2126 decl_macro := &decl.variant.(Macro) 2127 if !decl_macro.has_been_evaluated { 2128 evaluate_macro(state, decl.cursor, decl_macro) 2129 } 2130 2131 if decl_macro.is_function { 2132 params: []string 2133 params, offset = get_fn_macro_params(state, cursor, macro, index + 1) 2134 if params == nil { 2135 // We couldn't find the parameters. 2136 macro.should_not_output = true 2137 return "", 0 2138 } 2139 2140 parsed_fn_string := parse_format_string(decl_macro.val, params) 2141 if parsed_fn_string == "" { 2142 // Couldn't parse the function macro. 2143 // Parameters were probably wrong. 2144 macro.should_not_output = true 2145 return "", 0 2146 } 2147 2148 val = parsed_fn_string 2149 } else { 2150 val = decl_macro.val 2151 } 2152 return 2153 } 2154 2155 evaluate_fn_macro :: proc(state: ^Gen_State, cursor: clang.Cursor, macro: ^Macro) { 2156 macro.should_not_output = true 2157 params, offset := get_fn_macro_params(state, cursor, macro, 1) 2158 if params == nil { 2159 // We couldn't find the parameters. 2160 return 2161 } 2162 2163 paramsMap: map[string]int 2164 for p, i in params { 2165 paramsMap[p] = i 2166 } 2167 2168 tu := clang.Cursor_getTranslationUnit(cursor) 2169 builder := strings.builder_make() 2170 for index := offset + 1; index < len(macro.tokens); index += 1 { 2171 token := macro.tokens[index] 2172 2173 token_str := token_string(tu, token) 2174 2175 if replace_val, has_replace := paramsMap[token_str]; has_replace { 2176 // If the token is a parameter, replace it with the corresponding value. 2177 buf: [10]byte // We can have upto 10 digits 2178 strings.write_string(&builder, "${") 2179 strings.write_string(&builder, strconv.write_int(buf[:], i64(replace_val), 10)) 2180 strings.write_rune(&builder, '}') 2181 continue 2182 } 2183 2184 #partial switch clang.getTokenKind(token) { 2185 case .Punctuation: 2186 switch token_str[0] { 2187 case '#': 2188 macro.should_not_output = true 2189 strings.write_string(&builder, token_str) 2190 case: 2191 strings.write_string(&builder, token_str) 2192 } 2193 case .Keyword: 2194 tokens_str := token_str 2195 tokens_count := 0 2196 for t in macro.tokens[index + 1:] { 2197 if clang.getTokenKind(t) == .Keyword { 2198 tokens_str = fmt.tprint(tokens_str, token_string(tu, t)) 2199 tokens_count += 1 2200 } else { 2201 break 2202 } 2203 } 2204 2205 if keyword_string := translate_type_string(state, tokens_str); keyword_string != "" { 2206 strings.write_string(&builder, keyword_string) 2207 index += tokens_count 2208 } else { 2209 if keyword_string = translate_type_string(state, token_str); keyword_string != "" { 2210 strings.write_string(&builder, keyword_string) 2211 } 2212 } 2213 case .Identifier: 2214 val, offset2 := parse_identifier(state, cursor, macro, index) 2215 index += offset2 2216 if val == "" { 2217 // macro.should_not_output = true 2218 val = token_str // Fallback to the original token string. 2219 } 2220 strings.write_string(&builder, val) 2221 case .Literal: 2222 val := parse_literal(token_str) 2223 if val == "" { 2224 macro.should_not_output = true 2225 val = token_str // Fallback to the original token string. 2226 } 2227 strings.write_string(&builder, val) 2228 } 2229 } 2230 macro.val = strings.to_string(builder) 2231 } 2232 2233 evaluate_nonfn_macro :: proc(state: ^Gen_State, cursor: clang.Cursor, macro: ^Macro) { 2234 builder := strings.builder_make() 2235 curly_parens := 0 2236 for index := 1; index < len(macro.tokens); index += 1 { 2237 token := macro.tokens[index] 2238 tu := clang.Cursor_getTranslationUnit(cursor) 2239 token_str := token_string(tu, token) 2240 2241 #partial switch clang.getTokenKind(token) { 2242 case .Identifier: 2243 val, offset := parse_identifier(state, cursor, macro, index) 2244 index += offset 2245 if val == "" { 2246 // macro.should_not_output = true 2247 val = token_str // Fallback to the original token string. 2248 } 2249 strings.write_string(&builder, val) 2250 case .Literal: 2251 val := parse_literal(token_str) 2252 if val == "" { 2253 macro.should_not_output = true 2254 val = token_str // Fallback to the original token string. 2255 } 2256 strings.write_string(&builder, val) 2257 case .Punctuation: 2258 switch token_str[0] { 2259 case '#': 2260 macro.should_not_output = true 2261 strings.write_string(&builder, token_str) 2262 case '{': 2263 // If we hit a curly brace, we need to count how many we have. 2264 curly_parens += 1 2265 strings.write_string(&builder, token_str) 2266 case '}': 2267 curly_parens -= 1 2268 strings.write_string(&builder, token_str) 2269 case ',': 2270 if curly_parens == 0 { 2271 // If we are not in a parenthesis, we can't output a comma. 2272 macro.should_not_output = true 2273 } 2274 strings.write_string(&builder, token_str) 2275 strings.write_rune(&builder, ' ') 2276 case: 2277 // +, -, /, *, etc. 2278 strings.write_string(&builder, token_str) 2279 } 2280 case .Keyword: 2281 tokens_str := token_str 2282 tokens_count := 0 2283 for t in macro.tokens[index + 1:] { 2284 if clang.getTokenKind(t) == .Keyword { 2285 tokens_str = fmt.tprint(tokens_str, token_string(tu, t)) 2286 tokens_count += 1 2287 } else { 2288 break 2289 } 2290 } 2291 2292 if keyword_string := translate_type_string(state, tokens_str); keyword_string != "" { 2293 strings.write_string(&builder, keyword_string) 2294 index += tokens_count 2295 } else { 2296 if keyword_string = translate_type_string(state, token_str); keyword_string != "" { 2297 strings.write_string(&builder, keyword_string) 2298 } 2299 } 2300 } 2301 } 2302 macro.val = strings.to_string(builder) 2303 if macro.val == "" { 2304 macro.should_not_output = true // Empty macro, we don't want to output it. 2305 } 2306 } 2307 2308 evaluate_macro :: proc(state: ^Gen_State, cursor: clang.Cursor, macro: ^Macro) { 2309 // I set this to true before evaluating the macro to avoid infinite recursion. 2310 // This is just a guard against a macro that calls itself. 2311 macro.has_been_evaluated = true 2312 if macro.is_function { 2313 evaluate_fn_macro(state, cursor, macro) 2314 } else { 2315 evaluate_nonfn_macro(state, cursor, macro) 2316 } 2317 } 2318 2319 if d.is_function { 2320 continue 2321 } 2322 2323 if !d.has_been_evaluated { 2324 evaluate_macro(&s, decl.cursor, &d) 2325 } 2326 2327 if d.val == "{}" || d.val == "{0}" { 2328 continue 2329 } 2330 2331 if d.comment != "" { 2332 fpln(f, d.comment) 2333 } 2334 2335 if d.should_not_output || d.original_name in s.remove_macros_lookup { 2336 // When we're happy with the parser this can change to a continue. 2337 fp(f, "// ") 2338 } 2339 2340 fpf(f, "%v%*s:: %v", d.name, max(d.whitespace_after_name, 1), "", d.val) 2341 2342 if d.side_comment != "" { 2343 fpf(f, "%*s%v", d.whitespace_before_side_comment, "", d.side_comment) 2344 } 2345 2346 fp(f, "\n") 2347 2348 if decl_idx < len(s.decls) - 1 { 2349 next := &s.decls[decl_idx + 1] 2350 2351 _, next_is_macro := next.variant.(Macro) 2352 2353 if !next_is_macro || cursor_location(next.cursor) != cursor_location(decl.cursor) + 1 { 2354 fp(f, "\n") 2355 } 2356 } 2357 } 2358 } 2359 2360 for _, index in s.macro_defines { 2361 decl := &s.decls[index] 2362 tu := clang.Cursor_getTranslationUnit(decl.cursor) 2363 clang.disposeTokens(tu, raw_data(decl.variant.(Macro).tokens), u32(len(decl.variant.(Macro).tokens))) 2364 } 2365 2366 // 2367 // Turn functions into groups that are separated by comments. If a comment 2368 // is before a function then it is used as a "group". If comments are to the 2369 // right of a function, then the group continues. 2370 // 2371 // Everything within a group shares the same padding between the name and 2372 // the `::` 2373 // 2374 2375 Function_Group :: struct { 2376 header_comment: string, 2377 functions: [dynamic]Function, 2378 } 2379 2380 groups: [dynamic]Function_Group 2381 curr_group: Function_Group 2382 2383 for &decl in s.decls { 2384 du := &decl.variant 2385 if f, f_ok := du.(Function); f_ok { 2386 if f.comment != "" { 2387 if len(curr_group.functions) > 0 { 2388 append(&groups, curr_group) 2389 } 2390 2391 curr_group = { 2392 header_comment = f.comment, 2393 } 2394 } 2395 2396 append(&curr_group.functions, f) 2397 } 2398 } 2399 2400 if len(curr_group.functions) > 0 { 2401 append(&groups, curr_group) 2402 } 2403 2404 if len(groups) > 0 { 2405 fmt.fprintfln(f, `@(default_calling_convention="c", link_prefix="%v")`, s.remove_function_prefix) 2406 fmt.fprintln(f, "foreign lib {") 2407 2408 for &g, gidx in groups { 2409 if g.header_comment != "" { 2410 if gidx != 0 { 2411 fp(f, "\n") 2412 } 2413 2414 output_comment(f, g.header_comment, "\t") 2415 } 2416 2417 longest_function_name: int 2418 2419 for &d in g.functions { 2420 if len(d.name) > longest_function_name { 2421 longest_function_name = len(d.name) 2422 } 2423 } 2424 2425 Formatted_Function :: struct { 2426 function: string, 2427 post_comment: string, 2428 attributes: []string, 2429 } 2430 2431 Formatted_Member :: struct { 2432 name: string, 2433 member: string, 2434 enum_member: ^Enum_Member, 2435 } 2436 2437 formatted_functions: [dynamic]Formatted_Function 2438 2439 for &d in g.functions { 2440 b := strings.builder_make() 2441 attributes := make([dynamic]string) 2442 2443 w :: strings.write_string 2444 2445 if d.link_name != "" { 2446 append(&attributes, fmt.tprintf("link_name=\"%s\"", d.link_name)) 2447 } 2448 2449 w(&b, d.name) 2450 2451 for _ in 0..<longest_function_name-len(d.name) { 2452 strings.write_rune(&b, ' ') 2453 } 2454 2455 w(&b, " :: proc(") 2456 2457 for &p, i in d.parameters { 2458 n := vet_name(cursor_spelling(p)) 2459 2460 type: string 2461 type_override_key := fmt.tprintf("%v.%v", d.original_name, n) 2462 2463 if type_override, type_override_ok := s.procedure_type_overrides[type_override_key]; type_override_ok { 2464 switch type_override { 2465 case "#by_ptr": 2466 w(&b, "#by_ptr ") 2467 type, _ = parse_type(&s, clang.getCursorType(p), {.By_Pointer}) 2468 case "[^]": 2469 by_ptr := false 2470 type, by_ptr = parse_type(&s, clang.getCursorType(p), {.Pointer_To_Array}) 2471 if by_ptr { 2472 w(&b, "#by_ptr ") 2473 } 2474 case: 2475 type = type_override 2476 } 2477 } else { 2478 by_ptr := false 2479 type, by_ptr = parse_type(&s, clang.getCursorType(p), nil) 2480 if by_ptr { 2481 w(&b, "#by_ptr ") 2482 } 2483 } 2484 2485 if len(n) != 0 { 2486 w(&b, n) 2487 w(&b, ": ") 2488 } else { 2489 w(&b, "_: ") 2490 } 2491 2492 w(&b, type) 2493 2494 if i != len(d.parameters) - 1 { 2495 w(&b, ", ") 2496 } else { 2497 if d.variadic { 2498 w(&b,", #c_vararg _: ..any") 2499 } 2500 } 2501 } 2502 2503 w(&b, ")") 2504 2505 return_type := clang.getResultType(clang.getCursorType(d.cursor)) 2506 if return_type.kind != .Void { 2507 w(&b, " -> ") 2508 2509 return_type_string: string 2510 2511 if override, override_ok := s.procedure_type_overrides[d.original_name]; override_ok { 2512 switch override { 2513 case "[^]": 2514 return_type_string, _ = parse_type(&s, return_type, {.Pointer_To_Array}) 2515 case: 2516 return_type_string = override 2517 } 2518 } else { 2519 return_type_string, _ = parse_type(&s, return_type, nil) 2520 } 2521 2522 w(&b, return_type_string) 2523 } 2524 2525 w(&b, " ---") 2526 2527 append(&formatted_functions, Formatted_Function { 2528 function = strings.to_string(b), 2529 post_comment = d.post_comment, 2530 attributes = attributes[:], 2531 }) 2532 } 2533 2534 longest_formatted_function: int 2535 2536 for &ff in formatted_functions { 2537 if len(ff.function) < 90 && len(ff.function) > longest_formatted_function { 2538 longest_formatted_function = len(ff.function) 2539 } 2540 } 2541 2542 for &ff in formatted_functions { 2543 if len(ff.attributes) > 0 { 2544 fp(f, "\t") 2545 fp(f, fmt.tprintf("@(%s)", strings.join(ff.attributes[:], ", "))) 2546 fp(f, "\n") 2547 } 2548 fp(f, "\t") 2549 fp(f, ff.function) 2550 2551 if ff.post_comment != "" { 2552 for _ in 0..<(longest_formatted_function-len(ff.function)) { 2553 fp(f, ' ') 2554 } 2555 2556 fp(f, ' ') 2557 fp(f, ff.post_comment) 2558 } 2559 2560 fp(f, "\n") 2561 } 2562 } 2563 2564 fmt.fprintln(f, "}") 2565 } 2566 } 2567 2568 main :: proc() { 2569 permanent_arena: vmem.Arena 2570 permanent_allocator := vmem.arena_allocator(&permanent_arena) 2571 context.allocator = permanent_allocator 2572 context.temp_allocator = permanent_allocator 2573 2574 ensure(len(os.args) == 2, "Usage: bindgen directory") 2575 input_arg := os.args[1] 2576 2577 config_filename := "bindgen.sjson" 2578 config_dir: string 2579 if strings.has_suffix(input_arg, ".sjson") && os.is_file(input_arg) { 2580 config_filename = filepath.base(input_arg) 2581 config_dir = filepath.dir(input_arg, context.temp_allocator) 2582 } else if os.is_dir(input_arg) { 2583 config_dir = input_arg 2584 } else { 2585 fmt.panicf("%v is not a directory nor a valid config file", input_arg) 2586 } 2587 2588 // Config file is optional 2589 config: Config 2590 2591 default_output_folder := "output" 2592 default_package_name := "pkg" 2593 2594 if input_dir, input_dir_err := os2.open(input_arg); input_dir_err == nil { 2595 if stat, stat_err := input_dir.fstat(input_dir, context.allocator); stat_err == nil { 2596 default_output_folder = stat.name 2597 default_package_name = stat.name 2598 } 2599 } 2600 2601 if err := os.set_current_directory(config_dir); err != nil { 2602 fmt.panicf("failed to set current working directory: %v", err) 2603 } 2604 2605 if os.is_file(config_filename) { 2606 if config_data, config_data_ok := os.read_entire_file(config_filename); config_data_ok { 2607 config_err := json.unmarshal(config_data, &config, .SJSON) 2608 fmt.ensuref( 2609 config_err == nil, 2610 "Failed parsing config %v: %v", 2611 config_filename, 2612 config_err, 2613 ) 2614 } else { 2615 fmt.ensuref(config_data_ok, "Failed parsing config %v", config_filename) 2616 } 2617 } else { 2618 config.inputs = {"."} 2619 } 2620 2621 if config.output_folder == "" { 2622 config.output_folder = default_output_folder 2623 } 2624 2625 if config.package_name == "" { 2626 config.package_name = default_package_name 2627 } 2628 2629 if config.remove_prefix != "" { 2630 panic( 2631 "Error in bindgen.sjson: remove_prefix has been split into remove_function_prefix and remove_type_prefix", 2632 ) 2633 } 2634 2635 if len(config.rename_types) > 0 { 2636 panic("Error in bindgen.sjson: rename_types has been renamed to rename") 2637 } 2638 2639 input_files: [dynamic]string 2640 2641 for i in config.inputs { 2642 if os.is_dir(i) { 2643 input_folder, input_folder_err := os2.open(i) 2644 fmt.ensuref(input_folder_err == nil, "Failed opening folder %v: %v", i, input_folder_err) 2645 iter := os2.read_directory_iterator_create(input_folder) 2646 2647 for f in os2.read_directory_iterator(&iter) { 2648 if f.type != .Regular || slice.contains(config.ignore_inputs, f.name) { 2649 continue 2650 } 2651 2652 append(&input_files, fmt.tprintf("%v/%v", i, f.name)) 2653 } 2654 2655 os2.close(input_folder) 2656 } else if os.is_file(i) { 2657 append(&input_files, i) 2658 } else { 2659 fmt.eprintfln("%v is neither directory or .h file", i) 2660 } 2661 } 2662 2663 if config.output_folder != "" && !os2.exists(config.output_folder) { 2664 make_dir_err := os2.make_directory_all(config.output_folder) 2665 fmt.ensuref(make_dir_err == nil, "Failed creating output directory %v: %v", config.output_folder, make_dir_err) 2666 } 2667 2668 for i in input_files { 2669 ext := filepath.ext(i) 2670 switch ext { 2671 case ".h": 2672 gen(i, config) 2673 case ".odin", ".lib", ".a", ".dll", ".dylib": 2674 // Bring along odin and library files 2675 name := filepath.base(i) 2676 os2.copy_file(fmt.tprintf("%v/%v", config.output_folder, name), i) 2677 } 2678 } 2679 }