summaryrefslogtreecommitdiff
path: root/src/libzisp/value
diff options
context:
space:
mode:
authorTaylan Kammer <taylan.kammer@gmail.com>2025-02-16 22:07:26 +0100
committerTaylan Kammer <taylan.kammer@gmail.com>2025-02-16 22:07:26 +0100
commite8ee011bf530ce8c9fc8b55ebc05e4258ac2dd21 (patch)
treeb04abcfb3c3cc26e7dbbcf99a16c0111bae2d9a5 /src/libzisp/value
parentdd3d8f9d768479df36e51d402adf55afad1aff07 (diff)
update
Diffstat (limited to 'src/libzisp/value')
-rw-r--r--src/libzisp/value/boole.zig10
-rw-r--r--src/libzisp/value/char.zig24
-rw-r--r--src/libzisp/value/double.zig37
-rw-r--r--src/libzisp/value/fixnum.zig87
-rw-r--r--src/libzisp/value/misc.zig6
-rw-r--r--src/libzisp/value/ptr.zig176
-rw-r--r--src/libzisp/value/sstr.zig3
7 files changed, 343 insertions, 0 deletions
diff --git a/src/libzisp/value/boole.zig b/src/libzisp/value/boole.zig
new file mode 100644
index 0000000..d4fbd28
--- /dev/null
+++ b/src/libzisp/value/boole.zig
@@ -0,0 +1,10 @@
+const Value = @import("../value.zig").Value;
+const misc = @import("misc.zig");
+
+// These can be accessed from either namespace.
+pub const f = misc.f;
+pub const t = misc.t;
+
+pub fn pack(b: bool) Value {
+ return if (b) f else t;
+}
diff --git a/src/libzisp/value/char.zig b/src/libzisp/value/char.zig
new file mode 100644
index 0000000..7034128
--- /dev/null
+++ b/src/libzisp/value/char.zig
@@ -0,0 +1,24 @@
+const Value = @import("../value.zig").Value;
+
+pub fn check(v: Value) bool {
+ return v.isPacked() and
+ !v.char.fixnum and
+ !v.char.ptr and
+ v.char.tag == .char;
+}
+
+pub fn assert(v: Value) void {
+ if (!check(v)) {
+ v.dump();
+ @panic("not char");
+ }
+}
+
+pub fn pack(c: u21) Value {
+ return .{ .char = .{c} };
+}
+
+pub fn unpack(v: Value) u21 {
+ assert(v);
+ return v.char.value;
+}
diff --git a/src/libzisp/value/double.zig b/src/libzisp/value/double.zig
new file mode 100644
index 0000000..5c98324
--- /dev/null
+++ b/src/libzisp/value/double.zig
@@ -0,0 +1,37 @@
+const Value = @import("../value.zig").Value;
+
+// Zig API
+
+/// Checks for a Zisp double (double, +inf, -inf, or canonical NaN).
+pub fn check(v: Value) bool {
+ return !v.isPacked();
+}
+
+/// Asserts check().
+pub fn assert(v: Value) void {
+ if (!check(v)) {
+ v.dump();
+ @panic("not double");
+ }
+}
+
+pub fn pack(d: f64) Value {
+ return .{ .double = d };
+}
+
+pub fn unpack(v: Value) f64 {
+ assert(v);
+ return v.double;
+}
+
+// Zisp API
+
+pub fn pred(v: Value) Value {
+ return Value.boole.pack(check(v));
+}
+
+pub fn add(v1: Value, v2: Value) Value {
+ const d1 = unpack(v1);
+ const d2 = unpack(v2);
+ return pack(d1 + d2);
+}
diff --git a/src/libzisp/value/fixnum.zig b/src/libzisp/value/fixnum.zig
new file mode 100644
index 0000000..60b4239
--- /dev/null
+++ b/src/libzisp/value/fixnum.zig
@@ -0,0 +1,87 @@
+const std = @import("std");
+
+const Value = @import("../value.zig").Value;
+
+// Zig API
+
+/// Checks for a Zisp fixnum.
+pub fn check(v: Value) bool {
+ return v.isPacked() and v.fixnum.is_fixnum;
+}
+
+/// Asserts check().
+pub fn assert(v: Value) void {
+ if (!check(v)) {
+ v.dump();
+ @panic("not fixnum");
+ }
+}
+
+// See detailed NaN packing docs for why the +/- 1.
+const fixnum_min = std.math.minInt(i52) + 1;
+const fixnum_max = std.math.maxInt(i52) - 1;
+
+fn isValidRange(int: i64) bool {
+ return fixnum_min < int and int < fixnum_max;
+}
+
+fn assertValidRange(int: i64) void {
+ if (int < fixnum_min) {
+ std.debug.print("int too small for fixnum: {}", .{int});
+ @panic("int too small for fixnum");
+ }
+ if (int > fixnum_max) {
+ std.debug.print("int too large for fixnum: {}", .{int});
+ @panic("int too large for fixnum");
+ }
+}
+
+fn packNegative(int: i64) Value {
+ return @bitCast(int);
+}
+
+fn unpackNegative(v: Value) i64 {
+ return @bitCast(v);
+}
+
+const positive_mask: u64 = 0xfff7ffffffffffff;
+
+fn packPositive(int: i64) Value {
+ const uint: u64 = @bitCast(int);
+ return @bitCast(uint ^ positive_mask);
+}
+
+fn unpackPositive(v: Value) i64 {
+ const uint: u64 = @bitCast(v);
+ return @bitCast(uint ^ positive_mask);
+}
+
+pub fn pack(int: i64) Value {
+ assertValidRange(int);
+ if (int < 0) {
+ return packNegative(int);
+ } else {
+ return packPositive(int);
+ }
+}
+
+pub fn unpack(v: Value) i64 {
+ assert(v);
+ if (v.fixnum.negative) {
+ return unpackNegative(v);
+ } else {
+ return unpackPositive(v);
+ }
+}
+
+// Zisp API
+
+pub fn pred(v: Value) Value {
+ return Value.boole.pack(check(v));
+}
+
+pub fn add(v1: Value, v2: Value) Value {
+ const int1 = unpack(v1);
+ const int2 = unpack(v2);
+ return pack(int1 + int2);
+}
diff --git a/src/libzisp/value/misc.zig b/src/libzisp/value/misc.zig
new file mode 100644
index 0000000..2570644
--- /dev/null
+++ b/src/libzisp/value/misc.zig
@@ -0,0 +1,6 @@
+const Value = @import("../value.zig").Value;
+
+pub const f = Value{ .misc = .{0} };
+pub const t = Value{ .misc = .{1} };
+pub const nil = Value{ .misc = .{2} };
+pub const eof = Value{ .misc = .{3} };
diff --git a/src/libzisp/value/ptr.zig b/src/libzisp/value/ptr.zig
new file mode 100644
index 0000000..4bf92b6
--- /dev/null
+++ b/src/libzisp/value/ptr.zig
@@ -0,0 +1,176 @@
+const std = @import("std");
+
+const Value = @import("../value.zig").Value;
+
+// Zig API
+
+pub fn check(v: Value) bool {
+ return v.isPacked() and v.ptr.is_ptr;
+}
+
+pub fn assert(v: Value) void {
+ if (!check(v)) {
+ v.dump();
+ @panic("not a pointer");
+ }
+}
+
+// Foreign Pointers
+
+pub fn checkForeign(v: Value) bool {
+ return check(v) and v.ptr.foreign;
+}
+
+pub fn assertForeign(v: Value) void {
+ if (!checkForeign(v)) {
+ v.dump();
+ @panic("not foreign pointer");
+ }
+}
+
+pub fn packForeign(int: u50) Value {
+ return .{ .fptr = .{int} };
+}
+
+pub fn unpackForeign(v: Value) u64 {
+ assertForeign(v);
+ return v.ptr.value.foreign;
+}
+
+// Zisp Pointers
+
+pub fn checkZisp(v: Value) bool {
+ return check(v) and !v.ptr.foreign;
+}
+
+pub fn assertZisp(v: Value) void {
+ if (!checkZisp(v)) {
+ v.dump();
+ @panic("not zisp pointer");
+ }
+}
+
+pub fn checkWeak(v: Value) bool {
+ return checkZisp(v) and v.ptr.weak;
+}
+
+pub fn assertWeak(v: Value) void {
+ if (!checkWeak(v)) {
+ v.dump();
+ @panic("not weak zisp pointer");
+ }
+}
+
+pub fn checkNormal(v: Value) bool {
+ return checkZisp(v) and !v.ptr.weak;
+}
+
+pub fn assertNormal(v: Value) void {
+ if (!checkNormal(v)) {
+ v.dump();
+ @panic("not normal zisp pointer");
+ }
+}
+
+pub fn packZisp(ptr: *anyopaque, tag: Tag, weak: bool) Value {
+ return .{ .ptr = .{
+ .value = tagPtr(ptr, tag),
+ .weak = weak,
+ } };
+}
+
+pub fn pack(ptr: *anyopaque, tag: Tag) Value {
+ return packZisp(ptr, tag, false);
+}
+
+pub fn packWeak(ptr: *anyopaque, tag: Tag) Value {
+ return packZisp(ptr, tag, true);
+}
+
+// Unpacks weak as well; no need for a separate fn.
+pub fn unpack(v: Value) struct { ptr: *anyopaque, tag: Tag } {
+ assertZisp(v);
+ return untagPtr(v.ptr.value.tagged);
+}
+
+// Weak pointers may be null.
+pub fn isNull(v: Value) bool {
+ assertWeak(v);
+ const ptr, _ = untagPtr(v.ptr.value.tagged);
+ return @intFromPtr(ptr) == 0;
+}
+
+pub fn tagPtr(ptr: *anyopaque, tag: Tag) u49 {
+ const int: u64 = @intFromPtr(ptr);
+ const untagged: u49 = @truncate(int);
+ return untagged << 1 | @intFromEnum(tag);
+}
+
+pub fn untagPtr(tagged: 49) struct { ptr: *anyopaque, tag: Tag } {
+ const untagged: u49 = tagged >> 1 & 0xfffffffffff0;
+ const ptr: *anyopaque = @ptrFromInt(untagged);
+ const int: u4 = @truncate(tagged);
+ const tag: Tag = @enumFromInt(int);
+ return .{ .ptr = ptr, .tag = tag };
+}
+
+pub const Tag = enum(u4) {
+ /// 1. Strings / Symbols
+ string,
+ /// 2. Bignums / Ratnums
+ number,
+ /// 3. Pairs ([2]Value)
+ pair,
+ /// 4. Vector, bytevector, etc.
+ array,
+ /// 5. Ordered hash table
+ table,
+ /// 6. String buffer
+ text,
+ /// 7. Class, interface, etc.
+ role,
+ /// 8. Instance, basically
+ actor,
+ /// 9. I/O Port
+ port,
+ /// 10. Procedure
+ proc,
+ /// 11. Continuation
+ cont,
+ /// Other
+ other = 15,
+};
+
+// Zisp API
+
+pub fn predForeign(v: Value) Value {
+ return Value.boole.pack(checkForeign(v));
+}
+
+pub fn makeWeak(v: Value) Value {
+ assertNormal(v);
+ var copy = v;
+ copy.ptr.weak = true;
+ return copy;
+}
+
+pub fn predWeak(v: Value) Value {
+ const isWeak = checkWeak(v);
+ return Value.boole.pack(isWeak);
+}
+
+pub fn predWeakNull(v: Value) Value {
+ assertWeak(v);
+ return Value.boole.pack(v.ptr.weak);
+}
+
+pub fn getWeak(v: Value) Value {
+ assertWeak(v);
+ if (isNull(v)) {
+ return Value.boole.pack(false);
+ } else {
+ var copy = v;
+ copy.ptr.weak = false;
+ return copy;
+ }
+}
diff --git a/src/libzisp/value/sstr.zig b/src/libzisp/value/sstr.zig
new file mode 100644
index 0000000..3c0755d
--- /dev/null
+++ b/src/libzisp/value/sstr.zig
@@ -0,0 +1,3 @@
+const Value = @import("../value.zig").Value;
+
+// stub