odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

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(&current_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(&current_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(&current_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(&current_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(&current_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(&current_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 }