1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
//! 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 c = @cImport(@cInclude("janet.h"));
const expectEqual = std.testing.expectEqual;
const sliceTo = std.mem.sliceTo;
const std = @import("std");
const zeroes = std.mem.zeroes;
const Table = zsanett.Table;
const Value = zsanett.Value;
const isNil = zsanett.isNil;
const unwrap = zsanett.unwrap;
const wrap = zsanett.wrap;
const zsanett = @import("root.zig");
const Environment = @This();
table: *Table,
pub fn init(base: ?Environment) Environment {
const replacements = if (base == null) null else base.?.table;
return .{ .table = c.janet_core_env(replacements) };
}
/// Evaluates the Janet program given in str.
pub fn eval(self: Environment, comptime T: type,
str: []const u8, src: [*:0]const u8) !T {
var ret: Value = undefined;
const errflags = c.janet_dobytes(self.table, 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();
const env = Environment.init(null);
try expectEqual(try env.eval(i32, "(+ 3 7)", @src().fn_name), 10);
}
/// Binds val to name.
pub fn def(self: Environment, name: [*:0]const u8, val: anytype,
documentation: [*:0]const u8) !void {
c.janet_def(self.table, 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();
const env = Environment.init(null);
try env.def("seven", Number{ .n = 7 }, "number 7 in a struct");
const nine = try env.eval(Number, "(:next-n (:next-n seven))",
@src().fn_name);
try expectEqual(Number{ .n = 9 }, nine);
}
/// Gets value.
pub fn resolve(self: Environment, T: type, name: []const u8) !T {
const symbol = c.janet_symbol(name.ptr, @intCast(name.len));
return try unwrap(T, c.janet_resolve_ext(self.table, symbol).value);
}
test resolve {
zsanett.init();
defer zsanett.deinit();
const env = Environment.init(null);
try env.def("pi", 3, "pi for engineers");
try expectEqual(try env.resolve(u8, "pi"), 3);
}
/// Exposes given function in Janet.
pub fn defn(self: Environment, comptime fun: anytype, reg: struct {
prefix: ?[*:0]const u8 = null,
name: [*:0]const u8,
documentation: [*:0]const u8,
source: SourceLocation,
}) !void {
const cfuns = [_]c.JanetRegExt{
.{
.name = reg.name,
.cfun = c.janet_unwrap_cfunction(try wrap(fun)),
.documentation = reg.documentation,
.source_file = reg.source.file,
.source_line = @bitCast(reg.source.line),
},
zeroes(c.JanetRegExt),
};
if (reg.prefix) |prefix|
c.janet_cfuns_ext_prefix(self.table, prefix, &cfuns)
else
c.janet_cfuns_ext(self.table, null, &cfuns);
}
test defn {
zsanett.init();
defer zsanett.deinit();
const env = Environment.init(null);
try env.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 env.eval(i32, "(add 3 7)", @src().fn_name), 10);
}
|