| ... | ... | @@ -12,6 +12,11 @@ |
| 12 | 12 | |
| 13 | 13 | #include <string.h> |
| 14 | 14 | |
| 15 | struct TypeDef { |
| 16 | Buf alias; |
| 17 | Buf target; |
| 18 | }; |
| 19 | |
| 15 | 20 | struct Arg { |
| 16 | 21 | Buf name; |
| 17 | 22 | Buf *type; |
| ... | ... | @@ -25,11 +30,26 @@ struct Fn { |
| 25 | 30 | bool is_variadic; |
| 26 | 31 | }; |
| 27 | 32 | |
| 33 | struct Field { |
| 34 | Buf name; |
| 35 | Buf *type; |
| 36 | }; |
| 37 | |
| 38 | struct Struct { |
| 39 | Buf name; |
| 40 | ZigList<Field*> fields; |
| 41 | bool have_def; |
| 42 | }; |
| 43 | |
| 28 | 44 | struct ParseH { |
| 29 | 45 | CXTranslationUnit tu; |
| 30 | 46 | FILE *f; |
| 31 | 47 | ZigList<Fn *> fn_list; |
| 48 | ZigList<Struct *> struct_list; |
| 49 | ZigList<TypeDef *> type_def_list; |
| 50 | ZigList<Struct *> incomplete_struct_list; |
| 32 | 51 | Fn *cur_fn; |
| 52 | Struct *cur_struct; |
| 33 | 53 | int arg_index; |
| 34 | 54 | int cur_indent; |
| 35 | 55 | CXSourceRange range; |
| ... | ... | @@ -38,6 +58,16 @@ struct ParseH { |
| 38 | 58 | |
| 39 | 59 | static const int indent_size = 4; |
| 40 | 60 | |
| 61 | static bool have_struct_def(ParseH *p, Buf *name) { |
| 62 | for (int i = 0; i < p->struct_list.length; i += 1) { |
| 63 | Struct *struc = p->struct_list.at(i); |
| 64 | if (struc->fields.length > 0 && buf_eql_buf(&struc->name, name)) { |
| 65 | return true; |
| 66 | } |
| 67 | } |
| 68 | return false; |
| 69 | } |
| 70 | |
| 41 | 71 | static bool str_has_prefix(const char *str, const char *prefix) { |
| 42 | 72 | while (*prefix) { |
| 43 | 73 | if (*str && *str == *prefix) { |
| ... | ... | @@ -81,6 +111,24 @@ static void print_location(ParseH *p) { |
| 81 | 111 | fprintf(stderr, "%s line %u, column %u\n", clang_getCString(file_name), line, column); |
| 82 | 112 | } |
| 83 | 113 | |
| 114 | static bool resolves_to_void(ParseH *p, CXType raw_type) { |
| 115 | if (raw_type.kind == CXType_Unexposed) { |
| 116 | CXType canonical = clang_getCanonicalType(raw_type); |
| 117 | if (canonical.kind == CXType_Unexposed) |
| 118 | zig_panic("clang C api insufficient"); |
| 119 | else |
| 120 | return resolves_to_void(p, canonical); |
| 121 | } |
| 122 | if (raw_type.kind == CXType_Void) { |
| 123 | return true; |
| 124 | } else if (raw_type.kind == CXType_Typedef) { |
| 125 | CXCursor typedef_cursor = clang_getTypeDeclaration(raw_type); |
| 126 | CXType underlying_type = clang_getTypedefDeclUnderlyingType(typedef_cursor); |
| 127 | return resolves_to_void(p, underlying_type); |
| 128 | } |
| 129 | return false; |
| 130 | } |
| 131 | |
| 84 | 132 | static Buf *to_zig_type(ParseH *p, CXType raw_type) { |
| 85 | 133 | if (raw_type.kind == CXType_Unexposed) { |
| 86 | 134 | CXType canonical = clang_getCanonicalType(raw_type); |
| ... | ... | @@ -94,15 +142,16 @@ static Buf *to_zig_type(ParseH *p, CXType raw_type) { |
| 94 | 142 | case CXType_Unexposed: |
| 95 | 143 | zig_unreachable(); |
| 96 | 144 | case CXType_Void: |
| 97 | | return buf_create_from_str("void"); |
| 145 | zig_panic("void type encountered"); |
| 98 | 146 | case CXType_Bool: |
| 99 | 147 | return buf_create_from_str("bool"); |
| 100 | 148 | case CXType_SChar: |
| 101 | | return buf_create_from_str("i8"); |
| 149 | return buf_create_from_str("c_schar"); |
| 150 | case CXType_UChar: |
| 151 | return buf_create_from_str("c_uchar"); |
| 102 | 152 | case CXType_Char_U: |
| 103 | 153 | case CXType_Char_S: |
| 104 | | case CXType_UChar: |
| 105 | | return buf_create_from_str("u8"); |
| 154 | return buf_create_from_str("c_char"); |
| 106 | 155 | case CXType_WChar: |
| 107 | 156 | print_location(p); |
| 108 | 157 | zig_panic("TODO wchar"); |
| ... | ... | @@ -153,7 +202,12 @@ static Buf *to_zig_type(ParseH *p, CXType raw_type) { |
| 153 | 202 | case CXType_Pointer: |
| 154 | 203 | { |
| 155 | 204 | CXType pointee_type = clang_getPointeeType(raw_type); |
| 156 | | Buf *pointee_buf = to_zig_type(p, pointee_type); |
| 205 | Buf *pointee_buf; |
| 206 | if (resolves_to_void(p, pointee_type)) { |
| 207 | pointee_buf = buf_create_from_str("u8"); |
| 208 | } else { |
| 209 | pointee_buf = to_zig_type(p, pointee_type); |
| 210 | } |
| 157 | 211 | if (clang_isConstQualifiedType(pointee_type)) { |
| 158 | 212 | return buf_sprintf("*const %s", buf_ptr(pointee_buf)); |
| 159 | 213 | } else { |
| ... | ... | @@ -192,7 +246,11 @@ static Buf *to_zig_type(ParseH *p, CXType raw_type) { |
| 192 | 246 | } else { |
| 193 | 247 | CXCursor typedef_cursor = clang_getTypeDeclaration(raw_type); |
| 194 | 248 | CXType underlying_type = clang_getTypedefDeclUnderlyingType(typedef_cursor); |
| 195 | | return to_zig_type(p, underlying_type); |
| 249 | if (resolves_to_void(p, underlying_type)) { |
| 250 | return buf_create_from_str("u8"); |
| 251 | } else { |
| 252 | return buf_create_from_str(name); |
| 253 | } |
| 196 | 254 | } |
| 197 | 255 | } |
| 198 | 256 | case CXType_ConstantArray: |
| ... | ... | @@ -205,7 +263,7 @@ static Buf *to_zig_type(ParseH *p, CXType raw_type) { |
| 205 | 263 | case CXType_FunctionProto: |
| 206 | 264 | fprintf(stderr, "warning: TODO function proto\n"); |
| 207 | 265 | print_location(p); |
| 208 | | return buf_create_from_str("*const u8"); |
| 266 | return buf_create_from_str("u8"); |
| 209 | 267 | case CXType_FunctionNoProto: |
| 210 | 268 | print_location(p); |
| 211 | 269 | zig_panic("TODO function no proto"); |
| ... | ... | @@ -277,6 +335,51 @@ static enum CXChildVisitResult visit_fn_children(CXCursor cursor, CXCursor paren |
| 277 | 335 | } |
| 278 | 336 | } |
| 279 | 337 | |
| 338 | static enum CXChildVisitResult visit_struct_children(CXCursor cursor, CXCursor parent, CXClientData client_data) { |
| 339 | ParseH *p = (ParseH*)client_data; |
| 340 | enum CXCursorKind kind = clang_getCursorKind(cursor); |
| 341 | |
| 342 | switch (kind) { |
| 343 | case CXCursor_FieldDecl: |
| 344 | { |
| 345 | assert(p->cur_struct); |
| 346 | CXString name = clang_getCursorSpelling(cursor); |
| 347 | Field *field = allocate<Field>(1); |
| 348 | buf_init_from_str(&field->name, clang_getCString(name)); |
| 349 | CXType cursor_type = clang_getCursorType(cursor); |
| 350 | field->type = to_zig_type(p, cursor_type); |
| 351 | |
| 352 | p->cur_struct->fields.append(field); |
| 353 | |
| 354 | return CXChildVisit_Continue; |
| 355 | } |
| 356 | default: |
| 357 | return CXChildVisit_Recurse; |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | static bool handle_struct_cursor(ParseH *p, CXCursor cursor, const char *name, bool expect_name) { |
| 362 | p->cur_struct = allocate<Struct>(1); |
| 363 | |
| 364 | buf_init_from_str(&p->cur_struct->name, name); |
| 365 | |
| 366 | bool got_name = (buf_len(&p->cur_struct->name) != 0); |
| 367 | if (expect_name != got_name) |
| 368 | return false; |
| 369 | |
| 370 | clang_visitChildren(cursor, visit_struct_children, p); |
| 371 | |
| 372 | if (p->cur_struct->fields.length > 0) { |
| 373 | p->struct_list.append(p->cur_struct); |
| 374 | } else { |
| 375 | p->incomplete_struct_list.append(p->cur_struct); |
| 376 | } |
| 377 | |
| 378 | p->cur_struct = nullptr; |
| 379 | |
| 380 | return true; |
| 381 | } |
| 382 | |
| 280 | 383 | |
| 281 | 384 | static enum CXChildVisitResult fn_visitor(CXCursor cursor, CXCursor parent, CXClientData client_data) { |
| 282 | 385 | ParseH *p = (ParseH*)client_data; |
| ... | ... | @@ -306,7 +409,9 @@ static enum CXChildVisitResult fn_visitor(CXCursor cursor, CXCursor parent, CXCl |
| 306 | 409 | p->cur_fn->is_variadic = clang_isFunctionTypeVariadic(fn_type); |
| 307 | 410 | |
| 308 | 411 | CXType return_type = clang_getResultType(fn_type); |
| 309 | | p->cur_fn->return_type = to_zig_type(p, return_type); |
| 412 | if (!resolves_to_void(p, return_type)) { |
| 413 | p->cur_fn->return_type = to_zig_type(p, return_type); |
| 414 | } |
| 310 | 415 | |
| 311 | 416 | buf_init_from_str(&p->cur_fn->name, clang_getCString(name)); |
| 312 | 417 | |
| ... | ... | @@ -330,7 +435,44 @@ static enum CXChildVisitResult fn_visitor(CXCursor cursor, CXCursor parent, CXCl |
| 330 | 435 | case CXCursor_CompoundStmt: |
| 331 | 436 | case CXCursor_FieldDecl: |
| 332 | 437 | case CXCursor_TypedefDecl: |
| 333 | | return CXChildVisit_Continue; |
| 438 | { |
| 439 | CXType underlying_type = clang_getTypedefDeclUnderlyingType(cursor); |
| 440 | |
| 441 | if (resolves_to_void(p, underlying_type)) { |
| 442 | return CXChildVisit_Continue; |
| 443 | } |
| 444 | |
| 445 | if (underlying_type.kind == CXType_Unexposed) { |
| 446 | underlying_type = clang_getCanonicalType(underlying_type); |
| 447 | } |
| 448 | bool skip_typedef; |
| 449 | if (underlying_type.kind == CXType_Unexposed) { |
| 450 | fprintf(stderr, "warning: unexposed type\n"); |
| 451 | print_location(p); |
| 452 | skip_typedef = true; |
| 453 | } else if (underlying_type.kind == CXType_Record) { |
| 454 | CXCursor decl_cursor = clang_getTypeDeclaration(underlying_type); |
| 455 | skip_typedef = handle_struct_cursor(p, decl_cursor, clang_getCString(name), false); |
| 456 | } else { |
| 457 | skip_typedef = false; |
| 458 | } |
| 459 | |
| 460 | if (!skip_typedef) { |
| 461 | CXType typedef_type = clang_getCursorType(cursor); |
| 462 | TypeDef *type_def = allocate<TypeDef>(1); |
| 463 | buf_init_from_str(&type_def->alias, prefixes_stripped(typedef_type)); |
| 464 | buf_init_from_buf(&type_def->target, to_zig_type(p, underlying_type)); |
| 465 | p->type_def_list.append(type_def); |
| 466 | } |
| 467 | |
| 468 | return CXChildVisit_Continue; |
| 469 | } |
| 470 | case CXCursor_StructDecl: |
| 471 | { |
| 472 | handle_struct_cursor(p, cursor, clang_getCString(name), true); |
| 473 | |
| 474 | return CXChildVisit_Continue; |
| 475 | } |
| 334 | 476 | default: |
| 335 | 477 | return CXChildVisit_Recurse; |
| 336 | 478 | } |
| ... | ... | @@ -401,6 +543,44 @@ void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FI |
| 401 | 543 | CXCursor cursor = clang_getTranslationUnitCursor(p->tu); |
| 402 | 544 | clang_visitChildren(cursor, fn_visitor, p); |
| 403 | 545 | |
| 546 | for (int struct_i = 0; struct_i < p->struct_list.length; struct_i += 1) { |
| 547 | Struct *struc = p->struct_list.at(struct_i); |
| 548 | fprintf(f, "struct %s {\n", buf_ptr(&struc->name)); |
| 549 | p->cur_indent += indent_size; |
| 550 | for (int field_i = 0; field_i < struc->fields.length; field_i += 1) { |
| 551 | Field *field = struc->fields.at(field_i); |
| 552 | print_indent(p); |
| 553 | fprintf(f, "%s: %s,\n", buf_ptr(&field->name), buf_ptr(field->type)); |
| 554 | } |
| 555 | |
| 556 | p->cur_indent -= indent_size; |
| 557 | fprintf(f, "}\n\n"); |
| 558 | } |
| 559 | |
| 560 | int total_typedef_count = p->type_def_list.length; |
| 561 | for (int i = 0; i < p->incomplete_struct_list.length; i += 1) { |
| 562 | Struct *struc = p->incomplete_struct_list.at(i); |
| 563 | struc->have_def = have_struct_def(p, &struc->name); |
| 564 | total_typedef_count += (int)!struc->have_def; |
| 565 | } |
| 566 | |
| 567 | if (total_typedef_count) { |
| 568 | for (int i = 0; i < p->incomplete_struct_list.length; i += 1) { |
| 569 | Struct *struc = p->incomplete_struct_list.at(i); |
| 570 | if (struc->have_def) |
| 571 | continue; |
| 572 | |
| 573 | fprintf(f, "struct %s;\n", buf_ptr(&struc->name)); |
| 574 | } |
| 575 | |
| 576 | for (int type_def_i = 0; type_def_i < p->type_def_list.length; type_def_i += 1) { |
| 577 | TypeDef *type_def = p->type_def_list.at(type_def_i); |
| 578 | fprintf(f, "type %s = %s;\n", buf_ptr(&type_def->alias), buf_ptr(&type_def->target)); |
| 579 | } |
| 580 | |
| 581 | fprintf(f, "\n"); |
| 582 | } |
| 583 | |
| 404 | 584 | if (p->fn_list.length) { |
| 405 | 585 | fprintf(f, "extern {\n"); |
| 406 | 586 | p->cur_indent += indent_size; |
| ... | ... | @@ -419,11 +599,12 @@ void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FI |
| 419 | 599 | fprintf(p->f, "..."); |
| 420 | 600 | } |
| 421 | 601 | fprintf(p->f, ")"); |
| 422 | | if (!buf_eql_str(fn->return_type, "void")) { |
| 602 | if (fn->return_type) { |
| 423 | 603 | fprintf(p->f, " -> %s", buf_ptr(fn->return_type)); |
| 424 | 604 | } |
| 425 | 605 | fprintf(p->f, ";\n"); |
| 426 | 606 | } |
| 607 | p->cur_indent -= indent_size; |
| 427 | 608 | fprintf(f, "}\n"); |
| 428 | 609 | } |
| 429 | 610 | } |