output.odin (13415B)
1 // Never import clang within this file. Resolve any clang-related things in one of the 2 // translate_X.odin files. 3 #+private file 4 package bindgen2 5 6 import "core:os" 7 import "core:fmt" 8 import "core:strings" 9 import "core:log" 10 11 Output_Input :: Translate_Process_Result 12 13 // Takes the result of `translate_process` and outputs bindings into `filename`. 14 @(private="package") 15 output :: proc(types: Type_List, decls: Decl_List, o: Output_Input, filename: string, footer: string, package_name: string) { 16 ensure(filename != "") 17 ensure(package_name != "") 18 builder := strings.builder_make() 19 sb := &builder 20 21 if o.top_comment != "" { 22 pln(sb, o.top_comment) 23 } 24 25 pfln(sb, "package %v\n", package_name) 26 27 if len(o.extra_imports) > 0 { 28 for ei in o.extra_imports { 29 pfln(sb, "import \"%s\"", ei) 30 } 31 p(sb, "\n") 32 } 33 34 if o.top_code != "" { 35 pln(sb, o.top_code) 36 p(sb, "\n") 37 } 38 39 Output_Group_Kind :: enum { 40 Default, 41 Macro, 42 Proc, 43 } 44 45 Output_Group_Decl :: struct { 46 decl: Decl, 47 rhs: string, 48 } 49 50 Output_Group :: struct { 51 decls: [dynamic]Output_Group_Decl, 52 kind: Output_Group_Kind, 53 start_foreign_block: bool, 54 end_foreign_block: bool, 55 proc_calling_convention: Calling_Convention, 56 } 57 58 current_group: Output_Group 59 60 for &d in decls { 61 if d.invalid { 62 continue 63 } 64 65 kind: Output_Group_Kind 66 67 proc_type, is_proc := resolve_type_definition(types, d.def, Type_Procedure) 68 69 if is_proc { 70 kind = .Proc 71 } else if d.from_macro { 72 kind = .Macro 73 } 74 75 rhs_builder := strings.builder_make() 76 77 if kind == .Proc { 78 output_procedure_signature(types, proc_type, &rhs_builder, 1, false) 79 } else { 80 output_definition(types, d.def, &rhs_builder, 0) 81 } 82 83 rhs := strings.to_string(rhs_builder) 84 85 if rhs == string(d.name) { 86 continue 87 } 88 89 multiline := strings.contains_rune(rhs, '\n') 90 91 if kind != current_group.kind || 92 (kind == .Proc && current_group.proc_calling_convention != proc_type.calling_convention) || 93 d.comment_before != "" || 94 multiline { 95 current_group.end_foreign_block = current_group.kind == .Proc && (kind != .Proc || 96 proc_type.calling_convention != current_group.proc_calling_convention) 97 output_group(current_group, o, sb) 98 clear(¤t_group.decls) 99 prev_kind := current_group.kind 100 prev_proc_calling_conventation := current_group.proc_calling_convention 101 current_group.kind = kind 102 current_group.start_foreign_block = kind == .Proc && (prev_kind != .Proc || 103 proc_type.calling_convention != prev_proc_calling_conventation) 104 current_group.end_foreign_block = kind == .Proc 105 106 current_group.proc_calling_convention = kind == .Proc ? proc_type.calling_convention : {} 107 } 108 109 append(¤t_group.decls, Output_Group_Decl { 110 decl = d, 111 rhs = rhs, 112 }) 113 114 if multiline { 115 output_group(current_group, o, sb) 116 clear(¤t_group.decls) 117 } 118 } 119 120 output_group(current_group, o, sb) 121 122 output_group :: proc(g: Output_Group, o: Output_Input, sb: ^strings.Builder) { 123 if len(g.decls) == 0 { 124 return 125 } 126 127 k := g.kind 128 129 if g.start_foreign_block { 130 pf(sb, "@(default_calling_convention=\"%s\"", calling_convention_string(g.proc_calling_convention)) 131 132 if o.link_prefix != "" { 133 pf(sb, `, link_prefix="%v"`, o.link_prefix) 134 } 135 136 pln(sb, ")") 137 138 pln(sb, "foreign lib {") 139 } 140 141 longest_name: int 142 for &od in g.decls { 143 d := od.decl 144 145 if len(d.name) > longest_name { 146 longest_name = len(d.name) 147 } 148 } 149 150 group_member_texts := make([]string, len(g.decls)) 151 assert(len(group_member_texts) == len(g.decls)) 152 longest_member_that_has_comment_on_right: int 153 154 for &od, i in g.decls { 155 d := od.decl 156 rhs := od.rhs 157 158 tb := strings.builder_make() 159 160 pf(&tb, "%v%*s:: %v", d.name, max(longest_name-len(d.name) + 1, d.explicit_whitespace_after_name), "", rhs) 161 162 if k == .Proc { 163 pf(&tb, " ---") 164 } 165 166 text := strings.to_string(tb) 167 group_member_texts[i] = text 168 169 if d.side_comment != "" && len(text) < 90 && len(text) > longest_member_that_has_comment_on_right { 170 longest_member_that_has_comment_on_right = len(text) 171 } 172 } 173 174 for &od, i in g.decls { 175 d := od.decl 176 177 indent := k == .Proc ? 1 : 0 178 179 if d.comment_before != "" { 180 cb := d.comment_before 181 for l in strings.split_lines_iterator(&cb) { 182 output_indent(sb, indent) 183 pln(sb, strings.trim_space(l)) 184 } 185 } 186 187 output_indent(sb, indent) 188 text := group_member_texts[i] 189 p(sb, text) 190 191 if d.side_comment != "" { 192 pf(sb, "%*s%v", max(max(longest_member_that_has_comment_on_right-len(text) + 1, 1), d.explicit_whitespace_before_side_comment), "", d.side_comment) 193 } 194 195 p(sb, "\n") 196 } 197 198 if g.end_foreign_block { 199 pln(sb, "}") 200 } 201 202 pln(sb, "") 203 } 204 205 p(sb, footer) 206 207 write_err := os.write_entire_file(filename, transmute([]u8)(strings.to_string(builder))) 208 fmt.ensuref(write_err == true, "Failed writing %v", filename) 209 } 210 211 output_indent :: proc(b: ^strings.Builder, indent: int) { 212 for _ in 0..<indent { 213 pf(b, "\t") 214 } 215 } 216 217 p :: fmt.sbprint 218 pfln :: fmt.sbprintfln 219 pf :: fmt.sbprintf 220 pln :: fmt.sbprintln 221 222 output_struct_definition :: proc(types: ^[dynamic]Type, idx: Type_Index, b: ^strings.Builder, indent: int) { 223 t := types[idx] 224 t_struct := &t.(Type_Struct) 225 226 if len(t_struct.fields) == 0 { 227 p(b, "struct {}") 228 return 229 } 230 231 Struct_Field :: struct { 232 type: Type_Struct_Field, 233 name: string, 234 rhs: string, 235 } 236 237 Struct_Fields_Group :: struct { 238 header: string, 239 line_break_before: bool, 240 fields: [dynamic]Struct_Field, 241 } 242 243 244 p(b, "struct") 245 246 if t_struct.raw_union { 247 p(b, " #raw_union") 248 } 249 250 pln(b, " {") 251 252 current_group: Struct_Fields_Group 253 first_field := true 254 255 for &f in t_struct.fields { 256 if len(f.names) == 0 && !f.anonymous { 257 log.error("Struct field has no name and is not anonymous") 258 continue 259 } 260 261 // name builder 262 nb := strings.builder_make() 263 264 if f.anonymous { 265 p(&nb, "using _: ") 266 } else { 267 if f.is_using { 268 p(&nb, "using ") 269 } 270 271 for fn, nidx in f.names { 272 if nidx != 0 { 273 p(&nb, ", ") 274 } 275 p(&nb, fn) 276 } 277 278 p(&nb, ": ") 279 } 280 281 name := strings.to_string(nb) 282 283 rhs_builder := strings.builder_make() 284 285 if f.type_overrride != "" { 286 p(&rhs_builder, f.type_overrride) 287 } else { 288 switch r in f.type { 289 case Type_Name, Fixed_Value, Macro_Name: 290 p(&rhs_builder, r) 291 case Type_Index: 292 parse_type_build(types, r, &rhs_builder, indent + 1) 293 } 294 } 295 296 if f.tag != "" { 297 pf(&rhs_builder, " `%s`", f.tag) 298 } 299 300 pf(&rhs_builder, ",") 301 302 rhs := strings.to_string(rhs_builder) 303 multiline := strings.contains_rune(rhs, '\n') 304 305 if f.comment_before != "" || multiline { 306 output_field_group(current_group, b, indent + 1) 307 clear(¤t_group.fields) 308 current_group.header = f.comment_before 309 current_group.line_break_before = !first_field 310 } 311 312 first_field = false 313 314 append(¤t_group.fields, Struct_Field { 315 type = f, 316 name = name, 317 rhs = rhs, 318 }) 319 320 if multiline { 321 output_field_group(current_group, b, indent + 1) 322 clear(¤t_group.fields) 323 current_group.header = f.comment_before 324 current_group.line_break_before = true 325 } 326 } 327 328 output_field_group(current_group, b, indent + 1) 329 330 output_field_group :: proc(g: Struct_Fields_Group, b: ^strings.Builder, indent: int) { 331 if len(g.fields) == 0 { 332 return 333 } 334 335 if g.line_break_before { 336 p(b, "\n") 337 } 338 339 if g.header != "" { 340 h := g.header 341 342 for l in strings.split_lines_iterator(&h) { 343 output_indent(b, indent) 344 pln(b, strings.trim_space(l)) 345 } 346 } 347 348 longest_name: int 349 for &f in g.fields { 350 if len(f.name) > longest_name { 351 longest_name = len(f.name) 352 } 353 } 354 355 longest_field_that_has_comment_on_right: int 356 field_texts := make([]string, len(g.fields)) 357 358 for f, fi in g.fields { 359 tb := strings.builder_make() 360 p(&tb, f.name) 361 362 after_name_padding := longest_name-len(f.name) 363 for _ in 0..<after_name_padding { 364 strings.write_rune(&tb, ' ') 365 } 366 367 p(&tb, f.rhs) 368 369 text := strings.to_string(tb) 370 field_texts[fi] = text 371 372 if f.type.comment_on_right != "" && len(text) < 120 && len(text) > longest_field_that_has_comment_on_right { 373 longest_field_that_has_comment_on_right = len(text) 374 } 375 } 376 377 for f, fi in g.fields { 378 output_indent(b, indent) 379 text := field_texts[fi] 380 p(b, text) 381 382 if f.type.comment_on_right != "" { 383 pf(b, "%*s%v", max(longest_field_that_has_comment_on_right-len(text) + 1, 1), "", f.type.comment_on_right) 384 } 385 386 p(b, "\n") 387 } 388 } 389 390 output_indent(b, indent) 391 p(b, "}") 392 } 393 394 output_enum_definition :: proc(types: ^[dynamic]Type, idx: Type_Index, b: ^strings.Builder, indent: int) { 395 t := types[idx] 396 t_enum := &t.(Type_Enum) 397 398 pfln(b, "enum %v {{", t_enum.storage_type) 399 400 longest_name: int 401 for &m in t_enum.members { 402 if len(m.name) > longest_name { 403 longest_name = len(m.name) 404 } 405 } 406 407 member_texts := make([]string, len(t_enum.members)) 408 longest_member_that_has_comment_on_right: int 409 410 for &m, mi in t_enum.members { 411 fb := strings.builder_make() 412 413 pf(&fb, "%s", m.name) 414 415 after_name_padding := longest_name-len(m.name) 416 for _ in 0..<after_name_padding { 417 strings.write_rune(&fb, ' ') 418 } 419 420 pf(&fb, " = %v,", m.value) 421 422 text := strings.to_string(fb) 423 member_texts[mi] = text 424 425 if m.comment_on_right != "" && len(text) > longest_member_that_has_comment_on_right { 426 longest_member_that_has_comment_on_right = len(text) 427 } 428 } 429 430 for &m, mi in t_enum.members { 431 if m.comment_before != "" { 432 cb := m.comment_before 433 434 if mi > 0 { 435 pln(b, "") 436 } 437 438 for l in strings.split_lines_iterator(&cb) { 439 output_indent(b, indent + 1) 440 pln(b, strings.trim_space(l)) 441 } 442 } 443 output_indent(b, indent + 1) 444 445 text := member_texts[mi] 446 p(b, text) 447 448 if m.comment_on_right != "" { 449 for _ in 0..<longest_member_that_has_comment_on_right-len(text) { 450 p(b, ' ') 451 } 452 453 p(b, " ") 454 p(b, m.comment_on_right) 455 } 456 457 p(b, "\n") 458 } 459 460 output_indent(b, indent) 461 p(b, "}") 462 } 463 464 // TODO: Clangs seems to always output C calling convention, investigate why. 465 calling_convention_string :: proc(calling_convention: Calling_Convention) -> string { 466 switch calling_convention { 467 case .C: 468 return "c" 469 case .Std_Call: 470 return "stdcall" 471 case .Fast_Call: 472 return "fastcall" 473 } 474 475 return "c" 476 } 477 478 output_definition :: proc(types: ^[dynamic]Type, def: Definition, b: ^strings.Builder, indent: int) { 479 switch d in def { 480 case Type_Name, Fixed_Value, Macro_Name: 481 p(b, d) 482 case Type_Index: 483 parse_type_build(types, d, b, indent) 484 } 485 } 486 487 output_procedure_signature :: proc(types: ^[dynamic]Type, tp: Type_Procedure, b: ^strings.Builder, indent: int, explicit_calling_convention: bool) { 488 pf(b, "proc") 489 490 if explicit_calling_convention { 491 pf(b, " \"%s\" ", calling_convention_string(tp.calling_convention)) 492 } 493 494 pf(b, "(") 495 496 all_params_are_unnamed := true 497 498 for param in tp.parameters { 499 if param.name != "" { 500 all_params_are_unnamed = false 501 break 502 } 503 } 504 505 for param, idx in tp.parameters { 506 if idx != 0 { 507 p(b, ", ") 508 } 509 510 _, by_ptr := resolve_type_definition(types, param.type, Type_Pointer_By_Ptr) 511 512 513 if by_ptr { 514 p(b, "#by_ptr ") 515 } 516 517 if param.any_int { 518 p(b, "#any_int ") 519 } 520 521 if param.name == "" { 522 // We can only write a parameter list without any names if all of them have no name. 523 if !all_params_are_unnamed { 524 p(b, "_: ") 525 } 526 527 output_definition(types, param.type, b, indent) 528 } else { 529 pf(b, "%s: ", param.name) 530 output_definition(types, param.type, b, indent) 531 } 532 533 if param.default != "" { 534 pf(b, " = %v", param.default) 535 } 536 } 537 538 if tp.is_variadic { 539 if len(tp.parameters) > 0 { 540 p(b, ", ") 541 } 542 543 if !all_params_are_unnamed { 544 p(b, "#c_vararg _: ..any") 545 } else { 546 p(b, "#c_vararg ..any") 547 } 548 } 549 550 pf(b, ")") 551 552 if tp.result_type != nil { 553 p(b, " -> ") 554 output_definition(types, tp.result_type, b, indent) 555 } 556 } 557 558 parse_type_build :: proc(types: ^[dynamic]Type, idx: Type_Index, b: ^strings.Builder, indent: int) { 559 t := types[idx] 560 switch &tv in t { 561 case Type_Unknown: 562 log.warn("Is this a bug?") 563 564 case Type_Pointer: 565 p(b, "^") 566 output_definition(types, tv.pointed_to_type, b, indent) 567 568 case Type_Multipointer: 569 p(b, "[^]") 570 output_definition(types, tv.pointed_to_type, b, indent) 571 572 case Type_Pointer_By_Ptr: 573 output_definition(types, tv.pointed_to_type, b, indent) 574 575 case Type_CString: 576 p(b, "cstring") 577 578 case Type_Raw_Pointer: 579 p(b, "rawptr") 580 581 case Type_Struct: 582 output_struct_definition(types, idx, b, indent) 583 584 case Type_Alias: 585 output_definition(types, tv.aliased_type, b, indent) 586 587 case Type_Enum: 588 output_enum_definition(types, idx, b, indent) 589 590 case Type_Procedure: 591 output_procedure_signature(types, tv, b, indent, true) 592 593 case Type_Fixed_Array: 594 pf(b, "[%i]", tv.size) 595 output_definition(types, tv.element_type, b, indent) 596 597 case Type_Bit_Set: 598 enum_name, enum_name_ok := tv.enum_decl_name.(Type_Name) 599 600 if !enum_name_ok { 601 log.error("Invalid type used with bit set") 602 return 603 } 604 605 pf(b, "bit_set[%v; i32]", enum_name) 606 607 case Type_Bit_Set_Constant: 608 bit_set_type, bit_set_type_ok := resolve_type_definition(types, tv.bit_set_type, Type_Bit_Set) 609 610 if !bit_set_type_ok { 611 return 612 } 613 614 pf(b, `%v {{`, tv.bit_set_type_name) 615 616 enum_type, enum_type_ok := resolve_type_definition(types, bit_set_type.enum_type, Type_Enum) 617 618 if enum_type_ok { 619 first_printed := false 620 for &m in enum_type.members { 621 if (1 << uint(m.value)) & tv.value != 0 { 622 if first_printed == true { 623 p(b, ", ") 624 } else { 625 first_printed = true 626 } 627 628 pf(b, ".%v", m.name) 629 } 630 } 631 } 632 633 p(b, "}") 634 } 635 }