1//! `Format` converts a parsed Ast back into source code. This uses
2//! the fact that `Ast` contains the original source code slice in
3//! order to preserve comments despite not representing them as nodes.
4ast: Ast,
5writer: AnyWriter,
6ident: usize = 0,
7
8// Formatting options such as tab size or character are intentionally
9// not exposed as configuration options. This makes all source files
10// follow the same style guide.
11const spaces_per_level = 3;
12
13pub fn format(ast: Ast, writer: AnyWriter) !void {
14 var state: Format = .{ .ast = ast, .writer = writer };
15
16 // Print the top-level documentation comment
17 {
18 var i: u32 = 0;
19 while (state.ast.tokenTag(i) == .module_doc_comment) {
20 i += 1;
21 }
22 if (i > 0) {
23 const start = state.ast.tokenOffset(0);
24 const end = (mem.lastIndexOf(
25 u8,
26 state.ast.utf8_source[0..state.ast.tokenOffset(i)],
27 "\n",
28 ) orelse unreachable) + 1;
29 try state.writer.writeAll(state.ast.utf8_source[start..end]);
30 }
31 }
32
33 try state.formatNode(0);
34
35 assert(state.ident == 0);
36}
37
38fn formatNode(state: Format, node: NodeIndex) !void {
39 switch (state.ast.nodeTag(node)) {
40 .root => {
41 for (state.ast.nodeData(node).list.slice(&state.ast)) |child| {
42 try state.formatNode(child);
43 }
44 },
45 .fn_decl => {
46 const decl = state.ast.fullFnDecl(node);
47
48 if (decl.public) {
49 try state.writer.writeAll("pub ");
50 }
51 try state.writer.writeAll("fn ");
52 try state.rewriteIdentifier(decl.id);
53 try state.writer.writeAll("(");
54 try state.writer.writeAll(")");
55
56 if (decl.return_type) |return_type| {
57 try state.writer.writeAll(" -> ");
58 try state.formatNode(return_type);
59 }
60
61 try state.writer.writeAll(" ");
62
63 try state.formatNode(decl.body);
64
65 try state.writer.writeAll("\n");
66 },
67 else => |unhandled_tag| std.debug.panic("TODO: unhandled tag .{s}", .{@tagName(unhandled_tag)}),
68 }
69}
70
71fn rewriteIdentifier(state: Format, id: []const u8) !void {
72 try state.writer.writeAll(id);
73}
74
75const Format = @This();
76
77const std = @import("std");
78const mem = std.mem;
79const assert = std.debug.assert;
80const AnyWriter = std.io.AnyWriter;
81
82const core = @import("core");
83const Ast = core.Ast;
84const NodeIndex = Ast.NodeIndex;