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.Reader.fixed(str); return io.parse.fromReader(&fbs); } 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\\x20;bar\\x0a;baz|").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 "print" { var buf: [32]u8 = undefined; var w = std.Io.Writer.fixed(&buf); const v = parse("#foo"); try io.print.toWriter(&w, v); try testing.expectEqualStrings("#foo", buf[0..w.end]); } test "print2" { var buf: [128]u8 = undefined; var w = std.Io.Writer.fixed(&buf); const v = parse("#{foo bar['x]}"); try io.print.toWriter(&w, v); try testing.expectEqualStrings( "(#HASH #BRACE foo (#JOIN bar #SQUARE (#QUOTE & x)))", buf[0..w.end], ); } fn parseAndPrint(str: []const u8) !void { var buf: [64]u8 = undefined; var fw = std.Io.File.stderr().writer(gstIo, &buf); const w = &fw.interface; const v = parse(str); try io.print.toWriter(w, v); try w.writeByte('\n'); try w.flush(); } test "print3" { try parseAndPrint("#{foo bar['x](y)(z)}"); } test "print4" { try parseAndPrint("(foo ;~bar)"); } test "print5" { try parseAndPrint("(;~foo foo ;~bar & ;~bar bar ;~bar)"); } test "print6" { try parseAndPrint("(foo bar ... baz bat.(qux))"); } test "print7" { try parseAndPrint("#`(#,(->keyword (syntax->datum #'sym)) & in)"); } fn parseBench(path: []const u8, iters: usize) !void { const file = try std.Io.Dir.cwd().openFile(gstIo, path, .{}); defer file.close(gstIo); var sfa = io.parse.DefaultSfa.init(std.heap.smp_allocator); var parser = try io.parse.initSfa(&sfa); defer parser.deinit(); var timer = try std.time.Timer.start(); for (0..iters) |i| { _ = i; var buf: [4096]u8 = undefined; var reader = file.reader(gstIo, &buf); var v: Value = undefined; while (true) { v = parser.run(&reader.interface) catch |e| { std.debug.print("\nfile pos: {}\n", .{ reader.logicalPos(), }); return e; }; if (value.eof.eq(v)) { break; } } try reader.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); }