diff options
-rw-r--r-- | src/Environment.zig | 121 | ||||
-rw-r--r-- | src/root.zig | 126 |
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)); } |