Skip to content

Luals docgen #99

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 33 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3c3e355
proof of concept
VisenDev Sep 21, 2024
c3dd769
more types working
VisenDev Sep 21, 2024
1a04340
more types working
VisenDev Sep 21, 2024
65488f8
custom build step now working
VisenDev Sep 21, 2024
84ce7ae
integrated things better
VisenDev Sep 23, 2024
c654025
fixed segfaults, still need to add install step
VisenDev Sep 23, 2024
9133013
refactored in new file
VisenDev Sep 24, 2024
3d6b352
fixed memory corruption issues
VisenDev Sep 24, 2024
7455f58
polished implementation
VisenDev Sep 24, 2024
8b4da8b
removed old comments
VisenDev Sep 24, 2024
cd0184d
removed old testing code
VisenDev Sep 24, 2024
4704964
reworked define system to support build arguments
VisenDev Sep 25, 2024
7a3a31f
add allocator parameter
VisenDev Sep 25, 2024
6c348cb
properly close file
VisenDev Sep 25, 2024
1c27125
adding debug logging for testing
VisenDev Sep 25, 2024
a6de64c
added init capacity
VisenDev Sep 25, 2024
8d53895
added more debugging logs
VisenDev Sep 25, 2024
2a29d99
removed deinit from hashmap for testing
VisenDev Sep 25, 2024
a6d8645
allocator experiments
VisenDev Sep 25, 2024
a4d69f1
changing id test
VisenDev Sep 25, 2024
572dfac
more changes to how Ids are defined:
VisenDev Sep 25, 2024
44a7d58
reworked how arraylists are stored
VisenDev Sep 25, 2024
b4402a9
removed debugging print statements, fixed bug
VisenDev Sep 25, 2024
8f01465
minor testing change
VisenDev Sep 25, 2024
435668b
added more spacing between types
VisenDev Sep 25, 2024
b878e3f
added annotations for testing
VisenDev Sep 25, 2024
07a2388
totally reworked implementation to fix bugs
VisenDev Sep 25, 2024
547c0da
allow struct fields with default values to be null
VisenDev Sep 25, 2024
4f6f021
change how optional fields are notated
VisenDev Sep 25, 2024
6ed5166
revert back to old optional field syntax
VisenDev Sep 25, 2024
5275b0a
added unit test
VisenDev Sep 27, 2024
1c8afa7
remove outdated decls
VisenDev Sep 27, 2024
21353cb
remove error union return type from build.zig
VisenDev Sep 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.zig-cache/
zig-out/
definitions.lua
13 changes: 13 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,19 @@ pub fn build(b: *Build) void {

const docs_step = b.step("docs", "Build and install the documentation");
docs_step.dependOn(&install_docs.step);

// definitions example
const def_exe = b.addExecutable(.{
.root_source_file = b.path("examples/define-exe.zig"),
.name = "define-zig-types",
.target = target,
});
def_exe.root_module.addImport("ziglua", ziglua);
var run_def_exe = b.addRunArtifact(def_exe);
run_def_exe.addFileArg(b.path("definitions.lua"));

const define_step = b.step("define", "Generate definitions.lua file");
define_step.dependOn(&run_def_exe.step);
}

fn buildLua(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, upstream: *Build.Dependency, lang: Language, shared: bool) *Step.Compile {
Expand Down
14 changes: 14 additions & 0 deletions examples/define-exe.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const std = @import("std");
const ziglua = @import("ziglua");

const T = struct { foo: i32 };
const MyEnum = enum { asdf, fdsa, qwer, rewq };
const SubType = struct { foo: i32, bar: bool, bip: MyEnum, bap: ?[]MyEnum };
const Bippity = struct { A: ?i32, B: *bool, C: []const u8, D: ?*SubType };
const TestType = struct { a: i32, b: f32, c: bool, d: SubType, e: [10]Bippity };
const Foo = struct { far: MyEnum, near: SubType };

pub fn main() !void {
const output_file_path = std.mem.sliceTo(std.os.argv[1], 0);
try ziglua.define(std.heap.c_allocator, output_file_path, &.{ T, TestType, Foo });
}
169 changes: 169 additions & 0 deletions src/define.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
const std = @import("std");

const String = std.ArrayList(u8);
const Database = std.StringHashMap(void);

pub const DefineState = struct {
allocator: std.mem.Allocator,
database: Database,
definitions: std.ArrayList(String),

pub fn init(alloc: std.mem.Allocator) DefineState {
return DefineState{
.allocator = alloc,
.database = Database.init(alloc),
.definitions = std.ArrayList(String).init(alloc),
};
}

pub fn deinit(self: *@This()) void {
for (self.definitions.items) |def| {
def.deinit();
}
defer self.database.deinit();
defer self.definitions.deinit();
}
};

pub fn define(
alloc: std.mem.Allocator,
absolute_output_path: []const u8,
comptime to_define: []const type,
) !void {
var state = DefineState.init(alloc);
defer state.deinit();

inline for (to_define) |T| {
_ = try addClass(&state, T);
}

var file = try std.fs.createFileAbsolute(absolute_output_path, .{});
defer file.close();

try file.seekTo(0);
try file.writeAll(file_header);

for (state.definitions.items) |def| {
try file.writeAll(def.items);
try file.writeAll("\n");
}

try file.setEndPos(try file.getPos());
}

const file_header: []const u8 =
\\---@meta
\\
\\--- This is an autogenerated file,
\\--- Do not modify
\\
\\
;

fn name(comptime T: type) []const u8 {
return (comptime std.fs.path.extension(@typeName(T)))[1..];
}

fn addEnum(
state: *DefineState,
comptime T: type,
) !void {
if (state.database.contains(@typeName(T)) == false) {
try state.database.put(@typeName(T), {});
try state.definitions.append(String.init(state.allocator));
const index = state.definitions.items.len - 1;

try state.definitions.items[index].appendSlice("---@alias ");
try state.definitions.items[index].appendSlice(name(T));
try state.definitions.items[index].appendSlice("\n");

inline for (@typeInfo(T).Enum.fields) |field| {
try state.definitions.items[index].appendSlice("---|\' \"");
try state.definitions.items[index].appendSlice(field.name);
try state.definitions.items[index].appendSlice("\" \'\n");
}
}
}

pub fn addClass(
state: *DefineState,
comptime T: type,
) !void {
if (state.database.contains(@typeName(T)) == false) {
try state.database.put(@typeName(T), {});
try state.definitions.append(String.init(state.allocator));
const index = state.definitions.items.len - 1;

try state.definitions.items[index].appendSlice("---@class (exact) ");
try state.definitions.items[index].appendSlice(name(T));
try state.definitions.items[index].appendSlice("\n");

inline for (@typeInfo(T).Struct.fields) |field| {
try state.definitions.items[index].appendSlice("---@field ");
try state.definitions.items[index].appendSlice(field.name);

if (field.default_value != null) {
try state.definitions.items[index].appendSlice("?");
}
try state.definitions.items[index].appendSlice(" ");
try luaTypeName(state, index, field.type);
try state.definitions.items[index].appendSlice("\n");
}
}
}

fn luaTypeName(
state: *DefineState,
index: usize,
comptime T: type,
) !void {
switch (@typeInfo(T)) {
.Struct => {
try state.definitions.items[index].appendSlice(name(T));
try addClass(state, T);
},
.Pointer => |info| {
if (info.child == u8 and info.size == .Slice) {
try state.definitions.items[index].appendSlice("string");
} else switch (info.size) {
.One => {
try state.definitions.items[index].appendSlice("lightuserdata");
},
.C, .Many, .Slice => {
try luaTypeName(state, index, info.child);
try state.definitions.items[index].appendSlice("[]");
},
}
},
.Array => |info| {
try luaTypeName(state, index, info.child);
try state.definitions.items[index].appendSlice("[]");
},

.Vector => |info| {
try luaTypeName(state, index, info.child);
try state.definitions.items[index].appendSlice("[]");
},
.Optional => |info| {
try luaTypeName(state, index, info.child);
try state.definitions.items[index].appendSlice(" | nil");
},
.Enum => {
try state.definitions.items[index].appendSlice(name(T));
try addEnum(state, T);
},
.Int => {
try state.definitions.items[index].appendSlice("integer");
},
.Float => {
try state.definitions.items[index].appendSlice("number");
},
.Bool => {
try state.definitions.items[index].appendSlice("boolean");
},
else => {
@compileLog(T);
@compileError("Type not supported");
},
}
}
3 changes: 3 additions & 0 deletions src/lib.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
const std = @import("std");

pub const def = @import("define.zig");
pub const define = def.define;

const c = @cImport({
@cInclude("luaconf.h");
@cInclude("lua.h");
Expand Down
66 changes: 66 additions & 0 deletions src/tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2816,3 +2816,69 @@ test "doFile" {

try expectEqualStrings("testing", try lua.get([]const u8, "GLOBAL"));
}

test "define" {
const expected =
\\---@class (exact) T
\\---@field foo integer
\\
\\---@class (exact) TestType
\\---@field a integer
\\---@field b number
\\---@field c boolean
\\---@field d SubType
\\---@field e Bippity[]
\\
\\---@class (exact) SubType
\\---@field foo integer
\\---@field bar boolean
\\---@field bip MyEnum
\\---@field bap MyEnum[] | nil
\\
\\---@alias MyEnum
\\---|' "asdf" '
\\---|' "fdsa" '
\\---|' "qwer" '
\\---|' "rewq" '
\\
\\---@class (exact) Bippity
\\---@field A integer | nil
\\---@field B lightuserdata
\\---@field C string
\\---@field D lightuserdata | nil
\\
\\---@class (exact) Foo
\\---@field far MyEnum
\\---@field near SubType
\\
\\
;

const T = struct { foo: i32 };
const MyEnum = enum { asdf, fdsa, qwer, rewq };
const SubType = struct { foo: i32, bar: bool, bip: MyEnum, bap: ?[]MyEnum };
const Bippity = struct { A: ?i32, B: *bool, C: []const u8, D: ?*SubType };
const TestType = struct { a: i32, b: f32, c: bool, d: SubType, e: [10]Bippity };
const Foo = struct { far: MyEnum, near: SubType };

const a = std.testing.allocator;

var state = ziglua.def.DefineState.init(a);
defer state.deinit();

const to_define: []const type = &.{ T, TestType, Foo };
inline for (to_define) |my_type| {
_ = try ziglua.def.addClass(&state, my_type);
}

var buffer: [10000]u8 = .{0} ** 10000;
var buffer_stream = std.io.fixedBufferStream(&buffer);
var writer = buffer_stream.writer();

for (state.definitions.items) |def| {
try writer.writeAll(def.items);
try writer.writeAll("\n");
}

try std.testing.expectEqualSlices(u8, expected, buffer_stream.getWritten());
}
Loading