about summary refs log tree commit diff
path: root/src/root.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/root.zig')
-rw-r--r--src/root.zig76
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: {