commit 9a3da9d4fa2075ca5a908dd115845df4e769c987
parent 9a9d1790e01a3bc646f5a478f3a8825c2b49de2c
Author: gracefu <81774659+gracefuu@users.noreply.github.com>
Date: Thu, 22 May 2025 02:59:34 +0800
revamp build and test layout
Diffstat:
8 files changed, 702 insertions(+), 686 deletions(-)
diff --git a/build.zig b/build.zig
@@ -1,86 +1,106 @@
const std = @import("std");
+/// ## Table of contents
+///
+/// - Opts (`zig build -D<opt>` setup)
+/// - Deps (external `build.zig` Builds)
+/// - Mods (Code + semantically relevant config, e.g. target and optimization levels)
+/// - Compiles (Module + "implementation details" of how to do the compile, e.g. use_llvm)
+/// - Bins (Compile + Run, a hack to make the build system work better[^fno-emit-bin])
+/// - Steps (`zig build <command>` setup)
+///
+/// [^fno-emit-bin]: See issue [#18877](https://github.com/ziglang/zig/issues/18877)
pub fn build(b: *std.Build) !void {
- const target = b.standardTargetOptions(.{});
- const optimize = b.standardOptimizeOption(.{});
-
- const llvm = b.option(
+ // ========
+ // Opts
+ const target_opt = b.standardTargetOptions(.{});
+ const optimize_opt = b.standardOptimizeOption(.{});
+ const llvm_opt = b.option(
bool,
"llvm",
"Force llvm to be used or not (default: whatever the compiler default is)",
- );
+ ) orelse null;
- const enable_tracy = b.option(
+ const tracy_opt = b.option(
bool,
"tracy",
- "Enable Tracy profiling",
+ "Enable Tracy profiling (default: false)",
) orelse false;
- const tracy = b.dependency("tracy", .{ .enable = enable_tracy });
+ // ========
+ // Deps
+ const tracy_dep = b.dependency("tracy", .{
+ .enable = tracy_opt,
+ });
- const mymarkdown = b.addModule("mymarkdown", .{
+ // ========
+ // Mods
+ const mymarkdown_mod = b.addModule("mymarkdown", .{
.root_source_file = b.path("src/root.zig"),
- .target = target,
- .optimize = optimize,
+ .target = target_opt,
+ .optimize = optimize_opt,
+ .imports = &.{
+ .{ .name = "tracy", .module = tracy_dep.module("tracy") },
+ },
});
- mymarkdown.addImport("tracy", tracy.module("tracy"));
- const mymarkdown_cli = b.addModule("mymarkdown", .{
+
+ const mymarkdown_cli_mod = b.addModule("mymarkdown_cli", .{
.root_source_file = b.path("src/main.zig"),
- .target = target,
- .optimize = optimize,
+ .target = target_opt,
+ .optimize = optimize_opt,
+ .imports = &.{
+ .{ .name = "mymarkdown", .module = mymarkdown_mod },
+ .{ .name = "tracy", .module = tracy_dep.module("tracy") },
+ },
});
- mymarkdown_cli.addImport("mymarkdown", mymarkdown);
- mymarkdown_cli.addImport("tracy", tracy.module("tracy"));
+ // ========
+ // Compiles
const mymarkdown_cli_compile = b.addExecutable(.{
.name = "mymarkdown",
- .root_module = mymarkdown_cli,
- .use_llvm = llvm,
+ .root_module = mymarkdown_cli_mod,
+ .use_llvm = llvm_opt,
});
- const check = b.step("check", "Check if the mymarkdown CLI compiles");
- setupTestStep(b, target, optimize, mymarkdown, mymarkdown_cli, check, llvm);
- setupRunStep(b, mymarkdown_cli_compile);
- try installAndCheck(b, check, mymarkdown_cli_compile);
-}
-fn installAndCheck(b: *std.Build, check: *std.Build.Step, exe: *std.Build.Step.Compile) !void {
- const copy = try b.allocator.create(std.Build.Step.Compile);
- copy.* = exe.*;
- check.dependOn(©.step);
- b.installArtifact(exe);
-}
+ const mymarkdown_test_compile = b.addTest(.{
+ .name = "mymarkdown_test",
+ .root_module = mymarkdown_mod,
+ .use_llvm = llvm_opt,
+ });
-fn setupTestStep(
- b: *std.Build,
- target: std.Build.ResolvedTarget,
- optimize: std.builtin.OptimizeMode,
- mymarkdown: *std.Build.Module,
- mymarkdown_cli: *std.Build.Module,
- check: *std.Build.Step,
- llvm: ?bool,
-) void {
- const test_step = b.step("test", "Run unit tests");
- test_step.dependOn(check);
- test_step.dependOn(&b.addRunArtifact(b.addTest(.{
- .root_module = mymarkdown,
- .target = target,
- .optimize = optimize,
- .use_llvm = llvm,
- })).step);
- test_step.dependOn(&b.addRunArtifact(b.addTest(.{
- .root_module = mymarkdown_cli,
- .target = target,
- .optimize = optimize,
- .use_llvm = llvm,
- })).step);
-}
+ // ========
+ // Bins
+ const mymarkdown_cli_bin: Binary = try .make(b, mymarkdown_cli_compile);
+ const mymarkdown_test_bin: Binary = try .make(b, mymarkdown_test_compile);
-fn setupRunStep(
- b: *std.Build,
- mymarkdown_cli_compile: *std.Build.Step.Compile,
-) void {
- const run_exe = b.addRunArtifact(mymarkdown_cli_compile);
- if (b.args) |args| run_exe.addArgs(args);
- const run_exe_step = b.step("run", "Run the mymarkdown CLI");
- run_exe_step.dependOn(&run_exe.step);
+ // ========
+ // Steps
+ const check_step = b.step("check", "Check if the mymarkdown CLI compiles");
+ check_step.dependOn(&mymarkdown_cli_compile.step);
+
+ const test_step = b.step("test", "Run tests");
+ test_step.dependOn(check_step);
+ mymarkdown_test_bin.runInStep(test_step);
+
+ const run_step = b.step("run", "Run the mymarkdown CLI");
+ mymarkdown_cli_bin.runInStep(run_step);
+ mymarkdown_cli_bin.install(b);
}
+
+const Binary = struct {
+ run: *std.Build.Step.Run,
+
+ fn make(b: *std.Build, compile: *std.Build.Step.Compile) !Binary {
+ const copy = try b.allocator.create(std.Build.Step.Compile);
+ copy.* = compile.*;
+ return .{ .run = b.addRunArtifact(copy) };
+ }
+
+ fn runInStep(runnable: Binary, step: *std.Build.Step) void {
+ step.dependOn(&runnable.run.step);
+ }
+
+ fn install(runnable: Binary, b: *std.Build) void {
+ b.installArtifact(runnable.run.producer.?);
+ }
+};
diff --git a/src/Ast.zig b/src/Ast.zig
@@ -114,18 +114,16 @@ pub const Error = utils.Packed(union(enum(u8)) {
pub const format = utils.unionFormat(@This());
});
-test "Tracking size of Node struct" {
- try std.testing.expectEqual(24, @bitSizeOf(Node.Idx));
- try std.testing.expectEqual(4, @sizeOf(Node.Idx));
- try std.testing.expectEqual(64, @bitSizeOf(Node));
- try std.testing.expectEqual(8, @sizeOf(Node));
-}
-
-test "Tracking size of Error struct" {
- try std.testing.expectEqual(24, @bitSizeOf(Error.Idx));
- try std.testing.expectEqual(4, @sizeOf(Error.Idx));
- try std.testing.expectEqual(64, @bitSizeOf(Error));
- try std.testing.expectEqual(8, @sizeOf(Error));
+comptime {
+ assert(24 == @bitSizeOf(Node.Idx));
+ assert(4 == @sizeOf(Node.Idx));
+ assert(64 == @bitSizeOf(Node));
+ assert(8 == @sizeOf(Node));
+
+ assert(24 == @bitSizeOf(Error.Idx));
+ assert(4 == @sizeOf(Error.Idx));
+ assert(64 == @bitSizeOf(Error));
+ assert(8 == @sizeOf(Error));
}
pub const Tagged = struct {
diff --git a/src/IndentationScanner.zig b/src/IndentationScanner.zig
@@ -174,7 +174,6 @@ const testing = std.testing;
const padded_str = @import("padded_str.zig");
const PaddedMany = @import("padded_str.zig").PaddedMany;
const PaddedSlice = @import("padded_str.zig").PaddedSlice;
-const str = @import("str.zig");
const IndentationScanner = @This();
diff --git a/src/padded_str.zig b/src/padded_str.zig
@@ -1,3 +1,3 @@
-pub const PADDING = @import("root.zig").PADDING;
+pub const PADDING = 128;
pub const PaddedSlice = @import("padded_str_impl.zig").PaddedSlice(PADDING);
pub const PaddedMany = @import("padded_str_impl.zig").PaddedMany(PADDING);
diff --git a/src/padded_str_impl.zig b/src/padded_str_impl.zig
@@ -289,15 +289,3 @@ pub fn PaddedMany(comptime PADDING_: usize) type {
}
};
}
-
-test "PaddedSlice" {
- const testing = std.testing;
- const input = "line 1\n";
- const padded_input = try PaddedSlice(128).pad(testing.allocator, input);
- defer testing.allocator.free(padded_input);
- var slice: PaddedSlice(128) = try .init(padded_input);
- const line_maybe, slice = slice.splitOneLine();
- if (line_maybe) |_| {
- return;
- } else return error.Sad;
-}
diff --git a/src/root.zig b/src/root.zig
@@ -8,13 +8,9 @@ pub const parse2 = AstGen2.parse;
pub const AstGen3 = @import("AstGen3.zig");
pub const parse3 = AstGen3.parse;
-pub const PADDING = 128;
+pub const PADDING = @import("padded_str.zig").PADDING;
test {
- _ = @import("test/test.zig");
- _ = Ast;
- _ = AstGen;
- _ = AstGen2;
- _ = AstGen3;
_ = @import("IndentationScanner.zig");
+ _ = @import("test.zig");
}
diff --git a/src/test.zig b/src/test.zig
@@ -0,0 +1,607 @@
+const std = @import("std");
+const parse = @import("AstGen.zig").parse;
+const parse2 = @import("AstGen2.zig").parse;
+const parse3 = @import("AstGen3.zig").parse;
+const Ast = @import("Ast.zig");
+const PADDING = @import("padded_str.zig").PADDING;
+
+const GeneralPurposeAllocator = std.heap.GeneralPurposeAllocator(.{});
+const ArenaAllocator = std.heap.ArenaAllocator;
+
+// This is in a function so the result is memoized.
+fn padInput(comptime input: []const u8) []const u8 {
+ return input ++ "\n" ** 128;
+}
+
+fn readFile(path: []const u8) !std.ArrayList(u8) {
+ var input_instance: std.ArrayList(u8) = .init(std.testing.allocator);
+ errdefer input_instance.deinit();
+ const input_file = try std.fs.cwd().openFile(path, .{});
+ defer input_file.close();
+ const file_size = (try input_file.stat()).size;
+ try input_instance.ensureTotalCapacity(file_size + PADDING);
+ try input_file.reader().readAllArrayList(&input_instance, std.math.maxInt(u32) - PADDING);
+ try input_instance.appendNTimes('\n', PADDING);
+ return input_instance;
+}
+
+fn testParse(comptime input: []const u8, expected: []const u8) !void {
+ try testParseWithFn(input, expected, parse);
+ try testParseWithFn(input, expected, parse3);
+}
+
+fn testParseWithFn(comptime input: []const u8, expected: []const u8, parseFn: anytype) !void {
+ var arena: ArenaAllocator = .init(std.testing.allocator);
+ defer arena.deinit();
+
+ const safe_input = padInput(input);
+
+ const ast = try parseFn(std.testing.allocator, arena.allocator(), safe_input);
+ var ast_render: std.ArrayListUnmanaged(u8) = .empty;
+ defer ast_render.deinit(std.testing.allocator);
+ try ast.renderAst(ast_render.writer(std.testing.allocator), safe_input);
+ try std.testing.expectEqualStrings(expected, ast_render.items);
+}
+
+test "Empty" {
+ try testParse("",
+ \\.document
+ \\
+ );
+}
+
+test "Happy path paragraph" {
+ try testParse(
+ \\text
+ \\
+ \\text
+ \\text
+ \\
+ \\text
+ \\ text
+ \\
+ ,
+ \\.document
+ \\ .paragraph
+ \\ .text
+ \\ "text"
+ \\ .paragraph
+ \\ .text
+ \\ "text"
+ \\ .space_text
+ \\ "text"
+ \\ .paragraph
+ \\ .text
+ \\ "text"
+ \\ .space_text
+ \\ " text"
+ \\
+ );
+}
+
+test "Happy path headings" {
+ try testParse(
+ \\# text
+ \\# text
+ \\# text
+ \\ text
+ \\
+ \\# text
+ \\
+ \\# text
+ \\ text
+ \\
+ \\# text
+ \\ text
+ \\
+ \\## text
+ \\## text
+ \\## text
+ \\ text
+ \\
+ \\## text
+ \\
+ \\## text
+ \\ text
+ \\
+ \\## text
+ \\ text
+ \\
+ ,
+ \\.document
+ \\ .heading
+ \\ .marker
+ \\ "#"
+ \\ .text
+ \\ "text"
+ \\ .heading
+ \\ .marker
+ \\ "#"
+ \\ .text
+ \\ "text"
+ \\ .heading
+ \\ .marker
+ \\ "#"
+ \\ .text
+ \\ "text"
+ \\ .space_text
+ \\ "text"
+ \\ .heading
+ \\ .marker
+ \\ "#"
+ \\ .text
+ \\ "text"
+ \\ .heading
+ \\ .marker
+ \\ "#"
+ \\ .text
+ \\ "text"
+ \\ .space_text
+ \\ "text"
+ \\ .heading
+ \\ .marker
+ \\ "#"
+ \\ .text
+ \\ "text"
+ \\ .space_text
+ \\ " text"
+ \\ .heading
+ \\ .marker
+ \\ "##"
+ \\ .text
+ \\ "text"
+ \\ .heading
+ \\ .marker
+ \\ "##"
+ \\ .text
+ \\ "text"
+ \\ .heading
+ \\ .marker
+ \\ "##"
+ \\ .text
+ \\ "text"
+ \\ .space_text
+ \\ "text"
+ \\ .heading
+ \\ .marker
+ \\ "##"
+ \\ .text
+ \\ "text"
+ \\ .heading
+ \\ .marker
+ \\ "##"
+ \\ .text
+ \\ "text"
+ \\ .space_text
+ \\ "text"
+ \\ .heading
+ \\ .marker
+ \\ "##"
+ \\ .text
+ \\ "text"
+ \\ .space_text
+ \\ " text"
+ \\
+ );
+}
+
+test "Happy path quote" {
+ try testParse(
+ \\> text
+ \\ text
+ \\
+ \\> text
+ \\ text
+ \\> text
+ \\> text
+ \\text
+ \\
+ ,
+ \\.document
+ \\ .quote
+ \\ .paragraph
+ \\ .text
+ \\ "text"
+ \\ .space_text
+ \\ "text"
+ \\ .quote
+ \\ .paragraph
+ \\ .text
+ \\ "text"
+ \\ .space_text
+ \\ " text"
+ \\ .quote
+ \\ .paragraph
+ \\ .text
+ \\ "text"
+ \\ .quote
+ \\ .paragraph
+ \\ .text
+ \\ "text"
+ \\ .paragraph
+ \\ .text
+ \\ "text"
+ \\
+ );
+}
+
+test "Happy path list" {
+ try testParseWithFn(
+ \\- text
+ \\- [ ] text
+ \\. text
+ \\: text
+ \\-- text
+ \\-- [ ] text
+ \\.. text
+ \\:: text
+ \\
+ ,
+ \\.document
+ \\ .list
+ \\ .unordered_item
+ \\ .marker
+ \\ "-"
+ \\ .text
+ \\ "text"
+ \\ .list
+ \\ .task_item
+ \\ .marker
+ \\ "- [ ]"
+ \\ .text
+ \\ "text"
+ \\ .list
+ \\ .ordered_item
+ \\ .marker
+ \\ "."
+ \\ .text
+ \\ "text"
+ \\ .list
+ \\ .term_item
+ \\ .marker
+ \\ ":"
+ \\ .text
+ \\ "text"
+ \\ .list
+ \\ .unordered_item
+ \\ .marker
+ \\ "--"
+ \\ .text
+ \\ "text"
+ \\ .list
+ \\ .task_item
+ \\ .marker
+ \\ "-- [ ]"
+ \\ .text
+ \\ "text"
+ \\ .list
+ \\ .ordered_item
+ \\ .marker
+ \\ ".."
+ \\ .text
+ \\ "text"
+ \\ .list
+ \\ .term_item
+ \\ .marker
+ \\ "::"
+ \\ .text
+ \\ "text"
+ \\
+ , parse3);
+}
+
+test "List grouping" {
+ try testParseWithFn(
+ \\- text
+ \\- text
+ \\-- text
+ \\- text
+ \\
+ \\- text
+ \\paragraph
+ \\- text
+ \\. text
+ \\
+ ,
+ \\.document
+ \\ .list
+ \\ .unordered_item
+ \\ .marker
+ \\ "-"
+ \\ .text
+ \\ "text"
+ \\ .unordered_item
+ \\ .marker
+ \\ "-"
+ \\ .text
+ \\ "text"
+ \\ .unordered_item
+ \\ .marker
+ \\ "--"
+ \\ .text
+ \\ "text"
+ \\ .unordered_item
+ \\ .marker
+ \\ "-"
+ \\ .text
+ \\ "text"
+ \\ .unordered_item
+ \\ .marker
+ \\ "-"
+ \\ .text
+ \\ "text"
+ \\ .paragraph
+ \\ .text
+ \\ "paragraph"
+ \\ .list
+ \\ .unordered_item
+ \\ .marker
+ \\ "-"
+ \\ .text
+ \\ "text"
+ \\ .list
+ \\ .ordered_item
+ \\ .marker
+ \\ "."
+ \\ .text
+ \\ "text"
+ \\
+ , parse3);
+}
+
+test "Happy path list elaboration" {
+ try testParseWithFn(
+ \\- a
+ \\+ bb
+ \\
+ \\ ccc
+ \\+ dddd
+ \\## heading
+ \\++ subtitle
+ \\
+ ,
+ \\.document
+ \\ .list
+ \\ .unordered_item
+ \\ .marker
+ \\ "-"
+ \\ .text
+ \\ "a"
+ \\ .elaboration
+ \\ .marker
+ \\ "+"
+ \\ .paragraph
+ \\ .text
+ \\ "bb"
+ \\ .paragraph
+ \\ .text
+ \\ "ccc"
+ \\ .elaboration
+ \\ .marker
+ \\ "+"
+ \\ .paragraph
+ \\ .text
+ \\ "dddd"
+ \\ .heading
+ \\ .marker
+ \\ "##"
+ \\ .text
+ \\ "heading"
+ \\ .elaboration
+ \\ .marker
+ \\ "++"
+ \\ .paragraph
+ \\ .text
+ \\ "subtitle"
+ \\
+ , parse3);
+}
+
+test "Elaboration errors" {
+ try testParseWithFn(
+ \\- a
+ \\++ bb
+ \\-- ccc
+ \\+ dddd
+ \\paragraph
+ \\+ eeeee
+ \\
+ ,
+ \\.document
+ \\ .list
+ \\ .unordered_item
+ \\ .error .incorrect_elaboration_marker
+ \\ .marker
+ \\ "-"
+ \\ .text
+ \\ "a"
+ \\ .elaboration
+ \\ .marker
+ \\ "++"
+ \\ .paragraph
+ \\ .text
+ \\ "bb"
+ \\ .unordered_item
+ \\ .error .incorrect_elaboration_marker
+ \\ .marker
+ \\ "--"
+ \\ .text
+ \\ "ccc"
+ \\ .elaboration
+ \\ .marker
+ \\ "+"
+ \\ .paragraph
+ \\ .text
+ \\ "dddd"
+ \\ .paragraph
+ \\ .text
+ \\ "paragraph"
+ \\ .elaboration
+ \\ .error .elaboration_after_unelaboratable_node
+ \\ .marker
+ \\ "+"
+ \\ .paragraph
+ \\ .text
+ \\ "eeeee"
+ \\
+ , parse3);
+}
+
+test "Thematic break" {
+ try testParse(
+ \\a
+ \\***
+ \\bb
+ \\*
+ \\ccc
+ \\
+ \\**bold text**
+ \\
+ ,
+ \\.document
+ \\ .paragraph
+ \\ .text
+ \\ "a"
+ \\ .thematic_break
+ \\ "***"
+ \\ .paragraph
+ \\ .text
+ \\ "bb"
+ \\ .space_text
+ \\ "*"
+ \\ .space_text
+ \\ "ccc"
+ \\ .paragraph
+ \\ .text
+ \\ "**bold text**"
+ \\
+ );
+}
+
+test "Mixed indentation" {
+ // AstGen1 used to try very hard to recover when it sees a tab
+ try testParseWithFn("" ++
+ "> aaa\n" ++
+ "\n" ++
+ "\tbbbbb\n",
+ \\.document
+ \\ .quote
+ \\ .error .inconsistent_indentation at 3:1
+ \\ .paragraph
+ \\ .text
+ \\ "aaa"
+ \\ .paragraph
+ \\ .text
+ \\ "bbbbb"
+ \\
+ , parse);
+
+ try testParseWithFn("" ++
+ "> aaa\n" ++
+ "\n" ++
+ "\tbbbbb\n",
+ \\.document
+ \\ .quote
+ \\ .paragraph
+ \\ .text
+ \\ "aaa"
+ \\ .paragraph
+ \\ .error .inconsistent_indentation at 3:1
+ \\ .text
+ \\ "bbbbb"
+ \\
+ , parse3);
+}
+
+test "Tabs in text" {
+ try testParse("" ++
+ "aaa\n" ++
+ "\tbbbbb\n",
+ \\.document
+ \\ .paragraph
+ \\ .text
+ \\ "aaa"
+ \\ .space_text
+ \\ "\tbbbbb"
+ \\
+ );
+}
+
+test "Empty line in heading" {
+ try testParse(
+ \\# heading
+ \\
+ \\ text
+ \\
+ \\text
+ \\
+ ,
+ \\.document
+ \\ .heading
+ \\ .marker
+ \\ "#"
+ \\ .text
+ \\ "heading"
+ \\ .space_text
+ \\ .error .unexpected_block_in_inline_context
+ \\ "text"
+ \\ .paragraph
+ \\ .text
+ \\ "text"
+ \\
+ );
+}
+
+test "Super long line" {
+ const input = try std.testing.allocator.create([(1 << 24) * 4]u8);
+ defer std.testing.allocator.destroy(input);
+ @memset(input, 'a');
+ var arena: ArenaAllocator = .init(std.testing.allocator);
+ defer arena.deinit();
+ const ast = try parse(std.testing.allocator, arena.allocator(), input);
+ const taggedAst = try ast.toTagged(arena.allocator());
+ try std.testing.expectEqualDeep(@as(Ast.Tagged, .{
+ .nodes = &.{
+ .{ .document = .{ .num_children = 1 } },
+ .{ .paragraph = .{ .off = 0, .num_children = 1 } },
+ .{ .text = .{ .off = 0, .len = 16777215 } },
+ .{ .text = .{ .off = 16777215, .len = 16777215 } },
+ .{ .text = .{ .off = 33554430, .len = 16777215 } },
+ .{ .text = .{ .off = 50331645, .len = 16777215 } },
+ .{ .text = .{ .off = 67108860, .len = 4 } },
+ },
+ .errors = &.{},
+ .extra = &.{},
+ }), taggedAst);
+}
+
+test "Many short lines" {
+ const input = try std.testing.allocator.create([(1 << 25) - 4 + PADDING]u8);
+ defer std.testing.allocator.destroy(input);
+ @memset(@as(*[(1 << 24) - 2][2]u8, @ptrCast(input)), "a\n"[0..2].*);
+ @memset(input[(1 << 25) - 4 ..], '\n');
+
+ var arena: ArenaAllocator = .init(std.testing.allocator);
+ defer arena.deinit();
+ const ast = try parse(std.testing.allocator, arena.allocator(), input);
+
+ try std.testing.expectEqual(1 << 24, ast.nodes.len);
+ try std.testing.expectEqual(
+ @as(Ast.Node.Tagged, .{ .document = .{ .num_children = 1 } }),
+ ast.nodes[0].toTagged(),
+ );
+ try std.testing.expectEqual(
+ @as(Ast.Node.Tagged, .{ .paragraph = .{ .off = 0, .num_children = (1 << 24) - 2 } }),
+ ast.nodes[1].toTagged(),
+ );
+ try std.testing.expectEqual(
+ @as(Ast.Node.Tagged, .{ .text = .{ .off = 0, .len = 1 } }),
+ ast.nodes[2].toTagged(),
+ );
+ for (1..(1 << 24) - 2) |i| {
+ try std.testing.expectEqual(
+ @as(Ast.Node.Tagged, .{ .space_text = .{ .off = @intCast(i * 2), .len = 1 } }),
+ ast.nodes[i + 2].toTagged(),
+ );
+ }
+}
diff --git a/src/test/test.zig b/src/test/test.zig
@@ -1,592 +0,0 @@
-const std = @import("std");
-const parse = @import("../AstGen.zig").parse;
-const parse2 = @import("../AstGen2.zig").parse;
-const parse3 = @import("../AstGen3.zig").parse;
-const Ast = @import("../Ast.zig");
-
-const GeneralPurposeAllocator = std.heap.GeneralPurposeAllocator(.{});
-const ArenaAllocator = std.heap.ArenaAllocator;
-
-// This is in a function so the result is memoized.
-fn padInput(comptime input: []const u8) []const u8 {
- return input ++ "\n" ** 128;
-}
-
-fn testParse(comptime input: []const u8, expected: []const u8) !void {
- try testParseWithFn(input, expected, parse);
- try testParseWithFn(input, expected, parse3);
-}
-
-fn testParseWithFn(comptime input: []const u8, expected: []const u8, parseFn: anytype) !void {
- var arena: ArenaAllocator = .init(std.testing.allocator);
- defer arena.deinit();
-
- const safe_input = padInput(input);
-
- const ast = try parseFn(std.testing.allocator, arena.allocator(), safe_input);
- var ast_render: std.ArrayListUnmanaged(u8) = .empty;
- defer ast_render.deinit(std.testing.allocator);
- try ast.renderAst(ast_render.writer(std.testing.allocator), safe_input);
- try std.testing.expectEqualStrings(expected, ast_render.items);
-}
-
-test "Empty" {
- try testParse("",
- \\.document
- \\
- );
-}
-
-test "Happy path paragraph" {
- try testParse(
- \\text
- \\
- \\text
- \\text
- \\
- \\text
- \\ text
- \\
- ,
- \\.document
- \\ .paragraph
- \\ .text
- \\ "text"
- \\ .paragraph
- \\ .text
- \\ "text"
- \\ .space_text
- \\ "text"
- \\ .paragraph
- \\ .text
- \\ "text"
- \\ .space_text
- \\ " text"
- \\
- );
-}
-
-test "Happy path headings" {
- try testParse(
- \\# text
- \\# text
- \\# text
- \\ text
- \\
- \\# text
- \\
- \\# text
- \\ text
- \\
- \\# text
- \\ text
- \\
- \\## text
- \\## text
- \\## text
- \\ text
- \\
- \\## text
- \\
- \\## text
- \\ text
- \\
- \\## text
- \\ text
- \\
- ,
- \\.document
- \\ .heading
- \\ .marker
- \\ "#"
- \\ .text
- \\ "text"
- \\ .heading
- \\ .marker
- \\ "#"
- \\ .text
- \\ "text"
- \\ .heading
- \\ .marker
- \\ "#"
- \\ .text
- \\ "text"
- \\ .space_text
- \\ "text"
- \\ .heading
- \\ .marker
- \\ "#"
- \\ .text
- \\ "text"
- \\ .heading
- \\ .marker
- \\ "#"
- \\ .text
- \\ "text"
- \\ .space_text
- \\ "text"
- \\ .heading
- \\ .marker
- \\ "#"
- \\ .text
- \\ "text"
- \\ .space_text
- \\ " text"
- \\ .heading
- \\ .marker
- \\ "##"
- \\ .text
- \\ "text"
- \\ .heading
- \\ .marker
- \\ "##"
- \\ .text
- \\ "text"
- \\ .heading
- \\ .marker
- \\ "##"
- \\ .text
- \\ "text"
- \\ .space_text
- \\ "text"
- \\ .heading
- \\ .marker
- \\ "##"
- \\ .text
- \\ "text"
- \\ .heading
- \\ .marker
- \\ "##"
- \\ .text
- \\ "text"
- \\ .space_text
- \\ "text"
- \\ .heading
- \\ .marker
- \\ "##"
- \\ .text
- \\ "text"
- \\ .space_text
- \\ " text"
- \\
- );
-}
-
-test "Happy path quote" {
- try testParse(
- \\> text
- \\ text
- \\
- \\> text
- \\ text
- \\> text
- \\> text
- \\text
- \\
- ,
- \\.document
- \\ .quote
- \\ .paragraph
- \\ .text
- \\ "text"
- \\ .space_text
- \\ "text"
- \\ .quote
- \\ .paragraph
- \\ .text
- \\ "text"
- \\ .space_text
- \\ " text"
- \\ .quote
- \\ .paragraph
- \\ .text
- \\ "text"
- \\ .quote
- \\ .paragraph
- \\ .text
- \\ "text"
- \\ .paragraph
- \\ .text
- \\ "text"
- \\
- );
-}
-
-test "Happy path list" {
- try testParseWithFn(
- \\- text
- \\- [ ] text
- \\. text
- \\: text
- \\-- text
- \\-- [ ] text
- \\.. text
- \\:: text
- \\
- ,
- \\.document
- \\ .list
- \\ .unordered_item
- \\ .marker
- \\ "-"
- \\ .text
- \\ "text"
- \\ .list
- \\ .task_item
- \\ .marker
- \\ "- [ ]"
- \\ .text
- \\ "text"
- \\ .list
- \\ .ordered_item
- \\ .marker
- \\ "."
- \\ .text
- \\ "text"
- \\ .list
- \\ .term_item
- \\ .marker
- \\ ":"
- \\ .text
- \\ "text"
- \\ .list
- \\ .unordered_item
- \\ .marker
- \\ "--"
- \\ .text
- \\ "text"
- \\ .list
- \\ .task_item
- \\ .marker
- \\ "-- [ ]"
- \\ .text
- \\ "text"
- \\ .list
- \\ .ordered_item
- \\ .marker
- \\ ".."
- \\ .text
- \\ "text"
- \\ .list
- \\ .term_item
- \\ .marker
- \\ "::"
- \\ .text
- \\ "text"
- \\
- , parse3);
-}
-
-test "List grouping" {
- try testParseWithFn(
- \\- text
- \\- text
- \\-- text
- \\- text
- \\
- \\- text
- \\paragraph
- \\- text
- \\. text
- \\
- ,
- \\.document
- \\ .list
- \\ .unordered_item
- \\ .marker
- \\ "-"
- \\ .text
- \\ "text"
- \\ .unordered_item
- \\ .marker
- \\ "-"
- \\ .text
- \\ "text"
- \\ .unordered_item
- \\ .marker
- \\ "--"
- \\ .text
- \\ "text"
- \\ .unordered_item
- \\ .marker
- \\ "-"
- \\ .text
- \\ "text"
- \\ .unordered_item
- \\ .marker
- \\ "-"
- \\ .text
- \\ "text"
- \\ .paragraph
- \\ .text
- \\ "paragraph"
- \\ .list
- \\ .unordered_item
- \\ .marker
- \\ "-"
- \\ .text
- \\ "text"
- \\ .list
- \\ .ordered_item
- \\ .marker
- \\ "."
- \\ .text
- \\ "text"
- \\
- , parse3);
-}
-
-test "Happy path list elaboration" {
- try testParseWithFn(
- \\- a
- \\+ bb
- \\
- \\ ccc
- \\+ dddd
- \\## heading
- \\++ subtitle
- \\
- ,
- \\.document
- \\ .list
- \\ .unordered_item
- \\ .marker
- \\ "-"
- \\ .text
- \\ "a"
- \\ .elaboration
- \\ .marker
- \\ "+"
- \\ .paragraph
- \\ .text
- \\ "bb"
- \\ .paragraph
- \\ .text
- \\ "ccc"
- \\ .elaboration
- \\ .marker
- \\ "+"
- \\ .paragraph
- \\ .text
- \\ "dddd"
- \\ .heading
- \\ .marker
- \\ "##"
- \\ .text
- \\ "heading"
- \\ .elaboration
- \\ .marker
- \\ "++"
- \\ .paragraph
- \\ .text
- \\ "subtitle"
- \\
- , parse3);
-}
-
-test "Elaboration errors" {
- try testParseWithFn(
- \\- a
- \\++ bb
- \\-- ccc
- \\+ dddd
- \\paragraph
- \\+ eeeee
- \\
- ,
- \\.document
- \\ .list
- \\ .unordered_item
- \\ .error .incorrect_elaboration_marker
- \\ .marker
- \\ "-"
- \\ .text
- \\ "a"
- \\ .elaboration
- \\ .marker
- \\ "++"
- \\ .paragraph
- \\ .text
- \\ "bb"
- \\ .unordered_item
- \\ .error .incorrect_elaboration_marker
- \\ .marker
- \\ "--"
- \\ .text
- \\ "ccc"
- \\ .elaboration
- \\ .marker
- \\ "+"
- \\ .paragraph
- \\ .text
- \\ "dddd"
- \\ .paragraph
- \\ .text
- \\ "paragraph"
- \\ .elaboration
- \\ .error .elaboration_after_unelaboratable_node
- \\ .marker
- \\ "+"
- \\ .paragraph
- \\ .text
- \\ "eeeee"
- \\
- , parse3);
-}
-
-test "Thematic break" {
- try testParse(
- \\a
- \\***
- \\bb
- \\*
- \\ccc
- \\
- \\**bold text**
- \\
- ,
- \\.document
- \\ .paragraph
- \\ .text
- \\ "a"
- \\ .thematic_break
- \\ "***"
- \\ .paragraph
- \\ .text
- \\ "bb"
- \\ .space_text
- \\ "*"
- \\ .space_text
- \\ "ccc"
- \\ .paragraph
- \\ .text
- \\ "**bold text**"
- \\
- );
-}
-
-test "Mixed indentation" {
- // AstGen1 used to try very hard to recover when it sees a tab
- try testParseWithFn("" ++
- "> aaa\n" ++
- "\n" ++
- "\tbbbbb\n",
- \\.document
- \\ .quote
- \\ .error .inconsistent_indentation at 3:1
- \\ .paragraph
- \\ .text
- \\ "aaa"
- \\ .paragraph
- \\ .text
- \\ "bbbbb"
- \\
- , parse);
-
- try testParseWithFn("" ++
- "> aaa\n" ++
- "\n" ++
- "\tbbbbb\n",
- \\.document
- \\ .quote
- \\ .paragraph
- \\ .text
- \\ "aaa"
- \\ .paragraph
- \\ .error .inconsistent_indentation at 3:1
- \\ .text
- \\ "bbbbb"
- \\
- , parse3);
-}
-
-test "Tabs in text" {
- try testParse("" ++
- "aaa\n" ++
- "\tbbbbb\n",
- \\.document
- \\ .paragraph
- \\ .text
- \\ "aaa"
- \\ .space_text
- \\ "\tbbbbb"
- \\
- );
-}
-
-test "Empty line in heading" {
- try testParse(
- \\# heading
- \\
- \\ text
- \\
- \\text
- \\
- ,
- \\.document
- \\ .heading
- \\ .marker
- \\ "#"
- \\ .text
- \\ "heading"
- \\ .space_text
- \\ .error .unexpected_block_in_inline_context
- \\ "text"
- \\ .paragraph
- \\ .text
- \\ "text"
- \\
- );
-}
-
-test "Super long line" {
- const input = try std.testing.allocator.create([(1 << 24) * 4]u8);
- defer std.testing.allocator.destroy(input);
- @memset(input, 'a');
- var arena: ArenaAllocator = .init(std.testing.allocator);
- defer arena.deinit();
- const ast = try parse(std.testing.allocator, arena.allocator(), input);
- const taggedAst = try ast.toTagged(arena.allocator());
- try std.testing.expectEqualDeep(@as(Ast.Tagged, .{
- .nodes = &.{
- .{ .document = .{ .num_children = 1 } },
- .{ .paragraph = .{ .off = 0, .num_children = 1 } },
- .{ .text = .{ .off = 0, .len = 16777215 } },
- .{ .text = .{ .off = 16777215, .len = 16777215 } },
- .{ .text = .{ .off = 33554430, .len = 16777215 } },
- .{ .text = .{ .off = 50331645, .len = 16777215 } },
- .{ .text = .{ .off = 67108860, .len = 4 } },
- },
- .errors = &.{},
- .extra = &.{},
- }), taggedAst);
-}
-
-test "Many short lines" {
- const input = try std.testing.allocator.create([(1 << 25) - 4]u8);
- defer std.testing.allocator.destroy(input);
- @memset(@as(*[(1 << 24) - 2][2]u8, @ptrCast(input)), "a\n"[0..2].*);
-
- var arena: ArenaAllocator = .init(std.testing.allocator);
- defer arena.deinit();
- const ast = try parse(std.testing.allocator, arena.allocator(), input);
- try std.testing.expectEqual(1 << 24, ast.nodes.len);
- try std.testing.expectEqual(
- @as(Ast.Node.Tagged, .{ .document = .{ .num_children = 1 } }),
- ast.nodes[0].toTagged(),
- );
- try std.testing.expectEqual(
- @as(Ast.Node.Tagged, .{ .paragraph = .{ .off = 0, .num_children = (1 << 24) - 2 } }),
- ast.nodes[1].toTagged(),
- );
- try std.testing.expectEqual(
- @as(Ast.Node.Tagged, .{ .text = .{ .off = 0, .len = 1 } }),
- ast.nodes[2].toTagged(),
- );
- for (1..(1 << 24) - 2) |i| {
- try std.testing.expectEqual(
- @as(Ast.Node.Tagged, .{ .space_text = .{ .off = @intCast(i * 2), .len = 1 } }),
- ast.nodes[i + 2].toTagged(),
- );
- }
-}