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