Converts the nodes into a Zig Ast. Caller must free the source slice.
pub fn render(gpa: Allocator, nodes: []const Node) !std.zig.Ast
pub fn render(gpa: Allocator, nodes: []const Node) !std.zig.Ast {
var ctx: Context = .{
.gpa = gpa,
};
defer ctx.buf.deinit(gpa);
defer ctx.nodes.deinit(gpa);
defer ctx.extra_data.deinit(gpa);
defer ctx.tokens.deinit(gpa);
// Estimate that each top level node has 10 child nodes.
const estimated_node_count = nodes.len * 10 + 1; // +1 for the .root node
try ctx.nodes.ensureTotalCapacity(gpa, estimated_node_count);
// Estimate that each each node has 2 tokens.
const estimated_tokens_count = estimated_node_count * 2;
try ctx.tokens.ensureTotalCapacity(gpa, estimated_tokens_count);
// Estimate that each each token is 3 bytes long.
const estimated_buf_len = estimated_tokens_count * 3;
try ctx.buf.ensureTotalCapacity(gpa, estimated_buf_len);
ctx.nodes.appendAssumeCapacity(.{
.tag = .root,
.main_token = 0,
.data = undefined,
});
const root_members = blk: {
var result: std.ArrayList(NodeIndex) = .empty;
defer result.deinit(gpa);
for (nodes) |node| {
const res = (try renderNodeOpt(&ctx, node)) orelse continue;
try result.append(gpa, res);
}
break :blk try ctx.listToSpan(result.items);
};
ctx.nodes.items(.data)[0] = .{ .extra_range = .{
.start = root_members.start,
.end = root_members.end,
} };
try ctx.tokens.append(gpa, .{
.tag = .eof,
.start = @as(u32, @intCast(ctx.buf.items.len)),
});
try ctx.buf.shrinkToLenSentinel(gpa);
try ctx.extra_data.shrinkToLen(gpa);
return .{
.source = ctx.buf.toOwnedSliceSentinelAssert(0),
.tokens = ctx.tokens.toOwnedSlice(),
.nodes = ctx.nodes.toOwnedSlice(),
.extra_data = ctx.extra_data.toOwnedSliceAssert(),
.errors = &.{},
.mode = .zig,
};
}