| ... | ... | @@ -1,5 +1,8 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const g = @import("spirv/grammar.zig"); |
| 3 | const Allocator = std.mem.Allocator; |
| 4 | |
| 5 | const ExtendedStructSet = std.StringHashMap(void); |
| 3 | 6 | |
| 4 | 7 | pub fn main() !void { |
| 5 | 8 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| ... | ... | @@ -20,101 +23,308 @@ pub fn main() !void { |
| 20 | 23 | var tokens = std.json.TokenStream.init(spec); |
| 21 | 24 | var registry = try std.json.parse(g.Registry, &tokens, .{ .allocator = allocator }); |
| 22 | 25 | |
| 26 | const core_reg = switch (registry) { |
| 27 | .core => |core_reg| core_reg, |
| 28 | .extension => return error.TODOSpirVExtensionSpec, |
| 29 | }; |
| 30 | |
| 23 | 31 | var bw = std.io.bufferedWriter(std.io.getStdOut().writer()); |
| 24 | | try render(bw.writer(), registry); |
| 32 | try render(bw.writer(), allocator, core_reg); |
| 25 | 33 | try bw.flush(); |
| 26 | 34 | } |
| 27 | 35 | |
| 28 | | fn render(writer: anytype, registry: g.Registry) !void { |
| 36 | /// Returns a set with types that require an extra struct for the `Instruction` interface |
| 37 | /// to the spir-v spec, or whether the original type can be used. |
| 38 | fn extendedStructs( |
| 39 | arena: Allocator, |
| 40 | kinds: []const g.OperandKind, |
| 41 | ) !ExtendedStructSet { |
| 42 | var map = ExtendedStructSet.init(arena); |
| 43 | try map.ensureTotalCapacity(@intCast(u32, kinds.len)); |
| 44 | |
| 45 | for (kinds) |kind| { |
| 46 | const enumerants = kind.enumerants orelse continue; |
| 47 | |
| 48 | for (enumerants) |enumerant| { |
| 49 | if (enumerant.parameters.len > 0) { |
| 50 | break; |
| 51 | } |
| 52 | } else continue; |
| 53 | |
| 54 | map.putAssumeCapacity(kind.kind, {}); |
| 55 | } |
| 56 | |
| 57 | return map; |
| 58 | } |
| 59 | |
| 60 | // Return a score for a particular priority. Duplicate instruction/operand enum values are |
| 61 | // removed by picking the tag with the lowest score to keep, and by making an alias for the |
| 62 | // other. Note that the tag does not need to be just a tag at this point, in which case it |
| 63 | // gets the lowest score automatically anyway. |
| 64 | fn tagPriorityScore(tag: []const u8) usize { |
| 65 | if (tag.len == 0) { |
| 66 | return 1; |
| 67 | } else if (std.mem.eql(u8, tag, "EXT")) { |
| 68 | return 2; |
| 69 | } else if (std.mem.eql(u8, tag, "KHR")) { |
| 70 | return 3; |
| 71 | } else { |
| 72 | return 4; |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | fn render(writer: anytype, allocator: Allocator, registry: g.CoreRegistry) !void { |
| 29 | 77 | try writer.writeAll( |
| 30 | 78 | \\//! This file is auto-generated by tools/gen_spirv_spec.zig. |
| 31 | 79 | \\ |
| 32 | 80 | \\const Version = @import("std").builtin.Version; |
| 33 | 81 | \\ |
| 82 | \\pub const Word = u32; |
| 83 | \\pub const IdResultType = struct{ |
| 84 | \\ id: Word, |
| 85 | \\ pub fn toRef(self: IdResultType) IdRef { |
| 86 | \\ return .{.id = self.id}; |
| 87 | \\ } |
| 88 | \\}; |
| 89 | \\pub const IdResult = struct{ |
| 90 | \\ id: Word, |
| 91 | \\ pub fn toRef(self: IdResult) IdRef { |
| 92 | \\ return .{.id = self.id}; |
| 93 | \\ } |
| 94 | \\ pub fn toResultType(self: IdResult) IdResultType { |
| 95 | \\ return .{.id = self.id}; |
| 96 | \\ } |
| 97 | \\}; |
| 98 | \\pub const IdRef = struct{ id: Word }; |
| 99 | \\ |
| 100 | \\pub const IdMemorySemantics = IdRef; |
| 101 | \\pub const IdScope = IdRef; |
| 102 | \\ |
| 103 | \\pub const LiteralInteger = Word; |
| 104 | \\pub const LiteralString = []const u8; |
| 105 | \\pub const LiteralContextDependentNumber = union(enum) { |
| 106 | \\ int32: i32, |
| 107 | \\ uint32: u32, |
| 108 | \\ int64: i64, |
| 109 | \\ uint64: u64, |
| 110 | \\ float32: f32, |
| 111 | \\ float64: f64, |
| 112 | \\}; |
| 113 | \\pub const LiteralExtInstInteger = struct{ inst: Word }; |
| 114 | \\pub const LiteralSpecConstantOpInteger = struct { opcode: Opcode }; |
| 115 | \\pub const PairLiteralIntegerIdRef = struct { value: LiteralInteger, label: IdRef }; |
| 116 | \\pub const PairIdRefLiteralInteger = struct { target: IdRef, member: LiteralInteger }; |
| 117 | \\pub const PairIdRefIdRef = [2]IdRef; |
| 118 | \\ |
| 119 | \\ |
| 34 | 120 | ); |
| 35 | 121 | |
| 36 | | switch (registry) { |
| 37 | | .core => |core_reg| { |
| 38 | | try writer.print( |
| 39 | | \\pub const version = Version{{ .major = {}, .minor = {}, .patch = {} }}; |
| 40 | | \\pub const magic_number: u32 = {s}; |
| 41 | | \\ |
| 42 | | , |
| 43 | | .{ core_reg.major_version, core_reg.minor_version, core_reg.revision, core_reg.magic_number }, |
| 44 | | ); |
| 45 | | try renderOpcodes(writer, core_reg.instructions); |
| 46 | | try renderOperandKinds(writer, core_reg.operand_kinds); |
| 47 | | }, |
| 48 | | .extension => |ext_reg| { |
| 49 | | try writer.print( |
| 50 | | \\pub const version = Version{{ .major = {}, .minor = 0, .patch = {} }}; |
| 51 | | \\ |
| 52 | | , |
| 53 | | .{ ext_reg.version, ext_reg.revision }, |
| 54 | | ); |
| 55 | | try renderOpcodes(writer, ext_reg.instructions); |
| 56 | | try renderOperandKinds(writer, ext_reg.operand_kinds); |
| 57 | | }, |
| 58 | | } |
| 122 | try writer.print( |
| 123 | \\pub const version = Version{{ .major = {}, .minor = {}, .patch = {} }}; |
| 124 | \\pub const magic_number: Word = {s}; |
| 125 | \\ |
| 126 | , |
| 127 | .{ registry.major_version, registry.minor_version, registry.revision, registry.magic_number }, |
| 128 | ); |
| 129 | const extended_structs = try extendedStructs(allocator, registry.operand_kinds); |
| 130 | try renderOpcodes(writer, allocator, registry.instructions, extended_structs); |
| 131 | try renderOperandKinds(writer, allocator, registry.operand_kinds, extended_structs); |
| 59 | 132 | } |
| 60 | 133 | |
| 61 | | fn renderOpcodes(writer: anytype, instructions: []const g.Instruction) !void { |
| 62 | | try writer.writeAll("pub const Opcode = extern enum(u16) {\n"); |
| 63 | | for (instructions) |instr| { |
| 64 | | try writer.print(" {} = {},\n", .{ std.zig.fmtId(instr.opname), instr.opcode }); |
| 134 | fn renderOpcodes( |
| 135 | writer: anytype, |
| 136 | allocator: Allocator, |
| 137 | instructions: []const g.Instruction, |
| 138 | extended_structs: ExtendedStructSet, |
| 139 | ) !void { |
| 140 | var inst_map = std.AutoArrayHashMap(u32, usize).init(allocator); |
| 141 | try inst_map.ensureTotalCapacity(instructions.len); |
| 142 | |
| 143 | var aliases = std.ArrayList(struct { inst: usize, alias: usize }).init(allocator); |
| 144 | try aliases.ensureTotalCapacity(instructions.len); |
| 145 | |
| 146 | for (instructions) |inst, i| { |
| 147 | const result = inst_map.getOrPutAssumeCapacity(inst.opcode); |
| 148 | if (!result.found_existing) { |
| 149 | result.value_ptr.* = i; |
| 150 | continue; |
| 151 | } |
| 152 | |
| 153 | const existing = instructions[result.value_ptr.*]; |
| 154 | |
| 155 | const tag_index = std.mem.indexOfDiff(u8, inst.opname, existing.opname).?; |
| 156 | const inst_priority = tagPriorityScore(inst.opname[tag_index..]); |
| 157 | const existing_priority = tagPriorityScore(existing.opname[tag_index..]); |
| 158 | |
| 159 | if (inst_priority < existing_priority) { |
| 160 | aliases.appendAssumeCapacity(.{ .inst = result.value_ptr.*, .alias = i }); |
| 161 | result.value_ptr.* = i; |
| 162 | } else { |
| 163 | aliases.appendAssumeCapacity(.{ .inst = i, .alias = result.value_ptr.* }); |
| 164 | } |
| 65 | 165 | } |
| 66 | | try writer.writeAll(" _,\n};\n"); |
| 166 | |
| 167 | const instructions_indices = inst_map.values(); |
| 168 | |
| 169 | try writer.writeAll("pub const Opcode = enum(u16) {\n"); |
| 170 | for (instructions_indices) |i| { |
| 171 | const inst = instructions[i]; |
| 172 | try writer.print("{} = {},\n", .{ std.zig.fmtId(inst.opname), inst.opcode }); |
| 173 | } |
| 174 | |
| 175 | try writer.writeByte('\n'); |
| 176 | |
| 177 | for (aliases.items) |alias| { |
| 178 | try writer.print("pub const {} = Opcode.{};\n", .{ |
| 179 | std.zig.fmtId(instructions[alias.inst].opname), |
| 180 | std.zig.fmtId(instructions[alias.alias].opname), |
| 181 | }); |
| 182 | } |
| 183 | |
| 184 | try writer.writeAll( |
| 185 | \\ |
| 186 | \\pub fn Operands(comptime self: Opcode) type { |
| 187 | \\return switch (self) { |
| 188 | \\ |
| 189 | ); |
| 190 | |
| 191 | for (instructions_indices) |i| { |
| 192 | const inst = instructions[i]; |
| 193 | try renderOperand(writer, .instruction, inst.opname, inst.operands, extended_structs); |
| 194 | } |
| 195 | try writer.writeAll("};\n}\n};\n"); |
| 196 | _ = extended_structs; |
| 67 | 197 | } |
| 68 | 198 | |
| 69 | | fn renderOperandKinds(writer: anytype, kinds: []const g.OperandKind) !void { |
| 199 | fn renderOperandKinds( |
| 200 | writer: anytype, |
| 201 | allocator: Allocator, |
| 202 | kinds: []const g.OperandKind, |
| 203 | extended_structs: ExtendedStructSet, |
| 204 | ) !void { |
| 70 | 205 | for (kinds) |kind| { |
| 71 | 206 | switch (kind.category) { |
| 72 | | .ValueEnum => try renderValueEnum(writer, kind), |
| 73 | | .BitEnum => try renderBitEnum(writer, kind), |
| 207 | .ValueEnum => try renderValueEnum(writer, allocator, kind, extended_structs), |
| 208 | .BitEnum => try renderBitEnum(writer, allocator, kind, extended_structs), |
| 74 | 209 | else => {}, |
| 75 | 210 | } |
| 76 | 211 | } |
| 77 | 212 | } |
| 78 | 213 | |
| 79 | | fn renderValueEnum(writer: anytype, enumeration: g.OperandKind) !void { |
| 80 | | try writer.print("pub const {s} = extern enum(u32) {{\n", .{enumeration.kind}); |
| 81 | | |
| 214 | fn renderValueEnum( |
| 215 | writer: anytype, |
| 216 | allocator: Allocator, |
| 217 | enumeration: g.OperandKind, |
| 218 | extended_structs: ExtendedStructSet, |
| 219 | ) !void { |
| 82 | 220 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; |
| 83 | | for (enumerants) |enumerant| { |
| 221 | |
| 222 | var enum_map = std.AutoArrayHashMap(u32, usize).init(allocator); |
| 223 | try enum_map.ensureTotalCapacity(enumerants.len); |
| 224 | |
| 225 | var aliases = std.ArrayList(struct { enumerant: usize, alias: usize }).init(allocator); |
| 226 | try aliases.ensureTotalCapacity(enumerants.len); |
| 227 | |
| 228 | for (enumerants) |enumerant, i| { |
| 229 | const result = enum_map.getOrPutAssumeCapacity(enumerant.value.int); |
| 230 | if (!result.found_existing) { |
| 231 | result.value_ptr.* = i; |
| 232 | continue; |
| 233 | } |
| 234 | |
| 235 | const existing = enumerants[result.value_ptr.*]; |
| 236 | |
| 237 | const tag_index = std.mem.indexOfDiff(u8, enumerant.enumerant, existing.enumerant).?; |
| 238 | const enum_priority = tagPriorityScore(enumerant.enumerant[tag_index..]); |
| 239 | const existing_priority = tagPriorityScore(existing.enumerant[tag_index..]); |
| 240 | |
| 241 | if (enum_priority < existing_priority) { |
| 242 | aliases.appendAssumeCapacity(.{ .enumerant = result.value_ptr.*, .alias = i }); |
| 243 | result.value_ptr.* = i; |
| 244 | } else { |
| 245 | aliases.appendAssumeCapacity(.{ .enumerant = i, .alias = result.value_ptr.* }); |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | const enum_indices = enum_map.values(); |
| 250 | |
| 251 | try writer.print("pub const {s} = enum(u32) {{\n", .{std.zig.fmtId(enumeration.kind)}); |
| 252 | |
| 253 | for (enum_indices) |i| { |
| 254 | const enumerant = enumerants[i]; |
| 84 | 255 | if (enumerant.value != .int) return error.InvalidRegistry; |
| 85 | 256 | |
| 86 | | try writer.print(" {} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), enumerant.value.int }); |
| 257 | try writer.print("{} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), enumerant.value.int }); |
| 258 | } |
| 259 | |
| 260 | try writer.writeByte('\n'); |
| 261 | |
| 262 | for (aliases.items) |alias| { |
| 263 | try writer.print("pub const {} = {}.{};\n", .{ |
| 264 | std.zig.fmtId(enumerants[alias.enumerant].enumerant), |
| 265 | std.zig.fmtId(enumeration.kind), |
| 266 | std.zig.fmtId(enumerants[alias.alias].enumerant), |
| 267 | }); |
| 268 | } |
| 269 | |
| 270 | if (!extended_structs.contains(enumeration.kind)) { |
| 271 | try writer.writeAll("};\n"); |
| 272 | return; |
| 273 | } |
| 274 | |
| 275 | try writer.print("\npub const Extended = union({}) {{\n", .{std.zig.fmtId(enumeration.kind)}); |
| 276 | |
| 277 | for (enum_indices) |i| { |
| 278 | const enumerant = enumerants[i]; |
| 279 | try renderOperand(writer, .@"union", enumerant.enumerant, enumerant.parameters, extended_structs); |
| 87 | 280 | } |
| 88 | 281 | |
| 89 | | try writer.writeAll(" _,\n};\n"); |
| 282 | try writer.writeAll("};\n};\n"); |
| 90 | 283 | } |
| 91 | 284 | |
| 92 | | fn renderBitEnum(writer: anytype, enumeration: g.OperandKind) !void { |
| 93 | | try writer.print("pub const {s} = packed struct {{\n", .{enumeration.kind}); |
| 285 | fn renderBitEnum( |
| 286 | writer: anytype, |
| 287 | allocator: Allocator, |
| 288 | enumeration: g.OperandKind, |
| 289 | extended_structs: ExtendedStructSet, |
| 290 | ) !void { |
| 291 | try writer.print("pub const {s} = packed struct {{\n", .{std.zig.fmtId(enumeration.kind)}); |
| 94 | 292 | |
| 95 | | var flags_by_bitpos = [_]?[]const u8{null} ** 32; |
| 293 | var flags_by_bitpos = [_]?usize{null} ** 32; |
| 96 | 294 | const enumerants = enumeration.enumerants orelse return error.InvalidRegistry; |
| 97 | | for (enumerants) |enumerant| { |
| 295 | |
| 296 | var aliases = std.ArrayList(struct { flag: usize, alias: u5 }).init(allocator); |
| 297 | try aliases.ensureTotalCapacity(enumerants.len); |
| 298 | |
| 299 | for (enumerants) |enumerant, i| { |
| 98 | 300 | if (enumerant.value != .bitflag) return error.InvalidRegistry; |
| 99 | 301 | const value = try parseHexInt(enumerant.value.bitflag); |
| 100 | | if (@popCount(u32, value) != 1) { |
| 101 | | continue; // Skip combinations and 'none' items |
| 302 | if (@popCount(u32, value) == 0) { |
| 303 | continue; // Skip 'none' items |
| 102 | 304 | } |
| 103 | 305 | |
| 306 | std.debug.assert(@popCount(u32, value) == 1); |
| 307 | |
| 104 | 308 | var bitpos = std.math.log2_int(u32, value); |
| 105 | 309 | if (flags_by_bitpos[bitpos]) |*existing| { |
| 106 | | // Keep the shortest |
| 107 | | if (enumerant.enumerant.len < existing.len) |
| 108 | | existing.* = enumerant.enumerant; |
| 310 | const tag_index = std.mem.indexOfDiff(u8, enumerant.enumerant, enumerants[existing.*].enumerant).?; |
| 311 | const enum_priority = tagPriorityScore(enumerant.enumerant[tag_index..]); |
| 312 | const existing_priority = tagPriorityScore(enumerants[existing.*].enumerant[tag_index..]); |
| 313 | |
| 314 | if (enum_priority < existing_priority) { |
| 315 | aliases.appendAssumeCapacity(.{ .flag = existing.*, .alias = bitpos }); |
| 316 | existing.* = i; |
| 317 | } else { |
| 318 | aliases.appendAssumeCapacity(.{ .flag = i, .alias = bitpos }); |
| 319 | } |
| 109 | 320 | } else { |
| 110 | | flags_by_bitpos[bitpos] = enumerant.enumerant; |
| 321 | flags_by_bitpos[bitpos] = i; |
| 111 | 322 | } |
| 112 | 323 | } |
| 113 | 324 | |
| 114 | | for (flags_by_bitpos) |maybe_flag_name, bitpos| { |
| 115 | | try writer.writeAll(" "); |
| 116 | | if (maybe_flag_name) |flag_name| { |
| 117 | | try writer.writeAll(flag_name); |
| 325 | for (flags_by_bitpos) |maybe_flag_index, bitpos| { |
| 326 | if (maybe_flag_index) |flag_index| { |
| 327 | try writer.print("{}", .{std.zig.fmtId(enumerants[flag_index].enumerant)}); |
| 118 | 328 | } else { |
| 119 | 329 | try writer.print("_reserved_bit_{}", .{bitpos}); |
| 120 | 330 | } |
| ... | ... | @@ -126,7 +336,169 @@ fn renderBitEnum(writer: anytype, enumeration: g.OperandKind) !void { |
| 126 | 336 | try writer.writeAll("= false,\n"); |
| 127 | 337 | } |
| 128 | 338 | |
| 129 | | try writer.writeAll("};\n"); |
| 339 | try writer.writeByte('\n'); |
| 340 | |
| 341 | for (aliases.items) |alias| { |
| 342 | try writer.print("pub const {}: {} = .{{.{} = true}};\n", .{ |
| 343 | std.zig.fmtId(enumerants[alias.flag].enumerant), |
| 344 | std.zig.fmtId(enumeration.kind), |
| 345 | std.zig.fmtId(enumerants[flags_by_bitpos[alias.alias].?].enumerant), |
| 346 | }); |
| 347 | } |
| 348 | |
| 349 | if (!extended_structs.contains(enumeration.kind)) { |
| 350 | try writer.writeAll("};\n"); |
| 351 | return; |
| 352 | } |
| 353 | |
| 354 | try writer.print("\npub const Extended = struct {{\n", .{}); |
| 355 | |
| 356 | for (flags_by_bitpos) |maybe_flag_index, bitpos| { |
| 357 | const flag_index = maybe_flag_index orelse { |
| 358 | try writer.print("_reserved_bit_{}: bool = false,\n", .{bitpos}); |
| 359 | continue; |
| 360 | }; |
| 361 | const enumerant = enumerants[flag_index]; |
| 362 | |
| 363 | try renderOperand(writer, .mask, enumerant.enumerant, enumerant.parameters, extended_structs); |
| 364 | } |
| 365 | |
| 366 | try writer.writeAll("};\n};\n"); |
| 367 | } |
| 368 | |
| 369 | fn renderOperand( |
| 370 | writer: anytype, |
| 371 | kind: enum { |
| 372 | @"union", |
| 373 | instruction, |
| 374 | mask, |
| 375 | }, |
| 376 | field_name: []const u8, |
| 377 | parameters: []const g.Operand, |
| 378 | extended_structs: ExtendedStructSet, |
| 379 | ) !void { |
| 380 | if (kind == .instruction) { |
| 381 | try writer.writeByte('.'); |
| 382 | } |
| 383 | try writer.print("{}", .{std.zig.fmtId(field_name)}); |
| 384 | if (parameters.len == 0) { |
| 385 | switch (kind) { |
| 386 | .@"union" => try writer.writeAll(",\n"), |
| 387 | .instruction => try writer.writeAll(" => void,\n"), |
| 388 | .mask => try writer.writeAll(": bool = false,\n"), |
| 389 | } |
| 390 | return; |
| 391 | } |
| 392 | |
| 393 | if (kind == .instruction) { |
| 394 | try writer.writeAll(" => "); |
| 395 | } else { |
| 396 | try writer.writeAll(": "); |
| 397 | } |
| 398 | |
| 399 | if (kind == .mask) { |
| 400 | try writer.writeByte('?'); |
| 401 | } |
| 402 | |
| 403 | try writer.writeAll("struct{"); |
| 404 | |
| 405 | for (parameters) |param, j| { |
| 406 | if (j != 0) { |
| 407 | try writer.writeAll(", "); |
| 408 | } |
| 409 | |
| 410 | try renderFieldName(writer, parameters, j); |
| 411 | try writer.writeAll(": "); |
| 412 | |
| 413 | if (param.quantifier) |q| { |
| 414 | switch (q) { |
| 415 | .@"?" => try writer.writeByte('?'), |
| 416 | .@"*" => try writer.writeAll("[]const "), |
| 417 | } |
| 418 | } |
| 419 | |
| 420 | try writer.print("{}", .{std.zig.fmtId(param.kind)}); |
| 421 | |
| 422 | if (extended_structs.contains(param.kind)) { |
| 423 | try writer.writeAll(".Extended"); |
| 424 | } |
| 425 | |
| 426 | if (param.quantifier) |q| { |
| 427 | switch (q) { |
| 428 | .@"?" => try writer.writeAll(" = null"), |
| 429 | .@"*" => try writer.writeAll(" = &.{}"), |
| 430 | } |
| 431 | } |
| 432 | } |
| 433 | |
| 434 | try writer.writeAll("}"); |
| 435 | |
| 436 | if (kind == .mask) { |
| 437 | try writer.writeAll(" = null"); |
| 438 | } |
| 439 | |
| 440 | try writer.writeAll(",\n"); |
| 441 | } |
| 442 | |
| 443 | fn renderFieldName(writer: anytype, operands: []const g.Operand, field_index: usize) !void { |
| 444 | const operand = operands[field_index]; |
| 445 | |
| 446 | // Should be enough for all names - adjust as needed. |
| 447 | var name_buffer = std.BoundedArray(u8, 64){ |
| 448 | .buffer = undefined, |
| 449 | }; |
| 450 | |
| 451 | derive_from_kind: { |
| 452 | // Operand names are often in the json encoded as "'Name'" (with two sets of quotes). |
| 453 | // Additionally, some operands have ~ in them at the end (D~ref~). |
| 454 | const name = std.mem.trim(u8, operand.name, "'~"); |
| 455 | if (name.len == 0) { |
| 456 | break :derive_from_kind; |
| 457 | } |
| 458 | |
| 459 | // Some names have weird characters in them (like newlines) - skip any such ones. |
| 460 | // Use the same loop to transform to snake-case. |
| 461 | for (name) |c| { |
| 462 | switch (c) { |
| 463 | 'a'...'z', '0'...'9' => try name_buffer.append(c), |
| 464 | 'A'...'Z' => try name_buffer.append(std.ascii.toLower(c)), |
| 465 | ' ', '~' => try name_buffer.append('_'), |
| 466 | else => break :derive_from_kind, |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | // Assume there are no duplicate 'name' fields. |
| 471 | try writer.print("{}", .{std.zig.fmtId(name_buffer.slice())}); |
| 472 | return; |
| 473 | } |
| 474 | |
| 475 | // Translate to snake case. |
| 476 | name_buffer.len = 0; |
| 477 | for (operand.kind) |c, i| { |
| 478 | switch (c) { |
| 479 | 'a'...'z', '0'...'9' => try name_buffer.append(c), |
| 480 | 'A'...'Z' => if (i > 0 and std.ascii.isLower(operand.kind[i - 1])) { |
| 481 | try name_buffer.appendSlice(&[_]u8{ '_', std.ascii.toLower(c) }); |
| 482 | } else { |
| 483 | try name_buffer.append(std.ascii.toLower(c)); |
| 484 | }, |
| 485 | else => unreachable, // Assume that the name is valid C-syntax (and contains no underscores). |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | try writer.print("{}", .{std.zig.fmtId(name_buffer.slice())}); |
| 490 | |
| 491 | // For fields derived from type name, there could be any amount. |
| 492 | // Simply check against all other fields, and if another similar one exists, add a number. |
| 493 | const need_extra_index = for (operands) |other_operand, i| { |
| 494 | if (i != field_index and std.mem.eql(u8, operand.kind, other_operand.kind)) { |
| 495 | break true; |
| 496 | } |
| 497 | } else false; |
| 498 | |
| 499 | if (need_extra_index) { |
| 500 | try writer.print("_{}", .{field_index}); |
| 501 | } |
| 130 | 502 | } |
| 131 | 503 | |
| 132 | 504 | fn parseHexInt(text: []const u8) !u31 { |
| ... | ... | @@ -142,7 +514,7 @@ fn usageAndExit(file: std.fs.File, arg0: []const u8, code: u8) noreturn { |
| 142 | 514 | \\ |
| 143 | 515 | \\Generates Zig bindings for a SPIR-V specification .json (either core or |
| 144 | 516 | \\extinst versions). The result, printed to stdout, should be used to update |
| 145 | | \\files in src/codegen/spirv. |
| 517 | \\files in src/codegen/spirv. Don't forget to format the output. |
| 146 | 518 | \\ |
| 147 | 519 | \\The relevant specifications can be obtained from the SPIR-V registry: |
| 148 | 520 | \\https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/ |