main.zig (38472B)
1 const std = @import("std"); 2 3 const lib = @import("wgpu_gen_zig_lib"); 4 5 const Api = struct { 6 copyright: []const u8, 7 name: []const u8, 8 enum_prefix: i64, 9 doc: []const u8, 10 typedefs: []TypeDef, 11 constants: []Constant, 12 enums: []Enum, 13 bitflags: []BitFlag, 14 structs: []Struct, 15 callbacks: []Callback, 16 functions: []Function, 17 objects: []Object, 18 }; 19 20 const Constant64 = enum { 21 usize_max, 22 uint32_max, 23 uint64_max, 24 nan, 25 26 pub fn render(self: Constant64, writer: anytype) !void { 27 switch (self) { 28 .usize_max => try writer.writeAll("std.math.maxInt(usize)"), 29 .uint32_max => try writer.writeAll("std.math.maxInt(u32)"), 30 .uint64_max => try writer.writeAll("std.math.maxInt(u64)"), 31 .nan => try writer.writeAll("std.math.nan(f64)"), 32 } 33 } 34 }; 35 36 const Value64 = union(enum) { 37 int: u64, 38 float: f64, 39 constant: Constant64, 40 41 pub fn render(self: Value64, writer: anytype) !void { 42 switch (self) { 43 .int => |val| try writer.print("{d}", .{ val }), 44 .float => |val| try writer.print("{d}", .{ val }), 45 .constant => |val| try val.render(writer), 46 } 47 } 48 49 pub fn jsonParse( 50 allocator: std.mem.Allocator, 51 source: anytype, 52 options: std.json.ParseOptions, 53 ) !Value64 { 54 _ = allocator; 55 _ = options; 56 const token = try source.next(); 57 return switch (token) { 58 .number => |inner| { 59 const val = try std.fmt.parseInt(u64, inner, 10); 60 return .{ .int = val }; 61 }, 62 .string => |inner| { 63 const val = std.meta.stringToEnum(Constant64, inner) orelse { 64 return error.UnexpectedToken; 65 }; 66 return .{ .constant = val }; 67 }, 68 else => error.UnexpectedToken, 69 }; 70 } 71 }; 72 73 const PrimitiveType = enum { 74 c_void, 75 @"bool", 76 nullable_string, 77 string_with_default_empty, 78 out_string, 79 uint16, 80 uint32, 81 uint64, 82 @"usize", 83 int16, 84 int32, 85 float32, 86 nullable_float32, 87 float64, 88 float64_supertype, 89 @"array<bool>", 90 @"array<string>", 91 @"array<uint16>", 92 @"array<uint32>", 93 @"array<uint64>", 94 @"array<usize>", 95 @"array<int16>", 96 @"array<int32>", 97 @"array<float32>", 98 @"array<float64>", 99 100 pub fn isArray(self: PrimitiveType) bool { 101 return switch (self) { 102 .@"array<bool>" => true, 103 .@"array<string>" => true, 104 .@"array<uint16>" => true, 105 .@"array<uint32>" => true, 106 .@"array<uint64>" => true, 107 .@"array<usize>" => true, 108 .@"array<int16>" => true, 109 .@"array<int32>" => true, 110 .@"array<float32>" => true, 111 .@"array<float64>" => true, 112 else => false, 113 }; 114 } 115 116 fn toString(self: PrimitiveType) []const u8 { 117 return switch (self) { 118 .c_void => "anyopaque", 119 .bool => "bool", 120 .nullable_string => "StringView", 121 .string_with_default_empty => "StringView", 122 .out_string => "StringView", 123 .uint16 => "u16", 124 .uint32 => "u32", 125 .uint64 => "u64", 126 .usize => "usize", 127 .int16 => "i16", 128 .int32 => "i32", 129 .float32 => "f32", 130 .nullable_float32 => "f32", 131 .float64 => "f64", 132 .float64_supertype => "f64", 133 .@"array<bool>" => "bool", 134 .@"array<string>" => "StringView", 135 .@"array<uint16>" => "u16", 136 .@"array<uint32>" => "u32", 137 .@"array<uint64>" => "u64", 138 .@"array<usize>" => "usize", 139 .@"array<int16>" => "i16", 140 .@"array<int32>" => "i32", 141 .@"array<float32>" => "f32", 142 .@"array<float64>" => "f64", 143 }; 144 } 145 146 pub fn renderDefn( 147 self: PrimitiveType, 148 writer: anytype, 149 name: []const u8, 150 optional: bool, 151 pointer: ?Pointer, 152 indent: u32, 153 ) !void { 154 try renderIndent(indent, writer); 155 try writer.writeAll(name); 156 try writer.writeAll(": "); 157 if (optional) { 158 try writer.writeAll("?"); 159 } 160 if (self.isArray()) { 161 try writer.writeAll("[]"); 162 if (pointer) |ptr_type| { 163 switch (ptr_type) { 164 .immutable => try writer.writeAll("const "), 165 .mutable => {}, 166 } 167 } 168 } else { 169 if (pointer) |ptr_type| { 170 switch (ptr_type) { 171 .immutable => try writer.writeAll("*const "), 172 .mutable => try writer.writeAll("*"), 173 } 174 } 175 } 176 try writer.writeAll(self.toString()); 177 try writer.writeAll(",\n"); 178 } 179 180 pub fn renderExternDefn( 181 self: PrimitiveType, 182 writer: anytype, 183 name: []const u8, 184 optional: bool, 185 pointer: ?Pointer, 186 indent: u32, 187 ) !void { 188 if (self.isArray()) { 189 try renderIndent(indent, writer); 190 try writer.writeAll(name); 191 try writer.writeAll("_len: usize,\n"); 192 try renderIndent(indent, writer); 193 try writer.writeAll(name); 194 try writer.writeAll("_ptr: "); 195 if (optional) { 196 try writer.writeAll("?"); 197 } 198 try writer.writeAll("[*]"); 199 if (pointer) |ptr_type| { 200 switch (ptr_type) { 201 .immutable => try writer.writeAll("const "), 202 .mutable => {}, 203 } 204 } 205 try writer.writeAll(self.toString()); 206 try writer.writeAll(",\n"); 207 } else { 208 try renderIndent(indent, writer); 209 try writer.writeAll(name); 210 try writer.writeAll(": "); 211 if (optional) { 212 try writer.writeAll("?"); 213 } 214 if (pointer) |ptr_type| { 215 switch (ptr_type) { 216 .immutable => try writer.writeAll("*const "), 217 .mutable => try writer.writeAll("*"), 218 } 219 } 220 try writer.writeAll(self.toString()); 221 try writer.writeAll(",\n"); 222 } 223 } 224 225 pub fn renderExternCall( 226 self: PrimitiveType, 227 writer: anytype, 228 name: []const u8, 229 indent: u32, 230 ) !void { 231 if (self.isArray()) { 232 try renderIndent(indent, writer); 233 try writer.writeAll(name); 234 try writer.writeAll(".len,\n"); 235 try renderIndent(indent, writer); 236 try writer.writeAll(name); 237 try writer.writeAll(".ptr,\n"); 238 } else { 239 try renderIndent(indent, writer); 240 try writer.writeAll(name); 241 try writer.writeAll(",\n"); 242 } 243 } 244 245 fn renderReturn( 246 self: PrimitiveType, 247 writer: anytype, 248 optional: bool, 249 pointer: ?Pointer, 250 ) !void { 251 if (self.isArray()) { 252 return error.Unimplemented; 253 } 254 if (optional) { 255 try writer.writeAll("?"); 256 } 257 if (pointer) |ptr_type| { 258 try switch (ptr_type) { 259 .immutable => writer.writeAll("*const "), 260 .mutable => writer.writeAll("*"), 261 }; 262 } 263 try writer.writeAll(self.toString()); 264 } 265 }; 266 267 const ComplexType = union(enum) { 268 single: TypeInfo, 269 array: TypeInfo, 270 271 const Category = enum { 272 typedef, 273 @"enum", 274 bitflag, 275 @"struct", 276 function_type, 277 object 278 }; 279 280 const TypeInfo = struct { 281 category: Category, 282 name: []const u8, 283 }; 284 285 pub fn isArray(self: ComplexType) bool { 286 return switch (self) { 287 .single => false, 288 .array => true, 289 }; 290 } 291 292 pub fn renderDefn( 293 self: ComplexType, 294 writer: anytype, 295 name: []const u8, 296 optional: bool, 297 pointer: ?Pointer, 298 indent: u32, 299 ) !void { 300 try renderIndent(indent, writer); 301 try writer.writeAll(name); 302 try writer.writeAll(": "); 303 if (optional) { 304 try writer.writeAll("?"); 305 } 306 switch (self) { 307 .single => |single| { 308 if (pointer) |ptr_type| { 309 switch (ptr_type) { 310 .immutable => try writer.writeAll("*const "), 311 .mutable => try writer.writeAll("*"), 312 } 313 } else if (single.category == .object) { 314 try writer.writeAll("*"); 315 } 316 try snakeToPascal(single.name, writer); 317 }, 318 .array => |array| { 319 try writer.writeAll("[]"); 320 if (pointer) |ptr_type| { 321 switch (ptr_type) { 322 .immutable => try writer.writeAll("const "), 323 .mutable => {}, 324 } 325 } 326 try snakeToPascal(array.name, writer); 327 }, 328 } 329 try writer.writeAll(",\n"); 330 } 331 332 pub fn renderExternDefn( 333 self: ComplexType, 334 writer: anytype, 335 name: []const u8, 336 optional: bool, 337 pointer: ?Pointer, 338 indent: u32, 339 ) !void { 340 switch (self) { 341 .single => |single| { 342 try renderIndent(indent, writer); 343 try writer.writeAll(name); 344 try writer.writeAll(": "); 345 if (optional) { 346 try writer.writeAll("?"); 347 } 348 if (pointer) |ptr_type| { 349 switch (ptr_type) { 350 .immutable => try writer.writeAll("*const "), 351 .mutable => try writer.writeAll("*"), 352 } 353 } else if (single.category == .object) { 354 try writer.writeAll("*"); 355 } 356 try snakeToPascal(single.name, writer); 357 try writer.writeAll(",\n"); 358 }, 359 .array => |array| { 360 try renderIndent(indent, writer); 361 try writer.writeAll(name); 362 try writer.writeAll("_len: usize,\n"); 363 try renderIndent(indent, writer); 364 try writer.writeAll(name); 365 try writer.writeAll("_ptr: "); 366 if (optional) { 367 try writer.writeAll("?"); 368 } 369 try writer.writeAll("[*]"); 370 if (pointer) |ptr_type| { 371 switch (ptr_type) { 372 .immutable => try writer.writeAll("const "), 373 .mutable => {}, 374 } 375 } 376 try snakeToPascal(array.name, writer); 377 try writer.writeAll(",\n"); 378 }, 379 } 380 } 381 382 pub fn renderExternCall( 383 self: ComplexType, 384 writer: anytype, 385 name: []const u8, 386 indent: u32, 387 ) !void { 388 switch (self) { 389 .single => { 390 try renderIndent(indent, writer); 391 try writer.writeAll(name); 392 try writer.writeAll(",\n"); 393 }, 394 .array => { 395 try renderIndent(indent, writer); 396 try writer.writeAll(name); 397 try writer.writeAll(".len,\n"); 398 try renderIndent(indent, writer); 399 try writer.writeAll(name); 400 try writer.writeAll(".ptr,\n"); 401 }, 402 } 403 } 404 405 fn renderReturn( 406 self: ComplexType, 407 writer: anytype, 408 optional: bool, 409 pointer: ?Pointer, 410 ) !void { 411 switch (self) { 412 .single => |single| { 413 if (optional) { 414 try writer.writeAll("?"); 415 } 416 if (pointer) |ptr_type| { 417 try switch (ptr_type) { 418 .immutable => writer.writeAll("*const "), 419 .mutable => writer.writeAll("*"), 420 }; 421 } 422 try snakeToPascal(single.name, writer); 423 }, 424 .array => return error.Unimplemented, 425 } 426 } 427 428 pub fn parse(raw: []const u8) ?ComplexType { 429 var array = false; 430 var type_str = raw; 431 if (std.mem.startsWith(u8, raw, "array<")) { 432 array = true; 433 type_str = raw[6..raw.len - 1]; 434 } 435 const dot_idx = std.mem.indexOfScalar(u8, type_str, '.') orelse { 436 return null; 437 }; 438 const category = std.meta.stringToEnum( 439 Category, 440 type_str[0..dot_idx], 441 ) orelse return null; 442 const name = type_str[dot_idx + 1..]; 443 const type_info = TypeInfo{ 444 .category = category, 445 .name = name, 446 }; 447 if (array) { 448 return .{ .array = type_info }; 449 } else { 450 return .{ .single = type_info }; 451 } 452 } 453 454 pub fn jsonParse( 455 allocator: std.mem.Allocator, 456 source: anytype, 457 options: std.json.ParseOptions, 458 ) !ComplexType { 459 _ = allocator; 460 _ = options; 461 const token = try source.next(); 462 return switch (token) { 463 .string => |inner| { 464 if (parse(inner)) |val| { 465 return val; 466 } else { 467 return error.UnexpectedToken; 468 } 469 return parse(inner); 470 }, 471 else => error.UnexpectedToken, 472 }; 473 } 474 }; 475 476 const CallbackType = struct { 477 name: []const u8, 478 479 pub fn renderDefn( 480 self: CallbackType, 481 writer: anytype, 482 name: []const u8, 483 optional: bool, 484 pointer: ?Pointer, 485 indent: u32, 486 ) !void { 487 try renderIndent(indent, writer); 488 try writer.writeAll(name); 489 try writer.writeAll(": "); 490 if (optional) { 491 try writer.writeAll("?"); 492 } 493 if (pointer) |ptr_type| { 494 switch (ptr_type) { 495 .immutable => try writer.writeAll("*const "), 496 .mutable => try writer.writeAll("*"), 497 } 498 } 499 try snakeToPascal(self.name, writer); 500 try writer.writeAll("Callback,\n"); 501 } 502 503 pub fn renderExternDefn( 504 self: CallbackType, 505 writer: anytype, 506 name: []const u8, 507 optional: bool, 508 pointer: ?Pointer, 509 indent: u32, 510 ) !void { 511 try renderIndent(indent, writer); 512 try writer.writeAll(name); 513 try writer.writeAll(": "); 514 if (optional) { 515 try writer.writeAll("?"); 516 } 517 if (pointer) |ptr_type| { 518 switch (ptr_type) { 519 .immutable => try writer.writeAll("*const "), 520 .mutable => try writer.writeAll("*"), 521 } 522 } 523 try snakeToPascal(self.name, writer); 524 try writer.writeAll("Callback,\n"); 525 } 526 527 pub fn renderExternCall( 528 self: CallbackType, 529 writer: anytype, 530 name: []const u8, 531 indent: u32, 532 ) !void { 533 _ = self; 534 try renderIndent(indent, writer); 535 try writer.writeAll(name); 536 try writer.writeAll(",\n"); 537 } 538 539 pub fn parse(raw: []const u8) ?CallbackType { 540 if (!std.mem.startsWith(u8, raw, "callback.")) { 541 return null; 542 } 543 const name = raw[9..]; 544 return .{ .name = name }; 545 } 546 547 pub fn jsonParse( 548 allocator: std.mem.Allocator, 549 source: anytype, 550 options: std.json.ParseOptions, 551 ) !CallbackType { 552 _ = allocator; 553 _ = options; 554 const token = try source.next(); 555 return switch (token) { 556 .string => |inner| { 557 if (parse(inner)) |val| { 558 return val; 559 } else { 560 return error.UnexpectedToken; 561 } 562 return parse(inner); 563 }, 564 else => error.UnexpectedToken, 565 }; 566 } 567 }; 568 569 const Type = union(enum) { 570 primitive: PrimitiveType, 571 complex: ComplexType, 572 callback: CallbackType, 573 574 pub fn isArray(self: Type) bool { 575 return switch (self) { 576 .primitive => |inner| inner.isArray(), 577 .complex => |inner| inner.isArray(), 578 .callback => false, 579 }; 580 } 581 582 pub fn renderDefn( 583 self: Type, 584 writer: anytype, 585 name: []const u8, 586 optional: bool, 587 pointer: ?Pointer, 588 indent: u32, 589 ) !void { 590 return switch (self) { 591 inline else => |inner| inner.renderDefn( 592 writer, 593 name, 594 optional, 595 pointer, 596 indent 597 ), 598 }; 599 } 600 601 pub fn renderExternDefn( 602 self: Type, 603 writer: anytype, 604 name: []const u8, 605 optional: bool, 606 pointer: ?Pointer, 607 indent: u32, 608 ) !void { 609 return switch (self) { 610 inline else => |inner| inner.renderExternDefn( 611 writer, 612 name, 613 optional, 614 pointer, 615 indent 616 ), 617 }; 618 } 619 620 pub fn renderExternCall( 621 self: Type, 622 writer: anytype, 623 name: []const u8, 624 indent: u32, 625 ) !void { 626 return switch (self) { 627 inline else => |inner| inner.renderExternCall( 628 writer, 629 name, 630 indent 631 ), 632 }; 633 } 634 635 fn renderReturn( 636 self: Type, 637 writer: anytype, 638 optional: bool, 639 pointer: ?Pointer, 640 ) !void { 641 return switch (self) { 642 .callback => return error.Unimplemented, 643 inline else => |inner| inner.renderReturn( 644 writer, 645 optional, 646 pointer, 647 ), 648 }; 649 } 650 651 pub fn jsonParse( 652 allocator: std.mem.Allocator, 653 source: anytype, 654 options: std.json.ParseOptions, 655 ) !Type { 656 _ = options; 657 const token = try source.nextAlloc(allocator, .alloc_if_needed); 658 switch (token) { 659 .string, .allocated_string => |inner| { 660 if (std.meta.stringToEnum(PrimitiveType, inner)) |val| { 661 return .{ .primitive = val }; 662 } 663 if (ComplexType.parse(inner)) |val| { 664 return .{ .complex = val }; 665 } 666 if (CallbackType.parse(inner)) |val| { 667 return .{ .callback = val }; 668 } 669 return error.UnexpectedToken; 670 }, 671 else => return error.UnexpectedToken, 672 } 673 return .{ .primitive = .c_void }; 674 } 675 }; 676 677 const Pointer = enum { 678 immutable, 679 mutable, 680 }; 681 682 const CallbackStyle = enum { 683 callback_mode, 684 immediate, 685 }; 686 687 const DefaultValue = union(enum) { 688 string: []const u8, 689 number: u64, 690 @"bool": bool, 691 692 pub fn jsonParse( 693 allocator: std.mem.Allocator, 694 source: anytype, 695 options: std.json.ParseOptions, 696 ) !DefaultValue { 697 _ = allocator; 698 _ = options; 699 const token = try source.next(); 700 return switch (token) { 701 .number => |inner| { 702 const val = try std.fmt.parseInt(u64, inner, 10); 703 return .{ .number = val }; 704 }, 705 .string => |inner| { 706 return .{ .string = inner }; 707 }, 708 .true => { 709 return .{ .bool = true }; 710 }, 711 .false => { 712 return .{ .bool = false }; 713 }, 714 else => error.UnexpectedToken, 715 }; 716 } 717 }; 718 719 const ParameterType = struct { 720 name: []const u8, 721 doc: []const u8, 722 type: Type, 723 passed_with_ownership: ?bool = null, 724 pointer: ?Pointer = null, 725 optional: bool = false, 726 default: ?DefaultValue = null, 727 728 fn isArray(self: ParameterType) bool { 729 return self.type.isArray(); 730 } 731 732 fn renderDefn( 733 self: ParameterType, 734 writer: anytype, 735 indent: u32, 736 ) !void { 737 try renderDoc(self, writer, indent); 738 try self.type.renderDefn( 739 writer, 740 self.name, 741 self.optional, 742 self.pointer, 743 indent 744 ); 745 } 746 747 fn renderExternDefn( 748 self: ParameterType, 749 writer: anytype, 750 indent: u32, 751 ) !void { 752 try renderDoc(self, writer, indent); 753 try self.type.renderExternDefn( 754 writer, 755 self.name, 756 self.optional, 757 self.pointer, 758 indent 759 ); 760 } 761 762 fn renderExternCall( 763 self: ParameterType, 764 writer: anytype, 765 indent: u32, 766 ) !void { 767 return self.type.renderExternCall( 768 writer, 769 self.name, 770 indent 771 ); 772 } 773 }; 774 775 const TypeDef = struct { 776 name: []const u8, 777 namespace: ?[]const u8 = null, 778 doc: []const u8, 779 type: PrimitiveType, 780 }; 781 782 const Constant = struct { 783 name: []const u8, 784 namespace: ?[]const u8 = null, 785 value: Value64, 786 doc: []const u8, 787 788 pub fn render(self: Constant, writer: anytype) !void { 789 try writer.print("pub const {s} = ", .{ self.name }); 790 try self.value.render(writer); 791 try writer.writeAll(";\n"); 792 } 793 }; 794 795 const Enum = struct { 796 name: []const u8, 797 namespace: ?[]const u8 = null, 798 doc: []const u8, 799 extended: ?bool = null, 800 entries: []const ?EnumEntry, 801 802 pub fn render(self: Enum, writer: anytype) !void { 803 if (self.extended) |extended| { 804 _ = extended; 805 return error.Unimplemented; 806 } 807 try renderDoc(self, writer, 0); 808 try writer.writeAll("pub const "); 809 try snakeToPascal(self.name, writer); 810 try writer.writeAll(" = enum(u32) {\n"); 811 for (self.entries, 0..) |maybe_entry, i| { 812 if (maybe_entry) |entry| { 813 try renderDoc(entry, writer, 1); 814 try writer.writeAll(" "); 815 try entry.render(writer); 816 try writer.print(" = {d},\n", .{ i }); 817 } 818 } 819 try writer.writeAll("};\n"); 820 } 821 }; 822 823 const EnumEntry = struct { 824 name: []const u8, 825 namespace: ?[] const u8 = null, 826 doc: []const u8, 827 value: ?u16 = null, 828 829 pub fn render(self: EnumEntry, writer: anytype) !void { 830 if (self.value) |value| { 831 _ = value; 832 return error.Unimplemented; 833 } 834 try writer.writeAll("@\""); 835 try toLowercase(self.name, writer); 836 try writer.writeAll("\""); 837 } 838 }; 839 840 const BitFlag = struct { 841 name: []const u8, 842 namespace: ?[]const u8 = null, 843 doc: []const u8, 844 extended: ?bool = null, 845 entries: []const ?BitFlagEntry, 846 847 pub fn render(self: BitFlag, writer: anytype, indent: u32) !void { 848 try renderDoc(self, writer, indent); 849 try renderIndent(indent, writer); 850 try writer.writeAll("pub const "); 851 try snakeToPascal(self.name, writer); 852 try writer.writeAll(" = packed struct(u64) {\n"); 853 for (self.entries) |maybe_entry| { 854 if (maybe_entry) |entry| { 855 if (entry.value) |value| { 856 _ = value; 857 return error.Unimplemented; 858 } 859 if (entry.value_combination) |value| { 860 _ = value; 861 continue; 862 } 863 try renderDoc(entry, writer, indent + 1); 864 try renderIndent(indent + 1, writer); 865 try writer.print("{s}: bool = false,\n", .{ entry.name }); 866 } 867 } 868 for (self.entries) |maybe_entry| { 869 if (maybe_entry) |entry| { 870 if (entry.value_combination) |values| { 871 try writer.writeAll("\n"); 872 try renderDoc(entry, writer, indent + 1); 873 try renderIndent(indent + 1, writer); 874 try writer.writeAll("pub fn "); 875 try snakeToCamel(entry.name, writer); 876 try writer.writeAll("() "); 877 try snakeToPascal(self.name, writer); 878 try writer.writeAll(" {\n"); 879 try renderIndent(indent + 2, writer); 880 try writer.writeAll("return .{\n"); 881 for (values) |value| { 882 try renderIndent(indent + 3, writer); 883 try writer.print(".{s} = true,\n", .{ value }); 884 } 885 try renderIndent(indent + 2, writer); 886 try writer.writeAll("};\n"); 887 try renderIndent(indent + 1, writer); 888 try writer.writeAll("}\n"); 889 } 890 } 891 } 892 try renderIndent(indent, writer); 893 try writer.writeAll("};\n"); 894 } 895 }; 896 897 const BitFlagEntry = struct { 898 name: []const u8, 899 namespace: ?[]const u8 = null, 900 doc: []const u8, 901 value: ?Value64 = null, 902 value_combination: ?[][]const u8 = null, 903 }; 904 905 const StructType = enum { 906 extensible, 907 extensible_callback_arg, 908 extension, 909 standalone, 910 }; 911 912 const Struct = struct { 913 name: []const u8, 914 namespace: ?[]const u8 = null, 915 doc: []const u8, 916 type: StructType, 917 extends: ?[][]const u8 = null, 918 free_members: ?bool = null, 919 members: ?[]ParameterType = null, 920 921 fn render(self: Struct, writer: anytype, indent: u32) !void { 922 try renderDoc(self, writer, indent); 923 try renderIndent(indent, writer); 924 try writer.writeAll("pub const "); 925 try snakeToPascal(self.name, writer); 926 try writer.writeAll(" = extern struct {\n"); 927 switch (self.type) { 928 .extensible, .extensible_callback_arg => { 929 try renderIndent(indent + 1, writer); 930 try writer.writeAll("next: ?*const ChainedStruct = null,\n"); 931 }, 932 .extension => { 933 try renderIndent(indent + 1, writer); 934 try writer.writeAll("chain: ChainedStruct = .{\n"); 935 try renderIndent(indent + 2, writer); 936 try writer.writeAll(".next = null,\n"); 937 try renderIndent(indent + 2, writer); 938 try writer.writeAll(".struct_type = ."); 939 try toLowercase(self.name, writer); 940 try writer.writeAll(",\n"); 941 try renderIndent(indent + 1, writer); 942 try writer.writeAll("},\n"); 943 }, 944 .standalone => {}, 945 } 946 if (self.members) |members| { 947 for (members) |member| { 948 try member.renderExternDefn(writer, indent + 1); 949 } 950 for (members) |member| { 951 if (member.isArray()) { 952 try writer.writeAll("\n"); 953 try self.renderInitMethod(writer, indent + 1); 954 break; 955 } 956 } 957 } 958 try renderIndent(indent, writer); 959 try writer.writeAll("};\n"); 960 } 961 962 fn renderInitMethod(self: Struct, writer: anytype, indent: u32) !void { 963 try renderIndent(indent, writer); 964 try writer.writeAll("pub fn init(\n"); 965 if (self.members) |members| { 966 for (members) |member| { 967 try member.renderDefn(writer, indent + 1); 968 } 969 } 970 try renderIndent(indent, writer); 971 try writer.writeAll(") "); 972 try snakeToPascal(self.name, writer); 973 try writer.writeAll(" {\n"); 974 try renderIndent(indent + 1, writer); 975 try writer.writeAll("return .{\n"); 976 if (self.members) |members| { 977 for (members) |member| { 978 try member.renderExternCall(writer, indent + 1); 979 } 980 } 981 try renderIndent(indent + 1, writer); 982 try writer.writeAll("};\n"); 983 try renderIndent(indent, writer); 984 try writer.writeAll("}\n"); 985 } 986 }; 987 988 const Callback = struct { 989 name: []const u8, 990 namespace: ?[]const u8 = null, 991 doc: []const u8, 992 style: CallbackStyle, 993 args: ?[]ParameterType = null, 994 995 pub fn render(self: Callback, writer: anytype) !void { 996 try writer.writeAll("pub const "); 997 try snakeToPascal(self.name, writer); 998 try writer.writeAll("Callback = fn (\n"); 999 if (self.args) |args| { 1000 for (args) |arg| { 1001 try arg.renderExternDefn(writer, 1); 1002 } 1003 } 1004 try writer.writeAll(") callconv(.C) void;\n"); 1005 } 1006 }; 1007 1008 const ReturnType = struct { 1009 doc: []const u8, 1010 type: Type, 1011 optional: bool = false, 1012 passed_with_ownership: ?bool = null, 1013 pointer: ?Pointer = null, 1014 1015 pub fn render(self: ReturnType, writer: anytype) !void { 1016 try self.type.renderReturn( 1017 writer, 1018 self.optional, 1019 self.pointer, 1020 ); 1021 } 1022 }; 1023 1024 const Function = struct { 1025 name: []const u8, 1026 namespace: ?[]const u8 = null, 1027 doc: []const u8, 1028 returns: ?ReturnType = null, 1029 callback: ?CallbackType = null, 1030 args: ?[]ParameterType = null, 1031 1032 fn renderArgsExternDefn( 1033 self: Function, 1034 writer: anytype, 1035 object: ?[]const u8, 1036 indent: u32 1037 ) !void { 1038 if (object) |obj| { 1039 try renderIndent(indent, writer); 1040 try writer.writeAll("self: *"); 1041 try snakeToPascal(obj, writer); 1042 try writer.writeAll(",\n"); 1043 } 1044 if (self.args) |args| { 1045 for (args) |arg| { 1046 try arg.renderExternDefn(writer, indent); 1047 } 1048 } 1049 } 1050 1051 fn renderArgsExternCall( 1052 self: Function, 1053 writer: anytype, 1054 object: ?[]const u8, 1055 indent: u32 1056 ) !void { 1057 if (object) |_| { 1058 try renderIndent(indent, writer); 1059 try writer.writeAll("self,\n"); 1060 } 1061 if (self.args) |args| { 1062 for (args) |arg| { 1063 try arg.renderExternCall(writer, indent); 1064 } 1065 } 1066 } 1067 1068 fn renderArgsDefn( 1069 self: Function, 1070 writer: anytype, 1071 object: ?[]const u8, 1072 indent: u32 1073 ) !void { 1074 if (object) |obj| { 1075 try renderIndent(indent, writer); 1076 try writer.writeAll("self: *"); 1077 try snakeToPascal(obj, writer); 1078 try writer.writeAll(",\n"); 1079 } 1080 if (self.args) |args| { 1081 for (args) |arg| { 1082 try arg.renderDefn(writer, indent); 1083 } 1084 } 1085 } 1086 1087 fn renderExternDefn( 1088 self: Function, 1089 writer: anytype, 1090 object: ?[]const u8, 1091 indent: u32, 1092 ) !void { 1093 try renderDoc(self, writer, indent); 1094 try renderIndent(indent, writer); 1095 try writer.writeAll("extern fn wgpu"); 1096 if (object) |obj| { 1097 try snakeToPascal(obj, writer); 1098 } 1099 try snakeToPascal(self.name, writer); 1100 try writer.writeAll("(\n"); 1101 try self.renderArgsExternDefn(writer, object, indent + 1); 1102 try renderIndent(indent, writer); 1103 try writer.writeAll(") "); 1104 if (self.returns) |returns| { 1105 try returns.render(writer); 1106 } else { 1107 try writer.writeAll("void"); 1108 } 1109 try writer.writeAll(";\n"); 1110 } 1111 1112 fn renderExternCall( 1113 self: Function, 1114 writer: anytype, 1115 object: ?[]const u8, 1116 indent: u32, 1117 ) !void { 1118 try writer.writeAll("wgpu"); 1119 if (object) |obj| { 1120 try snakeToPascal(obj, writer); 1121 } 1122 try snakeToPascal(self.name, writer); 1123 try writer.writeAll("(\n"); 1124 try self.renderArgsExternCall(writer, object, indent + 1); 1125 try renderIndent(indent, writer); 1126 try writer.writeAll(");\n"); 1127 } 1128 1129 fn renderDefn( 1130 self: Function, 1131 writer: anytype, 1132 object: ?[]const u8, 1133 indent: u32, 1134 ) !void { 1135 try renderDoc(self, writer, indent); 1136 try renderIndent(indent, writer); 1137 try writer.writeAll("pub fn "); 1138 try snakeToCamel(self.name, writer); 1139 try writer.writeAll("(\n"); 1140 try self.renderArgsDefn(writer, object, indent + 1); 1141 try renderIndent(indent, writer); 1142 try writer.writeAll(") "); 1143 if (self.returns) |returns| { 1144 try returns.render(writer); 1145 } else { 1146 try writer.writeAll("void"); 1147 } 1148 try writer.writeAll(" {\n"); 1149 try renderIndent(indent + 1, writer); 1150 if (self.returns) |_| { 1151 try writer.writeAll("return "); 1152 } 1153 try self.renderExternCall(writer, object, indent + 1); 1154 try renderIndent(indent, writer); 1155 try writer.writeAll("}\n"); 1156 } 1157 }; 1158 1159 const Object = struct { 1160 name: []const u8, 1161 namespace: ?[]const u8 = null, 1162 doc: []const u8, 1163 extended: ?bool = null, 1164 methods: []Function, 1165 1166 pub fn render(self: Object, writer: anytype, indent: u32) !void { 1167 for (self.methods) |method| { 1168 try method.renderExternDefn(writer, self.name, indent); 1169 try writer.writeAll("\n"); 1170 } 1171 1172 try renderDoc(self, writer, indent); 1173 try renderIndent(indent, writer); 1174 try writer.writeAll("pub const "); 1175 try snakeToPascal(self.name, writer); 1176 try writer.writeAll(" = opaque {\n"); 1177 try renderIndent(indent, writer); 1178 for (self.methods) |method| { 1179 try method.renderDefn(writer, self.name, indent + 1); 1180 try writer.writeAll("\n"); 1181 } 1182 try writer.writeAll("};\n"); 1183 } 1184 }; 1185 1186 fn snakeToPascal(str: []const u8, writer: anytype) !void { 1187 var it = std.mem.splitScalar(u8, str, '_'); 1188 while (it.next()) |token| { 1189 try writer.writeByte(std.ascii.toUpper(token[0])); 1190 try writer.writeAll(token[1..]); 1191 } 1192 } 1193 1194 fn snakeToCamel(str: []const u8, writer: anytype) !void { 1195 var it = std.mem.splitScalar(u8, str, '_'); 1196 if (it.next()) |token| { 1197 try writer.writeAll(token); 1198 } 1199 while (it.next()) |token| { 1200 try writer.writeByte(std.ascii.toUpper(token[0])); 1201 try writer.writeAll(token[1..]); 1202 } 1203 } 1204 1205 fn toLowercase(str: []const u8, writer: anytype) !void { 1206 for (str) |anycase| { 1207 const lowercase = std.ascii.toLower(anycase); 1208 try writer.writeByte(lowercase); 1209 } 1210 } 1211 1212 fn renderDoc( 1213 self: anytype, 1214 writer: anytype, 1215 indent: u32, 1216 ) !void { 1217 if (std.mem.startsWith(u8, self.doc, "TODO")) { 1218 return; 1219 } 1220 var it = std.mem.splitScalar(u8, self.doc, '\n'); 1221 while (it.next()) |token| { 1222 if (token.len <= 0) { 1223 continue; 1224 } 1225 try renderIndent(indent, writer); 1226 try writer.print("/// {s}", .{ token }); 1227 if (!std.mem.endsWith(u8, token, "\n")) { 1228 try writer.writeAll("\n"); 1229 } 1230 } 1231 } 1232 1233 fn renderIndent(indent: u32, writer: anytype) !void { 1234 for (0..indent) |_| { 1235 try writer.writeAll(" "); 1236 } 1237 } 1238 1239 fn renderHeader(writer: anytype) !void { 1240 const header = 1241 \\// auto-generated zig binding for webgpu 1242 \\ 1243 \\const std = @import("std"); 1244 \\ 1245 \\pub const ChainedStruct = extern struct { 1246 \\ next: ?*const ChainedStruct, 1247 \\ struct_type: SType, 1248 \\}; 1249 \\ 1250 \\pub const StringView = extern struct { 1251 \\ data: ?[*]const u8 = null, 1252 \\ length: usize = null_terminated, 1253 \\ 1254 \\ const null_terminated = std.math.maxInt(usize); 1255 \\ 1256 \\ pub fn fromSlice(slice: []const u8) StringView { 1257 \\ return .{ 1258 \\ .data = slice.ptr, 1259 \\ .length = slice.len, 1260 \\ }; 1261 \\ } 1262 \\ 1263 \\ pub fn toSlice(self: StringView) ?[]const u8 { 1264 \\ const data = self.data orelse return null; 1265 \\ if (self.length == null_terminated) { 1266 \\ const slice = std.mem.span( 1267 \\ @as([*:0]const u8, @ptrCast(data)) 1268 \\ ); 1269 \\ return slice[0..slice.len]; 1270 \\ } 1271 \\ return data[0..self.length]; 1272 \\ } 1273 \\}; 1274 \\ 1275 ; 1276 try writer.writeAll(header); 1277 try writer.writeAll("\n"); 1278 } 1279 1280 pub fn main() !void { 1281 var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 1282 const allocator = gpa.allocator(); 1283 1284 const args = try std.process.argsAlloc(allocator); 1285 const out_path = args[1]; 1286 const out_file = try std.fs.cwd().createFile(out_path, .{}); 1287 defer out_file.close(); 1288 1289 const file = try std.fs.cwd().openFile("webgpu.json", .{}); 1290 defer file.close(); 1291 1292 const json = try file.readToEndAlloc(allocator, 10 * 2024 * 1024); 1293 defer allocator.free(json); 1294 1295 const parsed = try std.json.parseFromSlice( 1296 Api, 1297 allocator, 1298 json, 1299 .{ 1300 .ignore_unknown_fields = true, 1301 }, 1302 ); 1303 defer parsed.deinit(); 1304 1305 const stdout = out_file.writer(); 1306 1307 try renderHeader(stdout); 1308 try stdout.writeAll("// constants\n"); 1309 for (parsed.value.constants) |constant| { 1310 try constant.render(stdout); 1311 } 1312 try stdout.writeAll("// enums\n"); 1313 for (parsed.value.enums) |entry| { 1314 try entry.render(stdout); 1315 try stdout.writeAll("\n"); 1316 } 1317 try stdout.writeAll("// bitflags\n"); 1318 for (parsed.value.bitflags) |bitflag| { 1319 try bitflag.render(stdout, 0); 1320 try stdout.writeAll("\n"); 1321 } 1322 try stdout.writeAll("// structs\n"); 1323 for (parsed.value.structs) |obj| { 1324 try obj.render(stdout, 0); 1325 try stdout.writeAll("\n"); 1326 } 1327 try stdout.writeAll("// callbacks\n"); 1328 for (parsed.value.callbacks) |callback| { 1329 try callback.render(stdout); 1330 try stdout.writeAll("\n"); 1331 } 1332 try stdout.writeAll("// extern functions\n"); 1333 for (parsed.value.functions) |function| { 1334 try function.renderExternDefn(stdout, null, 0); 1335 try stdout.writeAll("\n"); 1336 } 1337 try stdout.writeAll("// methods\n"); 1338 for (parsed.value.objects) |object| { 1339 try object.render(stdout, 0); 1340 try stdout.writeAll("\n"); 1341 } 1342 } 1343