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 `