odin-blend2d

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

translate_macros.odin (7648B)


      1 #+private file
      2 package bindgen2
      3 
      4 import "core:strings"
      5 import "core:fmt"
      6 import "core:log"
      7 
      8 _ :: log
      9 
     10 // TODO could we use a Declaration with some Raw_Macro type and just fix this in translate_process?
     11 @(private="package")
     12 Raw_Macro :: struct {
     13 	name: string,
     14 	tokens: []Raw_Macro_Token,
     15 	is_function_like: bool,
     16 	comment: string,
     17 	side_comment: string,
     18 	whitespace_before_side_comment: int,
     19 	whitespace_after_name: int,
     20 	original_line: int,
     21 }
     22 
     23 @(private="package")
     24 Raw_Macro_Token :: struct {
     25 	value: string,
     26 	kind: Raw_Macro_Token_Kind,
     27 }
     28 
     29 // Same as Token_Kind in clang, but without 'Comment'
     30 @(private="package")
     31 Raw_Macro_Token_Kind :: enum {
     32 	Punctuation,
     33 	Keyword,
     34 	Identifier,
     35 	Literal,
     36 }
     37 
     38 @(private="package")
     39 translate_macros :: proc(macros: []Raw_Macro, decls: Decl_List) {
     40 	existing_declaration_names: map[string]int
     41 
     42 	for d, i in decls {
     43 		existing_declaration_names[d.name] = i
     44 	}
     45 
     46 	macro_lookup: map[string]int
     47 
     48 	for m, i in macros {
     49 		macro_lookup[m.name] = i
     50 	}
     51 
     52 	for m, i in macros {
     53 		// Function-like macros are only used when figuring out a value of a non-function like macro.
     54 		// They will not have a value "of their own".
     55 		if m.is_function_like {
     56 			continue
     57 		}
     58 
     59 		odin_value := evaluate_macro(macros, macro_lookup, existing_declaration_names, i, {})
     60 
     61 		if odin_value != "" && odin_value[0] != '{' {
     62 			def: Definition
     63 			if decl_idx, decl_exists := existing_declaration_names[odin_value]; decl_exists {
     64 				// We want the value of this macro to change if there is some trimming set in the config etc.
     65 
     66 				if decls[decl_idx].from_macro {
     67 					def = Macro_Name(odin_value)
     68 				} else {
     69 					def = Type_Name(odin_value)
     70 				}
     71 			} else {
     72 				def = Fixed_Value(odin_value)
     73 			}
     74 
     75 			add_decl(decls, {
     76 				name = m.name,
     77 				def = def,
     78 				comment_before = m.comment,
     79 				side_comment = m.side_comment,
     80 				explicit_whitespace_before_side_comment = m.whitespace_before_side_comment,
     81 				explicit_whitespace_after_name = m.whitespace_after_name,
     82 				original_line = m.original_line,
     83 				from_macro = true,
     84 			})
     85 
     86 			existing_declaration_names[m.name] = len(decls) - 1
     87 		}
     88 	}
     89 }
     90 
     91 Macro_Index :: int
     92 
     93 Evalulate_Macro_State :: struct {
     94 	cur_token: int,
     95 	tokens: []Raw_Macro_Token,
     96 	cur_macro: Raw_Macro,
     97 	cur_macro_index: Macro_Index,
     98 	macros: []Raw_Macro,
     99 	macro_lookup: map[string]Macro_Index,
    100 	params: map[string]string,
    101 	existing_declarations: map[string]int,
    102 }
    103 
    104 cur :: proc(ems: Evalulate_Macro_State) -> Raw_Macro_Token {
    105 	return ems.tokens[ems.cur_token]
    106 }
    107 
    108 adv :: proc(ems: ^Evalulate_Macro_State) {
    109 	ems.cur_token += 1
    110 }
    111 
    112 evaluate_macro :: proc(macros: []Raw_Macro, macro_lookup: map[string]Macro_Index, existing_declarations: map[string]int, mi: Macro_Index, args: []string) -> string {
    113 	ems := Evalulate_Macro_State {
    114 		cur_token = 0,
    115 		tokens = macros[mi].tokens,
    116 		cur_macro = macros[mi],
    117 		cur_macro_index = mi,
    118 		macros = macros,
    119 		macro_lookup = macro_lookup,
    120 		existing_declarations = existing_declarations,
    121 	}
    122 
    123 	if ems.cur_macro.is_function_like {
    124 		params := parse_parameter_list(&ems)
    125 		adv(&ems)
    126 
    127 		if len(params) != len(args) {
    128 			return ""
    129 		}
    130 
    131 		for a, i in args {
    132 			ems.params[params[i]] = a
    133 		}
    134 	}
    135 
    136 	curly_braces: int
    137 
    138 	b := strings.builder_make()
    139 	literal_type: Literal_Type_Info
    140 	notted: bool
    141 
    142 	for ems.cur_token < len(ems.tokens) {
    143 		t := cur(ems)
    144 		tv := t.value
    145 
    146 		switch t.kind {
    147 		case .Punctuation:
    148 			switch tv {
    149 			case "#":
    150 				return ""
    151 
    152 			case "{":
    153 				curly_braces += 1
    154 				p(&b, tv)
    155 				
    156 
    157 			case "}":
    158 				curly_braces -= 1
    159 				p(&b, tv)
    160 
    161 			case ",":
    162 				if curly_braces == 0 {
    163 					return ""
    164 				}
    165 
    166 				p(&b, tv)
    167 				p(&b, ' ')
    168 
    169 			case "(", ")", "-", "*", "/", "+":
    170 				p(&b, tv)
    171 
    172 			case "~":
    173 				notted = true
    174 			}
    175 		case .Keyword:
    176 			return ""
    177 		case .Identifier:
    178 			if tv == "UINT64_MAX" {
    179 				notted = true
    180 				literal_type = .U64
    181 				break
    182 			}
    183 
    184 			if tv == "UINT32_MAX" {
    185 				notted = true
    186 				literal_type = .U32
    187 				break
    188 			}
    189 
    190 			if tv == "INT32_MAX" {
    191 				notted = true
    192 				literal_type = .I32
    193 				break
    194 			}
    195 
    196 			if tv == "INT64_MAX" {
    197 				notted = true
    198 				literal_type = .I64
    199 				break
    200 			}
    201 
    202 			if parse_identifier(&ems, &b) == false {
    203 				mapped, has_mapping := c_type_mapping[tv]
    204 
    205 				if has_mapping {
    206 					p(&b, mapped)
    207 				} else {
    208 					return ""
    209 				}
    210 			}
    211 		case .Literal:
    212 			if type, ok := parse_literal(&b, tv); ok == false {
    213 				return ""
    214 			} else {
    215 				literal_type = type
    216 			}
    217 		}
    218 
    219 		adv(&ems)
    220 	}
    221 
    222 	if notted {
    223 		switch literal_type {
    224 		case .None:
    225 		case .U32: return "max(u32)"
    226 		case .I32: return "max(i32)"
    227 		case .U64: return "max(u64)"
    228 		case .I64: return "max(i64)"
    229 		}
    230 
    231 		log.errorf("Unknown type: %v", ems.tokens)
    232 	}
    233 
    234 	return strings.to_string(b)
    235 }
    236 
    237 p :: fmt.sbprint
    238 pf :: fmt.sbprintf
    239 
    240 Literal_Type_Info :: enum {
    241 	None,
    242 	U32,
    243 	I32,
    244 	U64,
    245 	I64,
    246 }
    247 
    248 parse_literal :: proc(b: ^strings.Builder, val: string) -> (Literal_Type_Info, bool) {
    249 	if len(val) == 0 {
    250 		return {}, false
    251 	}
    252 
    253 	if val[0] >= '0' && val[0] <= '9' {
    254 		if len(val) == 1 {
    255 			p(b, val)
    256 			return {}, true
    257 		}
    258 
    259 		val_start := 0
    260 		hex := false
    261 
    262 		if val[1] == 'x' || val[1] == 'X' {
    263 			p(b, '0')
    264 			p(b, 'x')
    265 			hex = true
    266 			val_start = 2
    267 		}
    268 
    269 		end := len(val) - 1
    270 
    271 		l: int
    272 		u: int
    273 
    274 		// remove suffix chars such as ULL and f
    275 		LOOP: for ; end > 0; end -= 1 {
    276 			switch val[end] {
    277 			case 'L', 'l':
    278 				l += 1
    279 				continue LOOP
    280 
    281 			case 'U', 'u':
    282 				u += 1
    283 				continue LOOP
    284 
    285 			case 'F', 'f':
    286 				if hex {
    287 					break LOOP
    288 				}
    289 				// Floating point literals can have 'F' or 'f' suffixes.
    290 				continue LOOP
    291 			case:
    292 				// Not a suffix char.
    293 				break LOOP
    294 			}
    295 		}
    296 
    297 		ti: Literal_Type_Info
    298 
    299 		if l == 1 && u == 0 {
    300 			ti = .I32
    301 		}
    302 
    303 		if l == 0 && u == 1 || l == 1 && u == 1 {
    304 			ti = .U32
    305 		}
    306 
    307 		if l == 2 && u == 0 {
    308 			ti = .I64
    309 		}
    310 
    311 		if l == 2 && u == 1 {
    312 			ti = .U64
    313 		}
    314 
    315 		p(b, val[val_start:end + 1])
    316 		return ti, true
    317 	} else if val[0] == '"' {
    318 		p(b, val)
    319 		return {}, true
    320 	}
    321 	return {}, false
    322 }
    323 
    324 parse_parameter_list :: proc(ems: ^Evalulate_Macro_State) -> []string {
    325 	t := cur(ems^)
    326 
    327 	if t.kind != .Punctuation || t.value != "(" {
    328 		return {}
    329 	}
    330 
    331 	paren_count := 1
    332 
    333 	adv(ems)
    334 
    335 	arg_builder := strings.builder_make()
    336 	args: [dynamic]string
    337 
    338 	args_loop: for ems.cur_token < len(ems.tokens) {
    339 		t = cur(ems^)
    340 
    341 		#partial switch t.kind {
    342 		case .Punctuation:
    343 			switch t.value {
    344 			case "(":
    345 				paren_count += 1
    346 
    347 			case ")":
    348 				paren_count -= 1
    349 
    350 				if paren_count == 0 {
    351 					append(&args, strings.to_string(arg_builder))
    352 					arg_builder = strings.builder_make()
    353 					break args_loop
    354 				}
    355 
    356 			case ",":
    357 				append(&args, strings.to_string(arg_builder))
    358 				arg_builder = strings.builder_make()
    359 			}
    360 		case .Identifier:
    361 			p(&arg_builder, t.value)
    362 
    363 		case .Literal:
    364 			p(&arg_builder, t.value)
    365 		}
    366 
    367 		adv(ems)
    368 	}
    369 
    370 	return args[:]
    371 }
    372 
    373 parse_identifier :: proc(ems: ^Evalulate_Macro_State, b: ^strings.Builder) -> bool {
    374 	t := cur(ems^)
    375 	assert(t.kind == .Identifier)
    376 
    377 	tv := t.value
    378 
    379 	// We are inside a function-like macro and this identifier is one of the parameter names:
    380 	// Replace the identifier with the argument!
    381 	if parameter_replacement, has_parameter_replacement := ems.params[tv]; has_parameter_replacement {
    382 		p(b, parameter_replacement)
    383 		return true
    384 	}
    385 
    386 	if tv in ems.existing_declarations {
    387 		p(b, tv)
    388 		return true
    389 	}
    390 
    391 	if inner_macro_idx, inner_macro_exists := ems.macro_lookup[tv]; inner_macro_exists {
    392 		inner_macro := ems.macros[inner_macro_idx]
    393 
    394 		args: []string
    395 
    396 		if inner_macro.is_function_like {
    397 			adv(ems)
    398 			args = parse_parameter_list(ems)
    399 		}
    400 
    401 		inner := evaluate_macro(ems.macros, ems.macro_lookup, ems.existing_declarations, inner_macro_idx, args)
    402 
    403 		if inner == "" {
    404 			return false
    405 		}
    406 
    407 		p(b, inner)
    408 		return true
    409 	}
    410 
    411 	return false
    412 }