1const std = @import("std");
2const Allocator = std.mem.Allocator;
3
4pub const Node = extern union {
5 /// If the tag value is less than Tag.no_payload_count, then no pointer
6 /// dereference is needed.
7 tag_if_small_enough: usize,
8 ptr_otherwise: *Payload,
9
10 pub const Tag = enum {
11 /// Declarations add themselves to the correct scopes and should not be emitted as this tag.
12 declaration,
13 null_literal,
14 undefined_literal,
15 /// opaque {}
16 opaque_literal,
17 true_literal,
18 false_literal,
19 empty_block,
20 return_void,
21 zero_literal,
22 one_literal,
23 @"unreachable",
24 void_type,
25 noreturn_type,
26 @"anytype",
27 @"continue",
28 @"break",
29 // After this, the tag requires a payload.
30
31 integer_literal,
32 float_literal,
33 string_literal,
34 char_literal,
35 enum_literal,
36 /// "string"[0..end]
37 string_slice,
38 identifier,
39 @"if",
40 /// if (!operand) break;
41 if_not_break,
42 @"while",
43 /// while (true) operand
44 while_true,
45 @"switch",
46 /// else => operand,
47 switch_else,
48 /// items => body,
49 switch_prong,
50 break_val,
51 @"return",
52 field_access,
53 field_builtin,
54 array_access,
55 call,
56 var_decl,
57 /// const name = struct { init }
58 wrapped_local,
59 /// var name = init.*
60 mut_str,
61 func,
62 warning,
63 @"struct",
64 @"union",
65 @"opaque",
66 @"comptime",
67 @"defer",
68 array_init,
69 tuple,
70 container_init,
71 container_init_dot,
72 /// _ = operand;
73 discard,
74
75 // a + b
76 add,
77 // a = b
78 add_assign,
79 // c = (a = b)
80 add_wrap,
81 add_wrap_assign,
82 sub,
83 sub_assign,
84 sub_wrap,
85 sub_wrap_assign,
86 mul,
87 mul_assign,
88 mul_wrap,
89 mul_wrap_assign,
90 div,
91 div_assign,
92 shl,
93 shl_assign,
94 shr,
95 shr_assign,
96 mod,
97 mod_assign,
98 @"and",
99 @"or",
100 less_than,
101 less_than_equal,
102 greater_than,
103 greater_than_equal,
104 equal,
105 not_equal,
106 bit_and,
107 bit_and_assign,
108 bit_or,
109 bit_or_assign,
110 bit_xor,
111 bit_xor_assign,
112 array_cat,
113 ellipsis3,
114 assign,
115
116 /// @intCast(operand)
117 int_cast,
118 /// @constCast(operand)
119 const_cast,
120 /// @volatileCast(operand)
121 volatile_cast,
122 /// @divTrunc(lhs, rhs)
123 div_trunc,
124 /// @intFromBool(operand)
125 int_from_bool,
126 /// @as(lhs, rhs)
127 as,
128 /// @truncate(operand)
129 truncate,
130 /// @bitCast(operand)
131 bit_cast,
132 /// @floatCast(operand)
133 float_cast,
134 /// @intFromFloat(operand)
135 int_from_float,
136 /// @floatFromInt(operand)
137 float_from_int,
138 /// @ptrFromInt(operand)
139 ptr_from_int,
140 /// @intFromPtr(operand)
141 int_from_ptr,
142 /// @alignCast(operand)
143 align_cast,
144 /// @ptrCast(operand)
145 ptr_cast,
146 /// @divExact(lhs, rhs)
147 div_exact,
148 /// @offsetOf(lhs, rhs)
149 offset_of,
150 /// @splat(operand)
151 vector_zero_init,
152 /// @shuffle(type, a, b, mask)
153 shuffle,
154 /// @extern(ty, .{ .name = n })
155 builtin_extern,
156
157 /// @byteSwap(operand)
158 byte_swap,
159 /// @ceil(operand)
160 ceil,
161 /// @cos(operand)
162 cos,
163 /// @sin(operand)
164 sin,
165 /// @exp(operand)
166 exp,
167 /// @exp2(operand)
168 exp2,
169 /// @exp10(operand)
170 exp10,
171 /// @abs(operand)
172 abs,
173 /// @log(operand)
174 log,
175 /// @log2(operand)
176 log2,
177 /// @log10(operand)
178 log10,
179 /// @round(operand)
180 round,
181 /// @sqrt(operand)
182 sqrt,
183 /// @trunc(operand)
184 trunc,
185 /// @floor(operand)
186 floor,
187
188 /// __helpers.<name>(argshelper_call)
189 helper_call,
190 /// __helpers.<name>
191 helper_ref,
192
193 asm_simple,
194
195 negate,
196 negate_wrap,
197 bit_not,
198 not,
199 address_of,
200 /// .?
201 unwrap,
202 /// .*
203 deref,
204
205 block,
206 /// { operand }
207 block_single,
208
209 sizeof,
210 alignof,
211 typeof,
212 typeinfo,
213 type,
214
215 optional_type,
216 c_pointer,
217 single_pointer,
218 array_type,
219 null_sentinel_array_type,
220
221 /// @Vector(lhs, rhs)
222 vector,
223 /// @import("std").mem.zeroes(operand)
224 std_mem_zeroes,
225 /// @import("std").mem.zeroInit(lhs, rhs)
226 std_mem_zeroinit,
227 // pub const name = @compileError(msg);
228 fail_decl,
229 // var actual = mangled;
230 arg_redecl,
231 /// pub const alias = actual;
232 alias,
233 /// const name = init;
234 var_simple,
235 /// pub const name = init;
236 pub_var_simple,
237 /// pub? const name (: type)? = value
238 enum_constant,
239
240 /// pub inline fn name(params) return_type body
241 pub_inline_fn,
242
243 /// array_type{}
244 empty_array,
245 /// @as([count]type, @splat(val))
246 array_filler,
247
248 /// comptime { if (!(lhs)) @compileError(rhs); }
249 static_assert,
250
251 /// __root.<name>
252 root_ref,
253
254 pub const last_no_payload_tag = Tag.@"break";
255 pub const no_payload_count = @backingInt(last_no_payload_tag) + 1;
256
257 pub fn Type(comptime t: Tag) type {
258 return switch (t) {
259 .declaration,
260 .null_literal,
261 .undefined_literal,
262 .opaque_literal,
263 .true_literal,
264 .false_literal,
265 .empty_block,
266 .return_void,
267 .zero_literal,
268 .one_literal,
269 .void_type,
270 .noreturn_type,
271 .@"anytype",
272 .@"continue",
273 .@"break",
274 .@"unreachable",
275 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
276
277 .std_mem_zeroes,
278 .@"return",
279 .@"comptime",
280 .@"defer",
281 .asm_simple,
282 .negate,
283 .negate_wrap,
284 .bit_not,
285 .not,
286 .optional_type,
287 .address_of,
288 .unwrap,
289 .deref,
290 .int_from_ptr,
291 .empty_array,
292 .while_true,
293 .if_not_break,
294 .switch_else,
295 .block_single,
296 .int_from_bool,
297 .sizeof,
298 .alignof,
299 .typeof,
300 .typeinfo,
301 .align_cast,
302 .truncate,
303 .bit_cast,
304 .float_cast,
305 .int_from_float,
306 .float_from_int,
307 .ptr_from_int,
308 .ptr_cast,
309 .int_cast,
310 .const_cast,
311 .volatile_cast,
312 .vector_zero_init,
313 .byte_swap,
314 .ceil,
315 .cos,
316 .sin,
317 .exp,
318 .exp2,
319 .exp10,
320 .abs,
321 .log,
322 .log2,
323 .log10,
324 .round,
325 .sqrt,
326 .trunc,
327 .floor,
328 => Payload.UnOp,
329
330 .add,
331 .add_assign,
332 .add_wrap,
333 .add_wrap_assign,
334 .sub,
335 .sub_assign,
336 .sub_wrap,
337 .sub_wrap_assign,
338 .mul,
339 .mul_assign,
340 .mul_wrap,
341 .mul_wrap_assign,
342 .div,
343 .div_assign,
344 .shl,
345 .shl_assign,
346 .shr,
347 .shr_assign,
348 .mod,
349 .mod_assign,
350 .@"and",
351 .@"or",
352 .less_than,
353 .less_than_equal,
354 .greater_than,
355 .greater_than_equal,
356 .equal,
357 .not_equal,
358 .bit_and,
359 .bit_and_assign,
360 .bit_or,
361 .bit_or_assign,
362 .bit_xor,
363 .bit_xor_assign,
364 .div_trunc,
365 .as,
366 .array_cat,
367 .ellipsis3,
368 .assign,
369 .array_access,
370 .std_mem_zeroinit,
371 .vector,
372 .div_exact,
373 .offset_of,
374 .static_assert,
375 .field_builtin,
376 => Payload.BinOp,
377
378 .integer_literal,
379 .float_literal,
380 .string_literal,
381 .char_literal,
382 .enum_literal,
383 .identifier,
384 .warning,
385 .type,
386 => Payload.Value,
387 .discard => Payload.Discard,
388 .@"if" => Payload.If,
389 .@"while" => Payload.While,
390 .@"switch", .array_init, .switch_prong => Payload.Switch,
391 .break_val => Payload.BreakVal,
392 .call => Payload.Call,
393 .var_decl => Payload.VarDecl,
394 .func => Payload.Func,
395 .@"struct", .@"union", .@"opaque" => Payload.Container,
396 .tuple => Payload.TupleInit,
397 .container_init => Payload.ContainerInit,
398 .container_init_dot => Payload.ContainerInitDot,
399 .block => Payload.Block,
400 .c_pointer, .single_pointer => Payload.Pointer,
401 .array_type, .null_sentinel_array_type => Payload.Array,
402 .arg_redecl, .alias => Payload.ArgRedecl,
403 .fail_decl => Payload.FailDecl,
404 .var_simple, .pub_var_simple, .wrapped_local, .mut_str => Payload.SimpleVarDecl,
405 .enum_constant => Payload.EnumConstant,
406 .array_filler => Payload.ArrayFiller,
407 .pub_inline_fn => Payload.PubInlineFn,
408 .field_access => Payload.FieldAccess,
409 .string_slice => Payload.StringSlice,
410 .shuffle => Payload.Shuffle,
411 .builtin_extern => Payload.Extern,
412 .helper_call => Payload.HelperCall,
413 .helper_ref => Payload.HelperRef,
414 .root_ref => Payload.RootRef,
415 };
416 }
417
418 pub fn init(comptime t: Tag) Node {
419 comptime std.debug.assert(@backingInt(t) < Tag.no_payload_count);
420 return .{ .tag_if_small_enough = @backingInt(t) };
421 }
422
423 pub fn create(comptime t: Tag, ally: Allocator, data: Data(t)) error{OutOfMemory}!Node {
424 const ptr = try ally.create(t.Type());
425 ptr.* = .{
426 .base = .{ .tag = t },
427 .data = data,
428 };
429 return Node{ .ptr_otherwise = &ptr.base };
430 }
431
432 pub fn Data(comptime t: Tag) type {
433 return std.meta.fieldInfo(t.Type(), .data).type;
434 }
435 };
436
437 pub fn tag(self: Node) Tag {
438 if (self.tag_if_small_enough < Tag.no_payload_count) {
439 return @fromBackingInt(@intCast(@as(std.meta.Tag(Tag), @intCast(self.tag_if_small_enough))));
440 } else {
441 return self.ptr_otherwise.tag;
442 }
443 }
444
445 pub fn castTag(self: Node, comptime t: Tag) ?*t.Type() {
446 if (self.tag_if_small_enough < Tag.no_payload_count)
447 return null;
448
449 if (self.ptr_otherwise.tag == t)
450 return @alignCast(@fieldParentPtr("base", self.ptr_otherwise));
451
452 return null;
453 }
454
455 pub fn initPayload(payload: *Payload) Node {
456 std.debug.assert(@backingInt(payload.tag) >= Tag.no_payload_count);
457 return .{ .ptr_otherwise = payload };
458 }
459
460 pub fn isNoreturn(node: Node) bool {
461 return switch (node.tag()) {
462 .block => {
463 const block_node = node.castTag(.block).?;
464 if (block_node.data.stmts.len == 0) return false;
465
466 const last = block_node.data.stmts[block_node.data.stmts.len - 1];
467 return last.isNoreturn();
468 },
469 .@"switch" => {
470 const switch_node = node.castTag(.@"switch").?;
471
472 for (switch_node.data.cases) |case| {
473 const body = if (case.castTag(.switch_else)) |some|
474 some.data
475 else if (case.castTag(.switch_prong)) |some|
476 some.data.cond
477 else
478 unreachable;
479
480 if (!body.isNoreturn()) return false;
481 }
482 return true;
483 },
484 .@"return", .return_void => true,
485 .@"break" => true,
486 .@"continue" => true,
487 .@"unreachable" => true,
488 else => false,
489 };
490 }
491
492 pub fn isBoolRes(res: Node) bool {
493 switch (res.tag()) {
494 .@"or",
495 .@"and",
496 .equal,
497 .not_equal,
498 .less_than,
499 .less_than_equal,
500 .greater_than,
501 .greater_than_equal,
502 .not,
503 .false_literal,
504 .true_literal,
505 => return true,
506 else => return false,
507 }
508 }
509};
510
511pub const Payload = struct {
512 tag: Node.Tag,
513
514 pub const Value = struct {
515 base: Payload,
516 data: []const u8,
517 };
518
519 pub const UnOp = struct {
520 base: Payload,
521 data: Node,
522 };
523
524 pub const BinOp = struct {
525 base: Payload,
526 data: struct {
527 lhs: Node,
528 rhs: Node,
529 },
530 };
531
532 pub const Discard = struct {
533 base: Payload,
534 data: struct {
535 should_skip: bool,
536 value: Node,
537 },
538 };
539
540 pub const If = struct {
541 base: Payload,
542 data: struct {
543 cond: Node,
544 then: Node,
545 @"else": ?Node,
546 },
547 };
548
549 pub const While = struct {
550 base: Payload,
551 data: struct {
552 cond: Node,
553 body: Node,
554 cont_expr: ?Node,
555 },
556 };
557
558 pub const Switch = struct {
559 base: Payload,
560 data: struct {
561 cond: Node,
562 cases: []Node,
563 },
564 };
565
566 pub const BreakVal = struct {
567 base: Payload,
568 data: struct {
569 label: ?[]const u8,
570 val: Node,
571 },
572 };
573
574 pub const Call = struct {
575 base: Payload,
576 data: struct {
577 lhs: Node,
578 args: []Node,
579 },
580 };
581
582 pub const VarDecl = struct {
583 base: Payload,
584 data: struct {
585 is_pub: bool,
586 is_const: bool,
587 is_extern: bool,
588 is_export: bool,
589 is_threadlocal: bool,
590 alignment: ?c_uint,
591 linksection_string: ?[]const u8,
592 name: []const u8,
593 type: Node,
594 init: ?Node,
595 },
596 };
597
598 pub const Func = struct {
599 base: Payload,
600 data: struct {
601 is_pub: bool,
602 is_extern: bool,
603 is_export: bool,
604 is_inline: bool,
605 is_var_args: bool,
606 name: ?[]const u8,
607 linksection_string: ?[]const u8,
608 explicit_callconv: ?CallingConvention,
609 params: []Param,
610 return_type: Node,
611 body: ?Node,
612 alignment: ?c_uint,
613 },
614
615 pub const CallingConvention = enum {
616 c,
617 x86_64_sysv,
618 x86_64_win,
619 x86_stdcall,
620 x86_fastcall,
621 x86_thiscall,
622 x86_vectorcall,
623 x86_regcall,
624 aarch64_vfabi,
625 aarch64_sve_pcs,
626 arm_aapcs,
627 arm_aapcs_vfp,
628 m68k_rtd,
629 riscv_vector,
630 };
631 };
632
633 pub const Param = struct {
634 is_noalias: bool,
635 name: ?[]const u8,
636 type: Node,
637 };
638
639 pub const Container = struct {
640 base: Payload,
641 data: struct {
642 layout: enum { @"packed", @"extern", none },
643 fields: []Field,
644 decls: []Node,
645 },
646
647 pub const Field = struct {
648 name: []const u8,
649 type: Node,
650 alignment: ?c_uint,
651 default_value: ?Node,
652 };
653 };
654
655 pub const TupleInit = struct {
656 base: Payload,
657 data: []Node,
658 };
659
660 pub const ContainerInit = struct {
661 base: Payload,
662 data: struct {
663 lhs: Node,
664 inits: []Initializer,
665 },
666
667 pub const Initializer = struct {
668 name: []const u8,
669 value: Node,
670 };
671 };
672
673 pub const ContainerInitDot = struct {
674 base: Payload,
675 data: []Initializer,
676
677 pub const Initializer = struct {
678 name: []const u8,
679 value: Node,
680 };
681 };
682
683 pub const Block = struct {
684 base: Payload,
685 data: struct {
686 label: ?[]const u8,
687 stmts: []Node,
688 },
689 };
690
691 pub const Array = struct {
692 base: Payload,
693 data: ArrayTypeInfo,
694
695 pub const ArrayTypeInfo = struct {
696 elem_type: Node,
697 len: u64,
698 };
699 };
700
701 pub const Pointer = struct {
702 base: Payload,
703 data: struct {
704 elem_type: Node,
705 is_const: bool,
706 is_volatile: bool,
707 is_allowzero: bool,
708 },
709 };
710
711 pub const ArgRedecl = struct {
712 base: Payload,
713 data: struct {
714 actual: []const u8,
715 mangled: []const u8,
716 },
717 };
718
719 pub const FailDecl = struct {
720 base: Payload,
721 data: struct {
722 actual: []const u8,
723 mangled: []const u8,
724 local: bool,
725 },
726 };
727
728 pub const SimpleVarDecl = struct {
729 base: Payload,
730 data: struct {
731 name: []const u8,
732 init: Node,
733 },
734 };
735
736 pub const EnumConstant = struct {
737 base: Payload,
738 data: struct {
739 name: []const u8,
740 is_public: bool,
741 type: ?Node,
742 value: Node,
743 },
744 };
745
746 pub const ArrayFiller = struct {
747 base: Payload,
748 data: struct {
749 type: Node,
750 filler: Node,
751 count: u64,
752 },
753 };
754
755 pub const PubInlineFn = struct {
756 base: Payload,
757 data: struct {
758 name: []const u8,
759 params: []Param,
760 return_type: Node,
761 body: Node,
762 },
763 };
764
765 pub const FieldAccess = struct {
766 base: Payload,
767 data: struct {
768 lhs: Node,
769 field_name: []const u8,
770 },
771 };
772
773 pub const StringSlice = struct {
774 base: Payload,
775 data: struct {
776 string: Node,
777 end: u64,
778 },
779 };
780
781 pub const Shuffle = struct {
782 base: Payload,
783 data: struct {
784 element_type: Node,
785 a: Node,
786 b: Node,
787 mask_vector: Node,
788 },
789 };
790
791 pub const Extern = struct {
792 base: Payload,
793 data: struct {
794 type: Node,
795 name: Node,
796 },
797 };
798
799 pub const HelperCall = struct {
800 base: Payload,
801 data: struct {
802 name: []const u8,
803 args: []const Node,
804 },
805 };
806
807 pub const HelperRef = struct {
808 base: Payload,
809 data: []const u8,
810 };
811
812 pub const RootRef = struct {
813 base: Payload,
814 data: []const u8,
815 };
816};
817
818/// Converts the nodes into a Zig Ast.
819/// Caller must free the source slice.
820pub fn render(gpa: Allocator, nodes: []const Node) !std.zig.Ast {
821 var ctx: Context = .{
822 .gpa = gpa,
823 };
824 defer ctx.buf.deinit(gpa);
825 defer ctx.nodes.deinit(gpa);
826 defer ctx.extra_data.deinit(gpa);
827 defer ctx.tokens.deinit(gpa);
828
829 // Estimate that each top level node has 10 child nodes.
830 const estimated_node_count = nodes.len * 10 + 1; // +1 for the .root node
831 try ctx.nodes.ensureTotalCapacity(gpa, estimated_node_count);
832 // Estimate that each each node has 2 tokens.
833 const estimated_tokens_count = estimated_node_count * 2;
834 try ctx.tokens.ensureTotalCapacity(gpa, estimated_tokens_count);
835 // Estimate that each each token is 3 bytes long.
836 const estimated_buf_len = estimated_tokens_count * 3;
837 try ctx.buf.ensureTotalCapacity(gpa, estimated_buf_len);
838
839 ctx.nodes.appendAssumeCapacity(.{
840 .tag = .root,
841 .main_token = 0,
842 .data = undefined,
843 });
844
845 const root_members = blk: {
846 var result: std.ArrayList(NodeIndex) = .empty;
847 defer result.deinit(gpa);
848
849 for (nodes) |node| {
850 const res = (try renderNodeOpt(&ctx, node)) orelse continue;
851 try result.append(gpa, res);
852 }
853 break :blk try ctx.listToSpan(result.items);
854 };
855
856 ctx.nodes.items(.data)[0] = .{ .extra_range = .{
857 .start = root_members.start,
858 .end = root_members.end,
859 } };
860
861 try ctx.tokens.append(gpa, .{
862 .tag = .eof,
863 .start = @as(u32, @intCast(ctx.buf.items.len)),
864 });
865
866 try ctx.buf.shrinkToLenSentinel(gpa);
867 try ctx.extra_data.shrinkToLen(gpa);
868
869 return .{
870 .source = ctx.buf.toOwnedSliceSentinelAssert(0),
871 .tokens = ctx.tokens.toOwnedSlice(),
872 .nodes = ctx.nodes.toOwnedSlice(),
873 .extra_data = ctx.extra_data.toOwnedSliceAssert(),
874 .errors = &.{},
875 .mode = .zig,
876 };
877}
878
879const NodeIndex = std.zig.Ast.Node.Index;
880const NodeSubRange = std.zig.Ast.Node.SubRange;
881const TokenIndex = std.zig.Ast.TokenIndex;
882const TokenTag = std.zig.Token.Tag;
883
884const Context = struct {
885 gpa: Allocator,
886 buf: std.ArrayList(u8) = .empty,
887 nodes: std.zig.Ast.NodeList = .empty,
888 extra_data: std.ArrayList(u32) = .empty,
889 tokens: std.zig.Ast.TokenList = .empty,
890
891 fn addTokenFmt(c: *Context, tag: TokenTag, comptime format: []const u8, args: anytype) Allocator.Error!TokenIndex {
892 const start_index = c.buf.items.len;
893 try c.buf.print(c.gpa, format ++ " ", args);
894
895 try c.tokens.append(c.gpa, .{
896 .tag = tag,
897 .start = @intCast(start_index),
898 });
899
900 return @intCast(c.tokens.len - 1);
901 }
902
903 fn addToken(c: *Context, tag: TokenTag, bytes: []const u8) Allocator.Error!TokenIndex {
904 return c.addTokenFmt(tag, "{s}", .{bytes});
905 }
906
907 fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {
908 if (std.zig.primitives.isPrimitive(bytes))
909 return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes});
910 return c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtId(bytes)});
911 }
912
913 fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange {
914 try c.extra_data.appendSlice(c.gpa, @ptrCast(list));
915 return .{
916 .start = @fromBackingInt(@intCast(c.extra_data.items.len - list.len)),
917 .end = @fromBackingInt(@intCast(c.extra_data.items.len)),
918 };
919 }
920
921 fn addNode(c: *Context, elem: std.zig.Ast.Node) Allocator.Error!NodeIndex {
922 const result: NodeIndex = @fromBackingInt(@intCast(c.nodes.len));
923 try c.nodes.append(c.gpa, elem);
924 return result;
925 }
926
927 fn addExtra(c: *Context, extra: anytype) Allocator.Error!std.zig.Ast.ExtraIndex {
928 const info = @typeInfo(@TypeOf(extra)).@"struct";
929 try c.extra_data.ensureUnusedCapacity(c.gpa, info.field_names.len);
930 const result: std.zig.Ast.ExtraIndex = @fromBackingInt(@intCast(c.extra_data.items.len));
931 inline for (info.field_names, info.field_types) |field_name, field_type| {
932 const data: u32 = switch (field_type) {
933 NodeIndex,
934 std.zig.Ast.Node.OptionalIndex,
935 std.zig.Ast.OptionalTokenIndex,
936 std.zig.Ast.ExtraIndex,
937 => @backingInt(@field(extra, field_name)),
938 TokenIndex,
939 => @field(extra, field_name),
940 else => @compileError("unexpected field type"),
941 };
942 c.extra_data.appendAssumeCapacity(data);
943 }
944 return result;
945 }
946};
947
948fn renderNodeOpt(c: *Context, node: Node) Allocator.Error!?NodeIndex {
949 switch (node.tag()) {
950 .warning => {
951 const payload = node.castTag(.warning).?.data;
952 try c.buf.appendSlice(c.gpa, payload);
953 try c.buf.append(c.gpa, '\n');
954 return null;
955 },
956 .discard => {
957 const payload = node.castTag(.discard).?.data;
958 if (payload.should_skip) return null;
959
960 return try renderNode(c, node);
961 },
962 else => return try renderNode(c, node),
963 }
964}
965
966fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
967 switch (node.tag()) {
968 .declaration => unreachable,
969 .warning => unreachable,
970 .discard => {
971 const payload = node.castTag(.discard).?.data;
972 std.debug.assert(!payload.should_skip);
973
974 const lhs = try c.addNode(.{
975 .tag = .identifier,
976 .main_token = try c.addToken(.identifier, "_"),
977 .data = undefined,
978 });
979 const main_token = try c.addToken(.equal, "=");
980 if (payload.value.tag() == .identifier) {
981 // Render as `_ = &foo;` to avoid tripping "pointless discard" and "local variable never mutated" errors.
982 var addr_of_pl: Payload.UnOp = .{
983 .base = .{ .tag = .address_of },
984 .data = payload.value,
985 };
986 const addr_of: Node = .{ .ptr_otherwise = &addr_of_pl.base };
987 return try c.addNode(.{
988 .tag = .assign,
989 .main_token = main_token,
990 .data = .{ .node_and_node = .{
991 lhs, try renderNode(c, addr_of),
992 } },
993 });
994 } else {
995 return try c.addNode(.{
996 .tag = .assign,
997 .main_token = main_token,
998 .data = .{ .node_and_node = .{
999 lhs, try renderNode(c, payload.value),
1000 } },
1001 });
1002 }
1003 },
1004 .std_mem_zeroes => {
1005 const payload = node.castTag(.std_mem_zeroes).?.data;
1006 const import_node = try renderStdImport(c, &.{ "mem", "zeroes" });
1007 return renderCall(c, import_node, &.{payload});
1008 },
1009 .std_mem_zeroinit => {
1010 const payload = node.castTag(.std_mem_zeroinit).?.data;
1011 const import_node = try renderStdImport(c, &.{ "mem", "zeroInit" });
1012 return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
1013 },
1014 .vector => {
1015 const payload = node.castTag(.vector).?.data;
1016 return renderBuiltinCall(c, "@Vector", &.{ payload.lhs, payload.rhs });
1017 },
1018 .call => {
1019 const payload = node.castTag(.call).?.data;
1020 const lhs = try renderNodeGrouped(c, payload.lhs);
1021 return renderCall(c, lhs, payload.args);
1022 },
1023 .null_literal => return c.addNode(.{
1024 .tag = .identifier,
1025 .main_token = try c.addToken(.identifier, "null"),
1026 .data = undefined,
1027 }),
1028 .undefined_literal => return c.addNode(.{
1029 .tag = .identifier,
1030 .main_token = try c.addToken(.identifier, "undefined"),
1031 .data = undefined,
1032 }),
1033 .true_literal => return c.addNode(.{
1034 .tag = .identifier,
1035 .main_token = try c.addToken(.identifier, "true"),
1036 .data = undefined,
1037 }),
1038 .false_literal => return c.addNode(.{
1039 .tag = .identifier,
1040 .main_token = try c.addToken(.identifier, "false"),
1041 .data = undefined,
1042 }),
1043 .zero_literal => return c.addNode(.{
1044 .tag = .number_literal,
1045 .main_token = try c.addToken(.number_literal, "0"),
1046 .data = undefined,
1047 }),
1048 .one_literal => return c.addNode(.{
1049 .tag = .number_literal,
1050 .main_token = try c.addToken(.number_literal, "1"),
1051 .data = undefined,
1052 }),
1053 .@"unreachable" => return c.addNode(.{
1054 .tag = .unreachable_literal,
1055 .main_token = try c.addToken(.keyword_unreachable, "unreachable"),
1056 .data = undefined,
1057 }),
1058 .void_type => return c.addNode(.{
1059 .tag = .identifier,
1060 .main_token = try c.addToken(.identifier, "void"),
1061 .data = undefined,
1062 }),
1063 .noreturn_type => return c.addNode(.{
1064 .tag = .identifier,
1065 .main_token = try c.addToken(.identifier, "noreturn"),
1066 .data = undefined,
1067 }),
1068 .@"continue" => return c.addNode(.{
1069 .tag = .@"continue",
1070 .main_token = try c.addToken(.keyword_continue, "continue"),
1071 .data = .{ .opt_token_and_opt_node = .{
1072 .none, .none,
1073 } },
1074 }),
1075 .return_void => return c.addNode(.{
1076 .tag = .@"return",
1077 .main_token = try c.addToken(.keyword_return, "return"),
1078 .data = .{ .opt_node = .none },
1079 }),
1080 .@"break" => return c.addNode(.{
1081 .tag = .@"break",
1082 .main_token = try c.addToken(.keyword_break, "break"),
1083 .data = .{ .opt_token_and_opt_node = .{
1084 .none, .none,
1085 } },
1086 }),
1087 .break_val => {
1088 const payload = node.castTag(.break_val).?.data;
1089 const tok = try c.addToken(.keyword_break, "break");
1090 const break_label = if (payload.label) |some| blk: {
1091 _ = try c.addToken(.colon, ":");
1092 break :blk try c.addIdentifier(some);
1093 } else 0;
1094 return c.addNode(.{
1095 .tag = .@"break",
1096 .main_token = tok,
1097 .data = .{ .opt_token_and_opt_node = .{
1098 .fromToken(break_label), (try renderNode(c, payload.val)).toOptional(),
1099 } },
1100 });
1101 },
1102 .@"return" => {
1103 const payload = node.castTag(.@"return").?.data;
1104 return c.addNode(.{
1105 .tag = .@"return",
1106 .main_token = try c.addToken(.keyword_return, "return"),
1107 .data = .{ .opt_node = (try renderNode(c, payload)).toOptional() },
1108 });
1109 },
1110 .@"comptime" => {
1111 const payload = node.castTag(.@"comptime").?.data;
1112 return c.addNode(.{
1113 .tag = .@"comptime",
1114 .main_token = try c.addToken(.keyword_comptime, "comptime"),
1115 .data = .{
1116 .node = try renderNode(c, payload),
1117 },
1118 });
1119 },
1120 .@"defer" => {
1121 const payload = node.castTag(.@"defer").?.data;
1122 return c.addNode(.{
1123 .tag = .@"defer",
1124 .main_token = try c.addToken(.keyword_defer, "defer"),
1125 .data = .{
1126 .node = try renderNode(c, payload),
1127 },
1128 });
1129 },
1130 .asm_simple => {
1131 const payload = node.castTag(.asm_simple).?.data;
1132 const asm_token = try c.addToken(.keyword_asm, "asm");
1133 _ = try c.addToken(.l_paren, "(");
1134 return c.addNode(.{
1135 .tag = .asm_simple,
1136 .main_token = asm_token,
1137 .data = .{ .node_and_token = .{
1138 try renderNode(c, payload),
1139 try c.addToken(.r_paren, ")"),
1140 } },
1141 });
1142 },
1143 .type => {
1144 const payload = node.castTag(.type).?.data;
1145 return c.addNode(.{
1146 .tag = .identifier,
1147 .main_token = try c.addToken(.identifier, payload),
1148 .data = undefined,
1149 });
1150 },
1151 .identifier => {
1152 const payload = node.castTag(.identifier).?.data;
1153 return c.addNode(.{
1154 .tag = .identifier,
1155 .main_token = try c.addIdentifier(payload),
1156 .data = undefined,
1157 });
1158 },
1159 .float_literal => {
1160 const payload = node.castTag(.float_literal).?.data;
1161 return c.addNode(.{
1162 .tag = .number_literal,
1163 .main_token = try c.addToken(.number_literal, payload),
1164 .data = undefined,
1165 });
1166 },
1167 .integer_literal => {
1168 const payload = node.castTag(.integer_literal).?.data;
1169 return c.addNode(.{
1170 .tag = .number_literal,
1171 .main_token = try c.addToken(.number_literal, payload),
1172 .data = undefined,
1173 });
1174 },
1175 .string_literal => {
1176 const payload = node.castTag(.string_literal).?.data;
1177 return c.addNode(.{
1178 .tag = .string_literal,
1179 .main_token = try c.addToken(.string_literal, payload),
1180 .data = undefined,
1181 });
1182 },
1183 .char_literal => {
1184 const payload = node.castTag(.char_literal).?.data;
1185 return c.addNode(.{
1186 .tag = .char_literal,
1187 .main_token = try c.addToken(.char_literal, payload),
1188 .data = undefined,
1189 });
1190 },
1191 .enum_literal => {
1192 const payload = node.castTag(.enum_literal).?.data;
1193 _ = try c.addToken(.period, ".");
1194 return c.addNode(.{
1195 .tag = .enum_literal,
1196 .main_token = try c.addToken(.identifier, payload),
1197 .data = undefined,
1198 });
1199 },
1200 .string_slice => {
1201 const payload = node.castTag(.string_slice).?.data;
1202
1203 const string = try renderNode(c, payload.string);
1204 const l_bracket = try c.addToken(.l_bracket, "[");
1205 const start = try c.addNode(.{
1206 .tag = .number_literal,
1207 .main_token = try c.addToken(.number_literal, "0"),
1208 .data = undefined,
1209 });
1210 _ = try c.addToken(.ellipsis2, "..");
1211 const end = try c.addNode(.{
1212 .tag = .number_literal,
1213 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{payload.end}),
1214 .data = undefined,
1215 });
1216 _ = try c.addToken(.r_bracket, "]");
1217
1218 return c.addNode(.{
1219 .tag = .slice,
1220 .main_token = l_bracket,
1221 .data = .{ .node_and_extra = .{
1222 string,
1223 try c.addExtra(std.zig.Ast.Node.Slice{
1224 .start = start,
1225 .end = end,
1226 }),
1227 } },
1228 });
1229 },
1230 .fail_decl => {
1231 const payload = node.castTag(.fail_decl).?.data;
1232 // pub const name = (if (true))? @compileError(msg);
1233 if (!payload.local) _ = try c.addToken(.keyword_pub, "pub");
1234 const const_tok = try c.addToken(.keyword_const, "const");
1235 _ = try c.addIdentifier(payload.actual);
1236 _ = try c.addToken(.equal, "=");
1237
1238 var if_tok: TokenIndex = undefined;
1239 var true_node: NodeIndex = undefined;
1240 if (payload.local) {
1241 if_tok = try c.addToken(.keyword_if, "if");
1242 _ = try c.addToken(.l_paren, "(");
1243 true_node = try c.addNode(.{
1244 .tag = .identifier,
1245 .main_token = try c.addToken(.identifier, "true"),
1246 .data = undefined,
1247 });
1248 _ = try c.addToken(.r_paren, ")");
1249 }
1250
1251 const compile_error_tok = try c.addToken(.builtin, "@compileError");
1252 _ = try c.addToken(.l_paren, "(");
1253 const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(payload.mangled)});
1254 const err_msg = try c.addNode(.{
1255 .tag = .string_literal,
1256 .main_token = err_msg_tok,
1257 .data = undefined,
1258 });
1259 _ = try c.addToken(.r_paren, ")");
1260 const compile_error = try c.addNode(.{
1261 .tag = .builtin_call_two,
1262 .main_token = compile_error_tok,
1263 .data = .{ .opt_node_and_opt_node = .{
1264 err_msg.toOptional(), .none,
1265 } },
1266 });
1267 _ = try c.addToken(.semicolon, ";");
1268
1269 return c.addNode(.{
1270 .tag = .simple_var_decl,
1271 .main_token = const_tok,
1272 .data = .{
1273 .opt_node_and_opt_node = .{
1274 .none, // Type expression
1275 if (payload.local) // Init expression
1276 (try c.addNode(.{
1277 .tag = .if_simple,
1278 .main_token = if_tok,
1279 .data = .{ .node_and_node = .{
1280 true_node, compile_error,
1281 } },
1282 })).toOptional()
1283 else
1284 compile_error.toOptional(),
1285 },
1286 },
1287 });
1288 },
1289 .pub_var_simple, .var_simple => {
1290 const payload = @as(*Payload.SimpleVarDecl, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
1291 if (node.tag() == .pub_var_simple) _ = try c.addToken(.keyword_pub, "pub");
1292 const const_tok = try c.addToken(.keyword_const, "const");
1293 _ = try c.addIdentifier(payload.name);
1294 _ = try c.addToken(.equal, "=");
1295
1296 const init = try renderNode(c, payload.init);
1297 _ = try c.addToken(.semicolon, ";");
1298
1299 return c.addNode(.{
1300 .tag = .simple_var_decl,
1301 .main_token = const_tok,
1302 .data = .{
1303 .opt_node_and_opt_node = .{
1304 .none, // Type expression
1305 init.toOptional(), // Init expression
1306 },
1307 },
1308 });
1309 },
1310 .wrapped_local => {
1311 const payload = node.castTag(.wrapped_local).?.data;
1312
1313 const const_tok = try c.addToken(.keyword_const, "const");
1314 _ = try c.addIdentifier(payload.name);
1315 _ = try c.addToken(.equal, "=");
1316
1317 const kind_tok = try c.addToken(.keyword_struct, "struct");
1318 _ = try c.addToken(.l_brace, "{");
1319
1320 const container_def = try c.addNode(.{
1321 .tag = .container_decl_two_trailing,
1322 .main_token = kind_tok,
1323 .data = .{ .opt_node_and_opt_node = .{
1324 (try renderNode(c, payload.init)).toOptional(), .none,
1325 } },
1326 });
1327 _ = try c.addToken(.r_brace, "}");
1328 _ = try c.addToken(.semicolon, ";");
1329
1330 return c.addNode(.{
1331 .tag = .simple_var_decl,
1332 .main_token = const_tok,
1333 .data = .{
1334 .opt_node_and_opt_node = .{
1335 .none, // Type expression
1336 container_def.toOptional(), // Init expression
1337 },
1338 },
1339 });
1340 },
1341 .mut_str => {
1342 const payload = node.castTag(.mut_str).?.data;
1343
1344 const var_tok = try c.addToken(.keyword_var, "var");
1345 _ = try c.addIdentifier(payload.name);
1346 _ = try c.addToken(.equal, "=");
1347
1348 const deref = try c.addNode(.{
1349 .tag = .deref,
1350 .data = .{
1351 .node = try renderNodeGrouped(c, payload.init),
1352 },
1353 .main_token = try c.addToken(.period_asterisk, ".*"),
1354 });
1355 _ = try c.addToken(.semicolon, ";");
1356
1357 return c.addNode(.{
1358 .tag = .simple_var_decl,
1359 .main_token = var_tok,
1360 .data = .{
1361 .opt_node_and_opt_node = .{
1362 .none, // Type expression
1363 deref.toOptional(), // Init expression
1364 },
1365 },
1366 });
1367 },
1368 .var_decl => return renderVar(c, node),
1369 .arg_redecl, .alias => {
1370 const payload = @as(*Payload.ArgRedecl, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
1371 if (node.tag() == .alias) _ = try c.addToken(.keyword_pub, "pub");
1372 const mut_tok = if (node.tag() == .alias)
1373 try c.addToken(.keyword_const, "const")
1374 else
1375 try c.addToken(.keyword_var, "var");
1376 _ = try c.addIdentifier(payload.actual);
1377 _ = try c.addToken(.equal, "=");
1378
1379 const init = try c.addNode(.{
1380 .tag = .identifier,
1381 .main_token = try c.addIdentifier(payload.mangled),
1382 .data = undefined,
1383 });
1384 _ = try c.addToken(.semicolon, ";");
1385
1386 return c.addNode(.{
1387 .tag = .simple_var_decl,
1388 .main_token = mut_tok,
1389 .data = .{
1390 .opt_node_and_opt_node = .{
1391 .none, // Type expression
1392 init.toOptional(), // Init expression
1393 },
1394 },
1395 });
1396 },
1397 .int_cast => {
1398 const payload = node.castTag(.int_cast).?.data;
1399 return renderBuiltinCall(c, "@intCast", &.{payload});
1400 },
1401 .const_cast => {
1402 const payload = node.castTag(.const_cast).?.data;
1403 return renderBuiltinCall(c, "@constCast", &.{payload});
1404 },
1405 .volatile_cast => {
1406 const payload = node.castTag(.volatile_cast).?.data;
1407 return renderBuiltinCall(c, "@volatileCast", &.{payload});
1408 },
1409 .div_trunc => {
1410 const payload = node.castTag(.div_trunc).?.data;
1411 return renderBuiltinCall(c, "@divTrunc", &.{ payload.lhs, payload.rhs });
1412 },
1413 .int_from_bool => {
1414 const payload = node.castTag(.int_from_bool).?.data;
1415 return renderBuiltinCall(c, "@intFromBool", &.{payload});
1416 },
1417 .as => {
1418 const payload = node.castTag(.as).?.data;
1419 return renderBuiltinCall(c, "@as", &.{ payload.lhs, payload.rhs });
1420 },
1421 .truncate => {
1422 const payload = node.castTag(.truncate).?.data;
1423 return renderBuiltinCall(c, "@truncate", &.{payload});
1424 },
1425 .bit_cast => {
1426 const payload = node.castTag(.bit_cast).?.data;
1427 return renderBuiltinCall(c, "@bitCast", &.{payload});
1428 },
1429 .float_cast => {
1430 const payload = node.castTag(.float_cast).?.data;
1431 return renderBuiltinCall(c, "@floatCast", &.{payload});
1432 },
1433 .int_from_float => {
1434 const payload = node.castTag(.int_from_float).?.data;
1435 return renderBuiltinCall(c, "@intFromFloat", &.{payload});
1436 },
1437 .float_from_int => {
1438 const payload = node.castTag(.float_from_int).?.data;
1439 return renderBuiltinCall(c, "@floatFromInt", &.{payload});
1440 },
1441 .ptr_from_int => {
1442 const payload = node.castTag(.ptr_from_int).?.data;
1443 return renderBuiltinCall(c, "@ptrFromInt", &.{payload});
1444 },
1445 .int_from_ptr => {
1446 const payload = node.castTag(.int_from_ptr).?.data;
1447 return renderBuiltinCall(c, "@intFromPtr", &.{payload});
1448 },
1449 .align_cast => {
1450 const payload = node.castTag(.align_cast).?.data;
1451 return renderBuiltinCall(c, "@alignCast", &.{payload});
1452 },
1453 .ptr_cast => {
1454 const payload = node.castTag(.ptr_cast).?.data;
1455 return renderBuiltinCall(c, "@ptrCast", &.{payload});
1456 },
1457 .div_exact => {
1458 const payload = node.castTag(.div_exact).?.data;
1459 return renderBuiltinCall(c, "@divExact", &.{ payload.lhs, payload.rhs });
1460 },
1461 .offset_of => {
1462 const payload = node.castTag(.offset_of).?.data;
1463 return renderBuiltinCall(c, "@offsetOf", &.{ payload.lhs, payload.rhs });
1464 },
1465 .sizeof => {
1466 const payload = node.castTag(.sizeof).?.data;
1467 return renderBuiltinCall(c, "@sizeOf", &.{payload});
1468 },
1469 .shuffle => {
1470 const payload = node.castTag(.shuffle).?.data;
1471 return renderBuiltinCall(c, "@shuffle", &.{
1472 payload.element_type,
1473 payload.a,
1474 payload.b,
1475 payload.mask_vector,
1476 });
1477 },
1478 .builtin_extern => {
1479 const payload = node.castTag(.builtin_extern).?.data;
1480
1481 var info_inits: [1]Payload.ContainerInitDot.Initializer = .{
1482 .{ .name = "name", .value = payload.name },
1483 };
1484 var info_payload: Payload.ContainerInitDot = .{
1485 .base = .{ .tag = .container_init_dot },
1486 .data = &info_inits,
1487 };
1488
1489 return renderBuiltinCall(c, "@extern", &.{
1490 payload.type,
1491 .{ .ptr_otherwise = &info_payload.base },
1492 });
1493 },
1494 .helper_call => {
1495 const payload = node.castTag(.helper_call).?.data;
1496 const helpers_tok = try c.addNode(.{
1497 .tag = .identifier,
1498 .main_token = try c.addIdentifier("__helpers"),
1499 .data = undefined,
1500 });
1501 const func = try renderFieldAccess(c, helpers_tok, payload.name);
1502 return renderCall(c, func, payload.args);
1503 },
1504 .helper_ref => {
1505 const payload = node.castTag(.helper_ref).?.data;
1506 const helpers_tok = try c.addNode(.{
1507 .tag = .identifier,
1508 .main_token = try c.addIdentifier("__helpers"),
1509 .data = undefined,
1510 });
1511 return renderFieldAccess(c, helpers_tok, payload);
1512 },
1513 .alignof => {
1514 const payload = node.castTag(.alignof).?.data;
1515 return renderBuiltinCall(c, "@alignOf", &.{payload});
1516 },
1517 .typeof => {
1518 const payload = node.castTag(.typeof).?.data;
1519 return renderBuiltinCall(c, "@TypeOf", &.{payload});
1520 },
1521 .typeinfo => {
1522 const payload = node.castTag(.typeinfo).?.data;
1523 return renderBuiltinCall(c, "@typeInfo", &.{payload});
1524 },
1525 .byte_swap => {
1526 const payload = node.castTag(.byte_swap).?.data;
1527 return renderBuiltinCall(c, "@byteSwap", &.{payload});
1528 },
1529 .ceil => {
1530 const payload = node.castTag(.ceil).?.data;
1531 return renderBuiltinCall(c, "@ceil", &.{payload});
1532 },
1533 .cos => {
1534 const payload = node.castTag(.cos).?.data;
1535 return renderBuiltinCall(c, "@cos", &.{payload});
1536 },
1537 .sin => {
1538 const payload = node.castTag(.sin).?.data;
1539 return renderBuiltinCall(c, "@sin", &.{payload});
1540 },
1541 .exp => {
1542 const payload = node.castTag(.exp).?.data;
1543 return renderBuiltinCall(c, "@exp", &.{payload});
1544 },
1545 .exp2 => {
1546 const payload = node.castTag(.exp2).?.data;
1547 return renderBuiltinCall(c, "@exp2", &.{payload});
1548 },
1549 .exp10 => {
1550 const payload = node.castTag(.exp10).?.data;
1551 return renderBuiltinCall(c, "@exp10", &.{payload});
1552 },
1553 .abs => {
1554 const payload = node.castTag(.abs).?.data;
1555 return renderBuiltinCall(c, "@abs", &.{payload});
1556 },
1557 .log => {
1558 const payload = node.castTag(.log).?.data;
1559 return renderBuiltinCall(c, "@log", &.{payload});
1560 },
1561 .log2 => {
1562 const payload = node.castTag(.log2).?.data;
1563 return renderBuiltinCall(c, "@log2", &.{payload});
1564 },
1565 .log10 => {
1566 const payload = node.castTag(.log10).?.data;
1567 return renderBuiltinCall(c, "@log10", &.{payload});
1568 },
1569 .round => {
1570 const payload = node.castTag(.round).?.data;
1571 return renderBuiltinCall(c, "@round", &.{payload});
1572 },
1573 .sqrt => {
1574 const payload = node.castTag(.sqrt).?.data;
1575 return renderBuiltinCall(c, "@sqrt", &.{payload});
1576 },
1577 .trunc => {
1578 const payload = node.castTag(.trunc).?.data;
1579 return renderBuiltinCall(c, "@trunc", &.{payload});
1580 },
1581 .floor => {
1582 const payload = node.castTag(.floor).?.data;
1583 return renderBuiltinCall(c, "@floor", &.{payload});
1584 },
1585 .negate => return renderPrefixOp(c, node, .negation, .minus, "-"),
1586 .negate_wrap => return renderPrefixOp(c, node, .negation_wrap, .minus_percent, "-%"),
1587 .bit_not => return renderPrefixOp(c, node, .bit_not, .tilde, "~"),
1588 .not => return renderPrefixOp(c, node, .bool_not, .bang, "!"),
1589 .optional_type => return renderPrefixOp(c, node, .optional_type, .question_mark, "?"),
1590 .address_of => {
1591 const payload = node.castTag(.address_of).?.data;
1592
1593 const ampersand = try c.addToken(.ampersand, "&");
1594 const base = try renderNodeGrouped(c, payload);
1595 return c.addNode(.{
1596 .tag = .address_of,
1597 .main_token = ampersand,
1598 .data = .{
1599 .node = base,
1600 },
1601 });
1602 },
1603 .deref => {
1604 const payload = node.castTag(.deref).?.data;
1605 const operand = try renderNodeGrouped(c, payload);
1606 const deref_tok = try c.addToken(.period_asterisk, ".*");
1607 return c.addNode(.{
1608 .tag = .deref,
1609 .main_token = deref_tok,
1610 .data = .{
1611 .node = operand,
1612 },
1613 });
1614 },
1615 .unwrap => {
1616 const payload = node.castTag(.unwrap).?.data;
1617 const operand = try renderNodeGrouped(c, payload);
1618 const period = try c.addToken(.period, ".");
1619 const question_mark = try c.addToken(.question_mark, "?");
1620 return c.addNode(.{
1621 .tag = .unwrap_optional,
1622 .main_token = period,
1623 .data = .{ .node_and_token = .{
1624 operand, question_mark,
1625 } },
1626 });
1627 },
1628 .c_pointer, .single_pointer => {
1629 const payload = @as(*Payload.Pointer, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
1630
1631 const main_token = if (node.tag() == .single_pointer)
1632 try c.addToken(.asterisk, "*")
1633 else blk: {
1634 const res = try c.addToken(.l_bracket, "[");
1635 _ = try c.addToken(.asterisk, "*");
1636 _ = try c.addIdentifier("c");
1637 _ = try c.addToken(.r_bracket, "]");
1638 break :blk res;
1639 };
1640 if (payload.is_const) _ = try c.addToken(.keyword_const, "const");
1641 if (payload.is_volatile) _ = try c.addToken(.keyword_volatile, "volatile");
1642 if (payload.is_allowzero) _ = try c.addToken(.keyword_allowzero, "allowzero");
1643 const elem_type = try renderNodeGrouped(c, payload.elem_type);
1644
1645 return c.addNode(.{
1646 .tag = .ptr_type_aligned,
1647 .main_token = main_token,
1648 .data = .{
1649 .opt_node_and_node = .{
1650 .none, // Align node
1651 elem_type,
1652 },
1653 },
1654 });
1655 },
1656 .add => return renderBinOpGrouped(c, node, .add, .plus, "+"),
1657 .add_assign => return renderBinOp(c, node, .assign_add, .plus_equal, "+="),
1658 .add_wrap => return renderBinOpGrouped(c, node, .add_wrap, .plus_percent, "+%"),
1659 .add_wrap_assign => return renderBinOp(c, node, .assign_add_wrap, .plus_percent_equal, "+%="),
1660 .sub => return renderBinOpGrouped(c, node, .sub, .minus, "-"),
1661 .sub_assign => return renderBinOp(c, node, .assign_sub, .minus_equal, "-="),
1662 .sub_wrap => return renderBinOpGrouped(c, node, .sub_wrap, .minus_percent, "-%"),
1663 .sub_wrap_assign => return renderBinOp(c, node, .assign_sub_wrap, .minus_percent_equal, "-%="),
1664 .mul => return renderBinOpGrouped(c, node, .mul, .asterisk, "*"),
1665 .mul_assign => return renderBinOp(c, node, .assign_mul, .asterisk_equal, "*="),
1666 .mul_wrap => return renderBinOpGrouped(c, node, .mul_wrap, .asterisk_percent, "*%"),
1667 .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),
1668 .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),
1669 .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),
1670 .shl => return renderBinOpGrouped(c, node, .shl, .angle_bracket_angle_bracket_left, "<<"),
1671 .shl_assign => return renderBinOp(c, node, .assign_shl, .angle_bracket_angle_bracket_left_equal, "<<="),
1672 .shr => return renderBinOpGrouped(c, node, .shr, .angle_bracket_angle_bracket_right, ">>"),
1673 .shr_assign => return renderBinOp(c, node, .assign_shr, .angle_bracket_angle_bracket_right_equal, ">>="),
1674 .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),
1675 .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),
1676 .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),
1677 .@"or" => return renderBinOpGrouped(c, node, .bool_or, .keyword_or, "or"),
1678 .less_than => return renderBinOpGrouped(c, node, .less_than, .angle_bracket_left, "<"),
1679 .less_than_equal => return renderBinOpGrouped(c, node, .less_or_equal, .angle_bracket_left_equal, "<="),
1680 .greater_than => return renderBinOpGrouped(c, node, .greater_than, .angle_bracket_right, ">="),
1681 .greater_than_equal => return renderBinOpGrouped(c, node, .greater_or_equal, .angle_bracket_right_equal, ">="),
1682 .equal => return renderBinOpGrouped(c, node, .equal_equal, .equal_equal, "=="),
1683 .not_equal => return renderBinOpGrouped(c, node, .bang_equal, .bang_equal, "!="),
1684 .bit_and => return renderBinOpGrouped(c, node, .bit_and, .ampersand, "&"),
1685 .bit_and_assign => return renderBinOp(c, node, .assign_bit_and, .ampersand_equal, "&="),
1686 .bit_or => return renderBinOpGrouped(c, node, .bit_or, .pipe, "|"),
1687 .bit_or_assign => return renderBinOp(c, node, .assign_bit_or, .pipe_equal, "|="),
1688 .bit_xor => return renderBinOpGrouped(c, node, .bit_xor, .caret, "^"),
1689 .bit_xor_assign => return renderBinOp(c, node, .assign_bit_xor, .caret_equal, "^="),
1690 .array_cat => return renderBinOp(c, node, .array_cat, .plus_plus, "++"),
1691 .ellipsis3 => return renderBinOpGrouped(c, node, .switch_range, .ellipsis3, "..."),
1692 .assign => return renderBinOp(c, node, .assign, .equal, "="),
1693 .empty_block => {
1694 const l_brace = try c.addToken(.l_brace, "{");
1695 _ = try c.addToken(.r_brace, "}");
1696 return c.addNode(.{
1697 .tag = .block_two,
1698 .main_token = l_brace,
1699 .data = .{ .opt_node_and_opt_node = .{
1700 .none, .none,
1701 } },
1702 });
1703 },
1704 .block_single => {
1705 const payload = node.castTag(.block_single).?.data;
1706 const l_brace = try c.addToken(.l_brace, "{");
1707
1708 const stmt = (try renderNodeOpt(c, payload)) orelse {
1709 _ = try c.addToken(.r_brace, "}");
1710 return c.addNode(.{
1711 .tag = .block_two,
1712 .main_token = l_brace,
1713 .data = .{ .opt_node_and_opt_node = .{
1714 .none, .none,
1715 } },
1716 });
1717 };
1718 try addSemicolonIfNeeded(c, payload);
1719
1720 _ = try c.addToken(.r_brace, "}");
1721 return c.addNode(.{
1722 .tag = .block_two_semicolon,
1723 .main_token = l_brace,
1724 .data = .{ .opt_node_and_opt_node = .{
1725 stmt.toOptional(), .none,
1726 } },
1727 });
1728 },
1729 .block => {
1730 const payload = node.castTag(.block).?.data;
1731 if (payload.label) |some| {
1732 _ = try c.addIdentifier(some);
1733 _ = try c.addToken(.colon, ":");
1734 }
1735 const l_brace = try c.addToken(.l_brace, "{");
1736
1737 var stmts: std.ArrayList(NodeIndex) = .empty;
1738 defer stmts.deinit(c.gpa);
1739 for (payload.stmts) |stmt| {
1740 const res = (try renderNodeOpt(c, stmt)) orelse continue;
1741 try addSemicolonIfNeeded(c, stmt);
1742 try stmts.append(c.gpa, res);
1743 }
1744 const span = try c.listToSpan(stmts.items);
1745 _ = try c.addToken(.r_brace, "}");
1746
1747 const semicolon = c.tokens.items(.tag)[c.tokens.len - 2] == .semicolon;
1748 return c.addNode(.{
1749 .tag = if (semicolon) .block_semicolon else .block,
1750 .main_token = l_brace,
1751 .data = .{ .extra_range = span },
1752 });
1753 },
1754 .func => return renderFunc(c, node),
1755 .pub_inline_fn => return renderMacroFunc(c, node),
1756 .@"while" => {
1757 const payload = node.castTag(.@"while").?.data;
1758 const while_tok = try c.addToken(.keyword_while, "while");
1759 _ = try c.addToken(.l_paren, "(");
1760 const cond = try renderNode(c, payload.cond);
1761 _ = try c.addToken(.r_paren, ")");
1762
1763 const cont_expr_opt = if (payload.cont_expr) |some| blk: {
1764 _ = try c.addToken(.colon, ":");
1765 _ = try c.addToken(.l_paren, "(");
1766 const res = try renderNode(c, some);
1767 _ = try c.addToken(.r_paren, ")");
1768 break :blk res;
1769 } else null;
1770 const body = try renderNode(c, payload.body);
1771
1772 if (cont_expr_opt) |cont_expr| {
1773 return c.addNode(.{
1774 .tag = .while_cont,
1775 .main_token = while_tok,
1776 .data = .{ .node_and_extra = .{
1777 cond,
1778 try c.addExtra(std.zig.Ast.Node.WhileCont{
1779 .cont_expr = cont_expr,
1780 .then_expr = body,
1781 }),
1782 } },
1783 });
1784 } else {
1785 return c.addNode(.{
1786 .tag = .while_simple,
1787 .main_token = while_tok,
1788 .data = .{ .node_and_node = .{
1789 cond, body,
1790 } },
1791 });
1792 }
1793 },
1794 .while_true => {
1795 const payload = node.castTag(.while_true).?.data;
1796 const while_tok = try c.addToken(.keyword_while, "while");
1797 _ = try c.addToken(.l_paren, "(");
1798 const cond = try c.addNode(.{
1799 .tag = .identifier,
1800 .main_token = try c.addToken(.identifier, "true"),
1801 .data = undefined,
1802 });
1803 _ = try c.addToken(.r_paren, ")");
1804 const body = try renderNode(c, payload);
1805
1806 return c.addNode(.{
1807 .tag = .while_simple,
1808 .main_token = while_tok,
1809 .data = .{ .node_and_node = .{
1810 cond, body,
1811 } },
1812 });
1813 },
1814 .@"if" => {
1815 const payload = node.castTag(.@"if").?.data;
1816 const if_tok = try c.addToken(.keyword_if, "if");
1817 _ = try c.addToken(.l_paren, "(");
1818 const cond = try renderNode(c, payload.cond);
1819 _ = try c.addToken(.r_paren, ")");
1820
1821 const then_expr = try renderNode(c, payload.then);
1822 const else_node = payload.@"else" orelse return c.addNode(.{
1823 .tag = .if_simple,
1824 .main_token = if_tok,
1825 .data = .{ .node_and_node = .{
1826 cond, then_expr,
1827 } },
1828 });
1829 _ = try c.addToken(.keyword_else, "else");
1830 const else_expr = try renderNode(c, else_node);
1831
1832 return c.addNode(.{
1833 .tag = .@"if",
1834 .main_token = if_tok,
1835 .data = .{ .node_and_extra = .{
1836 cond,
1837 try c.addExtra(std.zig.Ast.Node.If{
1838 .then_expr = then_expr,
1839 .else_expr = else_expr,
1840 }),
1841 } },
1842 });
1843 },
1844 .if_not_break => {
1845 const payload = node.castTag(.if_not_break).?.data;
1846 const if_tok = try c.addToken(.keyword_if, "if");
1847 _ = try c.addToken(.l_paren, "(");
1848 const cond = try c.addNode(.{
1849 .tag = .bool_not,
1850 .main_token = try c.addToken(.bang, "!"),
1851 .data = .{
1852 .node = try renderNodeGrouped(c, payload),
1853 },
1854 });
1855 _ = try c.addToken(.r_paren, ")");
1856 const then_expr = try c.addNode(.{
1857 .tag = .@"break",
1858 .main_token = try c.addToken(.keyword_break, "break"),
1859 .data = .{ .opt_token_and_opt_node = .{
1860 .none, .none,
1861 } },
1862 });
1863
1864 return c.addNode(.{
1865 .tag = .if_simple,
1866 .main_token = if_tok,
1867 .data = .{ .node_and_node = .{
1868 cond, then_expr,
1869 } },
1870 });
1871 },
1872 .@"switch" => {
1873 const payload = node.castTag(.@"switch").?.data;
1874 const switch_tok = try c.addToken(.keyword_switch, "switch");
1875 _ = try c.addToken(.l_paren, "(");
1876 const cond = try renderNode(c, payload.cond);
1877 _ = try c.addToken(.r_paren, ")");
1878
1879 _ = try c.addToken(.l_brace, "{");
1880 var cases = try c.gpa.alloc(NodeIndex, payload.cases.len);
1881 defer c.gpa.free(cases);
1882 for (payload.cases, 0..) |case, i| {
1883 cases[i] = try renderNode(c, case);
1884 _ = try c.addToken(.comma, ",");
1885 }
1886 const span = try c.listToSpan(cases);
1887 _ = try c.addToken(.r_brace, "}");
1888 return c.addNode(.{
1889 .tag = .switch_comma,
1890 .main_token = switch_tok,
1891 .data = .{ .node_and_extra = .{
1892 cond,
1893 try c.addExtra(NodeSubRange{
1894 .start = span.start,
1895 .end = span.end,
1896 }),
1897 } },
1898 });
1899 },
1900 .switch_else => {
1901 const payload = node.castTag(.switch_else).?.data;
1902 _ = try c.addToken(.keyword_else, "else");
1903 return c.addNode(.{
1904 .tag = .switch_case_one,
1905 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1906 .data = .{ .opt_node_and_node = .{
1907 .none, try renderNode(c, payload),
1908 } },
1909 });
1910 },
1911 .switch_prong => {
1912 const payload = node.castTag(.switch_prong).?.data;
1913 var items = try c.gpa.alloc(NodeIndex, payload.cases.len);
1914 defer c.gpa.free(items);
1915
1916 for (payload.cases, 0..) |item, i| {
1917 if (i != 0) _ = try c.addToken(.comma, ",");
1918 items[i] = try renderNode(c, item);
1919 }
1920 _ = try c.addToken(.r_brace, "}");
1921 if (items.len < 2) {
1922 return c.addNode(.{
1923 .tag = .switch_case_one,
1924 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1925 .data = .{ .opt_node_and_node = .{
1926 if (payload.cases.len == 1) items[0].toOptional() else .none,
1927 try renderNode(c, payload.cond),
1928 } },
1929 });
1930 } else {
1931 return c.addNode(.{
1932 .tag = .switch_case,
1933 .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
1934 .data = .{ .extra_and_node = .{
1935 try c.addExtra(try c.listToSpan(items)),
1936 try renderNode(c, payload.cond),
1937 } },
1938 });
1939 }
1940 },
1941 .opaque_literal => {
1942 const opaque_tok = try c.addToken(.keyword_opaque, "opaque");
1943 _ = try c.addToken(.l_brace, "{");
1944 _ = try c.addToken(.r_brace, "}");
1945
1946 return c.addNode(.{
1947 .tag = .container_decl_two,
1948 .main_token = opaque_tok,
1949 .data = .{ .opt_node_and_opt_node = .{
1950 .none, .none,
1951 } },
1952 });
1953 },
1954 .array_access => {
1955 const payload = node.castTag(.array_access).?.data;
1956 const lhs = try renderNodeGrouped(c, payload.lhs);
1957 const l_bracket = try c.addToken(.l_bracket, "[");
1958 const index_expr = try renderNode(c, payload.rhs);
1959 _ = try c.addToken(.r_bracket, "]");
1960 return c.addNode(.{
1961 .tag = .array_access,
1962 .main_token = l_bracket,
1963 .data = .{ .node_and_node = .{
1964 lhs, index_expr,
1965 } },
1966 });
1967 },
1968 .array_type => {
1969 const payload = node.castTag(.array_type).?.data;
1970 return renderArrayType(c, payload.len, payload.elem_type);
1971 },
1972 .null_sentinel_array_type => {
1973 const payload = node.castTag(.null_sentinel_array_type).?.data;
1974 return renderNullSentinelArrayType(c, payload.len, payload.elem_type);
1975 },
1976 .array_filler => {
1977 const payload = node.castTag(.array_filler).?.data;
1978
1979 const as_tok = try c.addToken(.builtin, "@as");
1980 _ = try c.addToken(.l_paren, "(");
1981 const type_node = try renderArrayType(c, payload.count, payload.type);
1982 _ = try c.addToken(.comma, ",");
1983 const splat_node = try renderBuiltinCall(c, "@splat", &.{payload.filler});
1984 _ = try c.addToken(.r_paren, ")");
1985
1986 return c.addNode(.{
1987 .tag = .builtin_call_two,
1988 .main_token = as_tok,
1989 .data = .{ .opt_node_and_opt_node = .{
1990 .fromOptional(type_node), .fromOptional(splat_node),
1991 } },
1992 });
1993 },
1994 .empty_array => {
1995 const payload = node.castTag(.empty_array).?.data;
1996
1997 const type_expr = try renderNode(c, payload);
1998 return renderArrayInit(c, type_expr, &.{});
1999 },
2000 .array_init => {
2001 const payload = node.castTag(.array_init).?.data;
2002 const type_expr = try renderNode(c, payload.cond);
2003 return renderArrayInit(c, type_expr, payload.cases);
2004 },
2005 .vector_zero_init => {
2006 const payload = node.castTag(.vector_zero_init).?.data;
2007 return renderBuiltinCall(c, "@splat", &.{payload});
2008 },
2009 .field_access => {
2010 const payload = node.castTag(.field_access).?.data;
2011 const lhs = try renderNodeGrouped(c, payload.lhs);
2012 return renderFieldAccess(c, lhs, payload.field_name);
2013 },
2014 .field_builtin => {
2015 const payload = node.castTag(.field_builtin).?.data;
2016 return renderBuiltinCall(c, "@field", &.{ payload.lhs, payload.rhs });
2017 },
2018 .@"struct", .@"union", .@"opaque" => return renderContainer(c, node),
2019 .enum_constant => {
2020 const payload = node.castTag(.enum_constant).?.data;
2021
2022 if (payload.is_public) _ = try c.addToken(.keyword_pub, "pub");
2023 const const_tok = try c.addToken(.keyword_const, "const");
2024 _ = try c.addIdentifier(payload.name);
2025
2026 const type_node_opt = if (payload.type) |enum_const_type| blk: {
2027 _ = try c.addToken(.colon, ":");
2028 break :blk try renderNode(c, enum_const_type);
2029 } else null;
2030
2031 _ = try c.addToken(.equal, "=");
2032
2033 const init_node = try renderNode(c, payload.value);
2034 _ = try c.addToken(.semicolon, ";");
2035
2036 return c.addNode(.{
2037 .tag = .simple_var_decl,
2038 .main_token = const_tok,
2039 .data = .{ .opt_node_and_opt_node = .{
2040 .fromOptional(type_node_opt),
2041 init_node.toOptional(),
2042 } },
2043 });
2044 },
2045 .tuple => {
2046 const payload = node.castTag(.tuple).?.data;
2047 _ = try c.addToken(.period, ".");
2048 const l_brace = try c.addToken(.l_brace, "{");
2049 var inits = try c.gpa.alloc(NodeIndex, payload.len);
2050 defer c.gpa.free(inits);
2051
2052 for (payload, 0..) |init, i| {
2053 if (i != 0) _ = try c.addToken(.comma, ",");
2054 inits[i] = try renderNode(c, init);
2055 }
2056 _ = try c.addToken(.r_brace, "}");
2057 if (payload.len < 3) {
2058 return c.addNode(.{
2059 .tag = .array_init_dot_two,
2060 .main_token = l_brace,
2061 .data = .{ .opt_node_and_opt_node = .{
2062 if (inits.len >= 1) inits[0].toOptional() else .none,
2063 if (inits.len >= 2) inits[1].toOptional() else .none,
2064 } },
2065 });
2066 } else {
2067 return c.addNode(.{
2068 .tag = .array_init_dot,
2069 .main_token = l_brace,
2070 .data = .{ .extra_range = try c.listToSpan(inits) },
2071 });
2072 }
2073 },
2074 .container_init_dot => {
2075 const payload = node.castTag(.container_init_dot).?.data;
2076 _ = try c.addToken(.period, ".");
2077 const l_brace = try c.addToken(.l_brace, "{");
2078 var inits = try c.gpa.alloc(NodeIndex, payload.len);
2079 defer c.gpa.free(inits);
2080
2081 for (payload, 0..) |init, i| {
2082 _ = try c.addToken(.period, ".");
2083 _ = try c.addIdentifier(init.name);
2084 _ = try c.addToken(.equal, "=");
2085 inits[i] = try renderNode(c, init.value);
2086 _ = try c.addToken(.comma, ",");
2087 }
2088 _ = try c.addToken(.r_brace, "}");
2089
2090 if (payload.len < 3) {
2091 return c.addNode(.{
2092 .tag = .struct_init_dot_two_comma,
2093 .main_token = l_brace,
2094 .data = .{ .opt_node_and_opt_node = .{
2095 if (inits.len >= 1) inits[0].toOptional() else .none,
2096 if (inits.len >= 2) inits[1].toOptional() else .none,
2097 } },
2098 });
2099 } else {
2100 return c.addNode(.{
2101 .tag = .struct_init_dot_comma,
2102 .main_token = l_brace,
2103 .data = .{ .extra_range = try c.listToSpan(inits) },
2104 });
2105 }
2106 },
2107 .container_init => {
2108 const payload = node.castTag(.container_init).?.data;
2109 const lhs = try renderNode(c, payload.lhs);
2110
2111 const l_brace = try c.addToken(.l_brace, "{");
2112 var inits = try c.gpa.alloc(NodeIndex, payload.inits.len);
2113 defer c.gpa.free(inits);
2114
2115 for (payload.inits, 0..) |init, i| {
2116 _ = try c.addToken(.period, ".");
2117 _ = try c.addIdentifier(init.name);
2118 _ = try c.addToken(.equal, "=");
2119 inits[i] = try renderNode(c, init.value);
2120 _ = try c.addToken(.comma, ",");
2121 }
2122 _ = try c.addToken(.r_brace, "}");
2123
2124 switch (inits.len) {
2125 0 => return c.addNode(.{
2126 .tag = .struct_init_one,
2127 .main_token = l_brace,
2128 .data = .{ .node_and_opt_node = .{
2129 lhs, .none,
2130 } },
2131 }),
2132 1 => return c.addNode(.{
2133 .tag = .struct_init_one_comma,
2134 .main_token = l_brace,
2135 .data = .{ .node_and_opt_node = .{
2136 lhs, inits[0].toOptional(),
2137 } },
2138 }),
2139 else => return c.addNode(.{
2140 .tag = .struct_init_comma,
2141 .main_token = l_brace,
2142 .data = .{ .node_and_extra = .{
2143 lhs,
2144 try c.addExtra(try c.listToSpan(inits)),
2145 } },
2146 }),
2147 }
2148 },
2149 .static_assert => {
2150 const payload = node.castTag(.static_assert).?.data;
2151 const comptime_tok = try c.addToken(.keyword_comptime, "comptime");
2152 const l_brace = try c.addToken(.l_brace, "{");
2153
2154 const if_tok = try c.addToken(.keyword_if, "if");
2155 _ = try c.addToken(.l_paren, "(");
2156 const cond = try c.addNode(.{
2157 .tag = .bool_not,
2158 .main_token = try c.addToken(.bang, "!"),
2159 .data = .{
2160 .node = try renderNodeGrouped(c, payload.lhs),
2161 },
2162 });
2163 _ = try c.addToken(.r_paren, ")");
2164
2165 const compile_error_tok = try c.addToken(.builtin, "@compileError");
2166 _ = try c.addToken(.l_paren, "(");
2167 const err_msg = try renderNode(c, payload.rhs);
2168 _ = try c.addToken(.r_paren, ")");
2169 const compile_error = try c.addNode(.{
2170 .tag = .builtin_call_two,
2171 .main_token = compile_error_tok,
2172 .data = .{ .opt_node_and_opt_node = .{
2173 err_msg.toOptional(), .none,
2174 } },
2175 });
2176
2177 const if_node = try c.addNode(.{
2178 .tag = .if_simple,
2179 .main_token = if_tok,
2180 .data = .{ .node_and_node = .{
2181 cond, compile_error,
2182 } },
2183 });
2184 _ = try c.addToken(.semicolon, ";");
2185 _ = try c.addToken(.r_brace, "}");
2186 const block_node = try c.addNode(.{
2187 .tag = .block_two_semicolon,
2188 .main_token = l_brace,
2189 .data = .{ .opt_node_and_opt_node = .{
2190 if_node.toOptional(), .none,
2191 } },
2192 });
2193
2194 return c.addNode(.{
2195 .tag = .@"comptime",
2196 .main_token = comptime_tok,
2197 .data = .{
2198 .node = block_node,
2199 },
2200 });
2201 },
2202 .@"anytype" => unreachable, // Handled in renderParams
2203 .root_ref => {
2204 const payload = node.castTag(.root_ref).?.data;
2205 const root_tok = try c.addNode(.{
2206 .tag = .identifier,
2207 .main_token = try c.addIdentifier("__root"),
2208 .data = undefined,
2209 });
2210 return renderFieldAccess(c, root_tok, payload);
2211 },
2212 }
2213}
2214
2215fn renderContainer(c: *Context, node: Node) !NodeIndex {
2216 const payload = @as(*Payload.Container, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
2217 if (payload.layout == .@"packed")
2218 _ = try c.addToken(.keyword_packed, "packed")
2219 else if (payload.layout == .@"extern")
2220 _ = try c.addToken(.keyword_extern, "extern");
2221 const kind_tok = if (node.tag() == .@"struct")
2222 try c.addToken(.keyword_struct, "struct")
2223 else if (node.tag() == .@"union")
2224 try c.addToken(.keyword_union, "union")
2225 else if (node.tag() == .@"opaque")
2226 try c.addToken(.keyword_opaque, "opaque")
2227 else
2228 unreachable;
2229
2230 _ = try c.addToken(.l_brace, "{");
2231
2232 const num_decls = payload.decls.len;
2233 const total_members = payload.fields.len + num_decls;
2234 const members = try c.gpa.alloc(NodeIndex, total_members);
2235 defer c.gpa.free(members);
2236
2237 for (payload.fields, 0..) |field, i| {
2238 const name_tok = try c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(field.name, .{ .allow_primitive = true })});
2239 _ = try c.addToken(.colon, ":");
2240 const type_expr = try renderNode(c, field.type);
2241
2242 const align_expr_opt = if (field.alignment) |alignment| blk: {
2243 _ = try c.addToken(.keyword_align, "align");
2244 _ = try c.addToken(.l_paren, "(");
2245 const align_expr = try c.addNode(.{
2246 .tag = .number_literal,
2247 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{alignment}),
2248 .data = undefined,
2249 });
2250 _ = try c.addToken(.r_paren, ")");
2251 break :blk align_expr;
2252 } else null;
2253
2254 const value_expr_opt = if (field.default_value) |value| blk: {
2255 _ = try c.addToken(.equal, "=");
2256 break :blk try renderNode(c, value);
2257 } else null;
2258
2259 if (align_expr_opt) |align_expr| {
2260 if (value_expr_opt) |value_expr| {
2261 members[i] = try c.addNode(.{
2262 .tag = .container_field,
2263 .main_token = name_tok,
2264 .data = .{ .node_and_extra = .{
2265 type_expr,
2266 try c.addExtra(std.zig.Ast.Node.ContainerField{
2267 .align_expr = align_expr,
2268 .value_expr = value_expr,
2269 }),
2270 } },
2271 });
2272 } else {
2273 members[i] = try c.addNode(.{
2274 .tag = .container_field_align,
2275 .main_token = name_tok,
2276 .data = .{ .node_and_node = .{
2277 type_expr,
2278 align_expr,
2279 } },
2280 });
2281 }
2282 } else {
2283 members[i] = try c.addNode(.{
2284 .tag = .container_field_init,
2285 .main_token = name_tok,
2286 .data = .{ .node_and_opt_node = .{
2287 type_expr,
2288 .fromOptional(value_expr_opt),
2289 } },
2290 });
2291 }
2292 _ = try c.addToken(.comma, ",");
2293 }
2294 for (members[payload.fields.len..], payload.decls) |*member, decl| {
2295 member.* = try renderNode(c, decl);
2296 }
2297 const trailing = switch (c.tokens.items(.tag)[c.tokens.len - 1]) {
2298 .comma, .semicolon => true,
2299 else => false,
2300 };
2301 _ = try c.addToken(.r_brace, "}");
2302
2303 if (total_members == 0) {
2304 return c.addNode(.{
2305 .tag = .container_decl_two,
2306 .main_token = kind_tok,
2307 .data = .{ .opt_node_and_opt_node = .{
2308 .none, .none,
2309 } },
2310 });
2311 } else if (total_members <= 2) {
2312 return c.addNode(.{
2313 .tag = if (trailing) .container_decl_two_trailing else .container_decl_two,
2314 .main_token = kind_tok,
2315 .data = .{ .opt_node_and_opt_node = .{
2316 if (members.len >= 1) members[0].toOptional() else .none,
2317 if (members.len >= 2) members[1].toOptional() else .none,
2318 } },
2319 });
2320 } else {
2321 const span = try c.listToSpan(members);
2322 return c.addNode(.{
2323 .tag = if (trailing) .container_decl_trailing else .container_decl,
2324 .main_token = kind_tok,
2325 .data = .{ .extra_range = span },
2326 });
2327 }
2328}
2329
2330fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeIndex {
2331 return c.addNode(.{
2332 .tag = .field_access,
2333 .main_token = try c.addToken(.period, "."),
2334 .data = .{ .node_and_token = .{
2335 lhs, try c.addTokenFmt(.identifier, "{f}", .{std.zig.fmtIdFlags(field_name, .{ .allow_primitive = true })}),
2336 } },
2337 });
2338}
2339
2340fn renderArrayInit(c: *Context, lhs: NodeIndex, inits: []const Node) !NodeIndex {
2341 const l_brace = try c.addToken(.l_brace, "{");
2342 var rendered = try c.gpa.alloc(NodeIndex, inits.len);
2343 defer c.gpa.free(rendered);
2344
2345 for (inits, 0..) |init, i| {
2346 rendered[i] = try renderNode(c, init);
2347 _ = try c.addToken(.comma, ",");
2348 }
2349 _ = try c.addToken(.r_brace, "}");
2350 switch (inits.len) {
2351 0 => return c.addNode(.{
2352 .tag = .struct_init_one,
2353 .main_token = l_brace,
2354 .data = .{ .node_and_opt_node = .{
2355 lhs, .none,
2356 } },
2357 }),
2358 1 => return c.addNode(.{
2359 .tag = .array_init_one_comma,
2360 .main_token = l_brace,
2361 .data = .{ .node_and_node = .{
2362 lhs, rendered[0],
2363 } },
2364 }),
2365 else => return c.addNode(.{
2366 .tag = .array_init_comma,
2367 .main_token = l_brace,
2368 .data = .{ .node_and_extra = .{
2369 lhs,
2370 try c.addExtra(try c.listToSpan(rendered)),
2371 } },
2372 }),
2373 }
2374}
2375
2376fn renderArrayType(c: *Context, len: u64, elem_type: Node) !NodeIndex {
2377 const l_bracket = try c.addToken(.l_bracket, "[");
2378 const len_expr = try c.addNode(.{
2379 .tag = .number_literal,
2380 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{len}),
2381 .data = undefined,
2382 });
2383 _ = try c.addToken(.r_bracket, "]");
2384 const elem_type_expr = try renderNode(c, elem_type);
2385 return c.addNode(.{
2386 .tag = .array_type,
2387 .main_token = l_bracket,
2388 .data = .{ .node_and_node = .{
2389 len_expr, elem_type_expr,
2390 } },
2391 });
2392}
2393
2394fn renderNullSentinelArrayType(c: *Context, len: u64, elem_type: Node) !NodeIndex {
2395 const l_bracket = try c.addToken(.l_bracket, "[");
2396 const len_expr = try c.addNode(.{
2397 .tag = .number_literal,
2398 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{len}),
2399 .data = undefined,
2400 });
2401 _ = try c.addToken(.colon, ":");
2402
2403 const sentinel_expr = try c.addNode(.{
2404 .tag = .number_literal,
2405 .main_token = try c.addToken(.number_literal, "0"),
2406 .data = undefined,
2407 });
2408
2409 _ = try c.addToken(.r_bracket, "]");
2410 const elem_type_expr = try renderNode(c, elem_type);
2411 return c.addNode(.{
2412 .tag = .array_type_sentinel,
2413 .main_token = l_bracket,
2414 .data = .{ .node_and_extra = .{
2415 len_expr,
2416 try c.addExtra(std.zig.Ast.Node.ArrayTypeSentinel{
2417 .sentinel = sentinel_expr,
2418 .elem_type = elem_type_expr,
2419 }),
2420 } },
2421 });
2422}
2423
2424fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
2425 switch (node.tag()) {
2426 .warning => unreachable,
2427 .static_assert, .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .wrapped_local, .mut_str => {},
2428 .while_true => {
2429 const payload = node.castTag(.while_true).?.data;
2430 return addSemicolonIfNotBlock(c, payload);
2431 },
2432 .@"while" => {
2433 const payload = node.castTag(.@"while").?.data;
2434 return addSemicolonIfNotBlock(c, payload.body);
2435 },
2436 .@"if" => {
2437 const payload = node.castTag(.@"if").?.data;
2438 if (payload.@"else") |some|
2439 return addSemicolonIfNeeded(c, some);
2440 return addSemicolonIfNotBlock(c, payload.then);
2441 },
2442 else => _ = try c.addToken(.semicolon, ";"),
2443 }
2444}
2445
2446fn addSemicolonIfNotBlock(c: *Context, node: Node) !void {
2447 switch (node.tag()) {
2448 .block, .empty_block, .block_single => {},
2449 else => _ = try c.addToken(.semicolon, ";"),
2450 }
2451}
2452
2453fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2454 switch (node.tag()) {
2455 .declaration => unreachable,
2456 .null_literal,
2457 .undefined_literal,
2458 .true_literal,
2459 .false_literal,
2460 .return_void,
2461 .zero_literal,
2462 .one_literal,
2463 .void_type,
2464 .noreturn_type,
2465 .@"anytype",
2466 .div_trunc,
2467 .int_cast,
2468 .const_cast,
2469 .volatile_cast,
2470 .as,
2471 .truncate,
2472 .bit_cast,
2473 .float_cast,
2474 .int_from_float,
2475 .float_from_int,
2476 .ptr_from_int,
2477 .std_mem_zeroes,
2478 .int_from_ptr,
2479 .sizeof,
2480 .alignof,
2481 .typeof,
2482 .typeinfo,
2483 .vector,
2484 .std_mem_zeroinit,
2485 .integer_literal,
2486 .float_literal,
2487 .string_literal,
2488 .string_slice,
2489 .char_literal,
2490 .enum_literal,
2491 .identifier,
2492 .field_access,
2493 .ptr_cast,
2494 .type,
2495 .array_access,
2496 .align_cast,
2497 .optional_type,
2498 .c_pointer,
2499 .single_pointer,
2500 .unwrap,
2501 .deref,
2502 .not,
2503 .negate,
2504 .negate_wrap,
2505 .bit_not,
2506 .func,
2507 .call,
2508 .array_type,
2509 .null_sentinel_array_type,
2510 .int_from_bool,
2511 .div_exact,
2512 .offset_of,
2513 .shuffle,
2514 .builtin_extern,
2515 .wrapped_local,
2516 .mut_str,
2517 .helper_call,
2518 .helper_ref,
2519 .byte_swap,
2520 .ceil,
2521 .cos,
2522 .sin,
2523 .exp,
2524 .exp2,
2525 .exp10,
2526 .abs,
2527 .log,
2528 .log2,
2529 .log10,
2530 .round,
2531 .sqrt,
2532 .trunc,
2533 .floor,
2534 .root_ref,
2535 .field_builtin,
2536 .@"switch",
2537 => {
2538 // no grouping needed
2539 return renderNode(c, node);
2540 },
2541
2542 .opaque_literal,
2543 .@"opaque",
2544 .empty_array,
2545 .block_single,
2546 .add,
2547 .add_wrap,
2548 .sub,
2549 .sub_wrap,
2550 .mul,
2551 .mul_wrap,
2552 .div,
2553 .shl,
2554 .shr,
2555 .mod,
2556 .@"and",
2557 .@"or",
2558 .less_than,
2559 .less_than_equal,
2560 .greater_than,
2561 .greater_than_equal,
2562 .equal,
2563 .not_equal,
2564 .bit_and,
2565 .bit_or,
2566 .bit_xor,
2567 .empty_block,
2568 .array_cat,
2569 .array_filler,
2570 .@"if",
2571 .@"struct",
2572 .@"union",
2573 .array_init,
2574 .vector_zero_init,
2575 .tuple,
2576 .container_init,
2577 .container_init_dot,
2578 .block,
2579 .address_of,
2580 => return c.addNode(.{
2581 .tag = .grouped_expression,
2582 .main_token = try c.addToken(.l_paren, "("),
2583 .data = .{ .node_and_token = .{
2584 try renderNode(c, node),
2585 try c.addToken(.r_paren, ")"),
2586 } },
2587 }),
2588 .ellipsis3,
2589 .switch_prong,
2590 .warning,
2591 .var_decl,
2592 .fail_decl,
2593 .arg_redecl,
2594 .alias,
2595 .var_simple,
2596 .pub_var_simple,
2597 .enum_constant,
2598 .@"while",
2599 .@"break",
2600 .break_val,
2601 .pub_inline_fn,
2602 .discard,
2603 .@"continue",
2604 .@"return",
2605 .@"comptime",
2606 .@"defer",
2607 .asm_simple,
2608 .while_true,
2609 .if_not_break,
2610 .switch_else,
2611 .add_assign,
2612 .add_wrap_assign,
2613 .sub_assign,
2614 .sub_wrap_assign,
2615 .mul_assign,
2616 .mul_wrap_assign,
2617 .div_assign,
2618 .shl_assign,
2619 .shr_assign,
2620 .mod_assign,
2621 .bit_and_assign,
2622 .bit_or_assign,
2623 .bit_xor_assign,
2624 .assign,
2625 .static_assert,
2626 .@"unreachable",
2627 => {
2628 // these should never appear in places where grouping might be needed.
2629 unreachable;
2630 },
2631 }
2632}
2633
2634fn renderPrefixOp(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
2635 const payload = @as(*Payload.UnOp, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
2636 return c.addNode(.{
2637 .tag = tag,
2638 .main_token = try c.addToken(tok_tag, bytes),
2639 .data = .{
2640 .node = try renderNodeGrouped(c, payload),
2641 },
2642 });
2643}
2644
2645fn renderBinOpGrouped(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
2646 const payload = @as(*Payload.BinOp, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
2647 const lhs = try renderNodeGrouped(c, payload.lhs);
2648 return c.addNode(.{
2649 .tag = tag,
2650 .main_token = try c.addToken(tok_tag, bytes),
2651 .data = .{ .node_and_node = .{
2652 lhs, try renderNodeGrouped(c, payload.rhs),
2653 } },
2654 });
2655}
2656
2657fn renderBinOp(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
2658 const payload = @as(*Payload.BinOp, @alignCast(@fieldParentPtr("base", node.ptr_otherwise))).data;
2659 const lhs = try renderNode(c, payload.lhs);
2660 return c.addNode(.{
2661 .tag = tag,
2662 .main_token = try c.addToken(tok_tag, bytes),
2663 .data = .{ .node_and_node = .{
2664 lhs, try renderNode(c, payload.rhs),
2665 } },
2666 });
2667}
2668
2669fn renderStdImport(c: *Context, parts: []const []const u8) !NodeIndex {
2670 const import_tok = try c.addToken(.builtin, "@import");
2671 _ = try c.addToken(.l_paren, "(");
2672 const std_tok = try c.addToken(.string_literal, "\"std\"");
2673 const std_node = try c.addNode(.{
2674 .tag = .string_literal,
2675 .main_token = std_tok,
2676 .data = undefined,
2677 });
2678 _ = try c.addToken(.r_paren, ")");
2679
2680 const import_node = try c.addNode(.{
2681 .tag = .builtin_call_two,
2682 .main_token = import_tok,
2683 .data = .{ .opt_node_and_opt_node = .{
2684 std_node.toOptional(), .none,
2685 } },
2686 });
2687
2688 var access_chain = import_node;
2689 for (parts) |part| {
2690 access_chain = try renderFieldAccess(c, access_chain, part);
2691 }
2692 return access_chain;
2693}
2694
2695fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex {
2696 const lparen = try c.addToken(.l_paren, "(");
2697 const res = switch (args.len) {
2698 0 => try c.addNode(.{
2699 .tag = .call_one,
2700 .main_token = lparen,
2701 .data = .{ .node_and_opt_node = .{
2702 lhs, .none,
2703 } },
2704 }),
2705 1 => try c.addNode(.{
2706 .tag = .call_one,
2707 .main_token = lparen,
2708 .data = .{ .node_and_opt_node = .{
2709 lhs, (try renderNode(c, args[0])).toOptional(),
2710 } },
2711 }),
2712 else => blk: {
2713 var rendered = try c.gpa.alloc(NodeIndex, args.len);
2714 defer c.gpa.free(rendered);
2715
2716 for (args, 0..) |arg, i| {
2717 if (i != 0) _ = try c.addToken(.comma, ",");
2718 rendered[i] = try renderNode(c, arg);
2719 }
2720 const span = try c.listToSpan(rendered);
2721 break :blk try c.addNode(.{
2722 .tag = .call,
2723 .main_token = lparen,
2724 .data = .{ .node_and_extra = .{
2725 lhs,
2726 try c.addExtra(NodeSubRange{
2727 .start = span.start,
2728 .end = span.end,
2729 }),
2730 } },
2731 });
2732 },
2733 };
2734 _ = try c.addToken(.r_paren, ")");
2735 return res;
2736}
2737
2738fn renderBuiltinCall(c: *Context, builtin: []const u8, args: []const Node) !NodeIndex {
2739 const builtin_tok = try c.addToken(.builtin, builtin);
2740 _ = try c.addToken(.l_paren, "(");
2741 var arg_1: ?NodeIndex = null;
2742 var arg_2: ?NodeIndex = null;
2743 var arg_3: ?NodeIndex = null;
2744 var arg_4: ?NodeIndex = null;
2745 switch (args.len) {
2746 0 => {},
2747 1 => {
2748 arg_1 = try renderNode(c, args[0]);
2749 },
2750 2 => {
2751 arg_1 = try renderNode(c, args[0]);
2752 _ = try c.addToken(.comma, ",");
2753 arg_2 = try renderNode(c, args[1]);
2754 },
2755 4 => {
2756 arg_1 = try renderNode(c, args[0]);
2757 _ = try c.addToken(.comma, ",");
2758 arg_2 = try renderNode(c, args[1]);
2759 _ = try c.addToken(.comma, ",");
2760 arg_3 = try renderNode(c, args[2]);
2761 _ = try c.addToken(.comma, ",");
2762 arg_4 = try renderNode(c, args[3]);
2763 },
2764 else => unreachable, // expand this function as needed.
2765 }
2766
2767 _ = try c.addToken(.r_paren, ")");
2768 if (args.len <= 2) {
2769 return c.addNode(.{
2770 .tag = .builtin_call_two,
2771 .main_token = builtin_tok,
2772 .data = .{ .opt_node_and_opt_node = .{
2773 .fromOptional(arg_1), .fromOptional(arg_2),
2774 } },
2775 });
2776 } else {
2777 std.debug.assert(args.len == 4);
2778
2779 const params = try c.listToSpan(&.{ arg_1.?, arg_2.?, arg_3.?, arg_4.? });
2780 return c.addNode(.{
2781 .tag = .builtin_call,
2782 .main_token = builtin_tok,
2783 .data = .{ .extra_range = .{
2784 .start = params.start,
2785 .end = params.end,
2786 } },
2787 });
2788 }
2789}
2790
2791fn renderVar(c: *Context, node: Node) !NodeIndex {
2792 const payload = node.castTag(.var_decl).?.data;
2793 if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
2794 if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
2795 if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
2796 if (payload.is_threadlocal) _ = try c.addToken(.keyword_threadlocal, "threadlocal");
2797 const mut_tok = if (payload.is_const)
2798 try c.addToken(.keyword_const, "const")
2799 else
2800 try c.addToken(.keyword_var, "var");
2801 _ = try c.addIdentifier(payload.name);
2802 _ = try c.addToken(.colon, ":");
2803 const type_node = try renderNode(c, payload.type);
2804
2805 const align_node_opt = if (payload.alignment) |some| blk: {
2806 _ = try c.addToken(.keyword_align, "align");
2807 _ = try c.addToken(.l_paren, "(");
2808 const res = try c.addNode(.{
2809 .tag = .number_literal,
2810 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{some}),
2811 .data = undefined,
2812 });
2813 _ = try c.addToken(.r_paren, ")");
2814 break :blk res;
2815 } else null;
2816
2817 const section_node_opt = if (payload.linksection_string) |some| blk: {
2818 _ = try c.addToken(.keyword_linksection, "linksection");
2819 _ = try c.addToken(.l_paren, "(");
2820 const res = try c.addNode(.{
2821 .tag = .string_literal,
2822 .main_token = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(some)}),
2823 .data = undefined,
2824 });
2825 _ = try c.addToken(.r_paren, ")");
2826 break :blk res;
2827 } else null;
2828
2829 const init_node_opt = if (payload.init) |some| blk: {
2830 _ = try c.addToken(.equal, "=");
2831 break :blk try renderNode(c, some);
2832 } else null;
2833 _ = try c.addToken(.semicolon, ";");
2834
2835 if (section_node_opt) |section_node| {
2836 return c.addNode(.{
2837 .tag = .global_var_decl,
2838 .main_token = mut_tok,
2839 .data = .{ .extra_and_opt_node = .{
2840 try c.addExtra(std.zig.Ast.Node.GlobalVarDecl{
2841 .type_node = type_node.toOptional(),
2842 .align_node = .fromOptional(align_node_opt),
2843 .section_node = section_node.toOptional(),
2844 .addrspace_node = .none,
2845 }),
2846 .fromOptional(init_node_opt),
2847 } },
2848 });
2849 } else {
2850 if (align_node_opt) |align_node| {
2851 return c.addNode(.{
2852 .tag = .local_var_decl,
2853 .main_token = mut_tok,
2854 .data = .{ .extra_and_opt_node = .{
2855 try c.addExtra(std.zig.Ast.Node.LocalVarDecl{
2856 .type_node = type_node,
2857 .align_node = align_node,
2858 }),
2859 .fromOptional(init_node_opt),
2860 } },
2861 });
2862 } else {
2863 return c.addNode(.{
2864 .tag = .simple_var_decl,
2865 .main_token = mut_tok,
2866 .data = .{
2867 .opt_node_and_opt_node = .{
2868 type_node.toOptional(), // Type expression
2869 .fromOptional(init_node_opt), // Init expression
2870 },
2871 },
2872 });
2873 }
2874 }
2875}
2876
2877fn renderFunc(c: *Context, node: Node) !NodeIndex {
2878 const payload = node.castTag(.func).?.data;
2879 if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
2880 if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
2881 if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
2882 if (payload.is_inline) _ = try c.addToken(.keyword_inline, "inline");
2883 const fn_token = try c.addToken(.keyword_fn, "fn");
2884 if (payload.name) |some| _ = try c.addIdentifier(some);
2885
2886 var params = try renderParams(c, payload.params, payload.is_var_args);
2887 defer params.deinit(c.gpa);
2888 var span: NodeSubRange = undefined;
2889 if (params.items.len > 1) span = try c.listToSpan(params.items);
2890
2891 const align_expr_opt = if (payload.alignment) |some| blk: {
2892 _ = try c.addToken(.keyword_align, "align");
2893 _ = try c.addToken(.l_paren, "(");
2894 const res = try c.addNode(.{
2895 .tag = .number_literal,
2896 .main_token = try c.addTokenFmt(.number_literal, "{d}", .{some}),
2897 .data = undefined,
2898 });
2899 _ = try c.addToken(.r_paren, ")");
2900 break :blk res;
2901 } else null;
2902
2903 const section_expr_opt = if (payload.linksection_string) |some| blk: {
2904 _ = try c.addToken(.keyword_linksection, "linksection");
2905 _ = try c.addToken(.l_paren, "(");
2906 const res = try c.addNode(.{
2907 .tag = .string_literal,
2908 .main_token = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(some)}),
2909 .data = undefined,
2910 });
2911 _ = try c.addToken(.r_paren, ")");
2912 break :blk res;
2913 } else null;
2914
2915 const callconv_expr_opt = if (payload.explicit_callconv) |some| blk: {
2916 _ = try c.addToken(.keyword_callconv, "callconv");
2917 _ = try c.addToken(.l_paren, "(");
2918 const cc_node = switch (some) {
2919 .c => cc_node: {
2920 _ = try c.addToken(.period, ".");
2921 break :cc_node try c.addNode(.{
2922 .tag = .enum_literal,
2923 .main_token = try c.addToken(.identifier, "c"),
2924 .data = undefined,
2925 });
2926 },
2927 .x86_64_sysv,
2928 .x86_64_win,
2929 .x86_stdcall,
2930 .x86_fastcall,
2931 .x86_thiscall,
2932 .x86_vectorcall,
2933 .x86_regcall,
2934 .aarch64_vfabi,
2935 .aarch64_sve_pcs,
2936 .arm_aapcs,
2937 .arm_aapcs_vfp,
2938 .m68k_rtd,
2939 .riscv_vector,
2940 => cc_node: {
2941 // .{ .foo = .{} }
2942 _ = try c.addToken(.period, ".");
2943 const outer_lbrace = try c.addToken(.l_brace, "{");
2944 _ = try c.addToken(.period, ".");
2945 _ = try c.addToken(.identifier, @tagName(some));
2946 _ = try c.addToken(.equal, "=");
2947 _ = try c.addToken(.period, ".");
2948 const inner_lbrace = try c.addToken(.l_brace, "{");
2949 _ = try c.addToken(.r_brace, "}");
2950 _ = try c.addToken(.r_brace, "}");
2951 break :cc_node try c.addNode(.{
2952 .tag = .struct_init_dot_two,
2953 .main_token = outer_lbrace,
2954 .data = .{ .opt_node_and_opt_node = .{
2955 (try c.addNode(.{
2956 .tag = .struct_init_dot_two,
2957 .main_token = inner_lbrace,
2958 .data = .{ .opt_node_and_opt_node = .{
2959 .none, .none,
2960 } },
2961 })).toOptional(),
2962 .none,
2963 } },
2964 });
2965 },
2966 };
2967 _ = try c.addToken(.r_paren, ")");
2968 break :blk cc_node;
2969 } else null;
2970
2971 const return_type_expr = try renderNode(c, payload.return_type);
2972
2973 const fn_proto = try blk: {
2974 if (align_expr_opt == null and section_expr_opt == null and callconv_expr_opt == null) {
2975 if (params.items.len < 2)
2976 break :blk c.addNode(.{
2977 .tag = .fn_proto_simple,
2978 .main_token = fn_token,
2979 .data = .{ .opt_node_and_opt_node = .{
2980 if (params.items.len == 1) params.items[0].toOptional() else .none,
2981 return_type_expr.toOptional(),
2982 } },
2983 })
2984 else
2985 break :blk c.addNode(.{
2986 .tag = .fn_proto_multi,
2987 .main_token = fn_token,
2988 .data = .{ .extra_and_opt_node = .{
2989 try c.addExtra(span),
2990 return_type_expr.toOptional(),
2991 } },
2992 });
2993 }
2994 if (params.items.len < 2)
2995 break :blk c.addNode(.{
2996 .tag = .fn_proto_one,
2997 .main_token = fn_token,
2998 .data = .{
2999 .extra_and_opt_node = .{
3000 try c.addExtra(std.zig.Ast.Node.FnProtoOne{
3001 .param = if (params.items.len == 1) params.items[0].toOptional() else .none,
3002 .align_expr = .fromOptional(align_expr_opt),
3003 .addrspace_expr = .none, // TODO
3004 .section_expr = .fromOptional(section_expr_opt),
3005 .callconv_expr = .fromOptional(callconv_expr_opt),
3006 }),
3007 return_type_expr.toOptional(),
3008 },
3009 },
3010 })
3011 else
3012 break :blk c.addNode(.{
3013 .tag = .fn_proto,
3014 .main_token = fn_token,
3015 .data = .{
3016 .extra_and_opt_node = .{
3017 try c.addExtra(std.zig.Ast.Node.FnProto{
3018 .params_start = span.start,
3019 .params_end = span.end,
3020 .align_expr = .fromOptional(align_expr_opt),
3021 .addrspace_expr = .none, // TODO
3022 .section_expr = .fromOptional(section_expr_opt),
3023 .callconv_expr = .fromOptional(callconv_expr_opt),
3024 }),
3025 return_type_expr.toOptional(),
3026 },
3027 },
3028 });
3029 };
3030
3031 const payload_body = payload.body orelse {
3032 if (payload.is_extern) {
3033 _ = try c.addToken(.semicolon, ";");
3034 }
3035 return fn_proto;
3036 };
3037 const body = try renderNode(c, payload_body);
3038 return c.addNode(.{
3039 .tag = .fn_decl,
3040 .main_token = fn_token,
3041 .data = .{ .node_and_node = .{
3042 fn_proto, body,
3043 } },
3044 });
3045}
3046
3047fn renderMacroFunc(c: *Context, node: Node) !NodeIndex {
3048 const payload = node.castTag(.pub_inline_fn).?.data;
3049 _ = try c.addToken(.keyword_pub, "pub");
3050 _ = try c.addToken(.keyword_inline, "inline");
3051 const fn_token = try c.addToken(.keyword_fn, "fn");
3052 _ = try c.addIdentifier(payload.name);
3053
3054 var params = try renderParams(c, payload.params, false);
3055 defer params.deinit(c.gpa);
3056 var span: NodeSubRange = undefined;
3057 if (params.items.len > 1) span = try c.listToSpan(params.items);
3058
3059 const return_type_expr = try renderNodeGrouped(c, payload.return_type);
3060
3061 const fn_proto = blk: {
3062 if (params.items.len < 2) {
3063 break :blk try c.addNode(.{
3064 .tag = .fn_proto_simple,
3065 .main_token = fn_token,
3066 .data = .{ .opt_node_and_opt_node = .{
3067 if (params.items.len == 1) params.items[0].toOptional() else .none,
3068 return_type_expr.toOptional(),
3069 } },
3070 });
3071 } else {
3072 break :blk try c.addNode(.{
3073 .tag = .fn_proto_multi,
3074 .main_token = fn_token,
3075 .data = .{ .extra_and_opt_node = .{
3076 try c.addExtra(span),
3077 return_type_expr.toOptional(),
3078 } },
3079 });
3080 }
3081 };
3082 return c.addNode(.{
3083 .tag = .fn_decl,
3084 .main_token = fn_token,
3085 .data = .{ .node_and_node = .{
3086 fn_proto, try renderNode(c, payload.body),
3087 } },
3088 });
3089}
3090
3091fn renderParams(c: *Context, params: []Payload.Param, is_var_args: bool) !std.ArrayList(NodeIndex) {
3092 _ = try c.addToken(.l_paren, "(");
3093 var rendered: std.ArrayList(NodeIndex) = .empty;
3094 errdefer rendered.deinit(c.gpa);
3095 try rendered.ensureUnusedCapacity(c.gpa, @max(params.len, 1));
3096
3097 for (params, 0..) |param, i| {
3098 if (i != 0) _ = try c.addToken(.comma, ",");
3099 if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias");
3100 if (param.name) |some| {
3101 _ = try c.addIdentifier(some);
3102 _ = try c.addToken(.colon, ":");
3103 }
3104 if (param.type.tag() == .@"anytype") {
3105 _ = try c.addToken(.keyword_anytype, "anytype");
3106 continue;
3107 }
3108 rendered.appendAssumeCapacity(try renderNode(c, param.type));
3109 }
3110 if (is_var_args) {
3111 if (params.len != 0) _ = try c.addToken(.comma, ",");
3112 _ = try c.addToken(.ellipsis3, "...");
3113 }
3114 _ = try c.addToken(.r_paren, ")");
3115
3116 return rendered;
3117}