Parses a file and returns its Ast. If the file cannot be parsed, returns
the Ast of an empty file, so that the rest of the Autodoc logic does not
need to handle parse errors.
fn parse(file_name: []const u8, source: []u8) Oom!Ast
fn parse(file_name: []const u8, source: []u8) Oom!Ast {
// Require every source file to end with a newline so that Zig's tokenizer
// can continue to require null termination and Autodoc implementation can
// avoid copying source bytes from the decompressed tar file buffer.
const adjusted_source: [:0]const u8 = s: {
if (source.len == 0)
break :s "";
if (source[source.len - 1] != '\n') {
log.err("{s}: expected newline at end of file", .{file_name});
break :s "";
}
source[source.len - 1] = 0;
break :s source[0 .. source.len - 1 :0];
};
var ast = try Ast.parse(gpa, adjusted_source, .{});
if (ast.errors.len > 0) {
defer ast.deinit(gpa);
const token_offsets = ast.tokens.items(.start);
var rendered_err: std.Io.Writer.Allocating = .init(gpa);
defer rendered_err.deinit();
for (ast.errors) |err| {
const err_offset = token_offsets[err.token] + ast.errorOffset(err);
const err_loc = std.zig.findLineColumn(ast.source, err_offset);
rendered_err.clearRetainingCapacity();
ast.renderError(err, &rendered_err.writer) catch |e| switch (e) {
error.WriteFailed => return error.OutOfMemory,
};
log.err("{s}:{d}:{d}: {s}", .{
file_name, err_loc.line + 1, err_loc.column + 1, rendered_err.written(),
});
}
return Ast.parse(gpa, "", .{});
}
return ast;
}