From 75185f9fa26b5fc49036008df16c8bb4f1a51a13 Mon Sep 17 00:00:00 2001 From: Taylan Kammer Date: Tue, 6 Jan 2026 07:45:07 +0100 Subject: Writergate, other 0.16 changes, and test fixes for new grammar. --- src/main.zig | 14 ++++++++-- src/test/data/parser-test-1.scm | 4 +-- src/test/parse.zig | 61 ++++++++++++++++++++--------------------- src/test/strings.zig | 15 ++++++++-- src/zisp/gc.zig | 10 +++++-- src/zisp/io/Parser.zig | 10 ++++--- src/zisp/io/parser.zig | 5 ++-- src/zisp/io/unparser.zig | 30 ++++++++++---------- src/zisp/value.zig | 9 +++++- src/zisp/value/rune.zig | 2 +- src/zisp/value/sstr.zig | 2 +- 11 files changed, 98 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/main.zig b/src/main.zig index 9e86d03..faeb534 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,11 +2,20 @@ const std = @import("std"); const zisp = @import("zisp"); +const gstIo = std.Io.Threaded.global_single_threaded.io(); + pub fn main() !void { - const reader = std.io.getStdIn().reader().any(); - const writer = std.io.getStdOut().writer().any(); + var stdin_buffer: [4096]u8 = undefined; + var stdin_reader = std.Io.File.stdin().reader(gstIo, &stdin_buffer); + const reader = &stdin_reader.interface; + + var stdout_buffer: [4096]u8 = undefined; + var stdout_writer = std.Io.File.stdout().writer(gstIo, &stdout_buffer); + const writer = &stdout_writer.interface; + while (true) { try writer.writeAll("> "); + try writer.flush(); const datum = zisp.io.parser.parse(reader); if (datum.eq(zisp.value.eof)) { try writer.writeAll("\n"); @@ -15,5 +24,6 @@ pub fn main() !void { try writer.writeAll("= "); try zisp.io.unparser.unparse(writer, datum); try writer.writeAll("\n"); + try writer.flush(); } } diff --git a/src/test/data/parser-test-1.scm b/src/test/data/parser-test-1.scm index 87c41b5..374b4c6 100644 --- a/src/test/data/parser-test-1.scm +++ b/src/test/data/parser-test-1.scm @@ -149,7 +149,7 @@ (error "cannot" value dscr))))) -(define (bsrf/d bstr . indxs) +(define (bsrf/d bstr & indxs) (letvls (((bvec offset dscr) (bsunwp bstr))) (let loop ((bvec bvec) @@ -166,7 +166,7 @@ dscr* (cdr indxs))))))) -(define (bst!/d bstr . args) +(define (bst!/d bstr & args) (letvls (((bvec offset dscr) (bsunwp bstr))) (let loop ((bvec bvec) diff --git a/src/test/parse.zig b/src/test/parse.zig index dd26098..8c44454 100644 --- a/src/test/parse.zig +++ b/src/test/parse.zig @@ -3,14 +3,16 @@ const std = @import("std"); const testing = std.testing; const expect = testing.expect; +const gstIo = std.Io.Threaded.global_single_threaded.io(); + pub const io = @import("../zisp/io.zig"); pub const value = @import("../zisp/value.zig"); pub const Value = value.Value; fn parse(str: []const u8) Value { - var fbs = std.io.fixedBufferStream(str); - return io.parser.parse(fbs.reader().any()); + var fbs = std.Io.Reader.fixed(str); + return io.parser.parse(&fbs); } test "parse empty" { @@ -42,7 +44,7 @@ test "parse short bare string" { try expect(parse("|x()|").eq(str("x()"))); try expect(parse("|{\\|}|").eq(str("{|}"))); try expect(parse("foobar").eq(str("foobar"))); - try expect(parse("!$%&*+").eq(str("!$%&*+"))); + try expect(parse("!$%.*+").eq(str("!$%.*+"))); try expect(parse("-/<=>?").eq(str("-/<=>?"))); try expect(parse("@^_~00").eq(str("@^_~00"))); } @@ -54,7 +56,7 @@ test "parse long bare string" { try expect(parse("+foo.bar.baz").eq(str("+foo.bar.baz"))); try expect(parse("-foo.bar.baz").eq(str("-foo.bar.baz"))); try expect(parse("0foo.bar.baz").eq(str("0foo.bar.baz"))); - try expect(parse("!$%&*+-/<=>?@^_~").eq(str("!$%&*+-/<=>?@^_~"))); + try expect(parse("!$%*+-./<=>?@^_~").eq(str("!$%*+-./<=>?@^_~"))); try expect(parse("|foo\\x20;bar\\x0a;baz|").eq(str("foo bar\nbaz"))); } @@ -104,7 +106,7 @@ test "parse3" { } test "parse4" { - const val = parse("(foo . ;~x bar ;~y)"); + const val = parse("(foo & ;~x bar ;~y)"); const s = value.sstr.unpack(value.pair.car(val)); try testing.expectEqualStrings("foo", s.slice()); @@ -114,33 +116,33 @@ test "parse4" { } test "unparse" { - var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; - var out: std.ArrayList(u8) = .init(gpa.allocator()); - - const w = out.writer(); + var buf: [32]u8 = undefined; + var w = std.Io.Writer.fixed(&buf); const v = parse("#foo"); - try io.unparser.unparse(w, v); - try testing.expectEqualStrings("#foo", try out.toOwnedSlice()); + try io.unparser.unparse(&w, v); + try testing.expectEqualStrings("#foo", buf[0..w.end]); } test "unparse2" { - var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; - var out: std.ArrayList(u8) = .init(gpa.allocator()); - - const w = out.writer(); + var buf: [128]u8 = undefined; + var w = std.Io.Writer.fixed(&buf); const v = parse("#{foo bar['x]}"); - try io.unparser.unparse(w, v); + try io.unparser.unparse(&w, v); try testing.expectEqualStrings( - "(#HASH #BRACE foo (#JOIN bar #SQUARE (#QUOTE . x)))", - try out.toOwnedSlice(), + "(#HASH #BRACE foo (#JOIN bar #SQUARE (#QUOTE & x)))", + buf[0..w.end], ); } fn writeParseResult(str: []const u8) !void { - const w = std.io.getStdErr().writer(); + var buf: [64]u8 = undefined; + var fw = std.Io.File.stderr().writer(gstIo, &buf); + const w = &fw.interface; + const v = parse(str); try io.unparser.unparse(w, v); try w.writeByte('\n'); + try w.flush(); } test "unparse3" { @@ -152,7 +154,7 @@ test "unparse4" { } test "unparse5" { - try writeParseResult("(;~foo foo ;~bar . ;~bar bar ;~bar)"); + try writeParseResult("(;~foo foo ;~bar & ;~bar bar ;~bar)"); } test "unparse6" { @@ -160,12 +162,12 @@ test "unparse6" { } test "unparse7" { - try writeParseResult("#`(#,(->keyword (syntax->datum #'sym)) . in)"); + 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(); + const file = try std.Io.Dir.cwd().openFile(gstIo, path, .{}); + defer file.close(gstIo); var sfa = io.parser.DefaultSfa.init(std.heap.smp_allocator); var parser = try io.parser.initSfa(&sfa); @@ -174,24 +176,21 @@ fn parseBench(path: []const u8, iters: usize) !void { 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 buf: [4096]u8 = undefined; + var reader = file.reader(gstIo, &buf); var v: Value = undefined; while (true) { - // std.debug.print("hihi {s}\n", .{path}); - v = parser.run(reader) catch |e| { + v = parser.run(&reader.interface) catch |e| { std.debug.print("\nfile pos: {}\n", .{ - try file.getPos(), + reader.logicalPos(), }); return e; }; - // try io.unparser.unparse(std.io.getStdOut().writer().any(), v); if (value.eof.eq(v)) { break; } } - try file.seekTo(0); + try reader.seekTo(0); } const ns: f64 = @floatFromInt(timer.lap()); const secs = ns / 1_000_000_000; diff --git a/src/test/strings.zig b/src/test/strings.zig index 8f640f4..3a0585e 100644 --- a/src/test/strings.zig +++ b/src/test/strings.zig @@ -2,6 +2,8 @@ const std = @import("std"); const testing = std.testing; +const gstIo = std.Io.Threaded.global_single_threaded.io(); + pub const value = @import("../zisp/value.zig"); const istr = value.istr; @@ -11,20 +13,27 @@ test "istr" { const s1 = "foo bar baz"; const v1 = istr.intern(s1); const v1_len: usize = @intCast(fx.unpack(istr.len(v1))); + try testing.expectEqualStrings(s1, istr.assert(v1).bytes()); try testing.expectEqual(s1.len, v1_len); - const file = try std.fs.cwd().openFile("src/test/data/string.txt", .{}); - defer file.close(); + const path = "src/test/data/string.txt"; + var file = try std.Io.Dir.cwd().openFile(gstIo, path, .{}); + defer file.close(gstIo); + var s2_buf: [4096]u8 = undefined; - const s2_len = try file.readAll(&s2_buf); + const s2_len = try file.readStreaming(gstIo, &.{&s2_buf}); var s2: []u8 = s2_buf[0..s2_len]; + const v2 = istr.intern(s2); 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.assert(v2).bytes()); try testing.expectEqual(s2_len, v2_len); } diff --git a/src/zisp/gc.zig b/src/zisp/gc.zig index 2f51b9b..4fc2c90 100644 --- a/src/zisp/gc.zig +++ b/src/zisp/gc.zig @@ -13,10 +13,16 @@ pub const alloc = std.heap.smp_allocator; // Cons cells -var cons_pool = std.heap.MemoryPool([2]Value).init(alloc); +const ConsPool = std.heap.MemoryPool([2]Value); +var cons_pool: ConsPool = undefined; +var need_init = true; pub fn cons(v1: Value, v2: Value) *[2]Value { - const mem = cons_pool.create() catch @panic("OOM"); + if (need_init) { + cons_pool = ConsPool.initCapacity(alloc, 64) catch @panic("OOM"); + need_init = false; + } + const mem = cons_pool.create(alloc) catch @panic("OOM"); mem[0] = v1; mem[1] = v2; return mem; diff --git a/src/zisp/io/Parser.zig b/src/zisp/io/Parser.zig index 57cdc14..2c381e1 100644 --- a/src/zisp/io/Parser.zig +++ b/src/zisp/io/Parser.zig @@ -119,7 +119,8 @@ pub const Alloc = struct { }; alloc: Alloc, -input: std.io.AnyReader = undefined, +io: std.Io, +input: *std.Io.Reader = undefined, context: Context = .{}, stack: List(Context) = undefined, chars: List(u8) = undefined, @@ -130,10 +131,11 @@ err_msg: []const u8 = undefined, pub fn init( alloc: Alloc, + io: std.Io, init_stack_capacity: usize, init_chars_capacity: usize, ) !Parser { - var p: Parser = .{ .alloc = alloc }; + var p: Parser = .{ .alloc = alloc, .io = io }; p.stack = try .initCapacity(alloc.stack, init_stack_capacity); p.chars = try .initCapacity(alloc.chars, init_chars_capacity); return p; @@ -155,7 +157,7 @@ fn read(p: *Parser) !?u8 { .{p.unread_char.?}, ); } - const c = p.input.readByte() catch |e| switch (e) { + const c = p.input.takeByte() catch |e| switch (e) { error.EndOfStream => return null, else => return p.err(.ReadError, "???"), }; @@ -242,7 +244,7 @@ fn call(p: *Parser, f: Fn) !void { }; } -pub fn run(p: *Parser, input: std.io.AnyReader) !Value { +pub fn run(p: *Parser, input: *std.Io.Reader) !Value { p.input = input; p.context.next = .parseUnit; while (p.context.next) |next| { diff --git a/src/zisp/io/parser.zig b/src/zisp/io/parser.zig index cfe2bf1..49d260c 100644 --- a/src/zisp/io/parser.zig +++ b/src/zisp/io/parser.zig @@ -44,17 +44,18 @@ pub fn initSfa(alloc: anytype) !Parser { const SfaType = @typeInfo(@TypeOf(alloc)).pointer.child; return Parser.init( alloc.allocator(), + std.Io.Threaded.global_single_threaded.io(), SfaType.init_stack_capacity, SfaType.init_chars_capacity, ); } -pub fn parse(input: std.io.AnyReader) Value { +pub fn parse(input: *std.Io.Reader) Value { return _parse(input, true); } pub fn _parse( - input: std.io.AnyReader, + input: *std.Io.Reader, comptime panic: bool, ) if (panic) Value else error{ ParseError, OutOfMemory }!Value { const alloc = std.heap.smp_allocator; diff --git a/src/zisp/io/unparser.zig b/src/zisp/io/unparser.zig index 64e18d0..04184fa 100644 --- a/src/zisp/io/unparser.zig +++ b/src/zisp/io/unparser.zig @@ -6,10 +6,9 @@ const value = @import("../value.zig"); const istr = value.istr; const seq = value.seq; -const ShortString = value.ShortString; const Value = value.Value; -pub fn unparse(w: anytype, v: Value) anyerror!void { +pub fn unparse(w: *std.Io.Writer, v: Value) anyerror!void { // zig fmt: off try if (v.isDouble()) unparseDouble(w, v) else if (v.isFixnum()) unparseFixnum(w, v) @@ -22,40 +21,41 @@ pub fn unparse(w: anytype, v: Value) anyerror!void { else if (v.isSstr()) unparseSstr(w, v) ; // zig fmt: on + try w.flush(); } -fn unparseDouble(w: anytype, v: Value) !void { +fn unparseDouble(w: *std.Io.Writer, v: Value) !void { _ = w; _ = v; @panic("not implemented"); } -fn unparseFixnum(w: anytype, v: Value) !void { +fn unparseFixnum(w: *std.Io.Writer, v: Value) !void { _ = w; _ = v; @panic("not implemented"); } -fn unparseRune(w: anytype, v: Value) !void { +fn unparseRune(w: *std.Io.Writer, v: Value) !void { const name = value.rune.unpack(v); try w.writeByte('#'); - try w.writeAll(name.constSlice()); + try w.writeAll(name.slice()); } -fn unparseSstr(w: anytype, v: Value) !void { +fn unparseSstr(w: *std.Io.Writer, v: Value) !void { // TODO: Check if pipes/escapes necessary. const str = value.sstr.unpack(v); - try w.writeAll(str.constSlice()); + try w.writeAll(str.slice()); } -fn unparseChar(w: anytype, v: Value) !void { +fn unparseChar(w: *std.Io.Writer, v: Value) !void { const uc: u21 = value.char.unpack(v); var buf: [4]u8 = undefined; const len = try std.unicode.utf8Encode(uc, &buf); try w.writeAll(buf[0..len]); } -fn unparseMisc(w: anytype, v: Value) !void { +fn unparseMisc(w: *std.Io.Writer, v: Value) !void { try switch (v.bits) { value.f.bits => w.writeAll("#f"), value.t.bits => w.writeAll("#t"), @@ -67,13 +67,13 @@ fn unparseMisc(w: anytype, v: Value) !void { }; } -fn unparseSrat(w: anytype, v: Value) !void { +fn unparseSrat(w: *std.Io.Writer, v: Value) !void { _ = w; _ = v; @panic("not implemented"); } -fn unparsePair(w: anytype, p: *[2]Value) !void { +fn unparsePair(w: *std.Io.Writer, p: *[2]Value) !void { try w.writeByte('('); try unparse(w, p[0]); var cdr = p[1]; @@ -83,21 +83,21 @@ fn unparsePair(w: anytype, p: *[2]Value) !void { } if (!value.nil.eq(cdr)) { try w.writeByte(' '); - try w.writeByte('.'); + try w.writeByte('&'); try w.writeByte(' '); try unparse(w, cdr); } try w.writeByte(')'); } -fn unparseSeq(w: anytype, s: *seq.Header) !void { +fn unparseSeq(w: *std.Io.Writer, s: *seq.Header) !void { switch (s.type) { .string => try unparseString(w, s), else => @panic("not implemented"), } } -fn unparseString(w: anytype, s: *seq.Header) !void { +fn unparseString(w: *std.Io.Writer, s: *seq.Header) !void { // TODO: Check if pipes/escapes necessary. try w.writeByte('|'); try w.writeAll(s.bytes()); diff --git a/src/zisp/value.zig b/src/zisp/value.zig index 449a577..2d148ed 100644 --- a/src/zisp/value.zig +++ b/src/zisp/value.zig @@ -170,7 +170,14 @@ const endian = builtin.target.cpu.arch.endian(); const max = std.math.maxInt; /// Used when dealing with runes and short strings. -pub const ShortString = std.BoundedArray(u8, 6); +pub const ShortString = struct { + len: u8, + buf: [6]u8, + + pub fn slice(this: @This()) []const u8 { + return this.buf[0..this.len]; + } +}; /// Used to find the length of a rune or short string. pub fn sstrLen(x: u64) u8 { diff --git a/src/zisp/value/rune.zig b/src/zisp/value/rune.zig index e75c276..f49feb4 100644 --- a/src/zisp/value/rune.zig +++ b/src/zisp/value/rune.zig @@ -51,7 +51,7 @@ pub fn unpack(v: Value) ShortString { assert(v); const s: [6]u8 = @bitCast(v.rune.name); const l = value.sstrLen(v.bits); - return .{ .buffer = s, .len = l }; + return .{ .buf = s, .len = l }; } // Zisp API diff --git a/src/zisp/value/sstr.zig b/src/zisp/value/sstr.zig index 4f0336e..b4cabff 100644 --- a/src/zisp/value/sstr.zig +++ b/src/zisp/value/sstr.zig @@ -45,7 +45,7 @@ pub fn unpack(v: Value) ShortString { assert(v); const s: [6]u8 = @bitCast(v.sstr.string); const l = value.sstrLen(v.bits); - return .{ .buffer = s, .len = l }; + return .{ .buf = s, .len = l }; } // No Zisp API for sstr specifically, since it's a string. See string.zig. -- cgit v1.2.3