summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTaylan Kammer <taylan.kammer@gmail.com>2025-03-30 20:10:10 +0200
committerTaylan Kammer <taylan.kammer@gmail.com>2025-03-30 20:10:10 +0200
commit4922f3a4437f7ea8495f32aea0aa329830bd2d8b (patch)
tree2a31c4679114d27351f8d156a31409fd72eef60a
parent3d05c94b9d8aa964e4ff848c95d5999cec170e04 (diff)
moar cleanup
-rw-r--r--src/main.zig2
-rw-r--r--src/test/all.zig8
-rw-r--r--src/test/bench.zig15
-rw-r--r--src/test/data/parser-test-1.scm (renamed from test-data/parser-test-1.scm)0
-rw-r--r--src/test/data/parser-test-2.scm (renamed from test-data/parser-test-2.scm)0
-rw-r--r--src/test/data/parser-torture.scm (renamed from test-data/parser-torture.scm)0
-rw-r--r--src/test/data/string.txt (renamed from test-data/string.txt)0
-rw-r--r--src/test/parse.zig162
-rw-r--r--src/test/strings.zig30
-rw-r--r--src/test/values.zig247
-rw-r--r--src/zisp.zig428
-rw-r--r--src/zisp/io/Parser.zig27
-rw-r--r--src/zisp/io/unparser.zig7
-rw-r--r--src/zisp/lib/list.zig4
-rw-r--r--src/zisp/value.zig12
-rw-r--r--src/zisp/value/boole.zig11
-rw-r--r--src/zisp/value/eof.zig28
-rw-r--r--src/zisp/value/misc.zig23
-rw-r--r--src/zisp/value/nil.zig28
-rw-r--r--src/zisp/value/ptr.zig2
20 files changed, 523 insertions, 511 deletions
diff --git a/src/main.zig b/src/main.zig
index 769a906..9e86d03 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -8,7 +8,7 @@ pub fn main() !void {
while (true) {
try writer.writeAll("> ");
const datum = zisp.io.parser.parse(reader);
- if (datum.eq(zisp.value.eof.eof)) {
+ if (datum.eq(zisp.value.eof)) {
try writer.writeAll("\n");
return;
}
diff --git a/src/test/all.zig b/src/test/all.zig
new file mode 100644
index 0000000..d985f46
--- /dev/null
+++ b/src/test/all.zig
@@ -0,0 +1,8 @@
+pub const values = @import("values.zig");
+pub const strings = @import("strings.zig");
+
+pub const parse = @import("parse.zig");
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/src/test/bench.zig b/src/test/bench.zig
new file mode 100644
index 0000000..a6203a3
--- /dev/null
+++ b/src/test/bench.zig
@@ -0,0 +1,15 @@
+const std = @import("std");
+
+fn benchmark(name: []const u8, iters: usize, func: fn () anyerror!void) !void {
+ var timer = try std.time.Timer.start();
+ for (0..iters) |i| {
+ _ = i;
+ try func();
+ }
+ const ns: f64 = @floatFromInt(timer.lap());
+ const secs = ns / 1_000_000_000;
+ std.debug.print(
+ "bench {s} x {}: {d:.3}s\n",
+ .{ name, iters, secs },
+ );
+}
diff --git a/test-data/parser-test-1.scm b/src/test/data/parser-test-1.scm
index 87c41b5..87c41b5 100644
--- a/test-data/parser-test-1.scm
+++ b/src/test/data/parser-test-1.scm
diff --git a/test-data/parser-test-2.scm b/src/test/data/parser-test-2.scm
index 484c61e..484c61e 100644
--- a/test-data/parser-test-2.scm
+++ b/src/test/data/parser-test-2.scm
diff --git a/test-data/parser-torture.scm b/src/test/data/parser-torture.scm
index d475379..d475379 100644
--- a/test-data/parser-torture.scm
+++ b/src/test/data/parser-torture.scm
diff --git a/test-data/string.txt b/src/test/data/string.txt
index 31382be..31382be 100644
--- a/test-data/string.txt
+++ b/src/test/data/string.txt
diff --git a/src/test/parse.zig b/src/test/parse.zig
new file mode 100644
index 0000000..0e83258
--- /dev/null
+++ b/src/test/parse.zig
@@ -0,0 +1,162 @@
+const std = @import("std");
+
+const testing = std.testing;
+
+pub const io = @import("../zisp/io.zig");
+pub const value = @import("../zisp/value.zig");
+
+pub const Value = value.Value;
+
+fn parseString(str: []const u8) Value {
+ var fbs = std.io.fixedBufferStream(str);
+ return io.parser.parse(fbs.reader().any());
+}
+
+test "parse" {
+ const val = parseString("\"foo\"");
+
+ try testing.expect(value.sstr.check(val));
+
+ const s = value.sstr.unpack(val);
+ try testing.expectEqualStrings("foo", s.slice());
+}
+
+test "parse2" {
+ const val = parseString(
+ \\ ;; Testing some crazy datum comments
+ \\ ;~"bar"([x #"y"]{##`,'z}) #"foo"
+ \\ ;; end
+ );
+
+ const r = value.rune.unpack(value.pair.car(val));
+ try testing.expectEqualStrings("HASH", r.slice());
+
+ const s = value.pair.cdr(val);
+ try testing.expect(value.sstr.check(s));
+
+ const f = value.sstr.unpack(s);
+ try testing.expectEqualStrings("foo", f.slice());
+}
+
+test "parse3" {
+ const val = parseString(
+ \\(foo ;~x ;~(x y) ;~x #bar [#x #"baz"] 'bat)
+ );
+
+ const car = value.pair.car;
+ const cdr = value.pair.cdr;
+
+ const e1 = car(val);
+ const e2 = car(cdr(val));
+ const e3 = car(cdr(cdr(val)));
+ const e4 = car(cdr(cdr(cdr(val))));
+
+ try testing.expect(value.sstr.check(e1));
+ try testing.expect(value.rune.check(e2));
+ try testing.expect(value.pair.check(e3));
+ try testing.expect(value.pair.check(e4));
+}
+
+test "parse4" {
+ const val = parseString("(foo . ;~x bar ;~y)");
+
+ const s = value.sstr.unpack(value.pair.car(val));
+ try testing.expectEqualStrings("foo", s.slice());
+
+ const f = value.sstr.unpack(value.pair.cdr(val));
+ try testing.expectEqualStrings("bar", f.slice());
+}
+
+test "unparse" {
+ var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
+ var out: std.ArrayList(u8) = .init(gpa.allocator());
+
+ const w = out.writer();
+ const v = parseString("#foo");
+ try io.unparser.unparse(w, v);
+ try testing.expectEqualStrings("#foo", try out.toOwnedSlice());
+}
+
+test "unparse2" {
+ var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
+ var out: std.ArrayList(u8) = .init(gpa.allocator());
+
+ const w = out.writer();
+ const v = parseString("#{foo bar['x]}");
+ try io.unparser.unparse(w, v);
+ try testing.expectEqualStrings(
+ "(#HASH #BRACE foo (#JOIN bar #SQUARE (#QUOTE . x)))",
+ try out.toOwnedSlice(),
+ );
+}
+
+fn writeParseResult(str: []const u8) !void {
+ const w = std.io.getStdErr().writer();
+ const v = parseString(str);
+ try io.unparser.unparse(w, v);
+ try w.writeByte('\n');
+}
+
+test "unparse3" {
+ try writeParseResult("#{foo bar['x](y)(z)}");
+}
+
+test "unparse4" {
+ try writeParseResult("(foo ;~bar)");
+}
+
+test "unparse5" {
+ try writeParseResult("(;~foo foo ;~bar . ;~bar bar ;~bar)");
+}
+
+test "unparse6" {
+ try writeParseResult("(foo bar ... baz bat.(qux))");
+}
+
+test "unparse7" {
+ try writeParseResult("#`(#,(->keyword (syntax->datum #'sym)) . in)");
+}
+
+fn parseBench(path: []const u8, iters: usize) !void {
+ const file = try std.fs.cwd().openFile(path, .{});
+ defer file.close();
+
+ var sfa = io.parser.DefaultSfa.init(std.heap.smp_allocator);
+ var parser = try io.parser.initSfa(&sfa);
+ defer parser.deinit();
+
+ var timer = try std.time.Timer.start();
+ for (0..iters) |i| {
+ _ = i;
+ var br = std.io.bufferedReader(file.reader());
+ const reader = br.reader().any();
+ // const reader = file.reader().any();
+ var v: Value = undefined;
+ while (true) {
+ // std.debug.print("hihi {s}\n", .{path});
+ v = parser.run(reader) catch |e| {
+ std.debug.print("\nfile pos: {}\n", .{
+ try file.getPos(),
+ });
+ return e;
+ };
+ // try io.unparser.unparse(std.io.getStdOut().writer().any(), v);
+ if (value.eof.eq(v)) {
+ break;
+ }
+ }
+ try file.seekTo(0);
+ }
+ const ns: f64 = @floatFromInt(timer.lap());
+ const secs = ns / 1_000_000_000;
+ std.debug.print(
+ "parse {s} x {}: {d:.3}s\n",
+ .{ path, iters, secs },
+ );
+}
+
+test "parse bench" {
+ try parseBench("src/test/data/parser-test-1.scm", 200);
+ try parseBench("src/test/data/parser-test-2.scm", 800);
+ try parseBench("src/test/data/parser-torture.scm", 1);
+}
diff --git a/src/test/strings.zig b/src/test/strings.zig
new file mode 100644
index 0000000..629bc46
--- /dev/null
+++ b/src/test/strings.zig
@@ -0,0 +1,30 @@
+const std = @import("std");
+
+const testing = std.testing;
+
+pub const value = @import("../zisp/value.zig");
+
+test "istr" {
+ const istr = value.istr;
+ const fx = value.fixnum;
+
+ const s1 = "foo bar baz";
+ const v1 = istr.intern(s1, false);
+ const v1_len: usize = @intCast(fx.unpack(istr.len(v1)));
+ try testing.expectEqualStrings(s1, istr.getHeader(v1).bytes());
+ try testing.expectEqual(s1.len, v1_len);
+
+ const file = try std.fs.cwd().openFile("src/test/data/string.txt", .{});
+ defer file.close();
+ var s2_buf: [4096]u8 = undefined;
+ const s2_len = try file.readAll(&s2_buf);
+ var s2: []u8 = s2_buf[0..s2_len];
+ const v2 = istr.intern(s2, false);
+ const v2_len: usize = @intCast(fx.unpack(istr.len(v2)));
+ var s2_orig_buf: [4096]u8 = undefined;
+ @memcpy(&s2_orig_buf, &s2_buf);
+ const s2_orig = s2_orig_buf[0..s2_len];
+ s2[0] = s2[0] +% 1;
+ try testing.expectEqualStrings(s2_orig, istr.getHeader(v2).bytes());
+ try testing.expectEqual(s2_len, v2_len);
+}
diff --git a/src/test/values.zig b/src/test/values.zig
new file mode 100644
index 0000000..7339f22
--- /dev/null
+++ b/src/test/values.zig
@@ -0,0 +1,247 @@
+const builtin = @import("builtin");
+const std = @import("std");
+
+const testing = std.testing;
+
+pub const gc = @import("../zisp/gc.zig");
+pub const io = @import("../zisp/io.zig");
+pub const lib = @import("../zisp/lib.zig");
+pub const value = @import("../zisp/value.zig");
+
+pub const Hval = gc.Hval;
+
+pub const ShortString = value.ShortString;
+pub const Value = value.Value;
+
+test "double" {
+ const d1: f64 = 0.123456789;
+ const d2: f64 = -0.987654321;
+ const v1 = value.double.pack(d1);
+ const v2 = value.double.pack(d2);
+ const v3 = value.double.add(v1, v2);
+ const result = value.double.unpack(v3);
+
+ try testing.expect(value.double.check(v1));
+ try testing.expect(value.double.check(v2));
+ try testing.expect(value.double.check(v3));
+
+ try testing.expectEqual(d1 + d2, result);
+}
+
+test "fixnum" {
+ const int1: i64 = 123456789;
+ const int2: i64 = -987654321;
+ const v1 = value.fixnum.pack(int1);
+ const v2 = value.fixnum.pack(int2);
+ const v3 = value.fixnum.add(v1, v2);
+ const result = value.fixnum.unpack(v3);
+
+ try testing.expect(value.fixnum.check(v1));
+ try testing.expect(value.fixnum.check(v2));
+ try testing.expect(value.fixnum.check(v3));
+
+ try testing.expectEqual(int1 + int2, result);
+}
+
+test "ptr" {
+ const ptr = value.ptr;
+
+ const val: *Hval = @ptrFromInt(256);
+ const tag = ptr.Tag.pair;
+
+ const p = ptr.pack(val, tag);
+ try testing.expect(ptr.check(p));
+ try testing.expect(ptr.checkZispTag(p, tag));
+ try testing.expect(ptr.checkStrong(p));
+
+ const pv, const pt = ptr.unpack(p);
+ try testing.expectEqual(val, pv);
+ try testing.expectEqual(tag, pt);
+
+ var w = ptr.makeWeak(p);
+ try testing.expect(ptr.check(w));
+ try testing.expect(ptr.checkZispTag(w, tag));
+ try testing.expect(ptr.checkWeak(w));
+ try testing.expectEqual(true, value.boole.unpack(ptr.predWeak(w)));
+ try testing.expectEqual(false, value.boole.unpack(ptr.predWeakNull(w)));
+
+ const wv, const wt = ptr.unpack(w);
+ try testing.expectEqual(val, wv);
+ try testing.expectEqual(tag, wt);
+
+ const wv2, const wt2 = ptr.unpack(ptr.getWeak(w));
+ try testing.expectEqual(val, wv2);
+ try testing.expectEqual(tag, wt2);
+
+ ptr.setWeakNull(&w);
+ try testing.expect(ptr.check(w));
+ try testing.expect(ptr.checkWeak(w));
+ try testing.expect(ptr.isWeakNull(w));
+ try testing.expectEqual(true, value.boole.unpack(ptr.predWeak(w)));
+ try testing.expectEqual(true, value.boole.unpack(ptr.predWeakNull(w)));
+ try testing.expectEqual(false, value.boole.unpack(ptr.getWeak(w)));
+}
+
+test "fptr" {
+ const ptr = value.ptr;
+
+ const int1: u50 = 0;
+ const int2: u50 = std.math.maxInt(u50);
+
+ const f1 = ptr.packForeign(int1);
+ try testing.expect(ptr.checkForeign(f1));
+ try testing.expectEqual(int1, ptr.unpackForeign(f1));
+
+ const f2 = ptr.packForeign(int2);
+ try testing.expect(ptr.checkForeign(f2));
+ try testing.expectEqual(int2, ptr.unpackForeign(f2));
+}
+
+test "rune" {
+ const r = value.rune.pack("test");
+ try testing.expect(value.rune.check(r));
+
+ const s = value.rune.unpack(r);
+ try testing.expectEqualStrings("test", s.slice());
+}
+
+const SstrImpl = struct { SstrPack, SstrUnpack };
+const SstrPack = *const fn ([]const u8) Value;
+const SstrUnpack = *const fn (Value) ShortString;
+
+test "sstr" {
+ const impls = [_]SstrImpl{
+ .{ value.sstr.pack, value.sstr.unpack },
+ // .{ value.sstr.pack1, value.sstr.unpack1 },
+ // .{ value.sstr.pack2, value.sstr.unpack2 },
+ // .{ value.sstr.pack3, value.sstr.unpack3 },
+ // .{ value.sstr.pack4, value.sstr.unpack4 },
+ };
+
+ for (impls) |impl| {
+ try testSstr(impl);
+ }
+
+ if (impls.len > 1) {
+ const iters = switch (@import("builtin").mode) {
+ .Debug, .ReleaseSmall => 10_000_000,
+ .ReleaseSafe => 100_000_000,
+ .ReleaseFast => 1_000_000_000,
+ };
+ std.debug.print("Benchmarking sstr with {} iters.\n", .{iters});
+ inline for (impls, 0..) |impl, i| {
+ try benchmarkSstr(impl, i, iters);
+ }
+ }
+}
+
+fn testSstr(impl: SstrImpl) !void {
+ const pack, const unpack = impl;
+
+ const ss1 = pack("1");
+ const ss2 = pack("123");
+ const ss3 = pack("123456");
+
+ const s1 = unpack(ss1);
+ const s2 = unpack(ss2);
+ const s3 = unpack(ss3);
+
+ try testing.expect(value.sstr.check(ss1));
+ try testing.expect(value.sstr.check(ss2));
+ try testing.expect(value.sstr.check(ss3));
+
+ try testing.expectEqualStrings("1", s1.slice());
+ try testing.expectEqualStrings("123", s2.slice());
+ try testing.expectEqualStrings("123456", s3.slice());
+}
+
+fn benchmarkSstr(impl: SstrImpl, id: usize, iters: usize) !void {
+ const pack, const unpack = impl;
+
+ var timer = try std.time.Timer.start();
+ var ns: f64 = undefined;
+ var secs: f64 = undefined;
+
+ var ss1: Value = undefined;
+ var ss2: Value = undefined;
+ var ss3: Value = undefined;
+
+ for (0..iters) |_i| {
+ _ = _i;
+ ss1 = pack("1");
+ ss2 = pack("123");
+ ss3 = pack("123456");
+ }
+
+ ns = @floatFromInt(timer.lap());
+ secs = ns / 1_000_000_000;
+
+ std.debug.print("pack{}: {d:.3}s\t", .{ id, secs });
+
+ for (0..iters) |_i| {
+ _ = _i;
+ std.mem.doNotOptimizeAway(unpack(ss1));
+ std.mem.doNotOptimizeAway(unpack(ss2));
+ std.mem.doNotOptimizeAway(unpack(ss3));
+ }
+
+ ns = @floatFromInt(timer.lap());
+ secs = ns / 1_000_000_000;
+
+ std.debug.print("unpack{}: {d:.3}s\n", .{ id, secs });
+}
+
+test "char" {
+ const c1 = value.char.pack('\x00');
+ try testing.expect(value.char.check(c1));
+ try testing.expectEqual('\x00', value.char.unpack(c1));
+
+ const c2 = value.char.pack('😀');
+ try testing.expect(value.char.check(c2));
+ try testing.expectEqual('😀', value.char.unpack(c2));
+}
+
+test "misc" {
+ const f = value.boole.pack(false);
+ try testing.expect(value.f.eq(f));
+ try testing.expect(value.boole.check(f));
+ try testing.expectEqual(false, value.boole.unpack(f));
+ try testing.expect(value.boole.unpack(value.boole.pred(f)));
+
+ const t = value.boole.pack(true);
+ try testing.expect(value.t.eq(t));
+ try testing.expect(value.boole.check(t));
+ try testing.expectEqual(true, value.boole.unpack(t));
+ try testing.expect(value.boole.unpack(value.boole.pred(t)));
+
+ const nil = value.nil;
+ try testing.expect(value.boole.unpack(value.misc.isNil(nil)));
+
+ const eof = value.eof;
+ try testing.expect(value.boole.unpack(value.misc.isEof(eof)));
+}
+
+test "pair" {
+ const v1 = value.fixnum.pack(1);
+ const v2 = value.fixnum.pack(2);
+
+ const v3 = value.fixnum.pack(3);
+ const v4 = value.fixnum.pack(4);
+
+ const p = value.pair.cons(v1, v2);
+ try testing.expect(value.pair.check(p));
+ try testing.expect(value.boole.unpack(value.pair.pred(p)));
+
+ const car = value.pair.car(p);
+ const cdr = value.pair.cdr(p);
+ try testing.expectEqual(1, value.fixnum.unpack(car));
+ try testing.expectEqual(2, value.fixnum.unpack(cdr));
+
+ value.pair.setcar(p, v3);
+ value.pair.setcdr(p, v4);
+
+ const car2 = value.pair.car(p);
+ const cdr2 = value.pair.cdr(p);
+ try testing.expectEqual(3, value.fixnum.unpack(car2));
+ try testing.expectEqual(4, value.fixnum.unpack(cdr2));
+}
diff --git a/src/zisp.zig b/src/zisp.zig
index 6472daf..d349a5f 100644
--- a/src/zisp.zig
+++ b/src/zisp.zig
@@ -1,8 +1,6 @@
const builtin = @import("builtin");
const std = @import("std");
-const testing = std.testing;
-
pub const gc = @import("zisp/gc.zig");
pub const io = @import("zisp/io.zig");
pub const lib = @import("zisp/lib.zig");
@@ -13,428 +11,8 @@ pub const Hval = gc.Hval;
pub const ShortString = value.ShortString;
pub const Value = value.Value;
-fn benchmark(name: []const u8, iters: usize, func: fn () anyerror!void) !void {
- var timer = try std.time.Timer.start();
- for (0..iters) |i| {
- _ = i;
- try func();
- }
- const ns: f64 = @floatFromInt(timer.lap());
- const secs = ns / 1_000_000_000;
- std.debug.print(
- "bench {s} x {}: {d:.3}s\n",
- .{ name, iters, secs },
- );
-}
-
-test "double" {
- const d1: f64 = 0.123456789;
- const d2: f64 = -0.987654321;
- const v1 = value.double.pack(d1);
- const v2 = value.double.pack(d2);
- const v3 = value.double.add(v1, v2);
- const result = value.double.unpack(v3);
-
- try std.testing.expect(value.double.check(v1));
- try std.testing.expect(value.double.check(v2));
- try std.testing.expect(value.double.check(v3));
-
- try std.testing.expectEqual(d1 + d2, result);
-}
-
-test "fixnum" {
- const int1: i64 = 123456789;
- const int2: i64 = -987654321;
- const v1 = value.fixnum.pack(int1);
- const v2 = value.fixnum.pack(int2);
- const v3 = value.fixnum.add(v1, v2);
- const result = value.fixnum.unpack(v3);
-
- try std.testing.expect(value.fixnum.check(v1));
- try std.testing.expect(value.fixnum.check(v2));
- try std.testing.expect(value.fixnum.check(v3));
-
- try std.testing.expectEqual(int1 + int2, result);
-}
-
-test "ptr" {
- const ptr = value.ptr;
-
- const val: *Hval = @ptrFromInt(256);
- const tag = ptr.Tag.pair;
-
- const p = ptr.pack(val, tag);
- try std.testing.expect(ptr.check(p));
- try std.testing.expect(ptr.checkZispTag(p, tag));
- try std.testing.expect(ptr.checkStrong(p));
-
- const pv, const pt = ptr.unpack(p);
- try std.testing.expectEqual(val, pv);
- try std.testing.expectEqual(tag, pt);
-
- var w = ptr.makeWeak(p);
- try std.testing.expect(ptr.check(w));
- try std.testing.expect(ptr.checkZispTag(w, tag));
- try std.testing.expect(ptr.checkWeak(w));
- try std.testing.expectEqual(true, value.boole.unpack(ptr.predWeak(w)));
- try std.testing.expectEqual(false, value.boole.unpack(ptr.predWeakNull(w)));
-
- const wv, const wt = ptr.unpack(w);
- try std.testing.expectEqual(val, wv);
- try std.testing.expectEqual(tag, wt);
-
- const wv2, const wt2 = ptr.unpack(ptr.getWeak(w));
- try std.testing.expectEqual(val, wv2);
- try std.testing.expectEqual(tag, wt2);
-
- ptr.setWeakNull(&w);
- try std.testing.expect(ptr.check(w));
- try std.testing.expect(ptr.checkWeak(w));
- try std.testing.expect(ptr.isWeakNull(w));
- try std.testing.expectEqual(true, value.boole.unpack(ptr.predWeak(w)));
- try std.testing.expectEqual(true, value.boole.unpack(ptr.predWeakNull(w)));
- try std.testing.expectEqual(false, value.boole.unpack(ptr.getWeak(w)));
-}
-
-test "fptr" {
- const ptr = value.ptr;
-
- const int1: u50 = 0;
- const int2: u50 = std.math.maxInt(u50);
-
- const f1 = ptr.packForeign(int1);
- try std.testing.expect(ptr.checkForeign(f1));
- try std.testing.expectEqual(int1, ptr.unpackForeign(f1));
-
- const f2 = ptr.packForeign(int2);
- try std.testing.expect(ptr.checkForeign(f2));
- try std.testing.expectEqual(int2, ptr.unpackForeign(f2));
-}
-
-test "rune" {
- const r = value.rune.pack("test");
- try std.testing.expect(value.rune.check(r));
-
- const s = value.rune.unpack(r);
- try std.testing.expectEqualStrings("test", s.slice());
-}
-
-const SstrImpl = struct { SstrPack, SstrUnpack };
-const SstrPack = *const fn ([]const u8) Value;
-const SstrUnpack = *const fn (Value) ShortString;
-
-test "sstr" {
- const impls = [_]SstrImpl{
- .{ value.sstr.pack, value.sstr.unpack },
- // .{ value.sstr.pack1, value.sstr.unpack1 },
- // .{ value.sstr.pack2, value.sstr.unpack2 },
- // .{ value.sstr.pack3, value.sstr.unpack3 },
- // .{ value.sstr.pack4, value.sstr.unpack4 },
- };
-
- for (impls) |impl| {
- try testSstr(impl);
- }
-
- if (impls.len > 1) {
- const iters = switch (@import("builtin").mode) {
- .Debug, .ReleaseSmall => 10_000_000,
- .ReleaseSafe => 100_000_000,
- .ReleaseFast => 1_000_000_000,
- };
- std.debug.print("Benchmarking sstr with {} iters.\n", .{iters});
- inline for (impls, 0..) |impl, i| {
- try benchmarkSstr(impl, i, iters);
- }
- }
-}
-
-fn testSstr(impl: SstrImpl) !void {
- const pack, const unpack = impl;
-
- const ss1 = pack("1");
- const ss2 = pack("123");
- const ss3 = pack("123456");
-
- const s1 = unpack(ss1);
- const s2 = unpack(ss2);
- const s3 = unpack(ss3);
-
- try std.testing.expect(value.sstr.check(ss1));
- try std.testing.expect(value.sstr.check(ss2));
- try std.testing.expect(value.sstr.check(ss3));
-
- try std.testing.expectEqualStrings("1", s1.slice());
- try std.testing.expectEqualStrings("123", s2.slice());
- try std.testing.expectEqualStrings("123456", s3.slice());
-}
-
-fn benchmarkSstr(impl: SstrImpl, id: usize, iters: usize) !void {
- const pack, const unpack = impl;
-
- var timer = try std.time.Timer.start();
- var ns: f64 = undefined;
- var secs: f64 = undefined;
-
- var ss1: Value = undefined;
- var ss2: Value = undefined;
- var ss3: Value = undefined;
-
- for (0..iters) |_i| {
- _ = _i;
- ss1 = pack("1");
- ss2 = pack("123");
- ss3 = pack("123456");
- }
-
- ns = @floatFromInt(timer.lap());
- secs = ns / 1_000_000_000;
-
- std.debug.print("pack{}: {d:.3}s\t", .{ id, secs });
-
- for (0..iters) |_i| {
- _ = _i;
- std.mem.doNotOptimizeAway(unpack(ss1));
- std.mem.doNotOptimizeAway(unpack(ss2));
- std.mem.doNotOptimizeAway(unpack(ss3));
- }
-
- ns = @floatFromInt(timer.lap());
- secs = ns / 1_000_000_000;
-
- std.debug.print("unpack{}: {d:.3}s\n", .{ id, secs });
-}
-
-test "char" {
- const c1 = value.char.pack('\x00');
- try std.testing.expect(value.char.check(c1));
- try std.testing.expectEqual('\x00', value.char.unpack(c1));
-
- const c2 = value.char.pack('😀');
- try std.testing.expect(value.char.check(c2));
- try std.testing.expectEqual('😀', value.char.unpack(c2));
-}
-
-test "misc" {
- const f = value.boole.pack(false);
- try std.testing.expect(value.boole.check(f));
- try std.testing.expectEqual(false, value.boole.unpack(f));
- try std.testing.expect(value.boole.unpack(value.boole.pred(f)));
-
- const t = value.boole.pack(true);
- try std.testing.expect(value.boole.check(t));
- try std.testing.expectEqual(true, value.boole.unpack(t));
- try std.testing.expect(value.boole.unpack(value.boole.pred(t)));
-
- const nil = value.nil.get();
- try std.testing.expect(value.nil.check(nil));
- try std.testing.expect(value.boole.unpack(value.nil.pred(nil)));
-
- const eof = value.eof.get();
- try std.testing.expect(value.eof.check(eof));
- try std.testing.expect(value.boole.unpack(value.eof.pred(eof)));
-}
-
-test "pair" {
- const v1 = value.fixnum.pack(1);
- const v2 = value.fixnum.pack(2);
-
- const v3 = value.fixnum.pack(3);
- const v4 = value.fixnum.pack(4);
-
- const p = value.pair.cons(v1, v2);
- try std.testing.expect(value.pair.check(p));
- try std.testing.expect(value.boole.unpack(value.pair.pred(p)));
-
- const car = value.pair.car(p);
- const cdr = value.pair.cdr(p);
- try std.testing.expectEqual(1, value.fixnum.unpack(car));
- try std.testing.expectEqual(2, value.fixnum.unpack(cdr));
-
- value.pair.setcar(p, v3);
- value.pair.setcdr(p, v4);
-
- const car2 = value.pair.car(p);
- const cdr2 = value.pair.cdr(p);
- try std.testing.expectEqual(3, value.fixnum.unpack(car2));
- try std.testing.expectEqual(4, value.fixnum.unpack(cdr2));
-}
-
-test "istr" {
- const istr = value.istr;
- const fx = value.fixnum;
-
- const s1 = "foo bar baz";
- const v1 = istr.intern(s1, false);
- const v1_len: usize = @intCast(fx.unpack(istr.len(v1)));
- try std.testing.expectEqualStrings(s1, istr.getHeader(v1).bytes());
- try std.testing.expectEqual(s1.len, v1_len);
-
- const file = try std.fs.cwd().openFile("test-data/string.txt", .{});
- defer file.close();
- var s2_buf: [4096]u8 = undefined;
- const s2_len = try file.readAll(&s2_buf);
- var s2: []u8 = s2_buf[0..s2_len];
- const v2 = istr.intern(s2, false);
- const v2_len: usize = @intCast(fx.unpack(istr.len(v2)));
- var s2_orig_buf: [4096]u8 = undefined;
- @memcpy(&s2_orig_buf, &s2_buf);
- const s2_orig = s2_orig_buf[0..s2_len];
- s2[0] = s2[0] +% 1;
- try std.testing.expectEqualStrings(s2_orig, istr.getHeader(v2).bytes());
- try std.testing.expectEqual(s2_len, v2_len);
-}
-
-fn parseString(str: []const u8) Value {
- var fbs = std.io.fixedBufferStream(str);
- return io.parser.parse(fbs.reader().any());
-}
-
-test "parse" {
- const val = parseString("\"foo\"");
-
- try std.testing.expect(value.sstr.check(val));
-
- const s = value.sstr.unpack(val);
- try std.testing.expectEqualStrings("foo", s.slice());
-}
-
-test "parse2" {
- const val = parseString(
- \\ ;; Testing some crazy datum comments
- \\ ;~"bar"([x #"y"]{##`,'z}) #"foo"
- \\ ;; end
- );
-
- const r = value.rune.unpack(value.pair.car(val));
- try std.testing.expectEqualStrings("HASH", r.slice());
-
- const s = value.pair.cdr(val);
- try std.testing.expect(value.sstr.check(s));
-
- const f = value.sstr.unpack(s);
- try std.testing.expectEqualStrings("foo", f.slice());
-}
-
-test "parse3" {
- const val = parseString(
- \\(foo ;~x ;~(x y) ;~x #bar [#x #"baz"] 'bat)
- );
-
- const car = value.pair.car;
- const cdr = value.pair.cdr;
-
- const e1 = car(val);
- const e2 = car(cdr(val));
- const e3 = car(cdr(cdr(val)));
- const e4 = car(cdr(cdr(cdr(val))));
-
- try std.testing.expect(value.sstr.check(e1));
- try std.testing.expect(value.rune.check(e2));
- try std.testing.expect(value.pair.check(e3));
- try std.testing.expect(value.pair.check(e4));
-}
-
-test "parse4" {
- const val = parseString("(foo . ;~x bar ;~y)");
-
- const s = value.sstr.unpack(value.pair.car(val));
- try std.testing.expectEqualStrings("foo", s.slice());
-
- const f = value.sstr.unpack(value.pair.cdr(val));
- try std.testing.expectEqualStrings("bar", f.slice());
-}
-
-test "unparse" {
- var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
- var out: std.ArrayList(u8) = .init(gpa.allocator());
-
- const w = out.writer();
- const v = parseString("#foo");
- try io.unparser.unparse(w, v);
- try std.testing.expectEqualStrings("#foo", try out.toOwnedSlice());
-}
-
-test "unparse2" {
- var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
- var out: std.ArrayList(u8) = .init(gpa.allocator());
-
- const w = out.writer();
- const v = parseString("#{foo bar['x]}");
- try io.unparser.unparse(w, v);
- try std.testing.expectEqualStrings(
- "(#HASH #BRACE foo (#JOIN bar #SQUARE (#QUOTE . x)))",
- try out.toOwnedSlice(),
- );
-}
-
-fn writeParseResult(str: []const u8) !void {
- const w = std.io.getStdErr().writer();
- const v = parseString(str);
- try io.unparser.unparse(w, v);
- try w.writeByte('\n');
-}
-
-test "unparse3" {
- try writeParseResult("#{foo bar['x](y)(z)}");
-}
-
-test "unparse4" {
- try writeParseResult("(foo ;~bar)");
-}
-
-test "unparse5" {
- try writeParseResult("(;~foo foo ;~bar . ;~bar bar ;~bar)");
-}
-
-test "unparse6" {
- try writeParseResult("(foo bar ... baz bat.(qux))");
-}
-
-test "unparse7" {
- try writeParseResult("#`(#,(->keyword (syntax->datum #'sym)) . in)");
-}
-
-fn parseBench(path: []const u8, iters: usize) !void {
- const file = try std.fs.cwd().openFile(path, .{});
- defer file.close();
-
- var sfa = io.parser.DefaultSfa.init(std.heap.smp_allocator);
- var parser = try io.parser.initSfa(&sfa);
- defer parser.deinit();
-
- var timer = try std.time.Timer.start();
- for (0..iters) |i| {
- _ = i;
- var br = std.io.bufferedReader(file.reader());
- const reader = br.reader().any();
- // const reader = file.reader().any();
- var v: Value = undefined;
- while (true) {
- // std.debug.print("hihi {s}\n", .{path});
- v = parser.run(reader) catch |e| {
- std.debug.print("\nfile pos: {}\n", .{
- try file.getPos(),
- });
- return e;
- };
- // try io.unparser.unparse(std.io.getStdOut().writer().any(), v);
- if (value.eof.check(v)) {
- break;
- }
- }
- try file.seekTo(0);
- }
- const ns: f64 = @floatFromInt(timer.lap());
- const secs = ns / 1_000_000_000;
- std.debug.print(
- "parse {s} x {}: {d:.3}s\n",
- .{ path, iters, secs },
- );
-}
+pub const _test = @import("test/all.zig");
-test "parse bench" {
- try parseBench("test-data/parser-test-1.scm", 200);
- try parseBench("test-data/parser-test-2.scm", 800);
- try parseBench("test-data/parser-torture.scm", 1);
+test {
+ std.testing.refAllDecls(@This());
}
diff --git a/src/zisp/io/Parser.zig b/src/zisp/io/Parser.zig
index 08bf8ba..df9e238 100644
--- a/src/zisp/io/Parser.zig
+++ b/src/zisp/io/Parser.zig
@@ -78,7 +78,6 @@ const GRAVE = value.rune.pack("GRAVE");
const COMMA = value.rune.pack("COMMA");
const SQUARE = value.rune.pack("SQUARE");
const BRACE = value.rune.pack("BRACE");
-const VOID = value.rune.packForced("");
const LSTAIL = value.sstr.pack(".");
// zig fmt: on
@@ -328,7 +327,7 @@ fn jump(p: *Parser, next: Fn, val: ?Value) void {
}
fn abort(p: *Parser, next: Fn, unread_c: u8) void {
- p.result = VOID;
+ p.result = value.none;
p.unread_char = unread_c;
p.context.next = next;
}
@@ -354,7 +353,7 @@ fn parseUnit(p: *Parser) !void {
},
}
}
- return p.retval(value.eof.eof);
+ return p.retval(value.eof);
}
fn endUnit(p: *Parser) !void {
@@ -381,7 +380,7 @@ fn parseDatum(p: *Parser) !void {
}
fn endFirstDatum(p: *Parser) !void {
- if (p.result.eq(VOID)) {
+ if (p.result.eq(value.none)) {
return p.ret();
}
return parseJoin(p);
@@ -406,7 +405,7 @@ fn parseJoin(p: *Parser) !void {
fn endJoinDatum(p: *Parser) !void {
const prev = p.context.val;
const join = p.context.char;
- if (p.result.eq(VOID)) {
+ if (p.result.eq(value.none)) {
if (join == 0) {
return p.retval(prev);
} else {
@@ -573,7 +572,7 @@ fn parseHashDatum(p: *Parser) !void {
}
fn endHashDatum(p: *Parser) !void {
- if (p.result.eq(VOID)) {
+ if (p.result.eq(value.none)) {
return p.err(.InvalidCharacter, "hash datum");
}
return p.retval(p.cons(HASH, p.result));
@@ -620,7 +619,7 @@ fn parseRuneDatum(p: *Parser) !void {
}
fn endRuneDatum(p: *Parser) !void {
- if (p.result.eq(VOID)) {
+ if (p.result.eq(value.none)) {
p.retval(p.context.val);
}
return p.retval(p.cons(p.context.val, p.result));
@@ -645,7 +644,7 @@ fn parseLabelEnd(p: *Parser, l: Value, next: Fn) !void {
}
fn endLabelDatum(p: *Parser) !void {
- if (p.result.eq(VOID)) {
+ if (p.result.eq(value.none)) {
return p.err(.InvalidCharacter, "label datum");
}
return p.retval(p.cons(LABEL, p.cons(p.context.val, p.result)));
@@ -653,9 +652,9 @@ fn endLabelDatum(p: *Parser) !void {
fn parseList(p: *Parser, open: u8, next: Fn) !void {
const head = switch (open) {
- '(' => value.nil.nil,
- '[' => p.cons(SQUARE, value.nil.nil),
- '{' => p.cons(BRACE, value.nil.nil),
+ '(' => value.nil,
+ '[' => p.cons(SQUARE, value.nil),
+ '{' => p.cons(BRACE, value.nil),
else => unreachable,
};
const close: u8 = switch (open) {
@@ -694,7 +693,7 @@ fn listParserSetup(p: *Parser, head: Value, close: u8, next: Fn) !void {
fn continueList(p: *Parser) !void {
const close = p.context.char;
- if (p.result.eq(VOID)) {
+ if (p.result.eq(value.none)) {
const c = p.getUnread().?;
if (c == close) {
return endList(p);
@@ -733,7 +732,7 @@ fn endList(p: *Parser) !void {
}
fn endImproperList(p: *Parser) !void {
- if (p.result.eq(VOID)) {
+ if (p.result.eq(value.none)) {
return p.err(.InvalidCharacter, "list tail");
}
p.context.val = lib.list.reverseWithTail(p.context.val, p.result);
@@ -772,7 +771,7 @@ fn parseQuoteExpr(p: *Parser, c1: u8, next: Fn) !void {
}
fn endQuoteExpr(p: *Parser) !void {
- if (p.result.eq(VOID)) {
+ if (p.result.eq(value.none)) {
return p.err(.InvalidCharacter, "quote expression datum");
}
return p.retval(p.cons(p.context.val, p.result));
diff --git a/src/zisp/io/unparser.zig b/src/zisp/io/unparser.zig
index d703182..e72f130 100644
--- a/src/zisp/io/unparser.zig
+++ b/src/zisp/io/unparser.zig
@@ -80,8 +80,9 @@ fn unparseMisc(w: anytype, v: Value) !void {
.f => w.writeAll("#f"),
.t => w.writeAll("#t"),
.nil => w.writeAll("()"),
- .eof => w.writeAll("#eof"),
- .undef => w.writeAll("#undef"),
+ .eof => w.writeAll("#EOF"),
+ .none => w.writeAll("#NONE"),
+ .undef => w.writeAll("#UNDEF"),
};
}
@@ -93,7 +94,7 @@ fn unparsePair(w: anytype, p: *[2]Value) !void {
try w.writeByte(' ');
try unparse(w, value.pair.car(cdr));
}
- if (!value.nil.check(cdr)) {
+ if (!value.nil.eq(cdr)) {
try w.writeByte(' ');
try w.writeByte('.');
try w.writeByte(' ');
diff --git a/src/zisp/lib/list.zig b/src/zisp/lib/list.zig
index 9d6a6bc..be40af7 100644
--- a/src/zisp/lib/list.zig
+++ b/src/zisp/lib/list.zig
@@ -3,13 +3,13 @@ const value = @import("../value.zig");
const Value = value.Value;
pub fn reverse(list: Value) Value {
- return reverseWithTail(list, value.nil.nil);
+ return reverseWithTail(list, value.nil);
}
pub fn reverseWithTail(list: Value, tail: Value) Value {
var head = list;
var result = tail;
- while (!value.nil.check(head)) {
+ while (!value.nil.eq(head)) {
value.pair.assert(head);
const car = value.pair.car(head);
const cdr = value.pair.cdr(head);
diff --git a/src/zisp/value.zig b/src/zisp/value.zig
index e5b78ec..47ac144 100644
--- a/src/zisp/value.zig
+++ b/src/zisp/value.zig
@@ -154,9 +154,8 @@ pub const seq = @import("value/seq.zig");
pub const rune = @import("value/rune.zig");
pub const sstr = @import("value/sstr.zig");
pub const char = @import("value/char.zig");
+pub const misc = @import("value/misc.zig");
pub const boole = @import("value/boole.zig");
-pub const nil = @import("value/nil.zig");
-pub const eof = @import("value/eof.zig");
pub const pair = @import("value/pair.zig");
pub const istr = @import("value/istr.zig");
@@ -169,9 +168,16 @@ pub const ShortString = std.BoundedArray(u8, 6);
pub const OtherTag = enum(u3) { rune, sstr, qstr, char, misc };
-pub const MiscValue = enum(u8) { f, t, nil, eof, undef = 255 };
+pub const MiscValue = enum(u8) { f, t, nil, eof, none, undef };
+// zig fmt: off
+pub const f = Value{ .misc = .{ .value = .f } };
+pub const t = Value{ .misc = .{ .value = .t } };
+pub const nil = Value{ .misc = .{ .value = .nil } };
+pub const eof = Value{ .misc = .{ .value = .eof } };
+pub const none = Value{ .misc = .{ .value = .none } };
pub const undef = Value{ .misc = .{ .value = .undef } };
+// zig fmt: on
/// Represents a Zisp value/object.
pub const Value = packed union {
diff --git a/src/zisp/value/boole.zig b/src/zisp/value/boole.zig
index 2e94e4d..26a1a0a 100644
--- a/src/zisp/value/boole.zig
+++ b/src/zisp/value/boole.zig
@@ -1,13 +1,12 @@
-const Value = @import("../value.zig").Value;
+const value = @import("../value.zig");
-pub const f = Value{ .misc = .{ .value = .f } };
-pub const t = Value{ .misc = .{ .value = .t } };
+const Value = value.Value;
// Zig API
/// Checks if the value is a boole.
pub fn check(v: Value) bool {
- return v.bits == f.bits or v.bits == t.bits;
+ return v.eq(value.f) or v.eq(value.t);
}
pub fn assert(v: Value) void {
@@ -18,12 +17,12 @@ pub fn assert(v: Value) void {
}
pub fn pack(b: bool) Value {
- return if (b) t else f;
+ return if (b) value.t else value.f;
}
pub fn unpack(v: Value) bool {
assert(v);
- return v.bits == t.bits;
+ return v.eq(value.t);
}
// Zisp API
diff --git a/src/zisp/value/eof.zig b/src/zisp/value/eof.zig
deleted file mode 100644
index 4b16669..0000000
--- a/src/zisp/value/eof.zig
+++ /dev/null
@@ -1,28 +0,0 @@
-const value = @import("../value.zig");
-
-const Value = value.Value;
-
-pub const eof = Value{ .misc = .{ .value = .eof } };
-
-// Zig API
-
-pub fn check(v: Value) bool {
- return v.bits == eof.bits;
-}
-
-pub fn assert(v: Value) void {
- if (!check(v)) {
- v.dump();
- @panic("not bool");
- }
-}
-
-// Zisp API
-
-pub fn get() Value {
- return eof;
-}
-
-pub fn pred(v: Value) Value {
- return value.boole.pack(check(v));
-}
diff --git a/src/zisp/value/misc.zig b/src/zisp/value/misc.zig
new file mode 100644
index 0000000..9b15b8c
--- /dev/null
+++ b/src/zisp/value/misc.zig
@@ -0,0 +1,23 @@
+const value = @import("../value.zig");
+
+const Value = value.Value;
+
+// In Zig code, you would typically just use val.eq(nil) and the like, so this
+// file just contains some functions for Zisp code, like nil? and eof?.
+
+// Zisp APIs
+
+/// The Zisp function `nil?`.
+pub fn isNil(v: Value) Value {
+ return value.boole.pack(v.eq(value.nil));
+}
+
+/// The Zisp function `eof?`.
+pub fn isEof(v: Value) Value {
+ return value.boole.pack(v.eq(value.eof));
+}
+
+/// The Zisp function `eof`.
+pub fn eof() Value {
+ return value.eof;
+}
diff --git a/src/zisp/value/nil.zig b/src/zisp/value/nil.zig
deleted file mode 100644
index f95ecad..0000000
--- a/src/zisp/value/nil.zig
+++ /dev/null
@@ -1,28 +0,0 @@
-const value = @import("../value.zig");
-
-const Value = value.Value;
-
-pub const nil = Value{ .misc = .{ .value = .nil } };
-
-// Zig API
-
-pub fn check(v: Value) bool {
- return v.bits == nil.bits;
-}
-
-pub fn assert(v: Value) void {
- if (!check(v)) {
- v.dump();
- @panic("not bool");
- }
-}
-
-// Zisp API
-
-pub fn get() Value {
- return nil;
-}
-
-pub fn pred(v: Value) Value {
- return value.boole.pack(check(v));
-}
diff --git a/src/zisp/value/ptr.zig b/src/zisp/value/ptr.zig
index 2ed3765..aa2e9a6 100644
--- a/src/zisp/value/ptr.zig
+++ b/src/zisp/value/ptr.zig
@@ -165,7 +165,7 @@ pub fn predWeakNull(v: Value) Value {
pub fn getWeak(v: Value) Value {
if (isWeakNull(v)) {
- return value.boole.f;
+ return value.f;
} else {
var copy = v;
copy.zptr.is_weak = false;