zig-webgpu-gen

git clone git://git.electrosoup.com/zig-webgpu-gen
Log | Files | Refs

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