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