about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Environment.zig121
-rw-r--r--src/root.zig126
2 files changed, 114 insertions, 133 deletions
diff --git a/src/Environment.zig b/src/Environment.zig
deleted file mode 100644
index 2fe9099..0000000
--- a/src/Environment.zig
+++ /dev/null
@@ -1,121 +0,0 @@
-//! High-level interface
-// SPDX-FileCopyrightText: 2024-2025 Nguyễn Gia Phong
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-const SourceLocation = std.builtin.SourceLocation;
-const expectEqual = std.testing.expectEqual;
-const expectError = std.testing.expectError;
-const std = @import("std");
-const zeroes = std.mem.zeroes;
-
-const Value = zsanett.Value;
-const janet = @import("rename.zig").janet;
-const unwrap = zsanett.unwrap;
-const wrap = zsanett.wrap;
-const wrapFn = zsanett.wrapFn;
-const zsanett = @import("root.zig");
-
-const Environment = @This();
-table: *janet.Table,
-
-pub fn init(base: ?Environment) Environment {
-    const replacements = if (base == null) null else base.?.table;
-    return .{ .table = janet.coreEnv(replacements) };
-}
-
-/// Evaluates the Janet program given in str.
-pub fn eval(comptime T: type, str: []const u8, src: [*:0]const u8) !T {
-    var ret: Value = undefined;
-    const errflags = janet.dobytes(janet.coreEnv(null),
-                                   str.ptr, @intCast(str.len), src, &ret);
-    // Errors are already logged by Janet,
-    // among them only one is returned and not machine-readable.
-    return if (errflags == 0) unwrap(T, ret) else error.JanetError;
-}
-
-test eval {
-    zsanett.init();
-    defer zsanett.deinit();
-    try expectEqual(try eval(i32, "(+ 3 7)", @src().fn_name), 10);
-}
-
-/// Binds val to name.
-pub fn def(name: [*:0]const u8, val: anytype,
-           documentation: [*:0]const u8) !void {
-    janet.def(janet.coreEnv(null), name, try wrap(val), documentation);
-}
-
-test def {
-    const Number = struct {
-        const Number = @This();
-        n: i8,
-        pub fn nextN(self: Number) Number {
-            return Number{ .n = self.n + 1 };
-        }
-    };
-    zsanett.init();
-    defer zsanett.deinit();
-    try def("seven", Number{ .n = 7 }, "number 7 in a struct");
-    const nine = try eval(Number, "(:next-n (:next-n seven))",
-                              @src().fn_name);
-    try expectEqual(Number{ .n = 9 }, nine);
-}
-
-/// Gets value.
-pub fn resolve(T: type, name: []const u8) !T {
-    const symbol = janet.symbol(name.ptr, @intCast(name.len));
-    return try unwrap(T, janet.resolveExt(janet.coreEnv(null), symbol).value);
-}
-
-test resolve {
-    zsanett.init();
-    defer zsanett.deinit();
-    try def("pi", 3, "pi for engineers");
-    try expectEqual(try resolve(u8, "pi"), 3);
-}
-
-/// Exposes given function in Janet.
-pub fn defn(comptime fun: anytype, reg: struct {
-    prefix: ?[*:0]const u8 = null,
-    name: [*:0]const u8,
-    documentation: [*:0]const u8,
-    source: SourceLocation,
-}) !void {
-    const cfuns = [_]janet.RegExt{
-        .{
-            .name = reg.name,
-            .cfun = try wrapFn(fun),
-            .documentation = reg.documentation,
-            .source_file = reg.source.file,
-            .source_line = @bitCast(reg.source.line),
-        },
-        zeroes(janet.RegExt),
-    };
-    const env = janet.coreEnv(null);
-    if (reg.prefix) |prefix|
-        janet.cfunsExtPrefix(env, prefix, &cfuns)
-    else
-        janet.cfunsExt(env, null, &cfuns);
-}
-
-test defn {
-    zsanett.init();
-    defer zsanett.deinit();
-    try defn(struct {
-        fn add(a: i32, b: i32) i32 { return a + b; }
-    }.add, .{
-        .name = "add",
-        .documentation = "(add a b)\n\nReturn a + b.",
-        .source = @src(),
-    });
-    try expectEqual(try eval(i32, "(add 3 7)", @src().fn_name), 10);
-
-    try defn(struct {
-        fn raise() !void { return error.ThisIsFine; }
-    }.raise, .{
-        .name = "raise",
-        .documentation = "(raise)\n\nRaise an error.",
-        .source = @src(),
-    });
-    try expectError(error.JanetError, eval(void, "(raise)", @src().fn_name));
-}
diff --git a/src/root.zig b/src/root.zig
index bcc601d..57f99c0 100644
--- a/src/root.zig
+++ b/src/root.zig
@@ -5,22 +5,24 @@
 const Allocator = std.mem.Allocator;
 const ArgsTuple = std.meta.ArgsTuple;
 const HashMap = std.AutoHashMapUnmanaged;
+const SourceLocation = std.builtin.SourceLocation;
 const assert = std.debug.assert;
 const c_allocator = std.heap.c_allocator;
 const declarations = std.meta.declarations;
 const eql = std.mem.eql;
+const expectEqual = std.testing.expectEqual;
 const expectEqualDeep = std.testing.expectEqualDeep;
+const expectError = std.testing.expectError;
 const std = @import("std");
 const toLower = std.ascii.toLower;
+const zeroes = std.mem.zeroes;
 
-pub const Environment = @import("Environment.zig");
 pub const janet = rename.janet;
 pub const kebabCase = rename.kebabCase;
 const rename = @import("rename.zig");
 pub const renamespace = rename.space;
 
 test {
-    _ = Environment;
     _ = rename;
 }
 
@@ -31,18 +33,22 @@ fn isIntern(T: type) bool {
     } and (@import("builtin").is_test or @import("root").zsanettIntern(T));
 }
 
+/// Memoized root environment.
+var root_env: ?*janet.Table = null;
 /// Associative list of Zig and wrapped Janet functions.
 var fn_map: HashMap(*const anyopaque, janet.CFunction) = undefined;
 
 /// Initializes global Janet state, that is thread local.
 pub fn init() void {
     _ = janet.init(); // always 0
+    root_env = janet.coreEnv(null);
     fn_map = .empty;
 }
 
 /// Frees resources managed by Janet.
 pub fn deinit() void {
     fn_map.deinit(c_allocator);
+    root_env = null;
     janet.deinit();
 }
 
@@ -103,12 +109,10 @@ 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;
+fn raise(err: anyerror) noreturn {
     const name = @errorName(err);
-    var msg: [*]u8 = @ptrCast(janet.smalloc(name.len * 2));
-    var n: u16 = 0;
+    var msg: [*:0]u8 = @ptrCast(janet.smalloc(name.len * 2));
+    var n: usize = 0;
     for (name) |char| {
         switch (char) {
             'A'...'Z' => if (n > 0) {
@@ -120,9 +124,11 @@ fn raise(err: anyerror) Value {
         msg[n] = toLower(char);
         n += 1;
     }
+    msg[n] = 0;
+    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);
+    janet.panic(msg);
+    unreachable;
 }
 
 pub fn wrapFn(f: anytype) Allocator.Error!janet.CFunction {
@@ -138,7 +144,7 @@ pub fn wrapFn(f: anytype) Allocator.Error!janet.CFunction {
                 inline for (&args, info.params, 0..) |*arg, param, i| {
                     const P = param.type.?;
                     arg.* = getArg(P, argv, i) catch |err|
-                        return raise(err);
+                        raise(err);
                 }
                 const result = @call(.auto, f, args);
                 return if (@typeInfo(@TypeOf(result)) != .error_union)
@@ -334,11 +340,107 @@ test "isomorphism" {
     const i = Immutable{};
     try expectEqualDeep(i, try unwrap(Immutable, try wrap(i)));
     const sum = try unwrap(@TypeOf(&Immutable.sum), try wrap(Immutable.sum));
-    try expectEqualDeep(i.sum(), sum(i));
+    try expectEqual(i.sum(), sum(i));
 
     var array: [2]bool = undefined;
     const buffer: []bool = &array;
     buffer[0] = false;
     buffer[1] = true;
-    try expectEqualDeep(buffer, try unwrap(@TypeOf(buffer), try wrap(buffer)));
+    try expectEqual(buffer, try unwrap(@TypeOf(buffer), try wrap(buffer)));
+}
+
+/// Evaluates the Janet program given in str.
+pub fn eval(comptime T: type, str: []const u8, src: [*:0]const u8) !T {
+    var ret: Value = undefined;
+    const errflags = janet.dobytes(root_env, str.ptr, @intCast(str.len),
+                                   src, &ret);
+    // Errors are already logged by Janet,
+    // among them only one is returned and not machine-readable.
+    return if (errflags == 0) unwrap(T, ret) else error.JanetError;
+}
+
+test eval {
+    init();
+    defer deinit();
+    try expectEqual(try eval(i32, "(+ 3 7)", @src().fn_name), 10);
+}
+
+/// Binds val to name.
+pub fn def(name: [*:0]const u8, val: anytype,
+           documentation: [*:0]const u8) !void {
+    janet.def(root_env, name, try wrap(val), documentation);
+}
+
+test def {
+    const Number = struct {
+        const Number = @This();
+        n: i8,
+        pub fn nextN(self: Number) Number {
+            return Number{ .n = self.n + 1 };
+        }
+    };
+    init();
+    defer deinit();
+    try def("seven", Number{ .n = 7 }, "number 7 in a struct");
+    const nine = try eval(Number, "(:next-n (:next-n seven))",
+                              @src().fn_name);
+    try expectEqual(Number{ .n = 9 }, nine);
+}
+
+/// Gets value.
+pub fn resolve(T: type, name: []const u8) !T {
+    const symbol = janet.symbol(name.ptr, @intCast(name.len));
+    return try unwrap(T, janet.resolveExt(root_env, symbol).value);
+}
+
+test resolve {
+    init();
+    defer deinit();
+    try def("pi", 3, "pi for engineers");
+    try expectEqual(try resolve(u8, "pi"), 3);
+}
+
+/// Exposes given function in Janet.
+pub fn defn(comptime fun: anytype, reg: struct {
+    prefix: ?[*:0]const u8 = null,
+    name: [*:0]const u8,
+    documentation: [*:0]const u8,
+    source: SourceLocation,
+}) !void {
+    const cfuns = [_]janet.RegExt{
+        .{
+            .name = reg.name,
+            .cfun = try wrapFn(fun),
+            .documentation = reg.documentation,
+            .source_file = reg.source.file,
+            .source_line = @bitCast(reg.source.line),
+        },
+        zeroes(janet.RegExt),
+    };
+    if (reg.prefix) |prefix|
+        janet.cfunsExtPrefix(root_env, prefix, &cfuns)
+    else
+        janet.cfunsExt(root_env, null, &cfuns);
+}
+
+test defn {
+    init();
+    defer deinit();
+    try defn(struct {
+        fn add(a: i32, b: i32) i32 { return a + b; }
+    }.add, .{
+        .name = "add",
+        .documentation = "(add a b)\n\nReturn a + b.",
+        .source = @src(),
+    });
+    try expectEqual(try eval(i32, "(add 3 7)", @src().fn_name), 10);
+
+    try defn(struct {
+        fn raise() !void { return error.ThisIsFine; }
+    }.raise, .{
+        .name = "raise",
+        .documentation = "(raise)\n\nRaise an error.",
+        .source = @src(),
+    });
+    try expectError(error.JanetError, eval(void, "(raise)", @src().fn_name));
 }