commit 872212778fa2fe51462122f2bfc0a0463d0b564e
parent a293f5b6aea00d2a3465b0aa4a513953b60f4bfa
Author: gracefu <81774659+gracefuu@users.noreply.github.com>
Date: Fri, 16 May 2025 16:22:59 +0800
v0
Diffstat:
6 files changed, 254 insertions(+), 143 deletions(-)
diff --git a/build.zig b/build.zig
@@ -1,33 +1,54 @@
const std = @import("std");
-pub fn build(b: *std.Build) void {
+pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
+ const llvm = b.option(
+ bool,
+ "llvm",
+ "Force llvm to be used or not (default: compiler default for markdown_cli, false for tests)",
+ );
+
+ const enable_tracy = b.option(
+ bool,
+ "tracy",
+ "Enable Tracy profiling",
+ ) orelse false;
+
+ const tracy = b.dependency("tracy", .{ .enable = enable_tracy });
+
const mymarkdown = b.addModule("mymarkdown", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
mymarkdown.addImport("ziggy", b.dependency("ziggy", .{}).module("ziggy"));
+ mymarkdown.addImport("tracy", tracy.module("tracy"));
const mymarkdown_cli = b.addModule("mymarkdown", .{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
mymarkdown_cli.addImport("mymarkdown", mymarkdown);
+ mymarkdown_cli.addImport("tracy", tracy.module("tracy"));
const mymarkdown_cli_compile = b.addExecutable(.{
.name = "mymarkdown",
.root_module = mymarkdown_cli,
+ .use_llvm = llvm,
});
- b.installArtifact(mymarkdown_cli_compile);
-
const check = b.step("check", "Check if the mymarkdown CLI compiles");
- check.dependOn(&mymarkdown_cli_compile.step);
-
- setupTestStep(b, target, optimize, mymarkdown, mymarkdown_cli, check);
+ 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);
}
fn setupTestStep(
@@ -37,6 +58,7 @@ fn setupTestStep(
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);
@@ -44,11 +66,13 @@ fn setupTestStep(
.root_module = mymarkdown,
.target = target,
.optimize = optimize,
+ .use_llvm = llvm orelse true,
})).step);
test_step.dependOn(&b.addRunArtifact(b.addTest(.{
.root_module = mymarkdown_cli,
.target = target,
.optimize = optimize,
+ .use_llvm = llvm orelse true,
})).step);
}
diff --git a/build.zig.zon b/build.zig.zon
@@ -7,6 +7,9 @@
.ziggy = .{
.path = "../../../manual-software/ziggy",
},
+ .tracy = .{
+ .path = "../../../manual-software/tracy-zig",
+ },
},
.paths = .{
"build.zig",
diff --git a/src/Ast.zig b/src/Ast.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const ziggy = @import("ziggy");
+const tracy = @import("tracy");
const utils = @import("utils.zig");
const Allocator = std.mem.Allocator;
const Ast = @This();
@@ -197,6 +198,8 @@ pub fn toTagged(self: Ast, gpa: Allocator) !Tagged {
}
pub fn render(self: Ast, writer: anytype, input: []const u8, start_: ?Node.Idx) !?Node.Idx {
+ const tracy_frame = tracy.trace(@src());
+ defer tracy_frame.end();
const start: Node.Idx = start_ orelse @enumFromInt(0);
switch (self.nodes[@intFromEnum(start)].tag) {
.document => try writer.writeAll("<body>\n"),
@@ -210,7 +213,7 @@ pub fn render(self: Ast, writer: anytype, input: []const u8, start_: ?Node.Idx)
try writer.writeByte(' ');
try writer.writeAll(input[data.off .. data.off + data.len]);
},
- else => unreachable,
+ else => {},
}
var cur_idx: ?Node.Idx = start.next();
switch (self.nodes[@intFromEnum(start)].tag) {
@@ -230,7 +233,7 @@ pub fn render(self: Ast, writer: anytype, input: []const u8, start_: ?Node.Idx)
.document => try writer.writeAll("</body>\n"),
.paragraph => try writer.writeAll("</p>\n"),
.text, .space_text => {},
- else => unreachable,
+ else => {},
}
return cur_idx;
}
diff --git a/src/AstGen.zig b/src/AstGen.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const ziggy = @import("ziggy");
+const tracy = @import("tracy");
const utils = @import("utils.zig");
const str = @import("str.zig");
const ArenaAllocator = std.heap.ArenaAllocator;
@@ -45,14 +46,22 @@ pub fn deinit(self: *AstGen, gpa: Allocator) void {
}
pub fn parse(gpa: Allocator, output_gpa: ?Allocator, input: []const u8) error{ InputTooLarge, OutOfMemory }!Ast {
+ const tracy_frame = tracy.trace(@src());
+ defer tracy_frame.end();
+
if (input.len > std.math.maxInt(u32)) {
return error.InputTooLarge;
}
- const input_copy = try gpa.dupe(u8, input);
- defer gpa.free(input_copy);
+ const tracy_frame2 = tracy.traceNamed(@src(), "Allocate input copy");
+ // const input_copy = try gpa.dupe(u8, input);
+ // defer gpa.free(input_copy);
+ var input_copy: std.ArrayListUnmanaged(u8) = .empty;
+ defer input_copy.deinit(gpa);
+ try input_copy.ensureTotalCapacityPrecise(gpa, input.len + 1);
+ tracy_frame2.end();
var ast: AstGen = .{
- .input_base = input_copy.ptr,
+ .input_base = input_copy.items.ptr,
.nodes = .empty,
.errors = .empty,
.extra = .empty,
@@ -63,12 +72,23 @@ pub fn parse(gpa: Allocator, output_gpa: ?Allocator, input: []const u8) error{ I
var lines: std.ArrayListUnmanaged([]u8) = .empty;
defer lines.deinit(gpa);
- var lines_it = std.mem.splitScalar(u8, input_copy, '\n');
+ const tracy_frame3 = tracy.traceNamed(@src(), "Split into lines and copy");
+ var lines_it = std.mem.splitScalar(u8, input, '\n');
var maybe_line: ?[]u8 = @constCast(lines_it.first());
+ var off: usize = 0;
while (maybe_line) |line| : (maybe_line = @constCast(lines_it.next())) {
- try lines.append(gpa, line);
+ input_copy.appendSliceAssumeCapacity(line);
+ input_copy.appendAssumeCapacity('\n');
+ if (str.lastIndexOfNone(line, " \t\r\n")) |idx| {
+ try lines.append(gpa, input_copy.items[off .. off + idx + 1]);
+ } else {
+ try lines.append(gpa, input_copy.items[off..off]);
+ }
+ off += line.len + 1;
}
- stripTrailingWhitespace(&lines.items);
+ tracy_frame3.end();
+
+ // stripTrailingWhitespace(&lines.items);
try ast.parseColumn(gpa, lines.items, root);
@@ -90,6 +110,8 @@ pub fn parse(gpa: Allocator, output_gpa: ?Allocator, input: []const u8) error{ I
}
fn stripTrailingWhitespace(lines: *[][]u8) void {
+ const tracy_frame = tracy.trace(@src());
+ defer tracy_frame.end();
for (lines.*) |*line| {
if (str.lastIndexOfNone(line.*, " \t\r\n")) |idx| {
line.* = line.*[0 .. idx + 1];
@@ -104,6 +126,8 @@ fn calcOffset(self: *AstGen, c: *u8) u32 {
}
fn findIndentedColumn(self: *AstGen, gpa: Allocator, lines_: [][]u8, node_idx: Node.Idx) ![][]u8 {
+ const tracy_frame = tracy.trace(@src());
+ defer tracy_frame.end();
var lines = lines_;
// empty lines at the start of the inline block are fine, just skip these
@@ -144,6 +168,8 @@ fn findIndentedColumn(self: *AstGen, gpa: Allocator, lines_: [][]u8, node_idx: N
}
fn parseInlineBlock(self: *AstGen, gpa: Allocator, lines_: [][]u8, parent_idx: Node.Idx) !void {
+ const tracy_frame = tracy.trace(@src());
+ defer tracy_frame.end();
var lines = lines_;
var empty_line_off: ?u32 = null;
@@ -159,12 +185,6 @@ fn parseInlineBlock(self: *AstGen, gpa: Allocator, lines_: [][]u8, parent_idx: N
self.getNode(parent_idx).incrementNumChildren();
- // determine indentation
- const indentation_idx = str.indexOfNone(lines[0], " \t\r\n") orelse unreachable;
- const indentation = lines[0][0..indentation_idx];
-
- lines[0] = lines[0][indentation.len..];
-
if (lines[0].len <= std.math.maxInt(Ast.StrLen)) {
_ = try self.appendNode(gpa, .{
.text = .{
@@ -203,19 +223,6 @@ fn parseInlineBlock(self: *AstGen, gpa: Allocator, lines_: [][]u8, parent_idx: N
}));
}
- const diff_idx = std.mem.indexOfDiff(u8, lines[0], indentation) orelse unreachable;
- std.debug.assert(diff_idx != lines[0].len);
- if (diff_idx != indentation.len) {
- try self.errors.append(gpa, .fromTagged(.{
- .inconsistent_indentation = .{ .idx = self.nextNodeIdx(), .off = self.calcOffset(&lines[0][0]) },
- }));
- // Recover by stripping all whitespace on this line
- const recover_indentation_idx = std.mem.indexOfNone(u8, lines[0], " \t\r\n") orelse unreachable;
- lines[0] = lines[0][recover_indentation_idx..];
- } else {
- lines[0] = lines[0][indentation.len..];
- }
-
self.getNode(parent_idx).incrementNumChildren();
if (lines[0].len <= std.math.maxInt(Ast.StrLen)) {
@@ -252,19 +259,26 @@ fn parseInlineBlock(self: *AstGen, gpa: Allocator, lines_: [][]u8, parent_idx: N
}
fn parseColumn(self: *AstGen, gpa: Allocator, lines_: [][]u8, parent_idx: Node.Idx) !void {
+ const tracy_frame = tracy.trace(@src());
+ defer tracy_frame.end();
var lines = lines_;
outer: while (true) {
// Skip empty lines
// special case: the first line consist of only whitespace
// because they may have been introduced via marker replacement
- if (lines.len > 0 and str.indexOfNone(lines[0], " \t\r\n") == null) lines = lines[1..];
- while (true) : (lines = lines[1..]) {
- if (lines.len == 0) break :outer;
- if (lines[0].len != 0) break;
+ {
+ const tracy_frame_skip = tracy.traceNamed(@src(), "skip empty lines");
+ defer tracy_frame_skip.end();
+ if (lines.len > 0 and str.indexOfNone(lines[0], " \t\r\n") == null) lines = lines[1..];
+ while (true) : (lines = lines[1..]) {
+ if (lines.len == 0) break :outer;
+ if (lines[0].len != 0) break;
+ }
}
// Use first character to determine marker
const mode, const child = try self.parseBlockStart(gpa, lines[0]);
+
self.getNode(parent_idx).incrementNumChildren();
switch (mode) {
@@ -273,7 +287,9 @@ fn parseColumn(self: *AstGen, gpa: Allocator, lines_: [][]u8, parent_idx: Node.I
var num_lines: usize = 1;
for (lines[1..]) |line| {
if (line.len == 0) break;
- if (block_specs[line[0]] != null) break;
+ if (line[0] == '*') {
+ if (std.mem.eql(u8, line, "***")) break;
+ } else if (block_specs[line[0]] != null) break;
num_lines += 1;
}
@@ -322,6 +338,7 @@ const ParseMode = union(enum) {
};
const MarkerSpec = union(enum) {
+ paragraph,
exact: []const u8,
starts_with: []const u8,
starts_with_multi: struct {
@@ -357,6 +374,12 @@ const block_specs = blockSpecs(struct {
.mode = .no_children,
.store_marker_child = .no_store,
},
+ .{
+ .tag = .paragraph,
+ .marker = .paragraph,
+ .mode = .paragraph,
+ .store_marker_child = .no_store,
+ },
};
pub const @"#": BlockSpec = &.{
.{
@@ -412,83 +435,130 @@ const block_specs = blockSpecs(struct {
.store_marker_child = .no_store,
},
};
+ pub const @";": BlockSpec = &.{
+ .{
+ .tag = .paragraph,
+ .marker = .{ .starts_with = ";" },
+ .mode = .indented_inline_block,
+ .store_marker_child = .no_store,
+ },
+ };
});
/// Appends the suitable block node to the ast,
/// then returns how parsing should proceed for the children of this block.
/// Also returns the idx of the container node created.
fn parseBlockStart(self: *AstGen, gpa: Allocator, line: []u8) !struct { ParseMode, Node.Idx } {
+ const tracy_frame = tracy.trace(@src());
+ defer tracy_frame.end();
+ if (block_specs[line[0]] == null) {
+ return .{
+ .paragraph,
+ try self.appendNode(gpa, .{
+ .paragraph = .{
+ .off = self.calcOffset(&line[0]),
+ },
+ }),
+ };
+ }
+
+ // Inline switch by starting character so codegen proceeds as if each blockspec was converted to code then concatenated.
+ // Note that we separately handle the null case above, then make the inline case below `unreachable`.
+ // That makes it so that we don't have 240+ branches that all just do exactly the same thing.
+ //
+ // Regardless, the blockspec must be comptime known (the inline for is mandatory) because we do @unionInit with case.tag.
switch (line[0]) {
inline else => |c| {
- const spec_or_null = block_specs[c];
- if (spec_or_null) |spec| {
- inline for (spec) |case| {
- switch (case.marker) {
- .exact, .starts_with => |marker| {
- if (std.mem.startsWith(u8, line, marker)) {
- const node = if (case.mode == .no_children) try self.appendNode(gpa, @unionInit(Node.Tagged, @tagName(case.tag), @as(Node.Tagged.Leaf, .{
- .off = self.calcOffset(&line[0]),
- .len = marker.len,
- }))) else try self.appendNode(gpa, @unionInit(Node.Tagged, @tagName(case.tag), @as(Node.Tagged.Container, .{
+ if (block_specs[c] == null) unreachable;
+ inline for (block_specs[c].?) |case| {
+ switch (case.marker) {
+ .exact, .starts_with => |marker| {
+ if (std.mem.startsWith(u8, line, marker)) {
+ const node = if (case.mode == .no_children)
+ try self.appendNode(gpa, @unionInit(
+ Node.Tagged,
+ @tagName(case.tag),
+ @as(Node.Tagged.Leaf, .{
+ .off = self.calcOffset(&line[0]),
+ .len = marker.len,
+ }),
+ ))
+ else
+ try self.appendNode(gpa, @unionInit(
+ Node.Tagged,
+ @tagName(case.tag),
+ @as(Node.Tagged.Container, .{
+ .off = self.calcOffset(&line[0]),
+ .num_children = if (case.store_marker_child == .store) 1 else 0,
+ }),
+ ));
+ @memset(line[0..marker.len], ' ');
+ if (case.store_marker_child == .store) {
+ _ = try self.appendNode(gpa, .{ .marker = .{
.off = self.calcOffset(&line[0]),
- .num_children = if (case.store_marker_child == .store) 1 else 0,
- })));
- @memset(line[0..marker.len], ' ');
+ .len = case.marker.len,
+ } });
+ }
+ return .{ case.mode, node };
+ }
+ },
+ .starts_with_multi => |marker_spec| {
+ var marker_len = str.indexOfNotChar(line, marker_spec.marker_char) orelse line.len;
+
+ inline for (marker_spec.extra) |extra| {
+ if (std.mem.startsWith(u8, line[marker_len..], extra)) {
+ marker_len += extra.len;
+
+ const node = if (case.mode == .no_children)
+ try self.appendNode(gpa, @unionInit(
+ Node.Tagged,
+ @tagName(case.tag),
+ @as(Node.Tagged.Leaf, .{
+ .off = self.calcOffset(&line[0]),
+ .len = marker_len,
+ }),
+ ))
+ else
+ try self.appendNode(gpa, @unionInit(
+ Node.Tagged,
+ @tagName(case.tag),
+ @as(Node.Tagged.Container, .{
+ .off = self.calcOffset(&line[0]),
+ .num_children = if (case.store_marker_child == .store) 1 else 0,
+ }),
+ ));
+
+ if (marker_spec.max_chars) |max|
+ if (marker_len > max)
+ try self.errors.append(gpa, .fromTagged(.{
+ .marker_too_long = .{
+ .idx = if (case.store_marker_child == .no_store)
+ self.lastNodeIdx()
+ else
+ self.nextNodeIdx(),
+ },
+ }));
+
+ @memset(line[0..marker_len], ' ');
if (case.store_marker_child == .store) {
_ = try self.appendNode(gpa, .{ .marker = .{
.off = self.calcOffset(&line[0]),
- .len = case.marker.len,
+ .len = utils.safeIntCast(Ast.StrLen, marker_len),
} });
}
return .{ case.mode, node };
}
- },
- .starts_with_multi => |marker_spec| {
- var marker_len = str.indexOfNotChar(line, marker_spec.marker_char) orelse line.len;
-
- inline for (marker_spec.extra) |extra| {
- if (std.mem.startsWith(u8, line[marker_len..], extra)) {
- marker_len += extra.len;
-
- const node = try self.appendNode(gpa, @unionInit(Node.Tagged, @tagName(case.tag), @as(Node.Tagged.Container, .{
- .off = self.calcOffset(&line[0]),
- .num_children = if (case.store_marker_child == .store) 1 else 0,
- })));
-
- if (marker_spec.max_chars) |max|
- if (marker_len > max)
- try self.errors.append(gpa, .fromTagged(.{
- .marker_too_long = .{
- .idx = if (case.store_marker_child == .no_store)
- self.lastNodeIdx()
- else
- self.nextNodeIdx(),
- },
- }));
-
- @memset(line[0..marker_len], ' ');
- if (case.store_marker_child == .store) {
- _ = try self.appendNode(gpa, .{ .marker = .{
- .off = self.calcOffset(&line[0]),
- .len = utils.safeIntCast(Ast.StrLen, marker_len),
- } });
- }
- return .{ case.mode, node };
- }
- }
- },
- }
+ }
+ },
+ .paragraph => return .{
+ .paragraph,
+ try self.appendNode(gpa, .{
+ .paragraph = .{
+ .off = self.calcOffset(&line[0]),
+ },
+ }),
+ },
}
- } else {
- // Default behaviour is to parse a paragraph until the next newline or block character
- return .{
- .paragraph,
- try self.appendNode(gpa, .{
- .paragraph = .{
- .off = self.calcOffset(&line[0]),
- },
- }),
- };
}
},
}
diff --git a/src/AstGen/test.zig b/src/AstGen/test.zig
@@ -238,9 +238,11 @@ test "Thematic break" {
try testParse(
\\a
\\***
- \\b
+ \\bb
\\*
- \\c
+ \\ccc
+ \\
+ \\**bold text**
\\
, .{
.nodes = &.{
@@ -248,15 +250,14 @@ test "Thematic break" {
.{ .paragraph = .{ .off = 0, .num_children = 1 } },
.{ .text = .{ .off = 0, .len = 1 } },
.{ .thematic_break = .{ .off = 2, .len = 3 } },
- .{ .paragraph = .{ .off = 6, .num_children = 1 } },
- .{ .text = .{ .off = 6, .len = 1 } },
- .{ .paragraph = .{ .off = 8, .num_children = 2 } },
- .{ .text = .{ .off = 8, .len = 1 } },
- .{ .space_text = .{ .off = 10, .len = 1 } },
- },
- .errors = &.{
- .{ .invalid_marker = .{ .idx = @enumFromInt(6), .off = 8 } },
+ .{ .paragraph = .{ .off = 6, .num_children = 3 } },
+ .{ .text = .{ .off = 6, .len = 2 } },
+ .{ .space_text = .{ .off = 9, .len = 1 } },
+ .{ .space_text = .{ .off = 11, .len = 3 } },
+ .{ .paragraph = .{ .off = 16, .num_children = 1 } },
+ .{ .text = .{ .off = 16, .len = 13 } },
},
+ .errors = &.{},
.extra = &.{},
});
}
@@ -307,40 +308,40 @@ test "Empty line in heading" {
});
}
-// test "Super long line" {
-// const input = try std.testing.allocator.create([(1 << 24) * 4]u8);
-// defer std.testing.allocator.destroy(input);
-// @memset(input, 'a');
-// input[1] = '\n';
-// try testParse(input, .{
-// .nodes = &.{
-// .{ .document = .{ .num_children = 1 } },
-// .{ .paragraph = .{ .off = 0, .num_children = 2 } },
-// .{ .text = .{ .off = 0, .len = 1 } },
-// .{ .space_text = .{ .off = 2, .len = 16777215 } },
-// .{ .text = .{ .off = 2, .len = 16777215 } },
-// .{ .text = .{ .off = 2, .len = 16777215 } },
-// .{ .text = .{ .off = 2, .len = 16777215 } },
-// .{ .text = .{ .off = 2, .len = 2 } },
-// },
-// .errors = &.{},
-// .extra = &.{},
-// });
-// }
+test "Super long line" {
+ const input = try std.testing.allocator.create([(1 << 24) * 4]u8);
+ defer std.testing.allocator.destroy(input);
+ @memset(input, 'a');
+ input[1] = '\n';
+ try testParse(input, .{
+ .nodes = &.{
+ .{ .document = .{ .num_children = 1 } },
+ .{ .paragraph = .{ .off = 0, .num_children = 2 } },
+ .{ .text = .{ .off = 0, .len = 1 } },
+ .{ .space_text = .{ .off = 2, .len = 16777215 } },
+ .{ .text = .{ .off = 2, .len = 16777215 } },
+ .{ .text = .{ .off = 2, .len = 16777215 } },
+ .{ .text = .{ .off = 2, .len = 16777215 } },
+ .{ .text = .{ .off = 2, .len = 2 } },
+ },
+ .errors = &.{},
+ .extra = &.{},
+ });
+}
-// test "Many short lines" {
-// const input = try std.testing.allocator.create([(1 << 23) - 2][2]u8);
-// defer std.testing.allocator.destroy(input);
-// @memset(input, [2]u8{ 'a', '\n' });
+test "Many short lines" {
+ const input = try std.testing.allocator.create([(1 << 23) - 2][2]u8);
+ defer std.testing.allocator.destroy(input);
+ @memset(input, [2]u8{ 'a', '\n' });
-// var arena: ArenaAllocator = .init(std.testing.allocator);
-// defer arena.deinit();
-// const ast = try parse(std.testing.allocator, arena.allocator(), @as([*]u8, @ptrCast(input))[0 .. (1 << 23) * 2 - 4]);
-// try std.testing.expectEqual(1 << 23, 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 << 23) - 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 << 23) - 2) |i| {
-// try std.testing.expectEqual(@as(Ast.Node.Tagged, .{ .space_text = .{ .off = @intCast(i * 2), .len = 1 } }), ast.nodes[i + 2].toTagged());
-// }
-// }
+ var arena: ArenaAllocator = .init(std.testing.allocator);
+ defer arena.deinit();
+ const ast = try parse(std.testing.allocator, arena.allocator(), @as([*]u8, @ptrCast(input))[0 .. (1 << 23) * 2 - 4]);
+ try std.testing.expectEqual(1 << 23, 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 << 23) - 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 << 23) - 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/main.zig b/src/main.zig
@@ -1,4 +1,5 @@
const std = @import("std");
+const tracy = @import("tracy");
const mymarkdown = @import("mymarkdown");
const GeneralPurposeAllocator = std.heap.GeneralPurposeAllocator(.{});
@@ -11,12 +12,21 @@ pub fn main() !void {
const input = try std.io.getStdIn().readToEndAlloc(arena.allocator(), std.math.maxInt(u32));
+ const parse_tracy_frame = tracy.namedFrame("parse");
const ast = try mymarkdown.parse(gpa.allocator(), arena.allocator(), input);
+ parse_tracy_frame.end();
// std.mem.doNotOptimizeAway(ast);
var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
const stdout = bw.writer();
// try stdout.print("{}\n", .{ast});
+ const render_tracy_frame = tracy.namedFrame("render");
_ = try ast.render(stdout, input, null);
+ render_tracy_frame.end();
try bw.flush();
+
+ if (tracy.enable) {
+ tracy.frameMarkNamed("waiting for tracy");
+ std.Thread.sleep(100 * std.time.ns_per_ms);
+ }
}