Commit 66bbd75a83

Veikka Tuominen <git@vexu.eu>
2021-02-11 16:44:04
translate-c: convert macro translation
1 parent c30c2f7
Changed files (2)
src
src/translate_c/ast.zig
@@ -47,7 +47,6 @@ pub const Node = extern union {
         field_access,
         array_access,
         call,
-        std_mem_zeroes,
         var_decl,
         func,
         warning,
@@ -57,6 +56,7 @@ pub const Node = extern union {
         @"struct",
         @"union",
         array_init,
+        tuple,
         container_init,
         std_meta_cast,
         discard,
@@ -162,6 +162,8 @@ pub const Node = extern union {
         deref,
 
         block,
+        /// { operand }
+        block_single,
         @"break",
 
         sizeof,
@@ -173,8 +175,12 @@ pub const Node = extern union {
         single_pointer,
         array_type,
 
-        /// @import("std").mem.zeroes(T)
+        /// @import("std").meta.sizeof(operand)
+        std_meta_sizeof,
+        /// @import("std").mem.zeroes(operand)
         std_mem_zeroes,
+        /// @import("std").mem.zeroInit(lhs, rhs)
+        std_mem_zeroinit,
         // pub const name = @compileError(msg);
         fail_decl,
         // var actual = mangled;
@@ -188,6 +194,9 @@ pub const Node = extern union {
         /// pub const enum_field_name = @enumToInt(enum_name.field_name);
         enum_redecl,
 
+        /// pub inline fn name(params) return_type body
+        pub_inline_fn,
+
         /// [0]type{}
         empty_array,
         /// [1]type{val} ** count
@@ -195,6 +204,7 @@ pub const Node = extern union {
 
         /// _ = operand;
         ignore,
+        @"anytype",
 
         pub const last_no_payload_tag = Tag.usingnamespace_builtins;
         pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
@@ -213,6 +223,7 @@ pub const Node = extern union {
                 .one_literal,
                 .void_type,
                 .noreturn_type,
+                .@"anytype",
                 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
 
                 .std_mem_zeroes,
@@ -234,6 +245,7 @@ pub const Node = extern union {
                 .if_not_break,
                 .switch_else,
                 .ignore,
+                .block_single,
                 => Payload.UnOp,
 
                 .add,
@@ -302,6 +314,8 @@ pub const Node = extern union {
                 .field_access,
                 .assign,
                 .align_cast,
+                .array_access,
+                .std_mem_zeroinit,
                 => Payload.BinOp,
 
                 .number_literal,
@@ -325,7 +339,7 @@ pub const Node = extern union {
                 .func => Payload.Func,
                 .@"enum" => Payload.Enum,
                 .@"struct", .@"union" => Payload.Record,
-                .array_init => Payload.ArrayInit,
+                .array_init, .tuple => Payload.ArrayInit,
                 .container_init => Payload.ContainerInit,
                 .std_meta_cast => Payload.Infix,
                 .block => Payload.Block,
@@ -336,6 +350,7 @@ pub const Node = extern union {
                 .typedef, .pub_typedef, .var_simple, .pub_var_simple => Payload.SimpleVarDecl,
                 .enum_redecl => Payload.EnumRedecl,
                 .array_filler => Payload.ArrayFiller,
+                .pub_inline_fn => Payload.PubInlineFn,
             };
         }
 
@@ -488,12 +503,12 @@ pub const Payload = struct {
             body: ?Node,
             alignment: ?c_uint,
         },
+    };
 
-        pub const Param = struct {
-            is_noalias: bool,
-            name: ?[]const u8,
-            type: Node,
-        };
+    pub const Param = struct {
+        is_noalias: bool,
+        name: ?[]const u8,
+        type: Node,
     };
 
     pub const Enum = struct {
@@ -598,6 +613,16 @@ pub const Payload = struct {
             count: usize,
         },
     };
+
+    pub const PubInlineFn = struct {
+        base: Node,
+        data: struct {
+            name: []const u8,
+            params: []Param,
+            return_type: Node,
+            body: Node,
+        },
+    };
 };
 
 /// Converts the nodes into a Zig ast.
src/translate_c.zig
@@ -3512,86 +3512,48 @@ fn transCreateNodeNumber(c: *Context, int: anytype) !Node {
     return Node.int_literal.create(c.arena, str);
 }
 
-fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias: *ast.Node.FnProto) !*ast.Node {
+fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: Node, proto_alias: *ast.Payload.Func) !Node {
     const scope = &c.global_scope.base;
 
-    const pub_tok = try appendToken(c, .Keyword_pub, "pub");
-    const fn_tok = try appendToken(c, .Keyword_fn, "fn");
-    const name_tok = try appendIdentifier(c, name);
-    _ = try appendToken(c, .LParen, "(");
-
-    var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa);
+    var fn_params = std.ArrayList(Node).init(c.gpa);
     defer fn_params.deinit();
 
     for (proto_alias.params()) |param, i| {
-        if (i != 0) {
-            _ = try appendToken(c, .Comma, ",");
-        }
-        const param_name_tok = param.name_token orelse
-            try appendTokenFmt(c, .Identifier, "arg_{d}", .{c.getMangle()});
+        const param_name = param.name orelse
+            try std.fmt.allocPrint(c.arena, "arg_{d}", .{c.getMangle()});
 
-        _ = try appendToken(c, .Colon, ":");
-
-        (try fn_params.addOne()).* = .{
-            .doc_comments = null,
-            .comptime_token = null,
-            .noalias_token = param.noalias_token,
-            .name_token = param_name_tok,
-            .param_type = param.param_type,
-        };
+        try fn_params.append(.{
+            .name = param_name,
+            .type = param.type,
+            .is_noalias = param.is_noalias,
+        });
     }
 
-    _ = try appendToken(c, .RParen, ")");
-
-    _ = try appendToken(c, .Keyword_callconv, "callconv");
-    _ = try appendToken(c, .LParen, "(");
-    const callconv_expr = try transCreateNodeEnumLiteral(c, "Inline");
-    _ = try appendToken(c, .RParen, ")");
-
-    const block_lbrace = try appendToken(c, .LBrace, "{");
-
-    const return_kw = try appendToken(c, .Keyword_return, "return");
-    const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.getInitNode().?);
-
-    const call_expr = try c.createCall(unwrap_expr, fn_params.items.len);
-    const call_params = call_expr.params();
+    const init = if (value.castTag(.var_decl)) |v|
+        v.data.init
+    else if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |v|
+        v.data.init
+    else
+        unreachable;
 
+    const unwrap_expr = try Node.unwrap.create(c.arena, init);
+    const call_params = try c.arena.alloc(Node, fn_params.items.len);
     for (fn_params.items) |param, i| {
-        if (i != 0) {
-            _ = try appendToken(c, .Comma, ",");
-        }
-        call_params[i] = try transCreateNodeIdentifier(c, tokenSlice(c, param.name_token.?));
+        call_params[i] = try Node.identifier.create(c.arena, param.name);
     }
-    call_expr.rtoken = try appendToken(c, .RParen, ")");
-
-    const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{
-        .ltoken = return_kw,
-        .tag = .Return,
-    }, .{
-        .rhs = &call_expr.base,
+    const call_expr = try Node.call.create(c.arean, .{
+        .lhs = unwrap_expr,
+        .args = call_params,
     });
-    _ = try appendToken(c, .Semicolon, ";");
+    const return_expr = try Node.@"return".create(c.arean, call_expr);
+    const block = try Node.block_single.create(c.arean, return_expr);
 
-    const block = try ast.Node.Block.alloc(c.arena, 1);
-    block.* = .{
-        .lbrace = block_lbrace,
-        .statements_len = 1,
-        .rbrace = try appendToken(c, .RBrace, "}"),
-    };
-    block.statements()[0] = &return_expr.base;
-
-    const fn_proto = try ast.Node.FnProto.create(c.arena, .{
-        .params_len = fn_params.items.len,
-        .fn_token = fn_tok,
+    return Node.pub_inline_fn.create(c.arena, .{
+        .name = name,
+        .params = try c.arena.dupe(ast.Node.Param, fn_params.items),
         .return_type = proto_alias.return_type,
-    }, .{
-        .visib_token = pub_tok,
-        .name_token = name_tok,
-        .body_node = &block.base,
-        .callconv_expr = callconv_expr,
+        .body = block,
     });
-    mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
-    return &fn_proto.base;
 }
 
 fn transCreateNodeShiftOp(
@@ -4108,27 +4070,13 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
 fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
     const scope = &c.global_scope.base;
 
-    const visib_tok = try appendToken(c, .Keyword_pub, "pub");
-    const mut_tok = try appendToken(c, .Keyword_const, "const");
-    const name_tok = try appendIdentifier(c, m.name);
-    const eq_token = try appendToken(c, .Equal, "=");
-
     const init_node = try parseCExpr(c, m, scope);
     const last = m.next().?;
     if (last != .Eof and last != .Nl)
         return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)});
 
-    const semicolon_token = try appendToken(c, .Semicolon, ";");
-    const node = try ast.Node.VarDecl.create(c.arena, .{
-        .name_token = name_tok,
-        .mut_token = mut_tok,
-        .semicolon_token = semicolon_token,
-    }, .{
-        .visib_token = visib_tok,
-        .eq_token = eq_token,
-        .init_node = init_node,
-    });
-    _ = try c.global_scope.macro_table.put(m.name, &node.base);
+    const var_decl = try Node.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node });
+    _ = try c.global_scope.macro_table.put(m.name, var_decl);
 }
 
 fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
@@ -4136,16 +4084,11 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
     defer block_scope.deinit();
     const scope = &block_scope.base;
 
-    const pub_tok = try appendToken(c, .Keyword_pub, "pub");
-    const fn_tok = try appendToken(c, .Keyword_fn, "fn");
-    const name_tok = try appendIdentifier(c, m.name);
-    _ = try appendToken(c, .LParen, "(");
-
     if (m.next().? != .LParen) {
         return m.fail(c, "unable to translate C expr: expected '('", .{});
     }
 
-    var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa);
+    var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);
     defer fn_params.deinit();
 
     while (true) {
@@ -4153,120 +4096,78 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
         _ = m.next();
 
         const mangled_name = try block_scope.makeMangledName(c, m.slice());
-        const param_name_tok = try appendIdentifier(c, mangled_name);
-        _ = try appendToken(c, .Colon, ":");
-
-        const any_type = try c.arena.create(ast.Node.OneToken);
-        any_type.* = .{
-            .base = .{ .tag = .AnyType },
-            .token = try appendToken(c, .Keyword_anytype, "anytype"),
-        };
-
-        (try fn_params.addOne()).* = .{
-            .doc_comments = null,
-            .comptime_token = null,
-            .noalias_token = null,
-            .name_token = param_name_tok,
-            .param_type = .{ .any_type = &any_type.base },
-        };
+        try fn_params.append(.{
+            .is_noalias = false,
+            .name = mangled_name,
+            .type = Node.@"anytype".init(),
+        });
 
         if (m.peek().? != .Comma) break;
         _ = m.next();
-        _ = try appendToken(c, .Comma, ",");
     }
 
     if (m.next().? != .RParen) {
         return m.fail(c, "unable to translate C expr: expected ')'", .{});
     }
 
-    _ = try appendToken(c, .RParen, ")");
-
-    _ = try appendToken(c, .Keyword_callconv, "callconv");
-    _ = try appendToken(c, .LParen, "(");
-    const callconv_expr = try transCreateNodeEnumLiteral(c, "Inline");
-    _ = try appendToken(c, .RParen, ")");
-
-    const type_of = try c.createBuiltinCall("@TypeOf", 1);
-
-    const return_kw = try appendToken(c, .Keyword_return, "return");
     const expr = try parseCExpr(c, m, scope);
     const last = m.next().?;
     if (last != .Eof and last != .Nl)
         return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)});
-    _ = try appendToken(c, .Semicolon, ";");
-    const type_of_arg = if (!expr.tag.isBlock()) expr else blk: {
-        const stmts = expr.blockStatements();
-        const blk_last = stmts[stmts.len - 1];
-        const br = blk_last.cast(ast.Node.ControlFlowExpression).?;
-        break :blk br.getRHS().?;
-    };
-    type_of.params()[0] = type_of_arg;
-    type_of.rparen_token = try appendToken(c, .RParen, ")");
-    const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{
-        .ltoken = return_kw,
-        .tag = .Return,
-    }, .{
-        .rhs = expr,
-    });
 
+    const typeof_arg = if (expr.castTag(.block)) |some| blk: {
+        const stmts = some.data.stmts;
+        const blk_last = stmts[stmts.len - 1];
+        const br = blk_last.castTag(.break_val).?;
+        break :blk br.data;
+    } else expr;
+    const typeof = try Node.typeof.create(c.arean, typeof_arg);
+    const return_expr = try Node.@"return".create(c.arena, expr);
     try block_scope.statements.append(&return_expr.base);
-    const block_node = try block_scope.complete(c);
-    const fn_proto = try ast.Node.FnProto.create(c.arena, .{
-        .fn_token = fn_tok,
-        .params_len = fn_params.items.len,
-        .return_type = .{ .Explicit = &type_of.base },
-    }, .{
-        .visib_token = pub_tok,
-        .name_token = name_tok,
-        .body_node = block_node,
-        .callconv_expr = callconv_expr,
+    
+    const fn_decl = try Node.pub_inline_fn.create(c.arena, .{
+        .name = m.name,
+        .params = try c.arena.dupe(ast.Payload.Param, fn_params.items),
+        .return_type = typeof,
+        .body = try block_scope.complete(c),
     });
-    mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
-
     _ = try c.global_scope.macro_table.put(m.name, &fn_proto.base);
 }
 
 const ParseError = Error || error{ParseError};
 
-fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     // TODO parseCAssignExpr here
     const node = try parseCCondExpr(c, m, scope);
     if (m.next().? != .Comma) {
         m.i -= 1;
         return node;
     }
-    _ = try appendToken(c, .Semicolon, ";");
     var block_scope = try Scope.Block.init(c, scope, true);
     defer block_scope.deinit();
 
     var last = node;
     while (true) {
         // suppress result
-        const lhs = try transCreateNodeIdentifier(c, "_");
-        const op_token = try appendToken(c, .Equal, "=");
-        const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
-        op_node.* = .{
-            .base = .{ .tag = .Assign },
-            .op_token = op_token,
-            .lhs = lhs,
-            .rhs = last,
-        };
-        try block_scope.statements.append(&op_node.base);
+        const ignore = try Node.ignore.create(c.arena, last);
+        try block_scope.statements.append(ignore);
 
         last = try parseCCondExpr(c, m, scope);
-        _ = try appendToken(c, .Semicolon, ";");
         if (m.next().? != .Comma) {
             m.i -= 1;
             break;
         }
     }
 
-    const break_node = try transCreateNodeBreak(c, block_scope.label, last);
-    try block_scope.statements.append(&break_node.base);
+    const break_node = try Node.break_val.create(c.arena, .{
+        .label = block_scope.label,
+        .val = last,
+    });
+    try block_scope.statements.append(break_node);
     return try block_scope.complete(c);
 }
 
-fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
+fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
     var lit_bytes = m.slice();
 
     switch (m.list[m.i].id) {
@@ -4286,11 +4187,10 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
             }
 
             if (suffix == .none) {
-                return transCreateNodeInt(c, lit_bytes);
+                return transCreateNodeNumber(c, lit_bytes);
             }
 
-            const cast_node = try c.createBuiltinCall("@as", 2);
-            cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
+            const type_node = try Node.type.create(c.arena, switch (suffix) {
                 .u => "c_uint",
                 .l => "c_long",
                 .lu => "c_ulong",
@@ -4304,27 +4204,22 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
                 .llu => 3,
                 else => unreachable,
             }];
-            _ = try appendToken(c, .Comma, ",");
-            cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes);
-            cast_node.rparen_token = try appendToken(c, .RParen, ")");
-            return &cast_node.base;
+            const rhs = try transCreateNodeNumber(c, lit_bytes);
+            return Node.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs });
         },
         .FloatLiteral => |suffix| {
             if (lit_bytes[0] == '.')
                 lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes});
             if (suffix == .none) {
-                return transCreateNodeFloat(c, lit_bytes);
+                return transCreateNodeNumber(c, lit_bytes);
             }
-            const cast_node = try c.createBuiltinCall("@as", 2);
-            cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
+            const type_node = try Node.type.create(c.arena, switch (suffix) {
                 .f => "f32",
                 .l => "c_longdouble",
                 else => unreachable,
             });
-            _ = try appendToken(c, .Comma, ",");
-            cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]);
-            cast_node.rparen_token = try appendToken(c, .RParen, ")");
-            return &cast_node.base;
+            const rhs = try transCreateNodeNumber(c, lit_bytes[0 .. lit_bytes.len - 1]);
+            return Node.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs });
         },
         else => unreachable,
     }
@@ -4490,79 +4385,62 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
     return bytes[0..i];
 }
 
-fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     const tok = m.next().?;
     const slice = m.slice();
     switch (tok) {
         .CharLiteral => {
             if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
-                const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, m));
-                const node = try c.arena.create(ast.Node.OneToken);
-                node.* = .{
-                    .base = .{ .tag = .CharLiteral },
-                    .token = token,
-                };
-                return &node.base;
+                return Node.char_literal.create(c.arena, try zigifyEscapeSequences(c, m));
             } else {
-                const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{slice[1 .. slice.len - 1]});
-                const node = try c.arena.create(ast.Node.OneToken);
-                node.* = .{
-                    .base = .{ .tag = .IntegerLiteral },
-                    .token = token,
-                };
-                return &node.base;
+                const str = try std.fmt.allocPrint(c.arena, "0x{x}", .{slice[1 .. slice.len - 1]});
+                return Node.int_literal.create(c.arena, str);
             }
         },
         .StringLiteral => {
-            const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, m));
-            const node = try c.arena.create(ast.Node.OneToken);
-            node.* = .{
-                .base = .{ .tag = .StringLiteral },
-                .token = token,
-            };
-            return &node.base;
+            return Node.string_literal.create(c.arena, try zigifyEscapeSequences(c, m));
         },
         .IntegerLiteral, .FloatLiteral => {
             return parseCNumLit(c, m);
         },
         // eventually this will be replaced by std.c.parse which will handle these correctly
-        .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"),
-        .Keyword_bool => return transCreateNodeIdentifierUnchecked(c, "bool"),
-        .Keyword_double => return transCreateNodeIdentifierUnchecked(c, "f64"),
-        .Keyword_long => return transCreateNodeIdentifierUnchecked(c, "c_long"),
-        .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
-        .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"),
-        .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
-        .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
+        .Keyword_void => return Node.type.create(c.arena, "c_void"),
+        .Keyword_bool => return Node.type.create(c.arena, "bool"),
+        .Keyword_double => return Node.type.create(c.arena, "f64"),
+        .Keyword_long => return Node.type.create(c.arena, "c_long"),
+        .Keyword_int => return Node.type.create(c.arena, "c_int"),
+        .Keyword_float => return Node.type.create(c.arena, "f32"),
+        .Keyword_short => return Node.type.create(c.arena, "c_short"),
+        .Keyword_char => return Node.type.create(c.arena, "u8"),
         .Keyword_unsigned => if (m.next()) |t| switch (t) {
-            .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
-            .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_ushort"),
-            .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_uint"),
+            .Keyword_char => return Node.type.create(c.arena, "u8"),
+            .Keyword_short => return Node.type.create(c.arena, "c_ushort"),
+            .Keyword_int => return Node.type.create(c.arena, "c_uint"),
             .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
                 _ = m.next();
-                return transCreateNodeIdentifierUnchecked(c, "c_ulonglong");
-            } else return transCreateNodeIdentifierUnchecked(c, "c_ulong"),
+                return Node.type.create(c.arena, "c_ulonglong");
+            } else return Node.type.create(c.arena, "c_ulong"),
             else => {
                 m.i -= 1;
-                return transCreateNodeIdentifierUnchecked(c, "c_uint");
+                return Node.type.create(c.arena, "c_uint");
             },
         } else {
-            return transCreateNodeIdentifierUnchecked(c, "c_uint");
+            return Node.type.create(c.arena, "c_uint");
         },
         .Keyword_signed => if (m.next()) |t| switch (t) {
-            .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "i8"),
-            .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
-            .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
+            .Keyword_char => return Node.type.create(c.arena, "i8"),
+            .Keyword_short => return Node.type.create(c.arena, "c_short"),
+            .Keyword_int => return Node.type.create(c.arena, "c_int"),
             .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) {
                 _ = m.next();
-                return transCreateNodeIdentifierUnchecked(c, "c_longlong");
-            } else return transCreateNodeIdentifierUnchecked(c, "c_long"),
+                return Node.type.create(c.arena, "c_longlong");
+            } else return Node.type.create(c.arena, "c_long"),
             else => {
                 m.i -= 1;
-                return transCreateNodeIdentifierUnchecked(c, "c_int");
+                return Node.type.create(c.arena, "c_int");
             },
         } else {
-            return transCreateNodeIdentifierUnchecked(c, "c_int");
+            return Node.type.create(c.arena, "c_int");
         },
         .Keyword_enum, .Keyword_struct, .Keyword_union => {
             // struct Foo will be declared as struct_Foo by transRecordDecl
@@ -4572,17 +4450,12 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*
                 return error.ParseError;
             }
 
-            const ident_token = try appendTokenFmt(c, .Identifier, "{s}_{s}", .{ slice, m.slice() });
-            const identifier = try c.arena.create(ast.Node.OneToken);
-            identifier.* = .{
-                .base = .{ .tag = .Identifier },
-                .token = ident_token,
-            };
-            return &identifier.base;
+            const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() });
+            return Node.identifier.create(c.arena, name);
         },
         .Identifier => {
             const mangled_name = scope.getAlias(slice);
-            return transCreateNodeIdentifier(c, checkForBuiltinTypedef(mangled_name) orelse mangled_name);
+            return Node.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name);
         },
         .LParen => {
             const inner_node = try parseCExpr(c, m, scope);
@@ -4612,10 +4485,6 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*
                 },
                 else => return inner_node,
             }
-
-            // hack to get zig fmt to render a comma in builtin calls
-            _ = try appendToken(c, .Comma, ",");
-
             const node_to_cast = try parseCExpr(c, m, scope);
 
             if (saw_l_paren and m.next().? != .RParen) {
@@ -4623,28 +4492,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*
                 return error.ParseError;
             }
 
-            const lparen = try appendToken(c, .LParen, "(");
-
-            //(@import("std").meta.cast(dest, x))
-            const import_fn_call = try c.createBuiltinCall("@import", 1);
-            const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
-            import_fn_call.params()[0] = std_node;
-            import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
-            const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
-            const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "cast");
-
-            const cast_fn_call = try c.createCall(outer_field_access, 2);
-            cast_fn_call.params()[0] = inner_node;
-            cast_fn_call.params()[1] = node_to_cast;
-            cast_fn_call.rtoken = try appendToken(c, .RParen, ")");
-
-            const group_node = try c.arena.create(ast.Node.GroupedExpression);
-            group_node.* = .{
-                .lparen = lparen,
-                .expr = &cast_fn_call.base,
-                .rparen = try appendToken(c, .RParen, ")"),
-            };
-            return &group_node.base;
+            return Node.std_meta_cast.create(c.arena, .{ .lhs = inner_node, .rhs = node_to_cast });
         },
         else => {
             try m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(tok)});
@@ -4653,447 +4501,255 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*
     }
 }
 
-fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     var node = try parseCPrimaryExprInner(c, m, scope);
     // In C the preprocessor would handle concatting strings while expanding macros.
     // This should do approximately the same by concatting any strings and identifiers
     // after a primary expression.
     while (true) {
-        var op_token: ast.TokenIndex = undefined;
-        var op_id: ast.Node.Tag = undefined;
         switch (m.peek().?) {
             .StringLiteral, .Identifier => {},
             else => break,
         }
-        const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
-        op_node.* = .{
-            .base = .{ .tag = .ArrayCat },
-            .op_token = try appendToken(c, .PlusPlus, "++"),
-            .lhs = node,
-            .rhs = try parseCPrimaryExprInner(c, m, scope),
-        };
-        node = &op_node.base;
+        node = try Node.array_cat.create(c.arena, .{ .lhs = node, .rhs = try parseCPrimaryExprInner(c, m, scope) });
     }
     return node;
 }
 
-fn nodeIsInfixOp(tag: ast.Node.Tag) bool {
-    return switch (tag) {
-        .Add,
-        .AddWrap,
-        .ArrayCat,
-        .ArrayMult,
-        .Assign,
-        .AssignBitAnd,
-        .AssignBitOr,
-        .AssignBitShiftLeft,
-        .AssignBitShiftRight,
-        .AssignBitXor,
-        .AssignDiv,
-        .AssignSub,
-        .AssignSubWrap,
-        .AssignMod,
-        .AssignAdd,
-        .AssignAddWrap,
-        .AssignMul,
-        .AssignMulWrap,
-        .BangEqual,
-        .BitAnd,
-        .BitOr,
-        .BitShiftLeft,
-        .BitShiftRight,
-        .BitXor,
-        .BoolAnd,
-        .BoolOr,
-        .Div,
-        .EqualEqual,
-        .ErrorUnion,
-        .GreaterOrEqual,
-        .GreaterThan,
-        .LessOrEqual,
-        .LessThan,
-        .MergeErrorSets,
-        .Mod,
-        .Mul,
-        .MulWrap,
-        .Period,
-        .Range,
-        .Sub,
-        .SubWrap,
-        .UnwrapOptional,
-        .Catch,
-        => true,
-
-        else => false,
-    };
-}
-
-fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node {
+fn macroBoolToInt(c: *Context, node: Node) !Node {
     if (!isBoolRes(node)) {
-        if (!nodeIsInfixOp(node.tag)) return node;
-
-        const group_node = try c.arena.create(ast.Node.GroupedExpression);
-        group_node.* = .{
-            .lparen = try appendToken(c, .LParen, "("),
-            .expr = node,
-            .rparen = try appendToken(c, .RParen, ")"),
-        };
-        return &group_node.base;
+        return node;
     }
 
-    const builtin_node = try c.createBuiltinCall("@boolToInt", 1);
-    builtin_node.params()[0] = node;
-    builtin_node.rparen_token = try appendToken(c, .RParen, ")");
-    return &builtin_node.base;
+    return Node.bool_to_int.create(c.arena, node);
 }
 
-fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
+fn macroIntToBool(c: *Context, node: Node) !Node {
     if (isBoolRes(node)) {
-        if (!nodeIsInfixOp(node.tag)) return node;
-
-        const group_node = try c.arena.create(ast.Node.GroupedExpression);
-        group_node.* = .{
-            .lparen = try appendToken(c, .LParen, "("),
-            .expr = node,
-            .rparen = try appendToken(c, .RParen, ")"),
-        };
-        return &group_node.base;
+        return node;
     }
 
-    const op_token = try appendToken(c, .BangEqual, "!=");
-    const zero = try transCreateNodeInt(c, 0);
-    const res = try c.arena.create(ast.Node.SimpleInfixOp);
-    res.* = .{
-        .base = .{ .tag = .BangEqual },
-        .op_token = op_token,
-        .lhs = node,
-        .rhs = zero,
-    };
-    const group_node = try c.arena.create(ast.Node.GroupedExpression);
-    group_node.* = .{
-        .lparen = try appendToken(c, .LParen, "("),
-        .expr = &res.base,
-        .rparen = try appendToken(c, .RParen, ")"),
-    };
-    return &group_node.base;
-}
-
-fn macroGroup(c: *Context, node: *ast.Node) !*ast.Node {
-    if (!nodeIsInfixOp(node.tag)) return node;
-
-    const group_node = try c.arena.create(ast.Node.GroupedExpression);
-    group_node.* = .{
-        .lparen = try appendToken(c, .LParen, "("),
-        .expr = node,
-        .rparen = try appendToken(c, .RParen, ")"),
-    };
-    return &group_node.base;
+    return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init() });
 }
 
-fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     const node = try parseCOrExpr(c, m, scope);
     if (m.peek().? != .QuestionMark) {
         return node;
     }
     _ = m.next();
 
-    // must come immediately after expr
-    _ = try appendToken(c, .RParen, ")");
-    const if_node = try transCreateNodeIf(c);
-    if_node.condition = node;
-    if_node.body = try parseCOrExpr(c, m, scope);
+    const then_body = try parseCOrExpr(c, m, scope);
     if (m.next().? != .Colon) {
         try m.fail(c, "unable to translate C expr: expected ':'", .{});
         return error.ParseError;
     }
-    if_node.@"else" = try transCreateNodeElse(c);
-    if_node.@"else".?.body = try parseCCondExpr(c, m, scope);
-    return &if_node.base;
+    const else_body = try parseCCondExpr(c, m, scope);
+    return Node.@"if".create(c.arena, .{ .cond = node, .then = then_body, .@"else" = else_body });
 }
 
-fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     var node = try parseCAndExpr(c, m, scope);
     while (m.next().? == .PipePipe) {
-        const lhs_node = try macroIntToBool(c, node);
-        const op_token = try appendToken(c, .Keyword_or, "or");
-        const rhs_node = try parseCAndExpr(c, m, scope);
-        const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
-        op_node.* = .{
-            .base = .{ .tag = .BoolOr },
-            .op_token = op_token,
-            .lhs = lhs_node,
-            .rhs = try macroIntToBool(c, rhs_node),
-        };
-        node = &op_node.base;
+        const lhs = try macroIntToBool(c, node);
+        const rhs = try macroIntToBool(c, try parseCAndExpr(c, m, scope));
+        node = try Node.@"or".create(c.arena, .{ .lhs = lhs, .rhs = rhs });
     }
     m.i -= 1;
     return node;
 }
 
-fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     var node = try parseCBitOrExpr(c, m, scope);
     while (m.next().? == .AmpersandAmpersand) {
-        const lhs_node = try macroIntToBool(c, node);
-        const op_token = try appendToken(c, .Keyword_and, "and");
-        const rhs_node = try parseCBitOrExpr(c, m, scope);
-        const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
-        op_node.* = .{
-            .base = .{ .tag = .BoolAnd },
-            .op_token = op_token,
-            .lhs = lhs_node,
-            .rhs = try macroIntToBool(c, rhs_node),
-        };
-        node = &op_node.base;
+        const lhs = try macroIntToBool(c, node);
+        const rhs = try macroIntToBool(c, try parseCBitOrExpr(c, m, scope));
+        node = try Node.@"and".create(c.arena, .{ .lhs = lhs, .rhs = rhs });
     }
     m.i -= 1;
     return node;
 }
 
-fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     var node = try parseCBitXorExpr(c, m, scope);
     while (m.next().? == .Pipe) {
-        const lhs_node = try macroBoolToInt(c, node);
-        const op_token = try appendToken(c, .Pipe, "|");
-        const rhs_node = try parseCBitXorExpr(c, m, scope);
-        const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
-        op_node.* = .{
-            .base = .{ .tag = .BitOr },
-            .op_token = op_token,
-            .lhs = lhs_node,
-            .rhs = try macroBoolToInt(c, rhs_node),
-        };
-        node = &op_node.base;
+        const lhs = try macroBoolToInt(c, node);
+        const rhs = try macroBoolToInt(c, try parseCBitXorExpr(c, m, scope));
+        node = try Node.bit_or.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
     }
     m.i -= 1;
     return node;
 }
 
-fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     var node = try parseCBitAndExpr(c, m, scope);
     while (m.next().? == .Caret) {
-        const lhs_node = try macroBoolToInt(c, node);
-        const op_token = try appendToken(c, .Caret, "^");
-        const rhs_node = try parseCBitAndExpr(c, m, scope);
-        const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
-        op_node.* = .{
-            .base = .{ .tag = .BitXor },
-            .op_token = op_token,
-            .lhs = lhs_node,
-            .rhs = try macroBoolToInt(c, rhs_node),
-        };
-        node = &op_node.base;
+        const lhs = try macroBoolToInt(c, node);
+        const rhs = try macroBoolToInt(c, try parseCBitAndExpr(c, m, scope));
+        node = try Node.bit_xor.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
     }
     m.i -= 1;
     return node;
 }
 
-fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     var node = try parseCEqExpr(c, m, scope);
     while (m.next().? == .Ampersand) {
-        const lhs_node = try macroBoolToInt(c, node);
-        const op_token = try appendToken(c, .Ampersand, "&");
-        const rhs_node = try parseCEqExpr(c, m, scope);
-        const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
-        op_node.* = .{
-            .base = .{ .tag = .BitAnd },
-            .op_token = op_token,
-            .lhs = lhs_node,
-            .rhs = try macroBoolToInt(c, rhs_node),
-        };
-        node = &op_node.base;
+        const lhs = try macroBoolToInt(c, node);
+        const rhs = try macroBoolToInt(c, try parseCEqExpr(c, m, scope));
+        node = try Node.bit_and.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
     }
     m.i -= 1;
     return node;
 }
 
-fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     var node = try parseCRelExpr(c, m, scope);
     while (true) {
-        var op_token: ast.TokenIndex = undefined;
-        var op_id: ast.Node.Tag = undefined;
         switch (m.peek().?) {
             .BangEqual => {
-                op_token = try appendToken(c, .BangEqual, "!=");
-                op_id = .BangEqual;
+                _ = m.next();
+                const lhs = try macroBoolToInt(c, node);
+                const rhs = try macroBoolToInt(c, try parseCRelExpr(c, m, scope));
+                node = try Node.not_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
             },
             .EqualEqual => {
-                op_token = try appendToken(c, .EqualEqual, "==");
-                op_id = .EqualEqual;
+                _ = m.next();
+                const lhs = try macroBoolToInt(c, node);
+                const rhs = try macroBoolToInt(c, try parseCRelExpr(c, m, scope));
+                node = try Node.equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
             },
             else => return node,
         }
-        _ = m.next();
-        const lhs_node = try macroBoolToInt(c, node);
-        const rhs_node = try parseCRelExpr(c, m, scope);
-        const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
-        op_node.* = .{
-            .base = .{ .tag = op_id },
-            .op_token = op_token,
-            .lhs = lhs_node,
-            .rhs = try macroBoolToInt(c, rhs_node),
-        };
-        node = &op_node.base;
     }
 }
 
-fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     var node = try parseCShiftExpr(c, m, scope);
     while (true) {
-        var op_token: ast.TokenIndex = undefined;
-        var op_id: ast.Node.Tag = undefined;
         switch (m.peek().?) {
             .AngleBracketRight => {
-                op_token = try appendToken(c, .AngleBracketRight, ">");
-                op_id = .GreaterThan;
+                _ = m.next();
+                const lhs = try macroBoolToInt(c, node);
+                const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope));
+                node = try Node.greater_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
             },
             .AngleBracketRightEqual => {
-                op_token = try appendToken(c, .AngleBracketRightEqual, ">=");
-                op_id = .GreaterOrEqual;
+                _ = m.next();
+                const lhs = try macroBoolToInt(c, node);
+                const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope));
+                node = try Node.greater_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
             },
             .AngleBracketLeft => {
-                op_token = try appendToken(c, .AngleBracketLeft, "<");
-                op_id = .LessThan;
+                _ = m.next();
+                const lhs = try macroBoolToInt(c, node);
+                const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope));
+                node = try Node.less_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
             },
             .AngleBracketLeftEqual => {
-                op_token = try appendToken(c, .AngleBracketLeftEqual, "<=");
-                op_id = .LessOrEqual;
+                _ = m.next();
+                const lhs = try macroBoolToInt(c, node);
+                const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope));
+                node = try Node.less_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
             },
             else => return node,
         }
-        _ = m.next();
-        const lhs_node = try macroBoolToInt(c, node);
-        const rhs_node = try parseCShiftExpr(c, m, scope);
-        const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
-        op_node.* = .{
-            .base = .{ .tag = op_id },
-            .op_token = op_token,
-            .lhs = lhs_node,
-            .rhs = try macroBoolToInt(c, rhs_node),
-        };
-        node = &op_node.base;
     }
 }
 
-fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     var node = try parseCAddSubExpr(c, m, scope);
     while (true) {
-        var op_token: ast.TokenIndex = undefined;
-        var op_id: ast.Node.Tag = undefined;
         switch (m.peek().?) {
             .AngleBracketAngleBracketLeft => {
-                op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<");
-                op_id = .BitShiftLeft;
+                _ = m.next();
+                const lhs = try macroBoolToInt(c, node);
+                const rhs = try macroBoolToInt(c, try parseCAddSubExpr(c, m, scope));
+                node = try Node.shl.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
             },
             .AngleBracketAngleBracketRight => {
-                op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>");
-                op_id = .BitShiftRight;
+                _ = m.next();
+                const lhs = try macroBoolToInt(c, node);
+                const rhs = try macroBoolToInt(c, try parseCAddSubExpr(c, m, scope));
+                node = try Node.shr.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
             },
             else => return node,
         }
-        _ = m.next();
-        const lhs_node = try macroBoolToInt(c, node);
-        const rhs_node = try parseCAddSubExpr(c, m, scope);
-        const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
-        op_node.* = .{
-            .base = .{ .tag = op_id },
-            .op_token = op_token,
-            .lhs = lhs_node,
-            .rhs = try macroBoolToInt(c, rhs_node),
-        };
-        node = &op_node.base;
     }
 }
 
-fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     var node = try parseCMulExpr(c, m, scope);
     while (true) {
-        var op_token: ast.TokenIndex = undefined;
-        var op_id: ast.Node.Tag = undefined;
         switch (m.peek().?) {
             .Plus => {
-                op_token = try appendToken(c, .Plus, "+");
-                op_id = .Add;
+                _ = m.next();
+                const lhs = try macroBoolToInt(c, node);
+                const rhs = try macroBoolToInt(c, try parseCMulExpr(c, m, scope));
+                node = try Node.add.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
             },
             .Minus => {
-                op_token = try appendToken(c, .Minus, "-");
-                op_id = .Sub;
+                _ = m.next();
+                const lhs = try macroBoolToInt(c, node);
+                const rhs = try macroBoolToInt(c, try parseCMulExpr(c, m, scope));
+                node = try Node.sub.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
             },
             else => return node,
         }
-        _ = m.next();
-        const lhs_node = try macroBoolToInt(c, node);
-        const rhs_node = try parseCMulExpr(c, m, scope);
-        const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
-        op_node.* = .{
-            .base = .{ .tag = op_id },
-            .op_token = op_token,
-            .lhs = lhs_node,
-            .rhs = try macroBoolToInt(c, rhs_node),
-        };
-        node = &op_node.base;
     }
 }
 
-fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     var node = try parseCUnaryExpr(c, m, scope);
     while (true) {
-        var op_token: ast.TokenIndex = undefined;
-        var op_id: ast.Node.Tag = undefined;
         switch (m.next().?) {
             .Asterisk => {
                 if (m.peek().? == .RParen) {
                     // type *)
 
-                    // hack to get zig fmt to render a comma in builtin calls
-                    _ = try appendToken(c, .Comma, ",");
-
                     // last token of `node`
                     const prev_id = m.list[m.i - 1].id;
 
                     if (prev_id == .Keyword_void) {
-                        const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
-                        ptr.rhs = node;
-                        const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?");
-                        optional_node.rhs = &ptr.base;
-                        return &optional_node.base;
+                        const ptr = try Node.single_pointer.create(c.arena, .{
+                            .is_const = false,
+                            .is_volatile = false,
+                            .elem_type = node,
+                        });
+                        return Node.optional_type.create(c.arena, ptr);
                     } else {
-                        const ptr = try transCreateNodePtrType(c, false, false, Token.Id.Identifier);
-                        ptr.rhs = node;
-                        return &ptr.base;
+                        return Node.c_pointer.create(c.arena, .{
+                            .is_const = false,
+                            .is_volatile = false,
+                            .elem_type = node,
+                        });
                     }
                 } else {
                     // expr * expr
-                    op_token = try appendToken(c, .Asterisk, "*");
-                    op_id = .BitShiftLeft;
+                    const lhs = try macroBoolToInt(c, node);
+                    const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
+                    node = try Node.mul.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
                 }
             },
             .Slash => {
-                op_id = .Div;
-                op_token = try appendToken(c, .Slash, "/");
+                const lhs = try macroBoolToInt(c, node);
+                const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
+                node = try Node.div.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
             },
             .Percent => {
-                op_id = .Mod;
-                op_token = try appendToken(c, .Percent, "%");
+                const lhs = try macroBoolToInt(c, node);
+                const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
+                node = try Node.mod.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
             },
             else => {
                 m.i -= 1;
                 return node;
             },
         }
-        const lhs_node = try macroBoolToInt(c, node);
-        const rhs_node = try parseCUnaryExpr(c, m, scope);
-        const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
-        op_node.* = .{
-            .base = .{ .tag = op_id },
-            .op_token = op_token,
-            .lhs = lhs_node,
-            .rhs = try macroBoolToInt(c, rhs_node),
-        };
-        node = &op_node.base;
     }
 }
 
-fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     var node = try parseCPrimaryExpr(c, m, scope);
     while (true) {
         switch (m.next().?) {
@@ -5103,38 +4759,31 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
                     return error.ParseError;
                 }
 
-                node = try transCreateNodeFieldAccess(c, node, m.slice());
-                continue;
+                const ident = try Node.identifier.create(c.arena, m.slice());
+                node = try Node.field_access.create(c.arena, .{ .lhs = node, .rhs = ident });
             },
             .Arrow => {
                 if (m.next().? != .Identifier) {
                     try m.fail(c, "unable to translate C expr: expected identifier", .{});
                     return error.ParseError;
                 }
-                const deref = try transCreateNodePtrDeref(c, node);
-                node = try transCreateNodeFieldAccess(c, deref, m.slice());
-                continue;
+
+                const deref = try Node.deref.create(c.arena, node);
+                const ident = try Node.identifier.create(c.arena, m.slice());
+                node = try Node.field_access.create(c.arena, .{ .lhs = deref, .rhs = ident });
             },
             .LBracket => {
-                const arr_node = try transCreateNodeArrayAccess(c, node);
-                arr_node.index_expr = try parseCExpr(c, m, scope);
-                arr_node.rtoken = try appendToken(c, .RBracket, "]");
-                node = &arr_node.base;
-                if (m.next().? != .RBracket) {
-                    try m.fail(c, "unable to translate C expr: expected ']'", .{});
-                    return error.ParseError;
-                }
-                continue;
+                const index = try macroBoolToInt(c, try parseCExpr(c, m, scope));
+                node = try Node.array_access.create(c.arena, .{ .lhs = node, .rhs = index });
             },
             .LParen => {
-                _ = try appendToken(c, .LParen, "(");
-                var call_params = std.ArrayList(*ast.Node).init(c.gpa);
+                var call_params = std.ArrayList(Node).init(c.gpa);
                 defer call_params.deinit();
                 while (true) {
                     const arg = try parseCCondExpr(c, m, scope);
                     try call_params.append(arg);
                     switch (m.next().?) {
-                        .Comma => _ = try appendToken(c, .Comma, ","),
+                        .Comma => {},
                         .RParen => break,
                         else => {
                             try m.fail(c, "unable to translate C expr: expected ',' or ')'", .{});
@@ -5142,32 +4791,17 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
                         },
                     }
                 }
-                const call_node = try ast.Node.Call.alloc(c.arena, call_params.items.len);
-                call_node.* = .{
-                    .lhs = node,
-                    .params_len = call_params.items.len,
-                    .async_token = null,
-                    .rtoken = try appendToken(c, .RParen, ")"),
-                };
-                mem.copy(*ast.Node, call_node.params(), call_params.items);
-                node = &call_node.base;
-                continue;
+                node = try Node.call.create(c.arena, .{ .lhs = node, .rhs = try c.arena.dupe(Node, call_params.items) });
             },
             .LBrace => {
-                // must come immediately after `node`
-                _ = try appendToken(c, .Comma, ",");
-
-                const dot = try appendToken(c, .Period, ".");
-                _ = try appendToken(c, .LBrace, "{");
-
-                var init_vals = std.ArrayList(*ast.Node).init(c.gpa);
+                var init_vals = std.ArrayList(Node).init(c.gpa);
                 defer init_vals.deinit();
 
                 while (true) {
                     const val = try parseCCondExpr(c, m, scope);
                     try init_vals.append(val);
                     switch (m.next().?) {
-                        .Comma => _ = try appendToken(c, .Comma, ","),
+                        .Comma => {},
                         .RBrace => break,
                         else => {
                             try m.fail(c, "unable to translate C expr: expected ',' or '}}'", .{});
@@ -5175,29 +4809,8 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
                         },
                     }
                 }
-                const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len);
-                tuple_node.* = .{
-                    .dot = dot,
-                    .list_len = init_vals.items.len,
-                    .rtoken = try appendToken(c, .RBrace, "}"),
-                };
-                mem.copy(*ast.Node, tuple_node.list(), init_vals.items);
-
-                //(@import("std").mem.zeroInit(T, .{x}))
-                const import_fn_call = try c.createBuiltinCall("@import", 1);
-                const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
-                import_fn_call.params()[0] = std_node;
-                import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
-                const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem");
-                const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroInit");
-
-                const zero_init_call = try c.createCall(outer_field_access, 2);
-                zero_init_call.params()[0] = node;
-                zero_init_call.params()[1] = &tuple_node.base;
-                zero_init_call.rtoken = try appendToken(c, .RParen, ")");
-
-                node = &zero_init_call.base;
-                continue;
+                const tuple_node = try Node.tuple.create(c.arena, try c.arena.dupe(Node, init_vals.items));
+                node = try Node.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node });
             },
             .PlusPlus, .MinusMinus => {
                 try m.fail(c, "TODO postfix inc/dec expr", .{});
@@ -5211,35 +4824,31 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
     }
 }
 
-fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
     switch (m.next().?) {
         .Bang => {
-            const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");
-            node.rhs = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope));
-            return &node.base;
+            const operand = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope));
+            return Node.not.create(c.arena, operand);
         },
         .Minus => {
-            const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");
-            node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
-            return &node.base;
+            const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
+            return Node.negate.create(c.arena, operand);
         },
         .Plus => return try parseCUnaryExpr(c, m, scope),
         .Tilde => {
-            const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");
-            node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
-            return &node.base;
+            const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
+            return Node.bit_not.create(c.arena, operand);
         },
         .Asterisk => {
-            const node = try macroGroup(c, try parseCUnaryExpr(c, m, scope));
-            return try transCreateNodePtrDeref(c, node);
+            const operand = try parseCUnaryExpr(c, m, scope);
+            return Node.deref.create(c.arena, operand);
         },
         .Ampersand => {
-            const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");
-            node.rhs = try macroGroup(c, try parseCUnaryExpr(c, m, scope));
-            return &node.base;
+            const operand = try parseCUnaryExpr(c, m, scope);
+            return Node.address_of.create(c.arena, operand);
         },
         .Keyword_sizeof => {
-            const inner = if (m.peek().? == .LParen) blk: {
+            const operand = if (m.peek().? == .LParen) blk: {
                 _ = m.next();
                 // C grammar says this should be 'type-name' but we have to
                 // use parseCMulExpr to correctly handle pointer types.
@@ -5251,18 +4860,7 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod
                 break :blk inner;
             } else try parseCUnaryExpr(c, m, scope);
 
-            //(@import("std").meta.sizeof(dest, x))
-            const import_fn_call = try c.createBuiltinCall("@import", 1);
-            const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
-            import_fn_call.params()[0] = std_node;
-            import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
-            const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
-            const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "sizeof");
-
-            const sizeof_call = try c.createCall(outer_field_access, 1);
-            sizeof_call.params()[0] = inner;
-            sizeof_call.rtoken = try appendToken(c, .RParen, ")");
-            return &sizeof_call.base;
+            return Node.std_meta_sizeof.create(c.arena, operand);
         },
         .Keyword_alignof => {
             // TODO this won't work if using <stdalign.h>'s
@@ -5273,16 +4871,13 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod
             }
             // C grammar says this should be 'type-name' but we have to
             // use parseCMulExpr to correctly handle pointer types.
-            const inner = try parseCMulExpr(c, m, scope);
+            const operand = try parseCMulExpr(c, m, scope);
             if (m.next().? != .RParen) {
                 try m.fail(c, "unable to translate C expr: expected ')'", .{});
                 return error.ParseError;
             }
 
-            const builtin_call = try c.createBuiltinCall("@alignOf", 1);
-            builtin_call.params()[0] = inner;
-            builtin_call.rparen_token = try appendToken(c, .RParen, ")");
-            return &builtin_call.base;
+            return Node.alignof.create(c.arena, operand);
         },
         .PlusPlus, .MinusMinus => {
             try m.fail(c, "TODO unary inc/dec expr", .{});
@@ -5295,50 +4890,40 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod
     }
 }
 
-fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {
-    const tok = c.token_locs.items[token];
-    const slice = c.source_buffer.items[tok.start..tok.end];
-    return if (mem.startsWith(u8, slice, "@\""))
-        slice[2 .. slice.len - 1]
-    else
-        slice;
-}
-
-fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
-    switch (node.tag) {
-        .ContainerDecl,
-        .AddressOf,
-        .Await,
-        .BitNot,
-        .BoolNot,
-        .OptionalType,
-        .Negation,
-        .NegationWrap,
-        .Resume,
-        .Try,
-        .ArrayType,
-        .ArrayTypeSentinel,
-        .PtrType,
-        .SliceType,
+fn getContainer(c: *Context, node: Node) ?Node {
+    switch (node.tag()) {
+        .@"union",
+        .@"struct",
+        .@"enum",
+        .address_of,
+        .bit_not,
+        .not,
+        .optional_type,
+        .negate,
+        .negate_wrap,
+        .array_type,
+        .c_pointer, 
+        .single_pointer,
         => return node,
 
-        .Identifier => {
-            const ident = node.castTag(.Identifier).?;
-            if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
-                if (value.cast(ast.Node.VarDecl)) |var_decl|
-                    return getContainer(c, var_decl.getInitNode().?);
+        .identifier => {
+            const ident = node.castTag(.identifier).?;
+            if (c.global_scope.sym_table.get(ident.data)) |value| {
+                if (value.castTag(.var_decl)) |var_decl|
+                    return getContainer(c, var_decl.data.init);
+                if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |var_decl|
+                    return getContainer(c, var_decl.data.init);
             }
         },
 
-        .Period => {
-            const infix = node.castTag(.Period).?;
+        .field_access => {
+            const infix = node.castTag(.field_access).?;
 
-            if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
-                if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
-                    for (container.fieldsAndDecls()) |field_ref| {
-                        const field = field_ref.cast(ast.Node.ContainerField).?;
-                        const ident = infix.rhs.castTag(.Identifier).?;
-                        if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
+            if (getContainerTypeOf(c, infix.data.lhs)) |ty_node| {
+                if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
+                    for (container.data.fields) |field| {
+                        const ident = infix.data.rhs.castTag(.identifier).?;
+                        if (mem.eql(u8, field.data.name, field.data)) {
                             return getContainer(c, field.type_expr.?);
                         }
                     }
@@ -5351,22 +4936,20 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
     return null;
 }
 
-fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
-    if (ref.castTag(.Identifier)) |ident| {
-        if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
-            if (value.cast(ast.Node.VarDecl)) |var_decl| {
-                if (var_decl.getTypeNode()) |ty|
-                    return getContainer(c, ty);
+fn getContainerTypeOf(c: *Context, ref: Node) ?Node {
+    if (ref.castTag(.identifier)) |ident| {
+        if (c.global_scope.sym_table.get(ident.data)) |value| {
+            if (value.castTag(.var_decl)) |var_decl| {
+                return getContainer(c, var_decl.data.type);
             }
         }
-    } else if (ref.castTag(.Period)) |infix| {
-        if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
-            if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
-                for (container.fieldsAndDecls()) |field_ref| {
-                    const field = field_ref.cast(ast.Node.ContainerField).?;
-                    const ident = infix.rhs.castTag(.Identifier).?;
-                    if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
-                        return getContainer(c, field.type_expr.?);
+    } else if (ref.castTag(.field_access)) |infix| {
+        if (getContainerTypeOf(c, infix.data.lhs)) |ty_node| {
+            if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
+                for (container.data.fields) |field| {
+                    const ident = infix.data.rhs.castTag(.identifier).?;
+                    if (mem.eql(u8, field.name, ident.data)) {
+                        return getContainer(c, field.type);
                     }
                 }
             } else
@@ -5376,11 +4959,16 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
     return null;
 }
 
-fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
-    const init = if (ref.cast(ast.Node.VarDecl)) |v| v.getInitNode().? else return null;
+fn getFnProto(c: *Context, ref: Node) ?*ast.Payload.Func {
+    const init = if (value.castTag(.var_decl)) |v|
+        v.data.init
+    else if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |v|
+        v.data.init
+    else
+        return null;
     if (getContainerTypeOf(c, init)) |ty_node| {
-        if (ty_node.castTag(.OptionalType)) |prefix| {
-            if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
+        if (ty_node.castTag(.optional_type)) |prefix| {
+            if (prefix.data.castTag(.func)) |fn_proto| {
                 return fn_proto;
             }
         }