1//! A flat array is not very useful for debugging, so this structure implements
2//! a debug formatter for Ast.
3ast: Ast,
4
5const State = struct {
6 ast: *const Ast,
7 indent: usize = 0,
8 node_tags: []const Node.Tag,
9 node_data: []const Node.Data,
10 node_tokens: []const TokenIndex,
11 tty: std.io.tty.Config,
12
13 fn newline(state: State, writer: anytype) !void {
14 try writer.writeByte('\n');
15 if (state.indent > 0)
16 try writer.writeByteNTimes(' ', state.indent * 2);
17 }
18};
19
20pub fn format(
21 dump: Dump,
22 comptime unused_fmt: []const u8,
23 _: std.fmt.FormatOptions,
24 writer: anytype,
25) !void {
26 comptime assert(unused_fmt.len == 0);
27 const ast = dump.ast;
28 var state = State{
29 .ast = &ast,
30 .node_tags = ast.nodes.items(.tag),
31 .node_data = ast.nodes.items(.data),
32 .node_tokens = ast.nodes.items(.main_token),
33 .tty = core.detectTTYFromWriter(writer),
34 };
35 try dumpNode(writer, &state, 0);
36 try writer.writeAll("\n");
37 assert(state.indent == 0);
38}
39
40fn dumpNode(writer: anytype, state: *State, node: NodeIndex) @TypeOf(writer).Error!void {
41 const tag = state.node_tags[node];
42 const data = state.node_data[node];
43 const data_tag = Node.data_tags[@intFromEnum(tag)];
44 const main_token = state.node_tokens[node];
45 const tty = state.tty;
46
47 try state.tty.setColor(writer, .bright_black);
48 try writer.print("{d}", .{node});
49 try state.tty.setColor(writer, .reset);
50 try writer.writeAll(" ");
51
52 try tty.setColor(writer, .cyan);
53 try writer.print("{s}", .{@tagName(tag)});
54 try tty.setColor(writer, .reset);
55
56 switch (tag) {
57 .builtin_call => {
58 try tty.setColor(writer, .red);
59 try writer.print(" {s}", .{state.ast.tokenSlice(main_token)});
60 try tty.setColor(writer, .reset);
61 },
62 else => {},
63 }
64
65 switch (data_tag) {
66 .none => {
67 switch (tag) {
68 .id => try tty.setColor(writer, .red),
69 .string => try tty.setColor(writer, .green),
70 .number_int, .number_float => try tty.setColor(writer, .yellow),
71 else => {},
72 }
73 switch (tag) {
74 .id,
75 .string,
76 .number_int,
77 .number_float,
78 => try writer.print(" {s}", .{state.ast.tokenSlice(main_token)}),
79
80 else => unreachable,
81 }
82 try tty.setColor(writer, .reset);
83 },
84 .function => switch (tag) {
85 else => unreachable,
86 inline .fn_decl, .fn_expr => |comptime_tag| {
87 const fn_decl = if (comptime_tag == .fn_decl)
88 state.ast.fullFnDecl(node)
89 else
90 state.ast.fullFnExpr(node);
91 if (comptime_tag == .fn_decl) {
92 try writer.writeAll(" ");
93 if (fn_decl.public) {
94 try writer.writeAll("[pub] ");
95 }
96 try tty.setColor(writer, .magenta);
97 try writer.writeAll(fn_decl.id);
98 try tty.setColor(writer, .reset);
99 }
100 try writer.writeAll(" {");
101 {
102 state.indent += 1;
103 defer state.indent -= 1;
104 try state.newline(writer);
105 try writer.writeAll("params:");
106 try dumpNodeList(writer, state, fn_decl.params, '[', ']');
107 if (fn_decl.return_type) |return_type| {
108 try state.newline(writer);
109 try writer.writeAll("return_type: ");
110 try dumpNode(writer, state, return_type);
111 }
112 try state.newline(writer);
113 try writer.writeAll("body: ");
114 try dumpNode(writer, state, fn_decl.body);
115 }
116 try state.newline(writer);
117 try writer.writeAll("}");
118 },
119 },
120 .list => {
121 const list = data.list.slice(state.ast);
122 switch (tag) {
123 .root,
124 .block_void,
125 .block_implicit_return,
126 .struct_decl,
127 => try dumpNodeList(writer, state, list, '{', '}'),
128 .builtin_call,
129 .tuple_init,
130 .tuple_type,
131 => try dumpNodeList(writer, state, list, '(', ')'),
132 .array_init,
133 => try dumpNodeList(writer, state, list, '[', ']'),
134 else => unreachable,
135 }
136 },
137 .var_decl => {
138 try writer.writeAll(" ");
139 const var_decl = state.ast.fullVarDecl(node);
140 if (var_decl.public) {
141 try writer.writeAll("[pub] ");
142 }
143 try writer.writeAll(switch (var_decl.is_const) {
144 true => "constant",
145 false => "mutable",
146 });
147 try tty.setColor(writer, .magenta);
148 try writer.print(" {s}", .{var_decl.id});
149 try tty.setColor(writer, .reset);
150 var indent = false;
151 if (var_decl.type) |type_node| {
152 try writer.writeAll(" {");
153 state.indent += 1;
154 indent = true;
155 try state.newline(writer);
156 try writer.writeAll("type: ");
157 try dumpNode(writer, state, type_node);
158 try writer.writeAll(",");
159 try state.newline(writer);
160 try writer.writeAll("init: ");
161 } else {
162 try writer.writeAll(" = ");
163 }
164 try dumpNode(writer, state, var_decl.init);
165 if (indent) {
166 state.indent -= 1;
167 }
168 },
169 .param_decl => {
170 try writer.writeAll(" ");
171 const param_decl = state.ast.fullParamDecl(node);
172 try tty.setColor(writer, .magenta);
173 try writer.writeAll(param_decl.id);
174 try tty.setColor(writer, .reset);
175 if (param_decl.type) |ty| {
176 try writer.writeAll(": ");
177 try dumpNode(writer, state, ty);
178 }
179 },
180 .bin => {
181 try writer.writeAll(" (");
182 {
183 state.indent += 1;
184 defer state.indent -= 1;
185 try state.newline(writer);
186 try dumpNode(writer, state, data.bin.lhs);
187 try state.newline(writer);
188 try dumpNode(writer, state, data.bin.rhs);
189 }
190 try state.newline(writer);
191 try writer.writeAll(")");
192 },
193 .test_decl => {
194 try writer.writeAll(" (");
195 if (data.test_decl.doc_test_name.unwrap()) |doc_test_name| {
196 state.indent += 1;
197 defer state.indent -= 1;
198 try state.newline(writer);
199 try dumpNode(writer, state, doc_test_name);
200 try state.newline(writer);
201 }
202 try dumpNode(writer, state, data.bin.rhs);
203 try state.newline(writer);
204 try writer.writeAll(")");
205 },
206 .un => {
207 try writer.writeAll(" (");
208 {
209 state.indent += 1;
210 defer state.indent -= 1;
211 try state.newline(writer);
212 try dumpNode(writer, state, data.un.expr);
213 }
214 try state.newline(writer);
215 try writer.writeAll(")");
216 },
217 .label => {
218 const label_text = state.ast.tokenSlice(main_token);
219 try writer.print(" {s}:", .{label_text});
220
221 try writer.writeAll(" (");
222 {
223 state.indent += 1;
224 defer state.indent -= 1;
225 try state.newline(writer);
226 try dumpNode(writer, state, data.label.expr);
227 }
228 try state.newline(writer);
229 try writer.writeAll(")");
230 },
231 .field => {
232 try writer.writeAll(" (");
233 {
234 state.indent += 1;
235 defer state.indent -= 1;
236 try state.newline(writer);
237 try dumpNode(writer, state, data.field.base);
238 }
239 try state.newline(writer);
240 try writer.writeAll(")");
241 },
242 .@"return" => {
243 if (data.@"return".expr.unwrap()) |expr| {
244 try writer.writeAll(" (");
245 try dumpNode(writer, state, expr);
246 try writer.writeAll(")");
247 }
248 },
249 .@"break" => {
250 const label = data.@"break".label;
251 const value = data.@"break".value;
252 if (label != .none or value != .none) {
253 // try writer.writeAll("(");
254 // try writer.writeAll(")");
255 if (label.unwrap()) |l| {
256 const label_text = state.ast.tokenSlice(l);
257 try writer.print(" :{s}", .{label_text});
258 }
259 if (value.unwrap()) |expr| {
260 try writer.writeAll(" (");
261 try dumpNode(writer, state, expr);
262 try writer.writeAll(")");
263 }
264 }
265 },
266 .call => {
267 const base = data.call.base;
268 const extra = state.ast.extraData(data.call.extra, Call);
269 const arg_list = extra.args.slice(state.ast);
270
271 try writer.writeAll("(");
272 try writer.writeAll("base: ");
273 try dumpNode(writer, state, base);
274 try writer.writeAll(", ");
275 try writer.writeAll("arg:");
276 try dumpNodeList(writer, state, arg_list, '[', ']');
277 try writer.writeAll(")");
278 },
279 .struct_init => {
280 const base = data.struct_init.base;
281 const extra = state.ast.extraData(data.struct_init.extra, Ast.SubList);
282 const fields = extra.slice(state.ast);
283
284 try writer.writeAll("(");
285 try writer.writeAll("base: ");
286 if (base.unwrap()) |b| {
287 try dumpNode(writer, state, b);
288 } else {
289 try tty.setColor(writer, .blue);
290 try writer.writeAll("<implicit>");
291 try tty.setColor(writer, .reset);
292 }
293 try writer.writeAll(", ");
294 try writer.writeAll("fields:");
295 try dumpNodeList(writer, state, fields, '[', ']');
296 try writer.writeAll(")");
297 },
298 .@"if" => {
299 const branch = data.@"if";
300 const if_data = state.ast.extraData(branch.extra, Ast.If);
301
302 try writer.writeAll(" (");
303 try dumpNode(writer, state, branch.cond);
304 try writer.writeAll(") {");
305 {
306 state.indent += 1;
307 defer state.indent -= 1;
308 try state.newline(writer);
309 try writer.writeAll("yes: ");
310 try dumpNode(writer, state, if_data.yes);
311 try state.newline(writer);
312 if (if_data.no.unwrap()) |no| {
313 try writer.writeAll("no: ");
314 try dumpNode(writer, state, no);
315 } else {
316 try writer.writeAll("no: [none]");
317 }
318 }
319 try state.newline(writer);
320 try writer.writeAll("}");
321 },
322 }
323}
324
325fn dumpNodeList(writer: anytype, state: *State, list: []const NodeIndex, l: u8, r: u8) !void {
326 try writer.print(" {c}", .{l});
327 {
328 state.indent += 1;
329 defer state.indent -= 1;
330
331 for (list) |node| {
332 try state.newline(writer);
333 try dumpNode(writer, state, node);
334 }
335 }
336 if (list.len > 0)
337 try state.newline(writer);
338 try writer.print("{c}", .{r});
339}
340
341const Dump = @This();
342
343const std = @import("std");
344const assert = std.debug.assert;
345
346const core = @import("mod.zig");
347const Ast = core.Ast;
348const NodeIndex = Ast.NodeIndex;
349const Node = Ast.Node;
350const TokenIndex = Ast.TokenIndex;
351const Call = Ast.Call;