diff options
Diffstat (limited to 'src/root.zig')
-rw-r--r-- | src/root.zig | 133 |
1 files changed, 79 insertions, 54 deletions
diff --git a/src/root.zig b/src/root.zig index 796bb04..353cfcb 100644 --- a/src/root.zig +++ b/src/root.zig @@ -6,29 +6,27 @@ const ArgsTuple = std.meta.ArgsTuple; const HashMap = std.AutoHashMapUnmanaged; const Type = std.builtin.Type; const assert = std.debug.assert; -const builtin = @import("builtin"); const c = @cImport(@cInclude("janet.h")); const c_allocator = std.heap.c_allocator; +const declarations = std.meta.declarations; const eql = std.mem.eql; const expectEqualDeep = std.testing.expectEqualDeep; -const toLower = std.ascii.toLower; +const isAlphanumeric = std.ascii.isAlphanumeric; const std = @import("std"); - -pub const IgnoredDecl = struct { - type: type, - decl: []const u8, -}; -const ignored_decls = if (builtin.is_test) [_]IgnoredDecl{ -} else @import("root").zsanett_ignored_decls; -comptime { - assert(@typeInfo(@TypeOf(ignored_decls)).array.child == IgnoredDecl); -} +const toLower = std.ascii.toLower; pub const Environment = @import("Environment.zig"); test { _ = Environment; } +fn isIntern(T: type) bool { + return switch (@typeInfo(T)) { + .@"struct", .@"enum", .@"union", .@"opaque" => true, + else => false, + } and (@import("builtin").is_test or @import("root").zsanettIntern(T)); +} + /// Associative list of Zig and wrapped Janet functions. var fn_map: HashMap(*const anyopaque, c.JanetCFunction) = undefined; @@ -49,15 +47,33 @@ pub const Table = c.JanetTable; /// Janet value structure. pub const Value = c.Janet; +pub const KV = c.JanetKV; /// Creates a Janet keyword in kebab-case. fn keyword(comptime name: [:0]const u8) Value { var n: usize = 0; + var prev: u8 = ':'; var kebab: [name.len * 2]u8 = undefined; for (name) |char| { switch (char) { + '(' => { + var i = n; + while (i > 0) : (i -= 1) + kebab[i] = switch (kebab[i - 1]) { + ' ' => break, + else => kebab[i - 1], + }; + kebab[i] = '('; + n += 1; + kebab[n] = ' '; + }, + ',' => { + prev = char; + continue; + }, + '.' => kebab[n] = '/', 'A'...'Z' => { - if (n > 0) { + if (isAlphanumeric(prev)) { kebab[n] = '-'; n += 1; } @@ -67,11 +83,39 @@ fn keyword(comptime name: [:0]const u8) Value { else => kebab[n] = char, } n += 1; + prev = char; } kebab[n] = 0; return c.janet_keywordv(&kebab, @as(i32, @intCast(n))); } +fn structWithMethods(T: type, fields_len: usize) !*KV { + if (!comptime isIntern(T)) + return c.janet_struct_begin(@intCast(fields_len)); + const decls = comptime declarations(T); + var cfuns: [decls.len]struct { Value, Value } = undefined; + var count: usize = 0; + inline for (decls) |decl_info| { + const decl = @field(T, decl_info.name); + switch (@typeInfo(@TypeOf(decl))) { + .@"fn" => |fn_info| if (!fn_info.is_generic) { + cfuns[count] = .{ + keyword(decl_info.name), + try wrap(decl), + }; + count += 1; + }, + else => {}, + } + } + const kv = c.janet_struct_begin(@intCast(fields_len + count)); + for (cfuns[0..count]) |cfun| { + const k, const v = cfun; + c.janet_struct_put(kv, k, v); + } + return kv; +} + /// Gets function argument. fn getArg(comptime T: type, argv: [*c]Value, n: i32) !T { return switch (@typeInfo(T)) { @@ -134,12 +178,18 @@ pub fn wrap(x: anytype) !Value { (wrap(err) catch unreachable); } }.cfun; - entry.value_ptr.* = y; + entry.value_ptr.* = &y; break :y c.janet_wrap_cfunction(y); }, .optional => if (x) |child| try wrap(child) else try wrap({}), .pointer => |info| switch (info.size) { - .one, .many, .c => c.janet_wrap_pointer(@constCast(@ptrCast(x))), + .one => if (isIntern(info.child)) y: { + const kv = try structWithMethods(info.child, 1); + const ptr = c.janet_wrap_pointer(@constCast(@ptrCast(x))); + c.janet_struct_put(kv, keyword(@typeName(info.child)), ptr); + break :y c.janet_wrap_struct(c.janet_struct_end(kv)); + } else c.janet_wrap_pointer(@constCast(@ptrCast(x))), + .many, .c => c.janet_wrap_pointer(@constCast(@ptrCast(x))), .slice => y: { const len: i32 = @intCast(x.len * @sizeOf(info.child)); if (info.is_const) { @@ -154,36 +204,7 @@ pub fn wrap(x: anytype) !Value { }, }, .@"struct" => |info| y: { - const cfuns = fns: { - var count: usize = 0; - var cfuns: [info.decls.len]struct { Value, Value } = undefined; - inline for (info.decls) |decl_info| { - if (comptime for (ignored_decls) |ignored| { - if (ignored.type == T and eql(u8, ignored.decl, - decl_info.name)) - break true; - } else false) - continue; - const decl = @field(T, decl_info.name); - switch (@typeInfo(@TypeOf(decl))) { - .@"fn" => |fn_info| if (!fn_info.is_generic) { - cfuns[count] = .{ - keyword(decl_info.name), - try wrap(decl), - }; - count += 1; - }, - else => {}, - } - } - break :fns cfuns[0..count]; - }; - const count = cfuns.len + info.fields.len; - const kv = c.janet_struct_begin(@intCast(count)); - for (cfuns) |cfun| { - const k, const v = cfun; - c.janet_struct_put(kv, k, v); - } + const kv = try structWithMethods(T, info.fields.len); inline for (info.fields) |field| { const k = keyword(field.name); const v = @field(x, field.name); @@ -243,16 +264,20 @@ pub fn unwrap(comptime T: type, x: Value) !T { if (equal(keyword(field.name), x)) break @field(T, field.name); } else error.NoCorrespodingEnum, - .@"fn" => y: { - var iterator = fn_map.iterator(); - while (iterator.next()) |entry| - if (entry.value_ptr.* == c.janet_unwrap_cfunction(x)) - break :y entry.key_ptr.*; - break :y error.UnknownFunction; - }, .optional => |info| if (isNil(x)) null else try unwrap(info.child, x), .pointer => |info| switch (info.size) { - .one, .many, .c => @alignCast(@ptrCast(c.janet_unwrap_pointer(x))), + .one => if (@typeInfo(info.child) == .@"fn") y: { + var iterator = fn_map.iterator(); + while (iterator.next()) |entry| + if (entry.value_ptr.* == c.janet_unwrap_cfunction(x)) + break :y @ptrCast(entry.key_ptr.*); + break :y error.UnknownFunction; + } else if (isIntern(info.child)) y: { + const ptr = c.janet_struct_get(c.janet_unwrap_struct(x), + keyword(@typeName(info.child))); + break :y @alignCast(@ptrCast(c.janet_unwrap_pointer(ptr))); + } else @alignCast(@ptrCast(c.janet_unwrap_pointer(x))), + .many, .c => @alignCast(@ptrCast(c.janet_unwrap_pointer(x))), .slice => slice: { const ptr = if (info.is_const) c.janet_unwrap_string(x) @@ -318,7 +343,7 @@ test "isomorphism" { } = .{ .integer = 123456789 }, vector: @Vector(3, u4) = .{ 5, 6, 7 }, - pub fn sum(i: @This()) f64 { + fn sum(i: @This()) f64 { return @as(f64, @floatFromInt(i.integer)) + i.float; } }; |