diff options
| author | Taylan Kammer <taylan.kammer@gmail.com> | 2025-03-30 20:10:10 +0200 |
|---|---|---|
| committer | Taylan Kammer <taylan.kammer@gmail.com> | 2025-03-30 20:10:10 +0200 |
| commit | 4922f3a4437f7ea8495f32aea0aa329830bd2d8b (patch) | |
| tree | 2a31c4679114d27351f8d156a31409fd72eef60a | |
| parent | 3d05c94b9d8aa964e4ff848c95d5999cec170e04 (diff) | |
moar cleanup
| -rw-r--r-- | src/main.zig | 2 | ||||
| -rw-r--r-- | src/test/all.zig | 8 | ||||
| -rw-r--r-- | src/test/bench.zig | 15 | ||||
| -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.zig | 162 | ||||
| -rw-r--r-- | src/test/strings.zig | 30 | ||||
| -rw-r--r-- | src/test/values.zig | 247 | ||||
| -rw-r--r-- | src/zisp.zig | 428 | ||||
| -rw-r--r-- | src/zisp/io/Parser.zig | 27 | ||||
| -rw-r--r-- | src/zisp/io/unparser.zig | 7 | ||||
| -rw-r--r-- | src/zisp/lib/list.zig | 4 | ||||
| -rw-r--r-- | src/zisp/value.zig | 12 | ||||
| -rw-r--r-- | src/zisp/value/boole.zig | 11 | ||||
| -rw-r--r-- | src/zisp/value/eof.zig | 28 | ||||
| -rw-r--r-- | src/zisp/value/misc.zig | 23 | ||||
| -rw-r--r-- | src/zisp/value/nil.zig | 28 | ||||
| -rw-r--r-- | src/zisp/value/ptr.zig | 2 |
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; |
