├── .gitignore ├── LICENSE ├── README.md ├── build.zig └── src ├── deps_graph.zig ├── example ├── exports.zig └── main.zig ├── generators ├── c.zig ├── ordered.zig └── python.zig ├── header_gen.zig └── runtime.zig /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | zig-* 3 | headers/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 suirad 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HeaderGen 2 | 3 | `HeaderGen` automatically generates headers/bindings for other languages given 4 | Zig code with exported functions/types 5 | 6 | Here are the following supported language binding outputs: 7 | 8 | - [x] C Bindings 9 | - [x] Python Bindings 10 | - [ ] Rust Bindings 11 | - [ ] Go Bindings 12 | - [ ] Nim Bindings 13 | 14 | Here are the following supported Zig language features: 15 | 16 | - [x] Extern Functions 17 | - [x] Extern Structs 18 | - [x] Extern Unions 19 | - [x] Extern Enums 20 | 21 | ## Getting started 22 | 23 | Given the following Zig code as the file `src/fancylib.zig` and using the C generator: 24 | 25 | ```zig 26 | const std = @import("std"); 27 | 28 | export fn print_msg(msg_ptr: ?[*]const u8, len: usize) bool { 29 | if (msg_ptr) |raw_msg| { 30 | const msg = raw_msg[0 .. len - 1]; 31 | std.debug.print("Msg is: {}", .{msg}); 32 | return true; 33 | } 34 | return false; 35 | } 36 | 37 | const Internal = struct { 38 | a: u64, 39 | }; 40 | 41 | const External = extern struct { 42 | b: u64, 43 | c: [100]u8, 44 | }; 45 | 46 | export fn use_internal_and_external(i: ?*Internal, e: ?*External) void { 47 | // do things... 48 | } 49 | ``` 50 | 51 | You will receive the following C header in `headers/fancylib.h`: 52 | 53 | ```c 54 | #ifndef _fancylib_H 55 | 56 | #define _fancylib_H 57 | #include 58 | #include 59 | #include 60 | 61 | typedef struct External { 62 | uint64_t b; 63 | uint8_t c[100]; 64 | } External_t; 65 | 66 | bool print_msg(uint8_t* arg0, size_t arg1); 67 | 68 | void use_internal_and_external(void* arg0, External_t* arg1); 69 | 70 | 71 | #endif 72 | ``` 73 | 74 | ## Usage Notes 75 | 76 | - Copy from this repo `header_gen.zig` and the folder `generators`; 77 | drop them next to your `build.zig` 78 | - See the `build.zig` in this repo for an example of integration 79 | 80 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const Builder = @import("std").build.Builder; 2 | 3 | const std = @import("std"); 4 | const warn = std.debug.print; 5 | 6 | // This build.zig is only used as an example of using header_gen 7 | 8 | pub fn build(b: *Builder) void { 9 | const target = b.standardTargetOptions(.{}); 10 | 11 | const mode = b.standardReleaseOptions(); 12 | 13 | // HEADER GEN BUILD STEP 14 | const exe = b.addExecutable("example", "src/example/exports.zig"); 15 | exe.main_pkg_path = "src/"; 16 | exe.setTarget(target); 17 | exe.setBuildMode(mode); 18 | exe.addPackagePath("header_gen", "src/header_gen.zig"); 19 | exe.install(); 20 | 21 | const run_cmd = exe.run(); 22 | run_cmd.step.dependOn(b.getInstallStep()); 23 | 24 | const run_step = b.step("headergen", "Run the app"); 25 | run_step.dependOn(&run_cmd.step); 26 | } 27 | -------------------------------------------------------------------------------- /src/deps_graph.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Allocator = std.mem.Allocator; 3 | const StringHashMap = std.StringHashMap; 4 | const ArrayList = std.ArrayList; 5 | const TailQueue = std.TailQueue; 6 | 7 | pub fn DepsGraph(comptime T: type) type { 8 | return struct { 9 | allocator: Allocator, 10 | // All the pointers to symbols inside this struct are owned by this struct 11 | // More specifically, they sould be freed when removed from the symbols 12 | // hash map. And they should only be removed from there when there are 13 | // no references to them in the dependants_of hash map 14 | symbols: StringHashMap(*Symbol), 15 | // *Symbol owned by self.symbols 16 | dependants_of: StringHashMap(ArrayList(*Symbol)), 17 | // ?*Symbol owned by self.symbols 18 | current_symbol: ?*Symbol, 19 | // Queue containing symbols ready to be emitted 20 | // Can be updated each time after calling endSymbol() 21 | emitted: TailQueue(EmittedSymbol), 22 | 23 | const Self = @This(); 24 | 25 | pub fn init(allocator: Allocator) Self { 26 | return .{ 27 | .allocator = allocator, 28 | .symbols = StringHashMap(*Symbol).init(allocator), 29 | .dependants_of = StringHashMap(ArrayList(*Symbol)).init(allocator), 30 | .current_symbol = null, 31 | .emitted = TailQueue(EmittedSymbol){}, 32 | }; 33 | } 34 | 35 | pub fn deinit(self: *Self) void { 36 | var s_iter = self.symbols.iterator(); 37 | while (s_iter.next()) |entry| { 38 | // Here entry.value is a *Symbol, so we deinit the symbol 39 | entry.value_ptr.*.deinit(self.allocator); 40 | 41 | // And free the pointer 42 | self.allocator.destroy(entry.value_ptr.*); 43 | } 44 | 45 | self.symbols.deinit(); 46 | 47 | var d_iter = self.dependants_of.iterator(); 48 | while (d_iter.next()) |entry| { 49 | // Here entry.value is an ArrayList(*Symbol), so we simply 50 | // deinit the array list (since the pointers were freed already) 51 | entry.value_ptr.*.deinit(); 52 | } 53 | 54 | self.dependants_of.deinit(); 55 | 56 | while (self.emitted.popFirst()) |node| { 57 | self.allocator.destroy(node); 58 | } 59 | 60 | self.current_symbol = null; 61 | } 62 | 63 | pub fn isBlocking(self: *Self, symbol_name: []const u8) bool { 64 | // A symbol_name can be blocking if either: 65 | // 1. There is no symbol declared with that name yet 66 | // 2. There is a symbol, but it is blocked by some dependencies too 67 | const symbol = self.symbols.get(symbol_name) orelse return true; 68 | 69 | // TODO Should a symbol be able to depend on itself? 70 | // If so, what to do in that case? For now, it blocks itself 71 | // if (symbol == self.current_symbol) return false; 72 | 73 | return symbol.hasDependenciesOfType(.Linear); 74 | } 75 | 76 | pub const BeginSymbolError = error{ DuplicateSymbol, OutOfMemory }; 77 | 78 | pub fn beginSymbol(self: *Self, name: []const u8, payload: T) BeginSymbolError!void { 79 | var result = try self.symbols.getOrPut(name); 80 | 81 | if (result.found_existing) { 82 | return error.DuplicateSymbol; 83 | } 84 | 85 | // Since the allocation can fail, we do not want to leave the state 86 | // inconsistent (with a KV whose value is empty) 87 | errdefer std.debug.assert(self.symbols.remove(name)); 88 | 89 | result.value_ptr.* = try self.allocator.create(Symbol); 90 | 91 | result.value_ptr.*.* = Symbol.init(self.allocator, name, payload); 92 | 93 | self.current_symbol = result.value_ptr.*; 94 | } 95 | 96 | pub const AddDependencyError = error{ NoSymbol, OutOfMemory }; 97 | 98 | pub fn addDependency(self: *Self, dependency_name: []const u8) AddDependencyError!void { 99 | // If the dependency is not blocking, then there's no need to add it 100 | if (!self.isBlocking(dependency_name)) return; 101 | 102 | var current_symbol = self.current_symbol orelse return error.NoSymbol; 103 | 104 | // If a symbol depends on itself, whatever, not our business 105 | if (std.mem.eql(u8, dependency_name, current_symbol.name)) return; 106 | 107 | var already_added: bool = false; 108 | var is_circular: bool = false; 109 | 110 | // Checks if there are other symbols that depend on dependency_name 111 | var result = try self.dependants_of.getOrPut(dependency_name); 112 | 113 | // Creates or retrieves the array list that contains what symbols 114 | // depend on dependency_name. Also checks if this symbol is already there 115 | if (result.found_existing) { 116 | for (result.value_ptr.items) |symbol| { 117 | if (symbol == current_symbol) { 118 | already_added = true; 119 | } 120 | } 121 | } else { 122 | result.value_ptr.* = ArrayList(*Symbol).init(self.allocator); 123 | } 124 | 125 | if (!already_added) { 126 | try result.value_ptr.append(current_symbol); 127 | 128 | if (self.dependants_of.getEntry(current_symbol.name)) |dependants| { 129 | for (dependants.value_ptr.items) |dep| { 130 | if (std.mem.eql(u8, dep.name, dependency_name)) { 131 | try dep.addDependency(.{ .Circular = current_symbol.name }); 132 | 133 | is_circular = true; 134 | 135 | break; 136 | } 137 | } 138 | } 139 | 140 | if (is_circular) { 141 | try current_symbol.addDependency(.{ .Circular = dependency_name }); 142 | } else { 143 | try current_symbol.addDependency(.{ .Linear = dependency_name }); 144 | } 145 | } 146 | } 147 | 148 | pub const EndSymbolError = error{OutOfMemory}; 149 | 150 | pub fn createNode(comptime V: type, data: V, allocator: Allocator) !*TailQueue(V).Node { 151 | var node = try allocator.create(TailQueue(V).Node); 152 | node.* = .{ .data = data }; 153 | return node; 154 | } 155 | 156 | pub fn endSymbol(self: *Self) EndSymbolError!void { 157 | var current_symbol = self.current_symbol orelse return; 158 | 159 | var unblock_queue = std.TailQueue(EmittedSymbol){}; 160 | 161 | if (!self.isBlocking(current_symbol.name)) { 162 | const node = try createNode(EmittedSymbol, .{ 163 | .symbol = current_symbol, 164 | .partial = current_symbol.hasDependencies(), 165 | }, self.allocator); 166 | 167 | unblock_queue.append(node); 168 | } 169 | 170 | // All items in unblock_queue have already been unblocked, and so 171 | // should be emitted. Also, any dependants of them should be checked 172 | // if they themselves can be unblocked as well 173 | while (unblock_queue.popFirst()) |symbol_node| { 174 | self.emitted.append(symbol_node); 175 | 176 | const symbol = symbol_node.data.symbol; 177 | 178 | if (self.dependants_of.getEntry(symbol.name)) |kv| { 179 | for (kv.value_ptr.items) |dependant| { 180 | if (dependant.removeDependency(symbol.name)) |_| { 181 | const unblock_dep = (!dependant.emitted and !dependant.hasDependenciesOfType(.Linear)) or !dependant.hasDependencies(); 182 | 183 | if (!unblock_dep) continue; 184 | 185 | dependant.emitted = true; 186 | 187 | const node = try createNode(EmittedSymbol, .{ 188 | .symbol = dependant, 189 | .partial = dependant.hasDependencies(), 190 | }, self.allocator); 191 | 192 | unblock_queue.append(node); 193 | } 194 | } 195 | } 196 | } 197 | 198 | self.current_symbol = null; 199 | } 200 | 201 | pub fn readEmitted(self: *Self) ?EmittedSymbol { 202 | var symbol_node = self.emitted.popFirst() orelse return null; 203 | 204 | var symbol = symbol_node.data; 205 | 206 | self.allocator.destroy(symbol_node); 207 | 208 | return symbol; 209 | } 210 | 211 | pub fn blockedIterator(self: *Self) BlockedSymbolsIterator { 212 | return BlockedSymbolsIterator.init(self); 213 | } 214 | 215 | const Dependency = union(enum) { 216 | Linear: []const u8, 217 | Circular: []const u8, 218 | 219 | pub fn getName(self: Dependency) []const u8 { 220 | return switch (self) { 221 | .Linear => |n| n, 222 | .Circular => |n| n, 223 | }; 224 | } 225 | 226 | pub fn eql(self: Dependency, other: Dependency) bool { 227 | switch (self) { 228 | .Linear => |n| return other == .Linear and std.mem.eql(u8, other.Linear, n), 229 | .Circular => |n| return other == .Circular and std.mem.eql(u8, other.Circular, n), 230 | } 231 | } 232 | 233 | pub fn eqlName(self: Dependency, other: Dependency) bool { 234 | return std.mem.eql(u8, self.getName(), other.getName()); 235 | } 236 | }; 237 | 238 | const EmittedSymbol = struct { 239 | symbol: *Symbol, 240 | partial: bool, 241 | }; 242 | 243 | const Symbol = struct { 244 | // Not owned 245 | name: []const u8, 246 | // Slices not owned 247 | dependencies: ArrayList(Dependency), 248 | emitted: bool = false, 249 | payload: T, 250 | 251 | pub fn init(allocator: Allocator, name: []const u8, payload: T) Symbol { 252 | return .{ 253 | .name = name, 254 | .dependencies = ArrayList(Dependency).init(allocator), 255 | .payload = payload, 256 | }; 257 | } 258 | 259 | pub fn deinit(self: *Symbol, allocator: Allocator) void { 260 | _ = allocator; 261 | self.dependencies.deinit(); 262 | } 263 | 264 | pub fn addDependency(self: *Symbol, dependency: Dependency) !void { 265 | for (self.dependencies.items) |*existing| { 266 | if (dependency.eqlName(existing.*)) { 267 | existing.* = dependency; 268 | 269 | return; 270 | } 271 | } 272 | 273 | try self.dependencies.append(dependency); 274 | } 275 | 276 | pub fn removeDependency(self: *Symbol, dependency_name: []const u8) ?Dependency { 277 | _ = dependency_name; 278 | var maybe_dep_index: ?usize = null; 279 | 280 | for (self.dependencies.items) |dependency, i| { 281 | if (dependency.eqlName(dependency)) { 282 | maybe_dep_index = i; 283 | break; 284 | } 285 | } 286 | 287 | if (maybe_dep_index) |dep_index| { 288 | // Since dependencies are not stored in any particurarly 289 | // important order, we can use swapRemove which is more 290 | // efficient than orderedRemove 291 | return self.dependencies.swapRemove(dep_index); 292 | } 293 | 294 | return null; 295 | } 296 | 297 | pub fn getDependency(self: *Symbol, dependency_name: []const u8) ?Dependency { 298 | _ = dependency_name; 299 | for (self.dependencies.items) |dependency| { 300 | if (dependency.eqlName(dependency)) { 301 | return dependency; 302 | } 303 | } 304 | 305 | return null; 306 | } 307 | 308 | pub fn hasDependencies(self: *Symbol) bool { 309 | return self.dependencies.items.len > 0; 310 | } 311 | 312 | pub fn hasDependenciesOfType(self: *Symbol, tag: std.meta.TagType(Dependency)) bool { 313 | for (self.dependencies.items) |dep| { 314 | if (dep == tag) return true; 315 | } 316 | 317 | return false; 318 | } 319 | }; 320 | 321 | pub const BlockedSymbolsIterator = struct { 322 | graph: *Self, 323 | hash_iter: StringHashMap(*Symbol).Iterator, 324 | 325 | pub fn init(graph: *Self) BlockedSymbolsIterator { 326 | return .{ 327 | .graph = graph, 328 | .hash_iter = graph.symbols.iterator(), 329 | }; 330 | } 331 | 332 | pub fn next(self: *BlockedSymbolsIterator) ?*Symbol { 333 | while (self.hash_iter.next()) |symbol| { 334 | if (symbol.value_ptr.*.hasDependenciesOfType(.Linear)) { 335 | return symbol.value_ptr.*; 336 | } 337 | } 338 | 339 | return null; 340 | } 341 | }; 342 | }; 343 | } 344 | 345 | const expect = std.testing.expect; 346 | const expectEqual = std.testing.expectEqual; 347 | const expectEqualStrings = std.testing.expectEqualStrings; 348 | 349 | fn expectSymbol(emitted: ?DepsGraph(void).EmittedSymbol, expected_name: []const u8, expected_partial: bool) !void { 350 | try expect(emitted != null); 351 | try expectEqualStrings(expected_name, emitted.?.symbol.name); 352 | try expectEqual(expected_partial, emitted.?.partial); 353 | } 354 | 355 | test "Simple dependency graph with circular dependencies" { 356 | const allocator = std.testing.allocator; 357 | 358 | var deps = DepsGraph(void).init(allocator); 359 | 360 | try deps.beginSymbol("SourceMap", {}); 361 | try deps.addDependency("TextSpan"); 362 | try deps.endSymbol(); 363 | try expect(deps.readEmitted() == null); 364 | 365 | try deps.beginSymbol("TextSpan", {}); 366 | try deps.addDependency("TextPosition"); 367 | try deps.endSymbol(); 368 | try expect(deps.readEmitted() == null); 369 | 370 | try deps.beginSymbol("TextPosition", {}); 371 | try deps.addDependency("TextSpan"); 372 | try deps.endSymbol(); 373 | 374 | try expect(deps.emitted.first != null); 375 | if (deps.readEmitted()) |s| { 376 | try expectEqualStrings(s.symbol.name, "TextPosition"); 377 | try expectEqual(s.partial, true); 378 | } 379 | 380 | try expect(deps.emitted.first != null); 381 | if (deps.readEmitted()) |s| { 382 | try expectEqualStrings(s.symbol.name, "TextSpan"); 383 | try expectEqual(s.partial, false); 384 | } 385 | 386 | try expect(deps.emitted.first != null); 387 | if (deps.readEmitted()) |s| { 388 | try expectEqualStrings(s.symbol.name, "SourceMap"); 389 | try expectEqual(s.partial, false); 390 | } 391 | 392 | try expect(deps.emitted.first != null); 393 | if (deps.readEmitted()) |s| { 394 | try expectEqualStrings(s.symbol.name, "TextPosition"); 395 | try expectEqual(s.partial, false); 396 | } 397 | 398 | try expect(deps.readEmitted() == null); 399 | 400 | deps.deinit(); 401 | } 402 | 403 | test "Blocked symbols iterator" { 404 | const allocator = std.testing.allocator; 405 | 406 | var deps = DepsGraph(void).init(allocator); 407 | 408 | try deps.beginSymbol("SourceMap", {}); 409 | try deps.addDependency("TextSpan"); 410 | try deps.endSymbol(); 411 | try expect(deps.readEmitted() == null); 412 | 413 | try deps.beginSymbol("TextSpan", {}); 414 | try deps.endSymbol(); 415 | try expect(deps.emitted.first != null); 416 | if (deps.readEmitted()) |s| { 417 | try expectEqualStrings(s.symbol.name, "TextSpan"); 418 | try expectEqual(s.partial, false); 419 | } 420 | try expect(deps.emitted.first != null); 421 | if (deps.readEmitted()) |s| { 422 | try expectEqualStrings(s.symbol.name, "SourceMap"); 423 | try expectEqual(s.partial, false); 424 | } 425 | try expect(deps.readEmitted() == null); 426 | 427 | try deps.beginSymbol("TextPosition", {}); 428 | try deps.addDependency("Cursor"); 429 | try deps.endSymbol(); 430 | try expect(deps.readEmitted() == null); 431 | 432 | var iter = deps.blockedIterator(); 433 | var symbol = iter.next(); 434 | 435 | try expect(symbol != null); 436 | try expectEqualStrings(symbol.?.name, "TextPosition"); 437 | try expect(iter.next() == null); 438 | 439 | deps.deinit(); 440 | } 441 | 442 | test "Three tier circular dependencies" { 443 | const allocator = std.testing.allocator; 444 | 445 | var deps = DepsGraph(void).init(allocator); 446 | 447 | try deps.beginSymbol("LookMaAnEnum", {}); 448 | try deps.endSymbol(); 449 | 450 | try deps.beginSymbol("WackType", {}); 451 | try deps.addDependency("LameType"); 452 | try deps.endSymbol(); 453 | 454 | try deps.beginSymbol("LameType", {}); 455 | try deps.addDependency("WackType"); 456 | try deps.addDependency("WhatsAUnion"); 457 | try deps.endSymbol(); 458 | 459 | try deps.beginSymbol("WhatsAUnion", {}); 460 | try deps.addDependency("LameType"); 461 | try deps.endSymbol(); 462 | 463 | try expectSymbol(deps.readEmitted(), "LookMaAnEnum", false); 464 | try expectSymbol(deps.readEmitted(), "WhatsAUnion", true); 465 | try expectSymbol(deps.readEmitted(), "LameType", true); 466 | try expectSymbol(deps.readEmitted(), "WackType", false); 467 | try expectSymbol(deps.readEmitted(), "WhatsAUnion", false); 468 | try expectSymbol(deps.readEmitted(), "LameType", false); 469 | 470 | try expect(deps.readEmitted() == null); 471 | 472 | deps.deinit(); 473 | } 474 | -------------------------------------------------------------------------------- /src/example/exports.zig: -------------------------------------------------------------------------------- 1 | const header_gen = @import("header_gen"); 2 | 3 | export fn thing(one: usize, two: *LameType, three: [*]u16) bool { 4 | _ = three; 5 | _ = two; 6 | return one == 1; 7 | } 8 | 9 | export fn break_point(v: [*]u8) callconv(.Naked) void { 10 | _ = v; 11 | @breakpoint(); 12 | } 13 | 14 | const WackType = packed struct { 15 | mr_field: *LameType, 16 | }; 17 | 18 | const LameType = extern struct { 19 | blah: WackType, 20 | bleh: *WhatsAUnion, 21 | }; 22 | const WhatsAUnion = extern union { 23 | a: *LameType, 24 | b: u64, 25 | }; 26 | 27 | const ThisWillBeVoid = struct { 28 | a: u64, 29 | }; 30 | 31 | const LookMaAnEnum = enum(c_int) { 32 | one = 1, 33 | three = 3, 34 | four, 35 | five = 5, 36 | }; 37 | 38 | pub fn main () void { 39 | comptime var gen = header_gen.HeaderGen(@This(), "lib").init(); 40 | 41 | gen.exec(header_gen.C_Generator); 42 | gen.exec(header_gen.Ordered_Generator(header_gen.Python_Generator)); 43 | } -------------------------------------------------------------------------------- /src/example/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn main() anyerror!void { 4 | std.debug.print("All your codebase are belong to us.\n", .{}); 5 | } 6 | -------------------------------------------------------------------------------- /src/generators/c.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Dir = std.fs.Dir; 3 | const FnMeta = std.builtin.Type.Fn; 4 | const FnDecl = std.builtin.Type.Declaration.Data.FnDecl; 5 | const StructMeta = std.builtin.Type.Struct; 6 | const EnumMeta = std.builtin.Type.Enum; 7 | const UnionMeta = std.builtin.Type.Union; 8 | const warn = std.debug.print; 9 | 10 | pub const C_Generator = struct { 11 | file: std.fs.File, 12 | 13 | const Self = @This(); 14 | 15 | pub fn init(comptime src_file: []const u8, dst_dir: *Dir) Self { 16 | 17 | var file = dst_dir.createFile(comptime filebase(src_file) ++ ".h", .{}) catch 18 | @panic("Failed to create header file for source: " ++ src_file); 19 | 20 | var res = Self{ .file = file }; 21 | 22 | // write the header's header, lol 23 | res.write("#ifndef _" ++ comptime filebase(src_file) ++ "_H\n\n#define _" ++ filebase(src_file) ++ "_H\n"); 24 | res.write("#include \n#include \n#include \n\n"); 25 | 26 | return res; 27 | } 28 | 29 | fn filebase(src_file: []const u8) []const u8 { 30 | const filebaseext = std.fs.path.basename(src_file); 31 | return filebaseext[0 .. filebaseext.len - 4]; 32 | } 33 | 34 | pub fn deinit(self: *Self) void { 35 | self.write("\n#endif\n"); 36 | self.file.close(); 37 | } 38 | 39 | pub fn gen_func(self: *Self, comptime name: []const u8, comptime meta: FnMeta) void { 40 | switch (meta.calling_convention) { 41 | .Naked => self.write("__attribute__((naked)) "), 42 | .Stdcall => self.write("__attribute__((stdcall)) "), 43 | .Fastcall => self.write("__attribute__((fastcall)) "), 44 | .Thiscall => self.write("__attribute__((thiscall)) "), 45 | else => {}, 46 | } 47 | 48 | self.writeType(meta.return_type.?); 49 | self.write(" " ++ name ++ "("); 50 | 51 | inline for (meta.params) |arg, i| { 52 | self.writeType(arg.type.?); 53 | //TODO: Figure out how to get arg names; for now just do arg0..argN 54 | _ = self.file.writer().print(" arg{}", .{i}) catch unreachable; 55 | if (i != meta.params.len - 1) 56 | self.write(", "); 57 | } 58 | 59 | self.write(");\n\n"); 60 | } 61 | 62 | pub fn gen_struct(self: *Self, comptime name: []const u8, comptime meta: StructMeta) void { 63 | self.write("typedef struct "); 64 | 65 | if (meta.layout == .Packed) 66 | self.write("__attribute__((__packed__)) "); 67 | 68 | self.write(name ++ " {\n"); 69 | 70 | inline for (meta.fields) |field| { 71 | self.write(" "); 72 | 73 | const info = @typeInfo(field.field_type); 74 | 75 | if (info == .Array) { 76 | self.writeType(info.Array.child); 77 | } else { 78 | self.writeType(field.field_type); 79 | } 80 | 81 | self.write(" " ++ field.name); 82 | 83 | if (info == .Array) { 84 | _ = self.file.writer().print("[{}]", .{info.Array.len}) catch unreachable; 85 | } 86 | 87 | self.write(";\n"); 88 | } 89 | self.write("} " ++ name ++ "_t;\n\n"); 90 | } 91 | 92 | pub fn gen_enum(self: *Self, comptime name: []const u8, comptime meta: EnumMeta) void { 93 | self.write("enum " ++ name ++ " {\n"); 94 | 95 | comptime var last = 0; 96 | inline for (meta.fields) |field, i| { 97 | self.write(" " ++ field.name); 98 | 99 | // if field value is unexpected/custom, manually define it 100 | if ((i == 0 and field.value != 0) or (i > 0 and field.value > last + 1)) { 101 | _ = self.file.writer().print(" = {}", .{field.value}) catch unreachable; 102 | } 103 | 104 | self.write(",\n"); 105 | 106 | last = field.value; 107 | } 108 | 109 | self.write("};\n\n"); 110 | } 111 | 112 | pub fn gen_union(self: *Self, comptime name: []const u8, comptime meta: UnionMeta) void { 113 | self.write("typedef union "); 114 | 115 | self.write(name ++ " {\n"); 116 | 117 | inline for (meta.fields) |field| { 118 | self.write(" "); 119 | self.writeType(field.field_type); 120 | self.write(" " ++ field.name ++ ";\n"); 121 | } 122 | self.write("} " ++ name ++ "_t;\n\n"); 123 | } 124 | 125 | fn writeType(self: *Self, comptime T: type) void { 126 | switch (T) { 127 | void => self.write("void"), 128 | bool => self.write("bool"), 129 | usize => self.write("size_t"), 130 | isize => self.write("int"), 131 | u8 => self.write("uint8_t"), 132 | u16 => self.write("uint16_t"), 133 | u32 => self.write("uint32_t"), 134 | u64 => self.write("uint64_t"), 135 | i8 => self.write("int8_t"), 136 | i16 => self.write("int16_t"), 137 | i24 => self.write("int24_t"), 138 | i32 => self.write("int32_t"), 139 | i64 => self.write("int64_t"), 140 | [*]bool => self.write("bool*"), 141 | [*]usize => self.write("size_t*"), 142 | [*]isize => self.write("int*"), 143 | [*]u8 => self.write("uint8_t*"), 144 | [*]u16 => self.write("uint16_t*"), 145 | [*]u32 => self.write("uint32_t*"), 146 | [*]u64 => self.write("uint64_t*"), 147 | [*]i8 => self.write("int8_t*"), 148 | [*]i16 => self.write("int16_t*"), 149 | [*]i32 => self.write("int32_t*"), 150 | [*]i64 => self.write("int64_t*"), 151 | else => { 152 | const meta = @typeInfo(T); 153 | switch (meta) { 154 | .Pointer => { 155 | const child = meta.Pointer.child; 156 | const childmeta = @typeInfo(child); 157 | if (childmeta == .Struct and childmeta.Struct.layout != .Extern) { 158 | self.write("void"); 159 | } else { 160 | self.writeType(child); 161 | } 162 | self.write("*"); 163 | }, 164 | .Optional => self.writeType(meta.Optional.child), 165 | .Array => @compileError("Handle goofy looking C Arrays in the calling function"), 166 | else => self.write(@typeName(T) ++ "_t"), 167 | } 168 | }, 169 | } 170 | } 171 | 172 | fn write(self: *Self, str: []const u8) void { 173 | _ = self.file.writeAll(str) catch unreachable; 174 | } 175 | }; 176 | -------------------------------------------------------------------------------- /src/generators/ordered.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const StringHashMap = std.StringHashMap; 3 | const Allocator = std.mem.Allocator; 4 | const FnMeta = std.builtin.Type.Fn; 5 | const FnDecl = std.builtin.Type.Declaration.Data.FnDecl; 6 | const StructMeta = std.builtin.Type.Struct; 7 | const EnumMeta = std.builtin.Type.Enum; 8 | const UnionMeta = std.builtin.Type.Union; 9 | const Dir = std.fs.Dir; 10 | const DepsGraph = @import("../deps_graph.zig").DepsGraph; 11 | const rt = @import("../runtime.zig"); 12 | 13 | const SymbolDeclaration = union(enum) { 14 | Struct: rt.TypeInfo.Struct, 15 | Union: rt.TypeInfo.Union, 16 | Enum: rt.TypeInfo.Enum, 17 | Fn: rt.TypeInfo.Fn, 18 | 19 | pub fn deinit(self: SymbolDeclaration, allocator: Allocator) void { 20 | switch (self) { 21 | .Struct => |s| s.deinit(allocator), 22 | .Union => |u| u.deinit(allocator), 23 | .Enum => |e| e.deinit(allocator), 24 | .Fn => |f| f.deinit(allocator), 25 | } 26 | } 27 | }; 28 | 29 | fn isSymbolDependency(comptime symbol_type: type) bool { 30 | const info = @typeInfo(symbol_type); 31 | 32 | return switch (info) { 33 | .Struct, .Union, .Enum => true, 34 | .Pointer => |p| isSymbolDependency(p.child), 35 | .Array => |a| isSymbolDependency(a.child), 36 | else => false, 37 | }; 38 | } 39 | 40 | fn getTypeName(comptime T: type) []const u8 { 41 | const type_info = @typeInfo(T); 42 | 43 | return switch (type_info) { 44 | .Pointer => |p| getTypeName(p.child), 45 | .Array => |p| getTypeName(p.child), 46 | else => @typeName(T), 47 | }; 48 | } 49 | 50 | pub const SymbolPhase = enum { 51 | Signature, Body, Full 52 | }; 53 | 54 | pub fn Ordered_Generator(comptime Generator: type) type { 55 | return struct { 56 | inner_gen: Generator, 57 | allocator: Allocator, 58 | symbols: DepsGraph(SymbolDeclaration), 59 | emitted_phase: StringHashMap(SymbolPhase), 60 | 61 | const Self = @This(); 62 | 63 | pub fn init(comptime src_file: []const u8, dst_dir: *Dir) Self { 64 | var allocator = std.heap.page_allocator; 65 | 66 | return Self{ 67 | .inner_gen = Generator.init(src_file, dst_dir), 68 | .allocator = allocator, 69 | .symbols = DepsGraph(SymbolDeclaration).init(allocator), 70 | .emitted_phase = StringHashMap(SymbolPhase).init(allocator), 71 | }; 72 | } 73 | 74 | pub fn deinit(self: *Self) void { 75 | self.flush(); 76 | 77 | self.symbols.deinit(); 78 | 79 | self.inner_gen.deinit(); 80 | 81 | self.emitted_phase.deinit(); 82 | } 83 | 84 | fn getNextPhaseFor(self: *Self, symbol_name: []const u8, partial: bool) !?SymbolPhase { 85 | var result = try self.emitted_phase.getOrPut(symbol_name); 86 | 87 | if (!result.found_existing) { 88 | result.value_ptr.* = if (partial) .Signature else .Full; 89 | 90 | return result.value_ptr.*; 91 | } else if (result.value_ptr.* == .Signature) { 92 | if (partial) { 93 | return null; 94 | } else { 95 | result.value_ptr.* = .Full; 96 | 97 | return .Body; 98 | } 99 | } 100 | 101 | return null; 102 | } 103 | 104 | fn flush(self: *Self) void { 105 | while (self.symbols.readEmitted()) |emitted| { 106 | const partial = if (emitted.symbol.payload == .Fn) false else emitted.partial; 107 | _ = partial; 108 | 109 | var phase = self.getNextPhaseFor(emitted.symbol.name, emitted.partial) catch unreachable orelse continue; 110 | 111 | switch (emitted.symbol.payload) { 112 | .Struct => |meta| self.inner_gen.gen_struct(emitted.symbol.name, meta, phase), 113 | .Union => |meta| self.inner_gen.gen_union(emitted.symbol.name, meta, phase), 114 | .Enum => |meta| self.inner_gen.gen_enum(emitted.symbol.name, meta, phase), 115 | .Fn => |meta| self.inner_gen.gen_func(emitted.symbol.name, meta), 116 | } 117 | } 118 | } 119 | 120 | pub fn gen_func(self: *Self, comptime name: []const u8, comptime meta: FnMeta) void { 121 | const decl: SymbolDeclaration = SymbolDeclaration{ 122 | .Fn = rt.TypeInfo.Fn.init(meta), 123 | }; 124 | 125 | self.symbols.beginSymbol(name, decl) catch |err| @panic(@errorName(err)); 126 | inline for (meta.params) |f| { 127 | if (f.arg_type != null and comptime isSymbolDependency(f.arg_type.?)) { 128 | self.symbols.addDependency(getTypeName(f.arg_type.?)) catch |err| @panic(@errorName(err)); 129 | } 130 | } 131 | if (meta.return_type) |t| { 132 | if (comptime isSymbolDependency(t)) { 133 | self.symbols.addDependency(getTypeName(t)) catch |err| @panic(@errorName(err)); 134 | } 135 | } 136 | self.symbols.endSymbol() catch |err| @panic(@errorName(err)); 137 | 138 | self.flush(); 139 | } 140 | 141 | pub fn gen_struct(self: *Self, comptime name: []const u8, comptime meta: StructMeta) void { 142 | const decl: SymbolDeclaration = SymbolDeclaration{ 143 | .Struct = rt.TypeInfo.Struct.init(meta, name), 144 | }; 145 | 146 | self.symbols.beginSymbol(name, decl) catch |err| @panic(@errorName(err)); 147 | inline for (meta.fields) |f| { 148 | if (comptime isSymbolDependency(f.field_type)) { 149 | self.symbols.addDependency(getTypeName(f.field_type)) catch |err| @panic(@errorName(err)); 150 | } 151 | } 152 | self.symbols.endSymbol() catch |err| @panic(@errorName(err)); 153 | 154 | self.flush(); 155 | } 156 | 157 | pub fn gen_enum(self: *Self, comptime name: []const u8, comptime meta: EnumMeta) void { 158 | const decl: SymbolDeclaration = SymbolDeclaration{ 159 | .Enum = rt.TypeInfo.Enum.init(meta, name), 160 | }; 161 | 162 | self.symbols.beginSymbol(name, decl) catch |err| @panic(@errorName(err)); 163 | // Enums have no type dependencies I think, yay 164 | self.symbols.endSymbol() catch |err| @panic(@errorName(err)); 165 | 166 | self.flush(); 167 | } 168 | 169 | pub fn gen_union(self: *Self, comptime name: []const u8, comptime meta: UnionMeta) void { 170 | const decl: SymbolDeclaration = SymbolDeclaration{ 171 | .Union = rt.TypeInfo.Union.init(meta, name), 172 | }; 173 | 174 | self.symbols.beginSymbol(name, decl) catch |err| @panic(@errorName(err)); 175 | inline for (meta.fields) |f| { 176 | if (comptime isSymbolDependency(f.field_type)) { 177 | self.symbols.addDependency(getTypeName(f.field_type)) catch |err| @panic(@errorName(err)); 178 | } 179 | } 180 | self.symbols.endSymbol() catch |err| @panic(@errorName(err)); 181 | 182 | self.flush(); 183 | } 184 | }; 185 | } 186 | -------------------------------------------------------------------------------- /src/generators/python.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Dir = std.fs.Dir; 3 | const warn = std.debug.print; 4 | const rt = @import("../runtime.zig"); 5 | const FnDecl = rt.TypeInfo.Declaration.Data.FnDecl; 6 | const FnMeta = rt.TypeInfo.Fn; 7 | const StructMeta = rt.TypeInfo.Struct; 8 | const EnumMeta = rt.TypeInfo.Enum; 9 | const UnionMeta = rt.TypeInfo.Union; 10 | const SymbolPhase = @import("ordered.zig").SymbolPhase; 11 | 12 | pub const Python_Generator = struct { 13 | pub const symbols_order: bool = false; 14 | 15 | file: std.fs.File, 16 | 17 | const Self = @This(); 18 | 19 | pub fn init(comptime src_file: []const u8, dst_dir: *Dir) Self { 20 | var file = dst_dir.createFile(comptime filebase(src_file) ++ ".py", .{}) catch 21 | @panic("Failed to create header file for source: " ++ src_file); 22 | 23 | var res = Self{ .file = file }; 24 | 25 | res.write( 26 | \\import ctypes 27 | \\import enum 28 | \\ 29 | ); 30 | 31 | res.write("lib = ctypes.cdll.LoadLibrary(\"" ++ comptime filebase(src_file) ++ ".dll\")\n\n"); 32 | 33 | return res; 34 | } 35 | 36 | fn filebase(src_file: []const u8) []const u8 { 37 | const filebaseext = std.fs.path.basename(src_file); 38 | return filebaseext[0 .. filebaseext.len - 4]; 39 | } 40 | 41 | pub fn deinit(self: *Self) void { 42 | self.file.close(); 43 | } 44 | 45 | pub fn gen_func(self: *Self, name: []const u8, meta: FnMeta) void { 46 | self.print("lib.{s}.argtypes = [", .{name}); 47 | 48 | for (meta.params) |arg, i| { 49 | if (arg.type) |t| { 50 | self.writeType(t.*); 51 | } else { 52 | self.write("None"); 53 | } 54 | 55 | if (i != meta.params.len - 1) { 56 | self.write(", "); 57 | } 58 | } 59 | 60 | self.write("]\n"); 61 | 62 | self.print("lib.{s}.restype = ", .{name}); 63 | if (meta.return_type) |return_type| { 64 | self.writeType(return_type.*); 65 | } else { 66 | self.write("None"); 67 | } 68 | self.write("\n\n"); 69 | } 70 | 71 | pub fn _gen_fields(self: *Self, name: []const u8, fields: anytype, phase: SymbolPhase) void { 72 | const prefix = "\t "; 73 | 74 | if (phase == .Body) { 75 | self.print("{s}._fields_ = [", .{name}); 76 | } else { 77 | self.write("\t_fields_ = ["); 78 | } 79 | 80 | for (fields) |field, i| { 81 | if (i > 0) { 82 | self.write(prefix); 83 | } 84 | 85 | self.print("(\"{s}\", ", .{field.name}); 86 | 87 | self.writeType(field.field_type.*); 88 | 89 | self.write(")"); 90 | 91 | if (i != fields.len - 1) { 92 | self.write(",\n"); 93 | } 94 | } 95 | 96 | self.write("]\n"); 97 | } 98 | 99 | pub fn gen_struct(self: *Self, name: []const u8, meta: StructMeta, phase: SymbolPhase) void { 100 | if (phase != .Body) { 101 | self.print("class {s}(ctypes.Structure):\n", .{name}); 102 | 103 | if (meta.layout == .Packed) { 104 | self.write("\t_pack_ = 1\n"); 105 | } 106 | } 107 | 108 | if (phase != .Signature) { 109 | self._gen_fields(name, meta.fields, phase); 110 | } else if (meta.layout != .Packed) { 111 | self.write("\tpass\n"); 112 | } 113 | 114 | self.write("\n"); 115 | } 116 | 117 | pub fn gen_enum(self: *Self, name: []const u8, meta: EnumMeta, phase: SymbolPhase) void { 118 | _ = phase; 119 | self.print("class {s}(enum.IntEnum):\n", .{name}); 120 | 121 | for (meta.fields) |field| { 122 | self.write("\t"); 123 | self.writeScreamingSnakeCase(field.name); 124 | self.print(" = {}\n", .{field.value}); 125 | } 126 | 127 | if (meta.fields.len == 0) { 128 | self.write("\tpass"); 129 | } 130 | 131 | self.write("\n"); 132 | } 133 | 134 | pub fn gen_union(self: *Self, name: []const u8, meta: UnionMeta, phase: SymbolPhase) void { 135 | if (phase != .Body) { 136 | self.print("class {s}(ctypes.Union):\n", .{name}); 137 | } 138 | 139 | if (phase != .Signature) { 140 | self._gen_fields(name, meta.fields, phase); 141 | } else { 142 | self.write("\tpass\n"); 143 | } 144 | 145 | self.write("\n"); 146 | } 147 | 148 | fn writeType(self: *Self, meta: rt.TypeInfo) void { 149 | switch (meta) { 150 | .Void => self.write("None"), 151 | .Bool => self.write("ctypes.c_bool"), 152 | // .usize => self.writeCtype("c_usize"), // TODO 153 | // .isize => self.writeCtype("c_isize"), // TODO 154 | .Int => |i| { 155 | switch (i.signedness == .signed) { 156 | true => self.print("ctypes.c_int{}", .{i.bits}), 157 | false => self.print("ctypes.c_uint{}", .{i.bits}), 158 | } 159 | }, 160 | .Float => |f| { 161 | switch (f.bits) { 162 | 32 => self.write("c_float"), 163 | 64 => self.write("c_double"), 164 | 128 => self.write("c_longdouble"), 165 | else => self.print("ctypes.c_f{}", .{f.bits}), 166 | } 167 | }, 168 | .Struct => |s| self.write(s.name orelse "__unknown__"), 169 | .Union => |s| self.write(s.name orelse "__unknown__"), 170 | .Enum => |s| self.write(s.name orelse "__unknown__"), 171 | .Pointer => |p| { 172 | const childmeta = p.child.*; 173 | self.writeCtype("POINTER("); 174 | if (childmeta == .Struct and childmeta.Struct.layout != .Extern) { 175 | self.writeCtype("c_size_t"); 176 | } else { 177 | self.writeType(childmeta); 178 | } 179 | self.write(")"); 180 | }, 181 | .Optional => self.writeType(meta.Optional.child.*), 182 | .Array => |a| { 183 | self.writeType(a.child.*); 184 | self.print(" * {}", .{a.len}); 185 | }, 186 | else => self.write(@tagName(meta)), // TODO!!!!! 187 | } 188 | } 189 | 190 | fn writeScreamingSnakeCase(self: *Self, str: []const u8) void { 191 | var new_word: bool = false; 192 | var was_lower: bool = false; 193 | var is_upper: bool = undefined; 194 | 195 | for (str) |char, i| { 196 | is_upper = std.ascii.isUpper(char); 197 | 198 | if (char == '_' and i > 0) { 199 | new_word = true; 200 | continue; 201 | } 202 | 203 | if (new_word == true or (is_upper and was_lower)) { 204 | new_word = false; 205 | was_lower = false; 206 | 207 | self.writeChar('_'); 208 | } else { 209 | was_lower = !is_upper; 210 | } 211 | 212 | self.writeChar(std.ascii.toUpper(char)); 213 | } 214 | } 215 | 216 | fn writeCtype(self: *Self, comptime str: []const u8) void { 217 | self.write("ctypes." ++ str); 218 | } 219 | 220 | fn writeChar(self: *Self, char: u8) void { 221 | self.write(&[1]u8{char}); 222 | } 223 | 224 | fn print(self: *Self, comptime fmt: []const u8, args: anytype) void { 225 | self.file.writer().print(fmt, args) catch unreachable; 226 | } 227 | 228 | fn write(self: *Self, str: []const u8) void { 229 | _ = self.file.writeAll(str) catch unreachable; 230 | } 231 | }; 232 | -------------------------------------------------------------------------------- /src/header_gen.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = std.builtin; 3 | const TypeInfo = builtin.Type; 4 | const Declaration = TypeInfo.Declaration; 5 | const warn = std.debug.print; 6 | 7 | // Provided generators 8 | pub const C_Generator = @import("generators/c.zig").C_Generator; 9 | pub const Python_Generator = @import("generators/python.zig").Python_Generator; 10 | pub const Ordered_Generator = @import("generators/ordered.zig").Ordered_Generator; 11 | 12 | const GeneratorInterface = struct { 13 | fn init() void {} 14 | fn deinit() void {} 15 | fn gen_func() void {} 16 | fn gen_struct() void {} 17 | fn gen_enum() void {} 18 | fn gen_union() void {} 19 | }; 20 | 21 | fn includeSymbol(comptime decl: Declaration) bool { 22 | if (decl.data == .Type) { 23 | const T = decl.data.Type; 24 | const info = @typeInfo(T); 25 | 26 | return switch (info) { 27 | .Struct => |s| s.layout == .Extern or s.layout == .Packed, 28 | .Union => |u| u.layout == .Extern, 29 | .Enum => |e| e.layout == .Extern, 30 | else => false, 31 | }; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | fn validateGenerator(comptime Generator: type) void { 38 | comptime { 39 | const interface = @typeInfo(GeneratorInterface).Struct.decls; 40 | 41 | for (interface) |decl| { 42 | if (@hasDecl(Generator, decl.name) == false) { 43 | @compileError("Generator: '" ++ 44 | @typeName(Generator) ++ 45 | "' is missing function: " ++ 46 | decl.name); 47 | } 48 | } 49 | } 50 | } 51 | 52 | pub fn HeaderGen(comptime S: type, comptime libname: []const u8) type { 53 | comptime var all_decls: []const Declaration = @typeInfo(S).Struct.decls; 54 | 55 | return struct { 56 | decls: @TypeOf(all_decls) = all_decls, 57 | source_file: []const u8 = libname ++ ".zig", 58 | 59 | const Self = @This(); 60 | 61 | pub fn init() Self { 62 | return Self{}; 63 | } 64 | 65 | pub fn exec(comptime self: Self, comptime Generator: type) void { 66 | validateGenerator(Generator); 67 | 68 | var cwd = std.fs.cwd(); 69 | cwd.makeDir("headers") catch |e| switch (e) { 70 | error.PathAlreadyExists => {}, 71 | else => @panic("Failed to init header folder"), 72 | }; 73 | 74 | var hdr_dir = cwd.openDir("headers", .{}) catch @panic("Failed to open header dir"); 75 | defer hdr_dir.close(); 76 | 77 | var gen = Generator.init(self.source_file, &hdr_dir); 78 | defer gen.deinit(); 79 | 80 | // iterate exported enums 81 | // do this first in case target lang needs enums defined before use 82 | inline for (self.decls) |decl| { 83 | if (@typeInfo(S) == .Type) { 84 | const T = S; 85 | const info = @typeInfo(T); 86 | if (info == .Enum) { 87 | const layout = info.Enum.layout; 88 | if (layout == .Extern) { 89 | gen.gen_enum(decl.name, info.Enum); 90 | } 91 | } 92 | } 93 | } 94 | 95 | // iterate exported structs 96 | inline for (self.decls) |decl| { 97 | if (@typeInfo(S) == .Type) { 98 | const T = S; 99 | const info = @typeInfo(T); 100 | if (info == .Struct) { 101 | const layout = info.Struct.layout; 102 | if (layout == .Extern or layout == .Packed) { 103 | gen.gen_struct(decl.name, info.Struct); 104 | } 105 | } 106 | } 107 | } 108 | 109 | inline for (self.decls) |decl| { 110 | if (@typeInfo(S) == .Type) { 111 | const T = decl.data.Type; 112 | const info = @typeInfo(T); 113 | if (info == .Union) { 114 | const layout = info.Union.layout; 115 | if (layout == .Extern) { 116 | gen.gen_union(decl.name, info.Union); 117 | } 118 | } 119 | } 120 | } 121 | 122 | // iterate exported fns 123 | inline for (self.decls) |decl| { 124 | if (@typeInfo(S) == .Fn) { 125 | const func = decl.data.Fn; 126 | if (func.is_export) { 127 | //TODO: Look into parsing file for argument names 128 | const fn_meta = @typeInfo(func.fn_type).Fn; 129 | gen.gen_func(decl.name, fn_meta); 130 | } 131 | } else if (@typeInfo(S) == .Type) { 132 | const fn_meta = @typeInfo(decl.data.Type); 133 | 134 | if (fn_meta == .Fn) { 135 | gen.gen_func(decl.name, fn_meta.Fn); 136 | } 137 | } 138 | } 139 | } 140 | }; 141 | } 142 | -------------------------------------------------------------------------------- /src/runtime.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Allocator = std.mem.Allocator; 3 | 4 | pub const TypeId = std.builtin.TypeId(TypeInfo); 5 | 6 | const TypeInfoSingleton = struct { 7 | resolved: bool = false, 8 | info: TypeInfo = .{ .Void = {} }, 9 | }; 10 | 11 | pub const TypeInfo = union(enum) { 12 | Type: void, 13 | Void: void, 14 | Bool: void, 15 | NoReturn: void, 16 | Int: Int, 17 | Float: Float, 18 | Pointer: Pointer, 19 | Array: Array, 20 | Struct: Struct, 21 | ComptimeFloat: void, 22 | ComptimeInt: void, 23 | Undefined: void, 24 | Null: void, 25 | Optional: Optional, 26 | ErrorUnion: ErrorUnion, 27 | ErrorSet: ErrorSet, 28 | Enum: Enum, 29 | Union: Union, 30 | Fn: Fn, 31 | BoundFn: Fn, 32 | Opaque: void, // TODO Opaque 33 | Frame: Frame, 34 | AnyFrame: AnyFrame, 35 | Vector: Vector, 36 | EnumLiteral: void, 37 | 38 | /// This data structure is used by the Zig language code generation and 39 | /// therefore must be kept in sync with the compiler implementation. 40 | pub const Int = struct { 41 | signedness: Signedness, 42 | bits: i32, 43 | 44 | pub fn init(comptime m: std.builtin.Type.Int) Int { 45 | return comptime .{ 46 | .signedness = @intToEnum(Signedness, @enumToInt(m.signedness)), 47 | .bits = m.bits, 48 | }; 49 | } 50 | }; 51 | comptime { 52 | validateSymbolInSync(Int, std.builtin.Type.Int, .{}); 53 | } 54 | 55 | /// This data structure is used by the Zig language code generation and 56 | /// therefore must be kept in sync with the compiler implementation. 57 | pub const Float = struct { 58 | bits: i32, 59 | 60 | pub fn init(comptime m: std.builtin.Type.Float) Float { 61 | return comptime .{ 62 | .bits = m.bits, 63 | }; 64 | } 65 | }; 66 | comptime { 67 | validateSymbolInSync(Float, std.builtin.Type.Float, .{}); 68 | } 69 | 70 | /// This data structure is used by the Zig language code generation and 71 | /// therefore must be kept in sync with the compiler implementation. 72 | pub const Pointer = struct { 73 | size: Size, 74 | is_const: bool, 75 | is_volatile: bool, 76 | alignment: i32, 77 | address_space: std.builtin.AddressSpace, 78 | child: *const TypeInfo, 79 | is_allowzero: bool, 80 | /// This field is an optional type. 81 | /// The type of the sentinel is the element type of the pointer, which is 82 | /// the value of the `child` field in this struct. However there is no way 83 | /// to refer to that type here, so we use `var`. 84 | // sentinel: anytype, 85 | /// This data structure is used by the Zig language code generation and 86 | /// therefore must be kept in sync with the compiler implementation. 87 | pub const Size = enum { 88 | One, 89 | Many, 90 | Slice, 91 | C, 92 | }; 93 | 94 | pub fn init(comptime m: std.builtin.Type.Pointer) Pointer { 95 | return comptime .{ 96 | .size = @intToEnum(TypeInfo.Pointer.Size, @enumToInt(m.size)), 97 | .is_const = m.is_const, 98 | .is_volatile = m.is_volatile, 99 | .alignment = m.alignment, 100 | .child = &TypeInfo.init(m.child), 101 | .is_allowzero = m.is_allowzero, 102 | }; 103 | } 104 | 105 | pub fn deinit(self: *const Pointer, allocator: Allocator) void { 106 | self.child.deinit(allocator); 107 | 108 | allocator.destroy(self.child); 109 | } 110 | }; 111 | comptime { 112 | validateSymbolInSync(Pointer, std.builtin.Type.Pointer, .{ 113 | .ignore_fields = .{"sentinel"}, 114 | }); 115 | } 116 | 117 | /// This data structure is used by the Zig language code generation and 118 | /// therefore must be kept in sync with the compiler implementation. 119 | pub const Array = struct { 120 | len: i32, 121 | child: *const TypeInfo, 122 | /// This field is an optional type. 123 | /// The type of the sentinel is the element type of the array, which is 124 | /// the value of the `child` field in this struct. However there is no way 125 | /// to refer to that type here, so we use `var`. 126 | // sentinel: anytype, 127 | pub fn init(comptime m: std.builtin.Type.Array) Array { 128 | return comptime .{ 129 | .len = m.len, 130 | .child = &TypeInfo.init(m.child), 131 | }; 132 | } 133 | 134 | pub fn deinit(self: *const Array, allocator: Allocator) void { 135 | self.child.deinit(allocator); 136 | 137 | allocator.destroy(self.child); 138 | } 139 | }; 140 | comptime { 141 | validateSymbolInSync(Array, std.builtin.Type.Array, .{ 142 | .ignore_fields = .{"sentinel"}, 143 | }); 144 | } 145 | 146 | /// This data structure is used by the Zig language code generation and 147 | /// therefore must be kept in sync with the compiler implementation. 148 | pub const ContainerLayout = enum { 149 | Auto, 150 | Extern, 151 | Packed, 152 | }; 153 | comptime { 154 | validateSymbolInSync(ContainerLayout, std.builtin.Type.ContainerLayout, .{}); 155 | } 156 | 157 | /// This data structure is used by the Zig language code generation and 158 | /// therefore must be kept in sync with the compiler implementation. 159 | pub const StructField = struct { 160 | name: []const u8, 161 | field_type: *const TypeInfo, 162 | type: ?*const TypeInfo, 163 | default_value: ?*const anyopaque, 164 | is_comptime: bool, 165 | alignment: i32, 166 | 167 | pub fn init(comptime f: std.builtin.Type.StructField) StructField { 168 | return comptime .{ 169 | .name = f.name, 170 | .field_type = &TypeInfo.init(f.field_type), 171 | .is_comptime = f.is_comptime, 172 | .alignment = f.alignment, 173 | }; 174 | } 175 | 176 | pub fn deinit(self: *const StructField, allocator: Allocator) void { 177 | allocator.free(self.name); 178 | 179 | self.field_type.deinit(allocator); 180 | 181 | allocator.destroy(self.field_type); 182 | } 183 | }; 184 | comptime { 185 | validateSymbolInSync(StructField, std.builtin.Type.StructField, .{ 186 | .ignore_fields = .{"default_value"}, 187 | }); 188 | } 189 | 190 | /// This data structure is used by the Zig language code generation and 191 | /// therefore must be kept in sync with the compiler implementation. 192 | pub const Struct = struct { 193 | // Additional Field 194 | name: ?[]const u8, 195 | 196 | layout: ContainerLayout, 197 | backing_integer: ?*const TypeInfo = null, 198 | fields: []const StructField, 199 | decls: []const Declaration, 200 | is_tuple: bool, 201 | 202 | pub fn init(comptime m: std.builtin.Type.Struct, comptime name: []const u8) Struct { 203 | return comptime .{ 204 | .name = name, 205 | .layout = @intToEnum(TypeInfo.ContainerLayout, @enumToInt(m.layout)), 206 | .fields = fields: { 207 | comptime var arr: [m.fields.len]StructField = undefined; 208 | 209 | inline for (m.fields) |f, i| { 210 | arr[i] = StructField.init(f); 211 | } 212 | 213 | break :fields &arr; 214 | }, 215 | .decls = decls: { 216 | comptime var arr: [m.decls.len]Declaration = undefined; 217 | 218 | inline for (m.decls) |f, i| { 219 | arr[i] = Declaration.init(f); 220 | } 221 | 222 | break :decls &arr; 223 | }, 224 | .is_tuple = m.is_tuple, 225 | }; 226 | } 227 | comptime { 228 | validateSymbolInSync(Struct, std.builtin.Type.Struct, .{}); 229 | } 230 | 231 | pub fn deinit(self: *const Struct, allocator: Allocator) void { 232 | for (self.fields) |f| f.deinit(allocator); 233 | for (self.decls) |f| f.deinit(allocator); 234 | 235 | allocator.free(self.fields); 236 | allocator.free(self.decls); 237 | } 238 | }; 239 | 240 | /// This data structure is used by the Zig language code generation and 241 | /// therefore must be kept in sync with the compiler implementation. 242 | pub const Optional = struct { 243 | child: *const TypeInfo, 244 | 245 | pub fn init(comptime m: std.builtin.Type.Optional) Optional { 246 | return comptime .{ 247 | .child = &TypeInfo.init(m.child), 248 | }; 249 | } 250 | 251 | pub fn deinit(self: *const Optional, allocator: Allocator) void { 252 | self.child.deinit(allocator); 253 | 254 | allocator.destroy(self.child); 255 | } 256 | }; 257 | comptime { 258 | validateSymbolInSync(Optional, std.builtin.Type.Optional, .{}); 259 | } 260 | 261 | /// This data structure is used by the Zig language code generation and 262 | /// therefore must be kept in sync with the compiler implementation. 263 | pub const ErrorUnion = struct { 264 | error_set: *const TypeInfo, 265 | payload: *const TypeInfo, 266 | 267 | pub fn init(comptime m: std.builtin.Type.ErrorUnion) ErrorUnion { 268 | return comptime .{ 269 | .error_set = &TypeInfo.init(m.error_set), 270 | .payload = &TypeInfo.init(m.payload), 271 | }; 272 | } 273 | 274 | pub fn deinit(self: *const ErrorUnion, allocator: Allocator) void { 275 | self.error_set.deinit(allocator); 276 | allocator.destroy(self.error_set); 277 | 278 | self.payload.deinit(allocator); 279 | allocator.destroy(self.payload); 280 | } 281 | }; 282 | comptime { 283 | validateSymbolInSync(ErrorUnion, std.builtin.Type.ErrorUnion, .{}); 284 | } 285 | 286 | /// This data structure is used by the Zig language code generation and 287 | /// therefore must be kept in sync with the compiler implementation. 288 | pub const Error = struct { 289 | name: []const u8, 290 | 291 | pub fn deinit(self: *const Error, allocator: Allocator) void { 292 | allocator.free(self.name); 293 | } 294 | }; 295 | comptime { 296 | validateSymbolInSync(Error, std.builtin.Type.Error, .{}); 297 | } 298 | 299 | /// This data structure is used by the Zig language code generation and 300 | /// therefore must be kept in sync with the compiler implementation. 301 | pub const ErrorSet = ?[]const Error; 302 | 303 | /// This data structure is used by the Zig language code generation and 304 | /// therefore must be kept in sync with the compiler implementation. 305 | pub const EnumField = struct { 306 | name: []const u8, 307 | value: i32, 308 | 309 | pub fn init(comptime f: std.builtin.Type.EnumField) EnumField { 310 | return comptime .{ 311 | .name = f.name, 312 | .value = f.value, 313 | }; 314 | } 315 | 316 | pub fn deinit(self: *const EnumField, allocator: Allocator) void { 317 | allocator.free(self.name); 318 | } 319 | }; 320 | comptime { 321 | validateSymbolInSync(EnumField, std.builtin.Type.EnumField, .{}); 322 | } 323 | 324 | /// This data structure is used by the Zig language code generation and 325 | /// therefore must be kept in sync with the compiler implementation. 326 | pub const Enum = struct { 327 | // Additional Field 328 | name: ?[]const u8, 329 | 330 | layout: ContainerLayout, 331 | tag_type: *const TypeInfo, 332 | fields: []const EnumField, 333 | decls: []const Declaration, 334 | is_exhaustive: bool, 335 | 336 | pub fn init(comptime m: std.builtin.Type.Enum, comptime name: []const u8) Enum { 337 | return comptime .{ 338 | .name = name, 339 | .layout = @intToEnum(TypeInfo.ContainerLayout, @enumToInt(m.layout)), 340 | .tag_type = &TypeInfo.init(m.tag_type), 341 | .fields = fields: { 342 | comptime var arr: [m.fields.len]EnumField = undefined; 343 | 344 | inline for (m.fields) |f, i| { 345 | arr[i] = EnumField.init(f); 346 | } 347 | 348 | break :fields &arr; 349 | }, 350 | .decls = decls: { 351 | comptime var arr: [m.decls.len]Declaration = undefined; 352 | 353 | inline for (m.decls) |f, i| { 354 | arr[i] = Declaration.init(f); 355 | } 356 | 357 | break :decls &arr; 358 | }, 359 | .is_exhaustive = m.is_exhaustive, 360 | }; 361 | } 362 | 363 | pub fn deinit(self: *const Enum, allocator: Allocator) void { 364 | for (self.fields) |f| f.deinit(allocator); 365 | for (self.decls) |f| f.deinit(allocator); 366 | 367 | allocator.free(self.fields); 368 | allocator.free(self.decls); 369 | 370 | self.tag_type.deinit(allocator); 371 | allocator.destroy(self.tag_type); 372 | } 373 | }; 374 | comptime { 375 | validateSymbolInSync(Enum, std.builtin.Type.Enum, .{}); 376 | } 377 | 378 | /// This data structure is used by the Zig language code generation and 379 | /// therefore must be kept in sync with the compiler implementation. 380 | pub const UnionField = struct { 381 | // Additional Field 382 | name: []const u8, 383 | type: ?*const TypeInfo, 384 | field_type: *const TypeInfo, 385 | alignment: i32, 386 | 387 | pub fn init(comptime f: std.builtin.Type.UnionField) UnionField { 388 | return comptime .{ 389 | .name = f.name, 390 | .field_type = &TypeInfo.init(f.field_type), 391 | .alignment = f.alignment, 392 | }; 393 | } 394 | 395 | pub fn deinit(self: *const UnionField, allocator: Allocator) void { 396 | allocator.free(self.name); 397 | 398 | self.field_type.deinit(allocator); 399 | 400 | allocator.destroy(self.field_type); 401 | 402 | if (self.enum_field) |ef| { 403 | ef.deinit(allocator); 404 | } 405 | } 406 | }; 407 | comptime { 408 | validateSymbolInSync(UnionField, std.builtin.Type.UnionField, .{}); 409 | } 410 | 411 | /// This data structure is used by the Zig language code generation and 412 | /// therefore must be kept in sync with the compiler implementation. 413 | pub const Union = struct { 414 | // Additional Field 415 | name: ?[]const u8, 416 | 417 | layout: ContainerLayout, 418 | tag_type: ?*const TypeInfo, 419 | fields: []const UnionField, 420 | decls: []const Declaration, 421 | 422 | pub fn init(comptime m: std.builtin.Type.Union, comptime name: []const u8) Union { 423 | return comptime .{ 424 | .name = name, 425 | .layout = @intToEnum(TypeInfo.ContainerLayout, @enumToInt(m.layout)), 426 | .tag_type = if (m.tag_type) |t| &TypeInfo.init(t) else null, 427 | .fields = fields: { 428 | comptime var arr: [m.fields.len]UnionField = undefined; 429 | 430 | inline for (m.fields) |f, i| { 431 | arr[i] = UnionField.init(f); 432 | } 433 | 434 | break :fields &arr; 435 | }, 436 | .decls = decls: { 437 | comptime var arr: [m.decls.len]Declaration = undefined; 438 | 439 | inline for (m.decls) |f, i| { 440 | arr[i] = Declaration.init(f); 441 | } 442 | 443 | break :decls &arr; 444 | }, 445 | }; 446 | } 447 | 448 | pub fn deinit(self: *const Union, allocator: Allocator) void { 449 | for (self.fields) |f| f.deinit(allocator); 450 | for (self.decls) |f| f.deinit(allocator); 451 | 452 | allocator.free(self.fields); 453 | allocator.free(self.decls); 454 | 455 | if (self.tag_type) |tag_type| { 456 | tag_type.deinit(allocator); 457 | 458 | allocator.destroy(tag_type); 459 | } 460 | } 461 | }; 462 | comptime { 463 | validateSymbolInSync(Union, std.builtin.Type.Union, .{}); 464 | } 465 | 466 | /// This data structure is used by the Zig language code generation and 467 | /// therefore must be kept in sync with the compiler implementation. 468 | pub const Param = struct { 469 | is_generic: bool, 470 | is_noalias: bool, 471 | type: ?*const TypeInfo, 472 | 473 | pub fn init(comptime f: std.builtin.Type.Param) Param { 474 | return comptime .{ 475 | .is_generic = f.is_generic, 476 | .is_noalias = f.is_noalias, 477 | .type = if (f.type) |t| &TypeInfo.init(t) else null, 478 | }; 479 | } 480 | 481 | pub fn deinit(self: *const Param, allocator: Allocator) void { 482 | if (self.arg_type) |t| { 483 | t.deinit(allocator); 484 | 485 | allocator.destroy(self.arg_type); 486 | } 487 | } 488 | }; 489 | comptime { 490 | validateSymbolInSync(Param, std.builtin.Type.Fn.Param, .{}); 491 | } 492 | 493 | /// This data structure is used by the Zig language code generation and 494 | /// therefore must be kept in sync with the compiler implementation. 495 | pub const Fn = struct { 496 | calling_convention: CallingConvention, 497 | alignment: i32, 498 | is_generic: bool, 499 | is_var_args: bool, 500 | return_type: ?*const TypeInfo, 501 | params: []const Param, 502 | 503 | pub fn init(comptime m: std.builtin.Type.Fn) Fn { 504 | return comptime .{ 505 | .calling_convention = @intToEnum(CallingConvention, @enumToInt(m.calling_convention)), 506 | .alignment = m.alignment, 507 | .is_generic = m.is_generic, 508 | .is_var_args = m.is_var_args, 509 | .return_type = if (m.return_type) |t| &TypeInfo.init(t) else null, 510 | .args = args: { 511 | comptime var arr: [m.args.len]Param = undefined; 512 | 513 | inline for (m.args) |f, i| { 514 | arr[i] = Param.init(f); 515 | } 516 | 517 | break :args &arr; 518 | }, 519 | }; 520 | } 521 | 522 | pub fn deinit(self: *const Fn, allocator: Allocator) void { 523 | if (self.return_type) |r| { 524 | r.deinit(allocator); 525 | 526 | allocator.destroy(r); 527 | } 528 | 529 | for (self.args) |arg| arg.deinit(allocator); 530 | 531 | allocator.free(self.args); 532 | } 533 | }; 534 | comptime { 535 | validateSymbolInSync(Fn, std.builtin.Type.Fn, .{}); 536 | } 537 | 538 | pub const Opaque = struct { 539 | decls: []const Declaration, 540 | 541 | pub fn init(comptime m: std.builtin.Type.Opaque) Opaque { 542 | return comptime .{ 543 | .decls = decls: { 544 | comptime var arr: [m.decls.len]Declaration = undefined; 545 | 546 | inline for (m.decls) |f, i| { 547 | arr[i] = Declaration.init(f); 548 | } 549 | 550 | break :decls &arr; 551 | }, 552 | }; 553 | } 554 | }; 555 | comptime { 556 | validateSymbolInSync(Opaque, std.builtin.Type.Opaque, .{}); 557 | } 558 | 559 | /// This data structure is used by the Zig language code generation and 560 | /// therefore must be kept in sync with the compiler implementation. 561 | pub const Frame = struct { 562 | // function: anytype, 563 | }; 564 | comptime { 565 | validateSymbolInSync(Frame, std.builtin.Type.Frame, .{ 566 | .ignore_fields = .{"function"}, 567 | }); 568 | } 569 | 570 | /// This data structure is used by the Zig language code generation and 571 | /// therefore must be kept in sync with the compiler implementation. 572 | pub const AnyFrame = struct { 573 | child: ?*const TypeInfo, 574 | 575 | pub fn init(comptime m: std.builtin.Type.AnyFrame) AnyFrame { 576 | return comptime .{ 577 | .child = if (m.child) |t| &TypeInfo.init(t) else null, 578 | }; 579 | } 580 | 581 | pub fn deinit(self: *const AnyFrame, allocator: Allocator) void { 582 | if (self.child) |child| { 583 | child.deinit(allocator); 584 | 585 | allocator.destroy(child); 586 | } 587 | } 588 | }; 589 | comptime { 590 | validateSymbolInSync(AnyFrame, std.builtin.Type.AnyFrame, .{}); 591 | } 592 | 593 | /// This data structure is used by the Zig language code generation and 594 | /// therefore must be kept in sync with the compiler implementation. 595 | pub const Vector = struct { 596 | len: i32, 597 | child: *const TypeInfo, 598 | 599 | pub fn init(comptime m: std.builtin.Type.Vector) Vector { 600 | return comptime .{ 601 | .len = m.len, 602 | .child = &TypeInfo.init(m.child), 603 | }; 604 | } 605 | 606 | pub fn deinit(self: *const Vector, allocator: Allocator) void { 607 | self.child.deinit(allocator); 608 | 609 | allocator.destroy(self.child); 610 | } 611 | }; 612 | comptime { 613 | validateSymbolInSync(Vector, std.builtin.Type.Vector, .{}); 614 | } 615 | 616 | /// This data structure is used by the Zig language code generation and 617 | /// therefore must be kept in sync with the compiler implementation. 618 | pub const Declaration = struct { 619 | name: []const u8, 620 | is_pub: bool, 621 | // data: Data, 622 | 623 | pub fn init(comptime f: std.builtin.Type.Declaration) Declaration { 624 | return comptime .{ 625 | .name = f.name, 626 | .is_pub = f.is_pub, 627 | // .data = Data.init(f.data), 628 | }; 629 | } 630 | 631 | pub fn deinit(self: *const Declaration, allocator: Allocator) void { 632 | self.data.deinit(allocator); 633 | 634 | allocator.free(self.name); 635 | } 636 | }; 637 | comptime { 638 | validateSymbolInSync(Declaration, std.builtin.Type.Declaration, .{}); 639 | } 640 | 641 | // Validate the whole TypeInfo sync 642 | comptime { 643 | @setEvalBranchQuota(2000); 644 | validateSymbolInSync(TypeInfo, std.builtin.Type, .{}); 645 | } 646 | 647 | usingnamespace blk: { 648 | var uniqueIdCounter: usize = 0; 649 | 650 | break :blk struct { 651 | pub fn uniqueId(comptime T: type) usize { 652 | _ = T; 653 | comptime { 654 | var id = uniqueIdCounter; 655 | 656 | uniqueIdCounter += 1; 657 | 658 | return id; 659 | } 660 | } 661 | }; 662 | }; 663 | 664 | pub fn alloc(comptime T: type) *TypeInfoSingleton { 665 | _ = T; 666 | comptime var ptr = TypeInfoSingleton{}; 667 | 668 | return &ptr; 669 | } 670 | 671 | pub fn init(comptime T: type) TypeInfo { 672 | return TypeInfo.initPtr(T).*; 673 | } 674 | 675 | pub fn initPtr(comptime T: type) *const TypeInfo { 676 | comptime var ptr = TypeInfo.alloc(T); 677 | 678 | if (ptr.resolved) { 679 | return &ptr.info; 680 | } 681 | 682 | ptr.resolved = true; 683 | 684 | const info = @typeInfo(T); 685 | 686 | ptr.info = comptime switch (info) { 687 | .Type => .{ .Type = {} }, 688 | .Void => .{ .Void = {} }, 689 | .Bool => .{ .Bool = {} }, 690 | .NoReturn => .{ .NoReturn = {} }, 691 | .Int => |m| .{ .Int = Int.init(m) }, 692 | .Float => |m| .{ .Float = Float.init(m) }, 693 | .Pointer => |m| .{ .Pointer = Pointer.init(m) }, 694 | .Array => |m| .{ .Array = Array.init(m) }, 695 | .Struct => |m| .{ .Struct = Struct.init(m, @typeName(T)) }, 696 | .ComptimeFloat => .{ .ComptimeFloat = {} }, 697 | .ComptimeInt => .{ .ComptimeInt = {} }, 698 | .Undefined => .{ .Undefined = {} }, 699 | .Null => .{ .Null = {} }, 700 | .Optional => |m| .{ .Optional = Optional.init(m) }, 701 | .ErrorUnion => |m| .{ .ErrorUnion = ErrorUnion.init(m) }, // TODO 702 | .ErrorSet => |m| .{ 703 | .ErrorSet = errorset: { 704 | if (m == null) return null; 705 | 706 | comptime var arr: [m.?.len]Error = undefined; 707 | 708 | inline for (m.?) |f, i| { 709 | arr[i] = .{ 710 | .name = f.name, 711 | }; 712 | } 713 | 714 | break :errorset &arr; 715 | }, 716 | }, 717 | .Enum => |m| .{ .Enum = Enum.init(m, @typeName(T)) }, 718 | .Union => |m| .{ .Union = Union.init(m, @typeName(T)) }, 719 | .Fn => |m| .{ .Fn = Fn.init(m) }, 720 | .BoundFn => |m| .{ .BoundedFn = Fn.init(m) }, 721 | .Opaque => .{ .Opaque = {} }, 722 | .Frame => .{ .Frame = {} }, // TODO 723 | .AnyFrame => |m| .{ .AnyFrame = AnyFrame.init(m) }, 724 | .Vector => |m| .{ .Vector = Vector.init(m) }, 725 | .EnumLiteral => .{ .EnumLiteral = {} }, 726 | }; 727 | 728 | return &ptr.info; 729 | } 730 | 731 | pub fn deinit(self: *TypeInfo, allocator: Allocator) void { 732 | switch (self.*) { 733 | .Array => |a| a.deinit(allocator), 734 | .Pointer => |p| p.deinit(allocator), 735 | .Struct => |s| s.deinit(allocator), 736 | .Union => |u| u.deinit(allocator), 737 | .Enum => |e| e.deinit(allocator), 738 | .Optional => |o| o.deinit(allocator), 739 | .Fn => |f| f.deinit(allocator), 740 | .ErrorUnion => |e| e.deinit(allocator), 741 | .ErrorSet => |maybe_set| { 742 | if (maybe_set) |set| { 743 | for (set) |err| err.deinit(allocator); 744 | 745 | allocator.free(set); 746 | } 747 | }, 748 | .AnyFrame => |a| a.deinit(allocator), 749 | .Vector => |v| v.deinit(allocator), 750 | else => {}, 751 | } 752 | } 753 | }; 754 | 755 | pub const CallingConvention = enum { 756 | Unspecified, 757 | C, 758 | Naked, 759 | Async, 760 | Inline, 761 | Interrupt, 762 | Signal, 763 | Stdcall, 764 | Fastcall, 765 | Vectorcall, 766 | Thiscall, 767 | APCS, 768 | AAPCS, 769 | AAPCSVFP, 770 | SysV, 771 | }; 772 | 773 | pub const Signedness = enum { 774 | signed, 775 | unsigned, 776 | }; 777 | 778 | pub fn hasField(comptime T: type, comptime field_name: []const u8) bool { 779 | inline for (comptime std.meta.fields(T)) |field| { 780 | if (std.mem.eql(u8, field.name, field_name) == true) { 781 | return true; 782 | } 783 | } 784 | 785 | return false; 786 | } 787 | 788 | /// Function to be run in compile time, responsible for verifying if the 789 | /// structures/enums/unions defined in this file to represent the TypeInfo at 790 | /// runtime in sync with the current Zig version's comptime structures/enums/unions 791 | pub fn validateSymbolInSync(comptime runtime_type: type, comptime builtin_type: type, comptime options: anytype) void { 792 | const builtin_type_info = @typeInfo(builtin_type); 793 | const runtime_type_info = @typeInfo(runtime_type); 794 | 795 | // Make sure that the runtime type is a struct as well 796 | if (std.mem.eql(u8, @tagName(builtin_type_info), @tagName(runtime_type_info)) == false) { 797 | @compileError( 798 | "Type of " ++ @typeName(builtin_type) ++ 799 | " is " ++ @tagName(builtin_type_info) ++ 800 | " but runtime type is " ++ @tagName(runtime_type_info), 801 | ); 802 | } 803 | 804 | switch (builtin_type_info) { 805 | .Struct, .Enum, .Union => { 806 | // Compare the fields 807 | inline for (std.meta.fields(builtin_type)) |builtin_field| { 808 | var missing_field: bool = false; 809 | 810 | if (hasField(runtime_type, builtin_field.name) == false) { 811 | missing_field = true; 812 | 813 | if (@hasField(@TypeOf(options), "ignore_fields")) { 814 | inline for (options.ignore_fields) |ignore_field| { 815 | if (std.mem.eql(u8, ignore_field, builtin_field.name) == true) { 816 | missing_field = false; 817 | break; 818 | } 819 | } 820 | } 821 | 822 | if (missing_field == true) { 823 | @compileError( 824 | "Field " ++ builtin_field.name ++ 825 | " is missing in type " ++ @typeName(runtime_type), 826 | ); 827 | } 828 | } 829 | } 830 | }, 831 | else => @compileError( 832 | "Cannot validate symbol in sync " ++ @typeName(builtin_type) ++ 833 | " because type " ++ @tagName(builtin_type_info) ++ 834 | " is not supported", 835 | ), 836 | } 837 | } 838 | 839 | const expect = std.testing.expect; 840 | const expectEqual = std.testing.expectEqual; 841 | const expectEqualStrings = std.testing.expectEqualStrings; 842 | 843 | const talloc = std.testing.allocator; 844 | 845 | // TODO .Type 846 | 847 | test "Runtime TypeInfo.Void" { 848 | var info_void = TypeInfo.init(void); 849 | try expect(info_void == .Void); 850 | } 851 | 852 | test "Runtime TypeInfo.Bool" { 853 | var info_bool = TypeInfo.init(bool); 854 | try expect(info_bool == .Bool); 855 | } 856 | 857 | // TODO .NoReturn 858 | 859 | test "Runtime TypeInfo.Int" { 860 | var info_i32 = TypeInfo.init(i32); 861 | try expect(info_i32 == .Int); 862 | try expectEqual(@as(i32, 32), info_i32.Int.bits); 863 | try expectEqual(true, info_i32.Int.signedness == .signed); 864 | } 865 | 866 | test "Runtime TypeInfo.Float" { 867 | var info_f64 = TypeInfo.init(f64); 868 | try expect(info_f64 == .Float); 869 | try expectEqual(@as(i32, 64), info_f64.Float.bits); 870 | } 871 | 872 | test "Runtime TypeInfo.Pointer" { 873 | var info_pointer_f64 = TypeInfo.init(*f64); 874 | try expect(info_pointer_f64 == .Pointer); 875 | try expectEqual(TypeInfo.Pointer.Size.One, info_pointer_f64.Pointer.size); 876 | try expectEqual(false, info_pointer_f64.Pointer.is_const); 877 | try expectEqual(false, info_pointer_f64.Pointer.is_volatile); 878 | try expectEqual(@as(i32, 8), info_pointer_f64.Pointer.alignment); 879 | try expect(info_pointer_f64.Pointer.child.* == .Float); 880 | try expectEqual(false, info_pointer_f64.Pointer.is_allowzero); 881 | 882 | var info_pointer_many = TypeInfo.init([*]f64); 883 | try expect(info_pointer_many == .Pointer); 884 | try expectEqual(TypeInfo.Pointer.Size.Many, info_pointer_many.Pointer.size); 885 | try expectEqual(false, info_pointer_many.Pointer.is_const); 886 | try expectEqual(false, info_pointer_many.Pointer.is_volatile); 887 | try expectEqual(@as(i32, 8), info_pointer_many.Pointer.alignment); 888 | try expect(info_pointer_many.Pointer.child.* == .Float); 889 | try expectEqual(false, info_pointer_many.Pointer.is_allowzero); 890 | } 891 | 892 | test "Runtime TypeInfo.Array" { 893 | var info_array = TypeInfo.init([2]i32); 894 | try expect(info_array == .Array); 895 | try expectEqual(@as(i32, 2), info_array.Array.len); 896 | try expect(info_array.Array.child.* == .Int); 897 | } 898 | 899 | test "Runtime TypeInfo.Struct" { 900 | const FooStruct = struct { 901 | int: i32, 902 | 903 | pub fn bar() void {} 904 | }; 905 | 906 | var info_struct = TypeInfo.init(FooStruct); 907 | try expect(info_struct == .Struct); 908 | try expect(info_struct.Struct.layout == .Auto); 909 | try expectEqual(@as(usize, 1), info_struct.Struct.fields.len); 910 | try expectEqualStrings("int", info_struct.Struct.fields[0].name); 911 | try expect(info_struct.Struct.fields[0].field_type.* == .Int); 912 | } 913 | 914 | test "Runtime TypeInfo.ComptimeFloat" { 915 | var info_comptime_float = TypeInfo.init(comptime_float); 916 | try expect(info_comptime_float == .ComptimeFloat); 917 | } 918 | 919 | test "Runtime TypeInfo.ComptimeInt" { 920 | var info_comptime_int = TypeInfo.init(comptime_int); 921 | try expect(info_comptime_int == .ComptimeInt); 922 | } 923 | 924 | // // TODO .Undefined 925 | // // TODO .Null 926 | 927 | test "Runtime TypeInfo.Optional" { 928 | var info_optional = TypeInfo.init(?i32); 929 | try expect(info_optional == .Optional); 930 | try expect(info_optional.Optional.child.* == .Int); 931 | } 932 | 933 | // // TODO .ErrorUnion 934 | // // TODO .ErrorSet 935 | 936 | test "Runtime TypeInfo.Enum" { 937 | const FooEnum = enum { Foo, Bar }; 938 | 939 | var info_enum = TypeInfo.init(FooEnum); 940 | try expect(info_enum == .Enum); 941 | } 942 | 943 | test "Runtime TypeInfo.Union" { 944 | const FooUnion = union { Foo: void, Bar: i32 }; 945 | 946 | var info_union = TypeInfo.init(FooUnion); 947 | try expect(info_union == .Union); 948 | } 949 | 950 | test "Runtime TypeInfo.Fn" { 951 | // .Fn 952 | var info_fn = TypeInfo.init(fn () void); 953 | try expect(info_fn == .Fn); 954 | } 955 | 956 | test "Runtime TypeInfo.Struct declarations" { 957 | // .Fn 958 | var info_fn = TypeInfo.init(struct { 959 | const WackType = packed struct { mr_field: *LameType, ola: u8 }; 960 | 961 | const LameType = struct { 962 | blah: **WackType, 963 | }; 964 | 965 | pub fn thing(one: usize, two: *LameType, three: [*]u16) bool { 966 | _ = three; 967 | _ = two; 968 | return one == 1; 969 | } 970 | }); 971 | try expect(info_fn == .Struct); 972 | } 973 | 974 | // TODO .BoundFn 975 | // TODO .Opaque 976 | // TODO .Frame 977 | // TODO .AnyFrame 978 | // TODO .Vector 979 | // TODO .EnumLiteral 980 | --------------------------------------------------------------------------------