odin-blend2d

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

translate_process.odin (17393B)


      1 #+private file
      2 package bindgen2
      3 
      4 import "core:strings"
      5 import "core:slice"
      6 import "core:log"
      7 import "core:math/bits"
      8 import "core:unicode"
      9 import "core:unicode/utf8"
     10 import "core:fmt"
     11 import "core:os"
     12 
     13 @(private="package")
     14 Translate_Process_Result :: struct {
     15 	// Comment at top of file
     16 	top_comment: string,
     17 	top_code: string,
     18 	link_prefix: string,
     19 
     20 	extra_imports: []string,
     21 }
     22 
     23 @(private="package")
     24 translate_process :: proc(tcr: Translate_Collect_Result, config: Config, types: Type_List, decls: Decl_List) -> Translate_Process_Result {
     25 	forward_declare_resolved: map[string]bool
     26 
     27 	to_remove: map[string]struct{}
     28 
     29 	for r in config.remove {
     30 		to_remove[r] = {}
     31 	}
     32 
     33 	for &d in decls {
     34 		if d.name in to_remove {
     35 			d.invalid = true
     36 			continue
     37 		}
     38 
     39 		if d.is_forward_declare {
     40 			if d.name in forward_declare_resolved {
     41 				d.invalid = true
     42 				continue
     43 			}
     44 
     45 			forward_declare_resolved[d.name] = false
     46 		}
     47 	}
     48 
     49 	for &d in decls {
     50 		// A bit of a hack due to aliases being disregarded later. Perhaps we can change that?
     51 		if _, is_alias := resolve_type_definition(types, d.def, Type_Alias); is_alias {
     52 			continue
     53 		}
     54 
     55 		if !d.is_forward_declare && d.name in forward_declare_resolved {
     56 			forward_declare_resolved[d.name] = true
     57 		}
     58 	}
     59 
     60 	// Replace types
     61 	for &d in decls {
     62 		override: bool
     63 		override_definition_text: string
     64 
     65 		if type_override, has_override := config.type_overrides[d.name]; has_override {
     66 			override = true
     67 			override_definition_text = type_override
     68 		}	
     69 
     70 		// Don't override if this is type is an alias that has the same name as the aliased name.
     71 		// Doing that override will just make this alias not get ignored, as it is no longer just
     72 		// doing Some_Type :: Some_Type, but rather Some_New_Type :: Some_Type.
     73 		if alias, is_alias := resolve_type_definition(types, d.def, Type_Alias); is_alias {
     74 			named_alias, alias_is_named := alias.aliased_type.(Type_Name)
     75 			if alias_is_named && d.name == string(named_alias) {
     76 				override = false
     77 			}
     78 		}
     79 
     80 		if override {
     81 			d.def = Fixed_Value(override_definition_text)
     82 		}
     83 	}
     84 
     85 	remove_enum_members: map[string]struct{}
     86 	remove_enum_suffixes: [dynamic]string
     87 	remove_enum_prefixes: [dynamic]string
     88 
     89 	for e in config.remove_enum_members {
     90 		if strings.has_prefix(e, "*") {
     91 			append(&remove_enum_suffixes, e[1:])
     92 		} else if strings.has_suffix(e, "*") {
     93 			append(&remove_enum_prefixes, e[:len(e) - 1])
     94 		} else {
     95 			remove_enum_members[e] = {}
     96 		}
     97 	}
     98 
     99 	// Declared here to reuse.
    100 	bit_set_make_constant: map[string]int
    101 
    102 	for &d, i in decls {
    103 		if i == 0 {
    104 			d.invalid = true
    105 			continue
    106 		}
    107 
    108 		if d.is_forward_declare && forward_declare_resolved[d.name] {
    109 			d.invalid = true
    110 			continue
    111 		}
    112 
    113 		if d.name == "" {
    114 			d.invalid = true
    115 			log.errorf("Declaration has no name: %v", d.name)
    116 			continue
    117 		}
    118 
    119 		if d.def == nil {
    120 			d.invalid = true
    121 			log.errorf("Type used in declaration %v is zero", d.name)
    122 			continue
    123 		}
    124 
    125 		if _, is_fixed_value := d.def.(Fixed_Value); is_fixed_value {
    126 			continue
    127 		}
    128 
    129 		if _, is_type_name := d.def.(Type_Name); is_type_name {
    130 			continue
    131 		}
    132 
    133 		if _, is_macro_name := d.def.(Macro_Name); is_macro_name {
    134 			continue
    135 		}
    136 
    137 		type := &types[d.def.(Type_Index)]
    138 
    139 		#partial switch &v in type {
    140 		case Type_Enum:
    141 			{
    142 				new_members: [dynamic]Type_Enum_Member
    143 				
    144 				member_loop: for m in v.members {
    145 					if m.name in remove_enum_members {
    146 						continue
    147 					}
    148 
    149 					for p in remove_enum_suffixes {
    150 						if strings.has_suffix(m.name, p) {
    151 							continue member_loop
    152 						}
    153 					}
    154 
    155 					for p in remove_enum_prefixes {
    156 						if strings.has_prefix(m.name, p) {
    157 							continue member_loop
    158 						}
    159 					}
    160 
    161 					append(&new_members, m)
    162 				}
    163 
    164 				v.members = new_members[:]
    165 			}
    166 
    167 			bit_set_enum_name, bit_setify := config.bit_setify[d.name]
    168 
    169 			if bit_setify {
    170 				clear(&bit_set_make_constant)
    171 
    172 				bs_idx := add_type(types, Type_Bit_Set {
    173 					enum_type = d.def.(Type_Index),
    174 					enum_decl_name = Type_Name(bit_set_enum_name),
    175 				})
    176 
    177 				new_members: [dynamic]Type_Enum_Member
    178 
    179 				// log2-ify value so `2` becomes `1`, `4` becomes `2` etc.
    180 				for m in v.members {
    181 					if m.value == 0 {
    182 						continue
    183 					}
    184 
    185 					if bits.count_ones(m.value) != 1 {
    186 						// Not a power of two, so not part of a bit_set. Save it for later for making
    187 						// it into a constant.
    188 						bs_constant_idx := add_type(types, Type_Bit_Set_Constant {
    189 							bit_set_type = bs_idx,
    190 							bit_set_type_name = Type_Name(d.name),
    191 							value = m.value,
    192 						})
    193 
    194 						all_constant := strings.to_screaming_snake_case(strings.trim_prefix(strings.to_lower(m.name), strings.to_lower(config.remove_type_prefix)))
    195 
    196 						add_decl(decls, {
    197 							original_line = d.original_line + 2,
    198 							name = all_constant,
    199 							def = bs_constant_idx,
    200 							explicitly_created = true,
    201 						})
    202 
    203 						continue
    204 					}
    205 
    206 					append(&new_members, Type_Enum_Member {
    207 						name = m.name,
    208 						value = int(bits.log2(uint(m.value))),
    209 						comment_before = m.comment_before,
    210 						comment_on_right = m.comment_on_right,
    211 					})
    212 				}
    213 
    214 				v.members = new_members[:]
    215 
    216 				enum_decl := d
    217 				enum_decl.comment_before = ""
    218 				enum_decl.side_comment = ""
    219 				d.def = bs_idx
    220 				d.original_line += 1
    221 
    222 				enum_decl.name = bit_set_enum_name
    223 
    224 				add_decl(decls, enum_decl)
    225 			}
    226 
    227 		case Type_Struct:
    228 			for &f in v.fields {
    229 				if len(f.names) != 1 {
    230 					continue
    231 				}
    232 
    233 				field_key := fmt.tprintf("%s.%s", d.name, f.names[0])
    234 				if override, has_override := config.struct_field_overrides[field_key]; has_override {
    235 					if override == "[^]" {
    236 						if ptr_type, is_ptr_type := resolve_type_definition(types, f.type, Type_Pointer); is_ptr_type {
    237 							f.type = add_type(types, Type_Multipointer {
    238 								pointed_to_type = ptr_type.pointed_to_type,
    239 							})
    240 						}	
    241 					} else if override == "using" {
    242 						f.is_using = true
    243 					} else {
    244 						f.type = Fixed_Value(override)
    245 					}
    246 				}
    247 
    248 				if proc_type := resolve_type_definition_ptr(types, f.type, Type_Procedure); proc_type != nil {
    249 					for &param in proc_type.parameters {
    250 						key := fmt.tprintf("%s.%s.%s", d.name, f.names[0], param.name)
    251 						
    252 						if default, has_default := config.procedure_parameter_defaults[key]; has_default {
    253 							param.default = default
    254 						}
    255 
    256 						if override, has_override := config.procedure_type_overrides[key]; has_override {
    257 							override_procedure_parameter(&param, types, override)
    258 						}
    259 					}
    260 				}
    261 
    262 				if tag, has_tag := config.struct_field_tags[field_key]; has_tag {
    263 					f.tag = tag
    264 				}
    265 			}
    266 		case Type_Procedure:
    267 			for &p in v.parameters {
    268 				param_key := fmt.tprintf("%s.%s", d.name, p.name)
    269 				if override, has_override := config.procedure_type_overrides[param_key]; has_override {
    270 					override_procedure_parameter(&p, types, override)
    271 				}
    272 
    273 				if default, has_default := config.procedure_parameter_defaults[param_key]; has_default {
    274 					p.default = default
    275 				}
    276 			}
    277 
    278 			return_override_key := d.name
    279 
    280 			if override, has_override := config.procedure_type_overrides[return_override_key]; has_override {
    281 				if override == "[^]" {
    282 					if ptr_type, is_ptr_type := resolve_type_definition(types, v.result_type, Type_Pointer); is_ptr_type {
    283 						v.result_type = add_type(types, Type_Multipointer {
    284 							pointed_to_type = ptr_type.pointed_to_type,
    285 						})	
    286 					}	
    287 				} else {
    288 					v.result_type = Fixed_Value(override)
    289 				}
    290 			}
    291 		}
    292 	}
    293 
    294 	top_code: string
    295 
    296 	if config.imports_file != "" {
    297 		if imports, imports_ok := os.read_entire_file(config.imports_file); imports_ok {
    298 			top_code = string(imports)
    299 		}
    300 	} else if config.import_lib != "" {
    301 		top_code = fmt.tprintf("foreign import lib \"%v\"\n_ :: lib", config.import_lib)
    302 	}
    303 
    304 	if config.procedures_at_end {
    305 		context.user_ptr = types
    306 		slice.sort_by(decls[:], proc(i, j: Decl) -> bool {
    307 			types := (Type_List)(context.user_ptr)
    308 			_, i_is_proc := resolve_type_definition(types, i.def, Type_Procedure)
    309 			_, j_is_proc := resolve_type_definition(types, j.def, Type_Procedure)
    310 
    311 			if i_is_proc != j_is_proc {
    312 				return j_is_proc
    313 			}
    314 
    315 			return i.original_line < j.original_line
    316 		})
    317 	} else {
    318 		slice.sort_by(decls[:], proc(i, j: Decl) -> bool {
    319 			return i.original_line < j.original_line
    320 		})
    321 	}
    322 
    323 	// Run this last! Otherwise mapping that assumes things has their original names may fail.
    324 	resolve_final_names(types, decls, config)
    325 
    326 	return {
    327 		top_comment = extract_top_comment(tcr.source),
    328 		top_code = top_code,
    329 		link_prefix = config.remove_function_prefix,
    330 		extra_imports = tcr.extra_imports,
    331 	}
    332 }
    333 
    334 strip_enum_member_prefixes :: proc(e: ^Type_Enum) {
    335 	overlap_length := 0
    336 
    337 	if len(e.members) > 1 {
    338 		overlap_length_source := e.members[0].name
    339 		overlap_length = len(overlap_length_source)
    340 
    341 		for idx in 1..<len(e.members) {
    342 			mn := e.members[idx].name
    343 			length := strings.prefix_length(mn, overlap_length_source)
    344 
    345 			if length < overlap_length {
    346 				overlap_length = length
    347 				overlap_length_source = mn
    348 			}
    349 		}
    350 
    351 		if overlap_length > 0 {
    352 			back_off := false
    353 			underscore_in_member := false
    354 
    355 			for &m in e.members {
    356 				if strings.contains(m.name[overlap_length:], "_") {
    357 					underscore_in_member = true
    358 					break
    359 				}
    360 			}
    361 
    362 			if !underscore_in_member && strings.count(overlap_length_source, "_") > 1 {
    363 				back_off = true
    364 			}
    365 
    366 			for &m in e.members {
    367 				if overlap_length == len(m.name) {
    368 					back_off = true
    369 					break
    370 				}
    371 			}
    372 
    373 			// We stripped too much! Back off to nearest underscore or camelCase change
    374 			if back_off {
    375 				found_underscore := false
    376 				#reverse for c, i in overlap_length_source {
    377 					if c == '_' {
    378 						overlap_length = i + 1
    379 						found_underscore = true
    380 						break
    381 					}
    382 				}
    383 
    384 				// No underscore found, try camelCase
    385 				if !found_underscore {
    386 					last_letter: rune
    387 
    388 					#reverse for c in overlap_length_source {
    389 						if unicode.is_letter(c) {
    390 							last_letter = c
    391 							break
    392 						}
    393 					}
    394 
    395 					#reverse for c, i in overlap_length_source {
    396 						if unicode.is_letter(c) && unicode.is_upper(c) != unicode.is_upper(last_letter) {
    397 							overlap_length = i + 1
    398 							break
    399 						}
    400 					}
    401 				}
    402 			}
    403 		}
    404 	}
    405 
    406 	for &m in e.members {
    407 		name_without_overlap := m.name[overlap_length:]
    408 
    409 		if len(name_without_overlap) != 0 {
    410 			m.name = name_without_overlap
    411 
    412 			if is_number(m.name[0]) {
    413 				m.name = fmt.tprintf("_%v", m.name)
    414 			}
    415 		}
    416 	}
    417 }
    418 
    419 // Give all types and declarations their final names. Based on config, but also strips enum prefixes etc.
    420 resolve_final_names :: proc(types: Type_List, decls: Decl_List, config: Config) {
    421 	for &t in types {
    422 		switch &tv in t {
    423 		case Type_Unknown:
    424 
    425 		case Type_Pointer:
    426 			if type_name, is_type_name := tv.pointed_to_type.(Type_Name); is_type_name {
    427 				tv.pointed_to_type = final_type_name(type_name, config)
    428 			}
    429 
    430 		case Type_Multipointer:
    431 			if type_name, is_type_name := tv.pointed_to_type.(Type_Name); is_type_name {
    432 				tv.pointed_to_type = final_type_name(type_name, config)
    433 			}
    434 
    435 		case Type_Pointer_By_Ptr:
    436 			if type_name, is_type_name := tv.pointed_to_type.(Type_Name); is_type_name {
    437 				tv.pointed_to_type = final_type_name(type_name, config)
    438 			}
    439 
    440 		case Type_Raw_Pointer:
    441 
    442 		case Type_CString:
    443 
    444 		case Type_Struct:
    445 			for &f in tv.fields {
    446 				for &n in f.names {
    447 					n = ensure_name_valid(n)
    448 				}
    449 
    450 				if type_name, is_type_name := f.type.(Type_Name); is_type_name {
    451 					f.type = final_type_name(type_name, config)
    452 				}
    453 			}
    454 
    455 		case Type_Enum:
    456 			strip_enum_member_prefixes(&tv)
    457 
    458 		case Type_Bit_Set:
    459 			if type_name, is_type_name := tv.enum_decl_name.(Type_Name); is_type_name {
    460 				tv.enum_decl_name = final_type_name(type_name, config)
    461 			}
    462 
    463 		case Type_Bit_Set_Constant:
    464 			tv.bit_set_type_name = final_type_name(tv.bit_set_type_name, config)
    465 
    466 		case Type_Alias:
    467 			if type_name, is_type_name := tv.aliased_type.(Type_Name); is_type_name {
    468 				tv.aliased_type = final_type_name(type_name, config)
    469 			}
    470 
    471 		case Type_Fixed_Array:
    472 			if type_name, is_type_name := tv.element_type.(Type_Name); is_type_name {
    473 				tv.element_type = final_type_name(type_name, config)
    474 			}
    475 
    476 		case Type_Procedure:
    477 			for &p in tv.parameters {
    478 				p.name = ensure_name_valid(p.name)
    479 
    480 				if type_name, is_type_name := p.type.(Type_Name); is_type_name {
    481 					p.type = final_type_name(type_name, config)
    482 				}
    483 			}
    484 
    485 			if type_name, is_type_name := tv.result_type.(Type_Name); is_type_name {
    486 				tv.result_type = final_type_name(type_name, config)
    487 			}
    488 		}
    489 	}
    490 
    491 	for &d in decls {
    492 		d.name = final_decl_name(d, types, config)
    493 		
    494 		switch &def in d.def {
    495 		case Type_Name: d.def = final_type_name(def, config)
    496 		case Macro_Name: d.def = final_macro_name(def, config)
    497 
    498 		case Fixed_Value:
    499 		case Type_Index:
    500 		}
    501 	}
    502 }
    503 
    504 override_procedure_parameter :: proc(p: ^Type_Procedure_Parameter, types: Type_List, override: string) {
    505 	if override == "[^]" {
    506 		if ptr_type, is_ptr_type := resolve_type_definition(types, p.type, Type_Pointer); is_ptr_type {
    507 			p.type = add_type(types, Type_Multipointer {
    508 				pointed_to_type = ptr_type.pointed_to_type,
    509 			})	
    510 		}	
    511 	} else if override == "#by_ptr" {
    512 		if ptr_type, is_ptr_type := resolve_type_definition(types, p.type, Type_Pointer); is_ptr_type {
    513 			p.type = add_type(types, Type_Pointer_By_Ptr {
    514 				pointed_to_type = ptr_type.pointed_to_type,
    515 			})
    516 		}
    517 	} else if override == "#any_int" {
    518 		p.any_int = true
    519 	} else {
    520 		p.type = Fixed_Value(override)
    521 	}
    522 }
    523 
    524 is_number :: proc(b: byte) -> bool {
    525 	return b >= '0' && b <= '9'
    526 }
    527 
    528 ensure_name_valid :: proc(s: string) -> string {
    529 	// TODO make sure this contains all Odin keywords
    530 	KEYWORDS :: [?]string {
    531 		"_bool",
    532 		"_b8",
    533 		"_b16",
    534 		"_b32",
    535 		"_b64",
    536 		"_int",
    537 		"_i8",
    538 		"_i16",
    539 		"_i32",
    540 		"_i64",
    541 		"_i128",
    542 		"_uint",
    543 		"_u8",
    544 		"_u16",
    545 		"_u32",
    546 		"_u64",
    547 		"_u128",
    548 		"_uintptr",
    549 		"_i16le",
    550 		"_i32le",
    551 		"_i64le",
    552 		"_i128le",
    553 		"_u16le",
    554 		"_u32le",
    555 		"_u64le",
    556 		"_u128le",
    557 		"_i16be",
    558 		"_i32be",
    559 		"_i64be",
    560 		"_i128be",
    561 		"_u16be",
    562 		"_u32be",
    563 		"_u64be",
    564 		"_u128be",
    565 		"_f16",
    566 		"_f32",
    567 		"_f64",
    568 		"_f16le",
    569 		"_f32le",
    570 		"_f64le",
    571 		"_f16be",
    572 		"_f32be",
    573 		"_f64be",
    574 		"_complex32",
    575 		"_complex64",
    576 		"_complex128",
    577 		"_quaternion64",
    578 		"_quaternion128",
    579 		"_quaternion256",
    580 		"_rune",
    581 		"_string",
    582 		"_cstring",
    583 		"_string16",
    584 		"_cstring16",
    585 		"_rawptr",
    586 		"_typeid",
    587 		"_any",
    588 		"_asm",
    589 		"_auto_cast",
    590 		"_bit_set",
    591 		"_break",
    592 		"_case",
    593 		"_cast",
    594 		"_context",
    595 		"_continue",
    596 		"_defer",
    597 		"_distinct",
    598 		"_do",
    599 		"_dynamic",
    600 		"_else",
    601 		"_enum",
    602 		"_fallthrough",
    603 		"_for",
    604 		"_foreign",
    605 		"_if",
    606 		"_import",
    607 		"_in",
    608 		"_map",
    609 		"_not_in",
    610 		"_or_else",
    611 		"_or_return",
    612 		"_package",
    613 		"_proc",
    614 		"_return",
    615 		"_struct",
    616 		"_switch",
    617 		"_transmute",
    618 		"_typeid",
    619 		"_union",
    620 		"_using",
    621 		"_when",
    622 		"_where",
    623 		"_matrix",
    624 
    625 		// Not keywords, but used names:
    626 		"_c",
    627 	}
    628 
    629 	for k in KEYWORDS {
    630 		if s == k[1:] {
    631 			return k
    632 		}
    633 	}
    634 
    635 	if len(s) > 0 && unicode.is_number(utf8.rune_at(s, 0)) {
    636 		return fmt.tprintf("_%v", s)
    637 	}
    638 
    639 	return s
    640 }
    641 
    642 final_decl_name :: proc(d: Decl, types: Type_List, config: Config) -> string {
    643 	if d.explicitly_created {
    644 		return d.name
    645 	}
    646 
    647 	if new_name, rename := config.rename[string(d.name)]; rename {
    648 		return new_name
    649 	}
    650 
    651 	_, is_proc := resolve_type_definition(types, d.def, Type_Procedure)
    652 
    653 	if is_proc {
    654 		return strings.trim_prefix(d.name, config.remove_function_prefix)
    655 	} else if d.from_macro {
    656 		return strings.trim_prefix(d.name, config.remove_macro_prefix)
    657 	} else {
    658 		res := strings.trim_prefix(d.name, config.remove_type_prefix)
    659 		res = strings.trim_suffix(res, config.remove_type_suffix)
    660 
    661 		if config.force_ada_case_types {
    662 			res = strings.to_ada_case(res)
    663 		}
    664 
    665 		return res
    666 	}
    667 
    668 	return d.name
    669 }
    670 
    671 final_type_name :: proc(name: Type_Name, config: Config) -> Type_Name {
    672 	if new_name, rename := config.rename[string(name)]; rename {
    673 		return Type_Name(new_name)
    674 	}
    675 
    676 	res := strings.trim_prefix(string(name), config.remove_type_prefix)
    677 	res = strings.trim_suffix(res, config.remove_type_suffix)
    678 
    679 	if config.force_ada_case_types {
    680 		res = strings.to_ada_case(res)
    681 	}
    682 
    683 	return Type_Name(res)
    684 }
    685 
    686 final_macro_name :: proc(name: Macro_Name, config: Config) -> Macro_Name {
    687 	return Macro_Name(strings.trim_prefix(string(name), config.remove_macro_prefix))
    688 }
    689 
    690 // Extracts any comment at the top of the source file. These will be put above the package line in
    691 // the bindings.
    692 extract_top_comment :: proc(src: string) -> string {
    693 	src := src
    694 	src = strings.trim_space(src)
    695 	top_comment_end: int
    696 	in_block := false
    697 	on_line_comment := false
    698 	
    699 	next_rune :: proc(s: string, cur: rune, cur_idx: int) -> rune {
    700 		next, _ := utf8.decode_rune(s[cur_idx + utf8.rune_size(cur):]) 
    701 		return next
    702 	}
    703 
    704 	top_comment_loop: for i := 0; i < len(src); {
    705 		r, r_sz := utf8.decode_rune(src[i:])
    706 		adv := r_sz
    707 		defer i += adv
    708 
    709 		if r_sz == 0 {
    710 			break
    711 		}
    712 
    713 		if on_line_comment {
    714 			if r == '\n' {
    715 				on_line_comment = false
    716 				top_comment_end = i + 1
    717 			}
    718 		} else if in_block {
    719 			if i + 2 >= len(src) {
    720 				continue
    721 			}
    722 
    723 			if src[i:i+2] == "*/" {
    724 				in_block = false
    725 				top_comment_end = i + 2
    726 				adv = 2
    727 			}
    728 		} else {
    729 			if i + 2 >= len(src) {
    730 				continue
    731 			}
    732 
    733 			// Only OK to skip whitespace here because `on_line_comment` etc needs to check for newlines.
    734 			if unicode.is_white_space(r) {
    735 				continue
    736 			}
    737 
    738 			switch src[i:i+2] {
    739 			case "//":
    740 				adv = 2
    741 				on_line_comment = true
    742 			case "/*":
    743 				adv = 2
    744 				in_block = true
    745 			case:
    746 				top_comment_end = i
    747 				break top_comment_loop
    748 			}
    749 		}
    750 	}
    751 
    752 	if top_comment_end > 0 {
    753 		return strings.trim_space(src[:top_comment_end])
    754 	}
    755 
    756 	return ""
    757 }