diff options
Diffstat (limited to 'src/root.zig')
-rw-r--r-- | src/root.zig | 76 |
1 files changed, 52 insertions, 24 deletions
diff --git a/src/root.zig b/src/root.zig index 2f593aa..e2f79c8 100644 --- a/src/root.zig +++ b/src/root.zig @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2024-2025 Nguyễn Gia Phong // SPDX-License-Identifier: GPL-3.0-or-later +const Allocator = std.mem.Allocator; const ArgsTuple = std.meta.ArgsTuple; const HashMap = std.AutoHashMapUnmanaged; const assert = std.debug.assert; @@ -10,6 +11,7 @@ const declarations = std.meta.declarations; const eql = std.mem.eql; const expectEqualDeep = std.testing.expectEqualDeep; const std = @import("std"); +const toLower = std.ascii.toLower; pub const Environment = @import("Environment.zig"); pub const janet = rename.janet; @@ -101,6 +103,55 @@ fn getArg(T: type, argv: [*c]Value, n: i32) !T { }; } +fn raise(err: anyerror) Value { + const symb = janet.symbol("error", 5); + const fun = janet.resolveExt(janet.coreEnv(null), symb).value; + const name = @errorName(err); + var msg: [*]u8 = @ptrCast(janet.smalloc(name.len * 2)); + var n: u16 = 0; + for (name) |char| { + switch (char) { + 'A'...'Z' => if (n > 0) { + msg[n] = ' '; + n += 1; + }, + else => {}, + } + msg[n] = toLower(char); + n += 1; + } + msg = @ptrCast(janet.srealloc(msg, n)); + var argv = [_]Value{ janet.wrapString(janet.string(msg, n)) }; + return janet.call(janet.unwrapFunction(fun), argv.len, &argv); +} + +pub fn wrapFn(f: anytype) Allocator.Error!janet.CFunction { + const F = @TypeOf(f); + const info = @typeInfo(F).@"fn"; + assert(!info.is_generic); + const entry = try fn_map.getOrPut(c_allocator, &f); + if (!entry.found_existing) + entry.value_ptr.* = struct { + fn cfun(argc: i32, argv: [*c]Value) callconv(.C) Value { + janet.fixarity(argc, info.params.len); + var args: ArgsTuple(F) = undefined; + inline for (&args, info.params, 0..) |*arg, param, i| { + const P = param.type.?; + arg.* = getArg(P, argv, i) catch |err| + return raise(err); + } + const result = @call(.auto, f, args); + return if (@typeInfo(@TypeOf(result)) != .error_union) + wrap(result) catch unreachable + else if (result) |res| + wrap(res) catch unreachable + else |err| + raise(err); + } + }.cfun; + return entry.value_ptr.*; +} + /// Wraps native type in Janet value. pub fn wrap(x: anytype) !Value { const T = @TypeOf(x); @@ -121,30 +172,7 @@ pub fn wrap(x: anytype) !Value { .@"enum" => switch (x) { inline else => |tag| keyword(@tagName(tag)), }, - .error_set => keyword("zsanett/error"), // FIXME: use janet error - .error_union => if (x) |p| try wrap(p) else |err| try wrap(err), - .@"fn" => |info| y: { - assert(!info.is_generic); - const entry = try fn_map.getOrPut(c_allocator, &x); - if (entry.found_existing) - break :y janet.wrapCfunction(entry.value_ptr.*); - const n = info.params.len; - const y = struct { - fn cfun(argc: i32, argv: [*c]Value) callconv(.C) Value { - janet.fixarity(argc, n); - var args: ArgsTuple(@TypeOf(x)) = undefined; - inline for (&args, info.params, 0..) |*arg, param, i| { - const P = param.type.?; - arg.* = getArg(P, argv, i) catch |err| - return wrap(err) catch unreachable; - } - return wrap(@call(.auto, x, args)) catch |err| - (wrap(err) catch unreachable); - } - }.cfun; - entry.value_ptr.* = &y; - break :y janet.wrapCfunction(y); - }, + .@"fn" => janet.wrapCfunction(try wrapFn(x)), .optional => if (x) |child| try wrap(child) else try wrap({}), .pointer => |info| switch (info.size) { .one => if (isIntern(info.child)) y: { |