summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2026-08-27 21:04:37 +0200
committerNathan Reiner <nathan@nathanreiner.xyz>2026-08-27 21:04:37 +0200
commit28f47b8399c4f3c4a64921fb16e38e44e8ce749e (patch)
tree53cde744130f2adf9c293fafde3c0a9c6a990d4b /src
first commit
Diffstat (limited to 'src')
-rw-r--r--src/Dict.zig66
-rw-r--r--src/Interpreter.zig39
-rw-r--r--src/Object.zig40
-rw-r--r--src/Str.zig22
-rw-r--r--src/c.h1
-rw-r--r--src/main.zig22
-rw-r--r--src/root.zig10
7 files changed, 200 insertions, 0 deletions
diff --git a/src/Dict.zig b/src/Dict.zig
new file mode 100644
index 0000000..89964cf
--- /dev/null
+++ b/src/Dict.zig
@@ -0,0 +1,66 @@
+const std = @import("std");
+const c = @import("c");
+
+const zpy = @import("root.zig");
+
+const Self = @This();
+
+object: zpy.Object,
+
+pub const Key = union(enum) {
+ string: [*c]const u8,
+ object: zpy.Object,
+};
+
+pub fn new() zpy.Error!Self {
+ if (c.PyDict_New()) |object| {
+ return .{ .object = .fromPtr(object) };
+ }
+
+ return error.Exception;
+}
+
+pub fn clear(self: *Self) void {
+ c.PyDict_Clear(self.object.ptr);
+}
+
+pub fn contains(self: *const Self, key: Key) bool {
+ return switch (key) {
+ .string => |str| c.PyDict_ContainsString(self.object.ptr, str),
+ .object => |obj| c.PyDict_Contains(self.object.ptr, obj.ptr),
+ } == 1;
+}
+
+pub fn copy(self: *const Self) Self {
+ return .{ .object = .fromPtr(c.PyDict_Copy(self.object.ptr)) };
+}
+
+pub fn set(self: *Self, key: Key, value: zpy.Object) !void {
+ const ret = switch (key) {
+ .string => |str| c.PyDict_SetItemString(self.object.ptr, str, value.ptr),
+ .object => |obj| c.PyDict_SetItem(self.object.ptr, obj.ptr, value.ptr),
+ };
+
+ if (ret == -1) return error.Exception;
+}
+
+pub fn get(self: *const Self, key: Key) ?zpy.Object {
+ return .fromC(switch (key) {
+ .string => |str| c.PyDict_GetItemString(self.object.ptr, str),
+ .object => |obj| c.PyDict_GetItem(self.object.ptr, obj.ptr),
+ });
+}
+
+pub fn delete(self: *Self, key: Key) !void {
+ const ret = switch (key) {
+ .string => |str| c.PyDict_DelItemString(self.object.ptr, str),
+ .object => |obj| c.PyDict_DelItem(self.object.ptr, obj.ptr),
+ };
+
+ if (ret == -1) return error.Exception;
+}
+
+pub fn deinit(self: *Self) void {
+ self.object.decref();
+ self.* = undefined;
+}
diff --git a/src/Interpreter.zig b/src/Interpreter.zig
new file mode 100644
index 0000000..6862706
--- /dev/null
+++ b/src/Interpreter.zig
@@ -0,0 +1,39 @@
+const c = @import("c");
+
+const zpy = @import("root.zig");
+
+const Self = @This();
+
+globals: zpy.Dict,
+
+pub fn init() !Self {
+ c.Py_Initialize();
+
+ const self: Self = .{
+ .globals = try .new(),
+ };
+
+ return self;
+}
+
+pub const RunOptions = struct {
+ locals: ?*zpy.Dict = null,
+};
+
+pub fn run(self: *Self, str: [*c]const u8, options: RunOptions) zpy.Error!zpy.Object {
+ if (c.PyRun_String(
+ str,
+ c.Py_file_input,
+ self.globals.object.ptr,
+ if (options.locals) |loc| loc.object.ptr else null,
+ )) |object| {
+ return .fromPtr(object);
+ }
+
+ return error.Exception;
+}
+
+pub fn deinit(self: *Self) void {
+ c.Py_Finalize();
+ self.* = undefined;
+}
diff --git a/src/Object.zig b/src/Object.zig
new file mode 100644
index 0000000..782071f
--- /dev/null
+++ b/src/Object.zig
@@ -0,0 +1,40 @@
+const std = @import("std");
+const c = @import("c");
+const zpy = @import("root.zig");
+
+const Self = @This();
+
+ptr: *c.PyObject,
+
+pub fn fromPtr(ptr: *c.PyObject) Self {
+ return .{ .ptr = ptr };
+}
+
+
+pub fn fromC(ptr: [*c]c.PyObject) ?Self {
+ return if (ptr) |p| .fromPtr(p) else null;
+}
+
+pub fn decref(self: *Self) void {
+ c.Py_DecRef(self.ptr);
+}
+
+pub fn repr(self: *const Self) zpy.Str {
+ return .{ .object = .fromPtr(c.PyObject_Repr(self.ptr)) };
+}
+
+pub fn call(self: *Self, args: anytype) zpy.Error!zpy.Object {
+ _ = args;
+ if (c.PyObject_CallObject(self.ptr, null)) |object| {
+ return .fromPtr(object);
+ }
+
+ return error.Exception;
+}
+
+pub fn format(self: *const Self, writer: *std.Io.Writer) !void {
+ var str = self.repr();
+ defer str.deinit();
+
+ try writer.print("{s}", .{str.asUtf8() catch return error.WriteFailed});
+}
diff --git a/src/Str.zig b/src/Str.zig
new file mode 100644
index 0000000..eaca378
--- /dev/null
+++ b/src/Str.zig
@@ -0,0 +1,22 @@
+
+const c = @import("c");
+const zpy = @import("root.zig");
+
+const Self = @This();
+
+object: zpy.Object,
+
+
+pub fn asUtf8(self: *Self) zpy.Error![]const u8 {
+ var size: c.Py_ssize_t = undefined;
+ const string: [*]const u8 = c.PyUnicode_AsUTF8AndSize(self.object.ptr, &size);
+
+ if (size == -1) return error.Exception;
+
+ return string[0..@as(usize, @intCast(size))];
+}
+
+pub fn deinit(self: *Self) void {
+ self.object.decref();
+ self.* = undefined;
+}
diff --git a/src/c.h b/src/c.h
new file mode 100644
index 0000000..576fc6d
--- /dev/null
+++ b/src/c.h
@@ -0,0 +1 @@
+#include <Python.h>
diff --git a/src/main.zig b/src/main.zig
new file mode 100644
index 0000000..a39c79f
--- /dev/null
+++ b/src/main.zig
@@ -0,0 +1,22 @@
+const std = @import("std");
+const Io = std.Io;
+
+const zpy = @import("zpy");
+
+pub fn main() !void {
+ var py: zpy.Interpreter = try .init();
+ defer py.deinit();
+
+ var locals = try zpy.Dict.new();
+ defer locals.deinit();
+
+ _ = try py.run("def test(): print('hello')", .{
+ .locals = &locals,
+ });
+
+ var test_fn = locals.get(.{ .string = "test" }) orelse unreachable;
+ defer test_fn.decref();
+ const result = try test_fn.call(.{});
+
+ std.debug.print("test() = {f}\n", .{result});
+}
diff --git a/src/root.zig b/src/root.zig
new file mode 100644
index 0000000..0e3d9e4
--- /dev/null
+++ b/src/root.zig
@@ -0,0 +1,10 @@
+const std = @import("std");
+
+pub const Object = @import("Object.zig");
+pub const Interpreter = @import("Interpreter.zig");
+pub const Dict = @import("Dict.zig");
+pub const Str = @import("Str.zig");
+
+pub const Error = error {
+ Exception,
+};