const std = @import("std"); const testing = std.testing; const expect = testing.expect; 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()); } test "parse empty" { try expect(parse("").eq(value.eof)); try expect(parse(";").eq(value.eof)); try expect(parse(";~").eq(value.eof)); try expect(parse(" ;").eq(value.eof)); try expect(parse(" ;~").eq(value.eof)); try expect(parse("; ").eq(value.eof)); try expect(parse(";~ ").eq(value.eof)); try expect(parse(";\n").eq(value.eof)); try expect(parse(";\n ").eq(value.eof)); try expect(parse(";~foo").eq(value.eof)); try expect(parse(";~foo ").eq(value.eof)); try expect(parse(";~foo;").eq(value.eof)); try expect(parse(";~foo ;").eq(value.eof)); try expect(parse("\t\r\n ;foo\n;~(\nbar\n)").eq(value.eof)); } test "parse short bare string" { const str = value.sstr.pack; try expect(parse(".").eq(str("."))); try expect(parse("..").eq(str(".."))); try expect(parse("...").eq(str("..."))); try expect(parse(".a.b").eq(str(".a.b"))); try expect(parse("+0.1").eq(str("+0.1"))); try expect(parse("-0.1").eq(str("-0.1"))); try expect(parse("10.1").eq(str("10.1"))); 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("@^_~00").eq(str("@^_~00"))); } test "parse long bare string" { const str = value.istr.intern; try expect(parse("foobarbaz").eq(str("foobarbaz"))); 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("-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("|foo\\x20bar\\x0abaz|").eq(str("foo bar\nbaz"))); } test "parse" { const val = parse("foo"); try testing.expect(value.sstr.check(val)); const s = value.sstr.unpack(val); try testing.expectEqualStrings("foo", s.slice()); } test "parse2" { const val = parse( \\ ;; 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(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 = parse( \\(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) != null); try testing.expect(value.pair.check(e4) != null); } test "parse4" { const val = parse("(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 = parse("#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 = parse("#{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 = parse(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); }