odin-libui

Odin bindings to libui
Log | Files | Refs | Submodules | README | LICENSE

gen.odin (7478B)


      1 package main
      2 
      3 import "core:bytes"
      4 import "core:fmt"
      5 import "core:io"
      6 import "core:strings"
      7 
      8 
      9 // generate the odin bindings.
     10 generate :: proc(wr: io.Writer, p: Parser) {
     11 	fmt.wprintf(wr, "// GENERATED CODE\n")
     12 	fmt.wprintf(wr, "package libui\n\n")
     13 
     14 	fmt.wprintln(wr, `import libc "core:c"`)
     15 	fmt.wprintln(wr, import_preamble)
     16 
     17 	// To handle the opaque (rawptr) types properly we want to avoid declaring
     18 	// them with a ^, since that would make them a pointer to a rawptr.
     19 	// However, we can only know if a type should be opaque if we haven't 
     20 	// encountered any definition for it (struct, enum, function). Thus as 
     21 	// quick and dirty hack, this logic will perform a dry run in order to 
     22 	// resolve opaque types. The second and true run then can omit the 
     23 	// superfluous caret appropriately.
     24 
     25 	dry_run: bytes.Buffer
     26 	write_everything(bytes.buffer_to_stream(&dry_run), p)
     27 
     28 	fmt.wprintln(wr, "// Unresolved symbols; typically opaque widget pointers")
     29 
     30 	outer: for type_name in idents {
     31 		for s in p.struct_defs {
     32 			if s.name == type_name {
     33 				continue outer
     34 			}
     35 		}
     36 		for e in p.enum_defs {
     37 			if e.name == type_name {
     38 				continue outer
     39 			}
     40 		}
     41 		for f in p.function_defs {
     42 			if f.name == type_name {
     43 				continue outer
     44 			}
     45 		}
     46 		opaque_types[type_name] = {}
     47 		fmt.wprintf(wr, "%s :: rawptr\n", ident(type_name))
     48 	}
     49 
     50 	fmt.wprintln(wr)
     51 
     52 	write_everything(wr, p)
     53 }
     54 
     55 write_everything :: proc(wr: io.Writer, p: Parser) {
     56 	fmt.println("writing edge-cases")
     57 
     58 	fmt.wprintln(wr, "// Hardcoded #defines ")
     59 	fmt.wprintf(wr, "DrawDefaultMiterLimit :: 10.0\n")
     60 	fmt.wprintln(wr)
     61 
     62 	fmt.wprintln(wr, "// Enum definitions")
     63 
     64 	/*
     65 		Enum definitions. 
     66 	*/
     67 
     68 	for enum_def in p.enum_defs {
     69 		fmt.println("writing", enum_def.name)
     70 		fmt.wprintf(wr, "%s :: enum {{\n", ident(enum_def.name))
     71 		for variant in enum_def.variants {
     72 			fmt.wprintf(wr, "\t%s", ident(variant.name))
     73 			if n, ok := variant.value.?; ok {
     74 				fmt.wprintf(wr, " = %d", n)
     75 			}
     76 			fmt.wprintf(wr, ",\n")
     77 		}
     78 		fmt.wprintf(wr, "}\n\n")
     79 	}
     80 
     81 	/*
     82 		Struct definitions. 
     83 	*/
     84 
     85 	fmt.wprintln(wr, "// Struct definitions")
     86 
     87 	// Explit case for tm (libc time struct)
     88 	fmt.wprintln(wr, broken_down_time_def)
     89 
     90 	for struct_def in p.struct_defs {
     91 		fmt.println("writing", struct_def.name)
     92 		fmt.wprintf(wr, "%s :: struct {{\n", ident(struct_def.name))
     93 		for field in struct_def.fields {
     94 			fn, fn_ok := field.function.?
     95 			if fn_ok {
     96 				fmt.wprintf(wr, "\t%s: %sproc", ident(fn.name), field.is_pointer ? "^" : "")
     97 				write_procedure_args(wr, fn.args[:])
     98 				if fn.return_type != "" && !(fn.return_type == "void" && !fn.return_is_pointer) {
     99 					fmt.wprintf(wr, " -> %s", ident(fn.return_type, fn.return_is_pointer, true))
    100 				}
    101 			} else {
    102 				fmt.wprintf(wr, "\t%s: %s", ident(field.name), ident(field.type, field.is_pointer))
    103 			}
    104 			fmt.wprintf(wr, ",\n")
    105 		}
    106 		fmt.wprintf(wr, "}\n\n")
    107 	}
    108 
    109 	/*
    110 		Procedure types are named proc types that are not part of the binding.
    111 	*/
    112 
    113 	fmt.wprintln(wr, "// Procedure types")
    114 
    115 	for function_def in p.function_defs {
    116 		if !function_def.is_type do continue
    117 		fmt.println("writing", function_def.name)
    118 		fmt.wprintf(
    119 			wr,
    120 			"%s :: %s proc",
    121 			ident(function_def.name),
    122 			function_def.is_type ? "#type" : "",
    123 		)
    124 		write_procedure_args(wr, function_def.args[:])
    125 		if function_def.return_type != "" &&
    126 		   !(function_def.return_type == "void" && !function_def.return_is_pointer) {
    127 			fmt.wprintf(
    128 				wr,
    129 				" -> %s",
    130 				ident(function_def.return_type, function_def.return_is_pointer, true),
    131 			)
    132 		}
    133 		if !function_def.is_type {
    134 			fmt.wprintf(wr, " ---")
    135 		}
    136 		fmt.wprintf(wr, "\n")
    137 	}
    138 
    139 	fmt.wprintf(wr, "\n")
    140 
    141 	/*
    142 		Procedure definitions. These procs are implemented by the library.
    143 	*/
    144 
    145 	fmt.wprintln(wr, "// Procedure definitions")
    146 
    147 	// TODO: maybe check for system installed version of libui.a?
    148 	// TODO: change to .lib for Windows with "where" clause
    149 	fmt.wprintln(wr, `@(default_calling_convention = "stdcall", link_prefix = "ui")`)
    150 	fmt.wprintf(wr, "foreign lib {{\n")
    151 
    152 	for function_def in p.function_defs {
    153 		if function_def.is_type do continue
    154 		fmt.println("writing", function_def.name)
    155 		fmt.wprintf(
    156 			wr,
    157 			"\t%s :: %s proc",
    158 			ident(function_def.name),
    159 			function_def.is_type ? "#type" : "",
    160 		)
    161 		write_procedure_args(wr, function_def.args[:])
    162 		if function_def.return_type != "" &&
    163 		   !(function_def.return_type == "void" && !function_def.return_is_pointer) {
    164 			fmt.wprintf(
    165 				wr,
    166 				" -> %s",
    167 				ident(function_def.return_type, function_def.return_is_pointer, true),
    168 			)
    169 		}
    170 		if !function_def.is_type {
    171 			fmt.wprintf(wr, " ---")
    172 		}
    173 		fmt.wprintf(wr, "\n")
    174 	}
    175 
    176 	fmt.wprintf(wr, "}\n")
    177 }
    178 
    179 write_procedure_args :: proc(wr: io.Writer, args: []Function_Argument) {
    180 	fmt.wprintf(wr, "(")
    181 	for arg, ii in args {
    182 		fn, fn_ok := arg.function.?
    183 		if fn_ok {
    184 			fmt.wprintf(wr, "%s: %sproc", ident(fn.name), arg.is_pointer ? "^" : "")
    185 			write_procedure_args(wr, fn.args[:])
    186 			if fn.return_type != "" && !(fn.return_type == "void" && !fn.return_is_pointer) {
    187 				fmt.wprintf(wr, " -> %s", ident(fn.return_type, fn.return_is_pointer))
    188 			}
    189 		} else {
    190 			if !(arg.type == "void" && !arg.is_pointer) {
    191 				fmt.wprintf(
    192 					wr,
    193 					"%s: %s",
    194 					arg.name == "" ? "_" : ident(arg.name),
    195 					ident(arg.type, arg.is_pointer, true),
    196 				)
    197 			}
    198 		}
    199 		if ii < len(args) - 1 {
    200 			fmt.wprintf(wr, ", ")
    201 		}
    202 	}
    203 	fmt.wprintf(wr, ")")
    204 }
    205 
    206 // opaque_types is a map of types that have no known definition. 
    207 // Such types will be defined as rawptr.
    208 opaque_types := map[string]struct {}{}
    209 
    210 // idents is a map of encoutered identifiers. Compared against
    211 // known defintions to figure out which types are opaque.
    212 idents := map[string]struct {}{}
    213 
    214 // ident strips off the "ui" prefix and does name replacements.
    215 //
    216 // opaque types (void pointers) must not be prefixed with ^ otherwise 
    217 // they become pointer-to-rawptr which is not correct.
    218 ident :: proc(text: string, is_pointer := false, is_type := false) -> string {
    219 	text := text
    220 
    221 	b := strings.Builder{}
    222 	strings.builder_init_none(&b, context.temp_allocator)
    223 
    224 	if is_pointer {
    225 		if _, ok := opaque_types[text]; !ok {
    226 			strings.write_string(&b, "^")
    227 		}
    228 	}
    229 
    230 	switch text {
    231 	case "int":
    232 		strings.write_string(&b, "libc.int")
    233 	case "uint32_t":
    234 		strings.write_string(&b, "libc.uint32_t")
    235 	case "uint64_t":
    236 		strings.write_string(&b, "libc.uint64_t")
    237 	case "uintptr_t":
    238 		strings.write_string(&b, "libc.uintptr_t")
    239 	case "size_t":
    240 		strings.write_string(&b, "libc.size_t")
    241 	case "void":
    242 		if is_pointer {
    243 			strings.builder_reset(&b)
    244 			strings.write_string(&b, "rawptr")
    245 		} else {
    246 			return ""
    247 		}
    248 	case "double":
    249 		strings.write_string(&b, "libc.double")
    250 	case "char":
    251 		if is_pointer {
    252 			strings.builder_reset(&b)
    253 			strings.write_string(&b, "cstring")
    254 		} else {
    255 			strings.write_string(&b, "byte")
    256 		}
    257 	case:
    258 		if is_type && text != "tm" && text != "int" {
    259 			idents[text] = {}
    260 		}
    261 		if strings.has_prefix(text, "ui") {
    262 			text = text[2:]
    263 		}
    264 		strings.write_string(&b, text)
    265 	}
    266 
    267 	return strings.to_string(b)
    268 }
    269 
    270 broken_down_time_def :: `
    271 // ISO C broken-down time structure.
    272 tm :: struct {
    273 	tm_sec: int,
    274 	tm_min: int,
    275 	tm_hour: int,
    276 	tm_mday: int,
    277 	tm_mon: int,
    278 	tm_year: int,
    279 	tm_wday: int,
    280 	tm_yday: int,
    281 	tm_isdst: int,
    282 }
    283 `
    284 
    285 import_preamble :: `
    286 when ODIN_OS == .Linux {
    287 	@(extra_linker_flags="-lgtk-3 -lgdk-3 -lz -lharfbuzz -lpangocairo-1.0 -lpango-1.0 -latk-1.0 -lcairo -lcairo-gobject -lgdk_pixbuf-2.0 -lgio-2.0 -lglib-2.0 -lgobject-2.0")
    288 	foreign import lib "system:ui"
    289 } else {
    290 	foreign import lib "system:ui"
    291 }
    292 `