zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob c8ccfa49 (99516B) - Raw


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