Commit 4c0c9b0755

Veikka Tuominen <git@vexu.eu>
2021-02-06 17:45:45
translate-c: convert transBinaryOperator
1 parent d835f5c
Changed files (2)
src
src/translate_c/ast.zig
@@ -1,7 +1,7 @@
 const std = @import("std");
 const Type = @import("../type.zig").Type;
 
-pub const Node = struct {
+pub const Node = extern union {
     /// If the tag value is less than Tag.no_payload_count, then no pointer
     /// dereference is needed.
     tag_if_small_enough: usize,
@@ -13,12 +13,15 @@ pub const Node = struct {
         opaque_literal,
         true_literal,
         false_literal,
+        empty_block,
+        /// pub usingnamespace @import("std").c.builtins;
+        usingnamespace_builtins,
         // After this, the tag requires a payload.
 
-        int,
-        float,
-        string,
-        char,
+        int_literal,
+        float_literal,
+        string_literal,
+        char_literal,
         identifier,
         @"if",
         @"while",
@@ -44,6 +47,67 @@ pub const Node = struct {
         discard,
         block,
 
+        // a + b
+        add,
+        // a = b
+        add_assign,
+        // c = (a = b)
+        add_assign_value,
+        add_wrap,
+        add_wrap_assign,
+        add_wrap_assign_value,
+        sub,
+        sub_assign,
+        sub_assign_value,
+        sub_wrap,
+        sub_wrap_assign,
+        sub_wrap_assign_value,
+        mul,
+        mul_assign,
+        mul_assign_value,
+        mul_wrap,
+        mul_wrap_assign,
+        mul_wrap_assign_value,
+        div,
+        div_assign,
+        div_assign_value,
+        shl,
+        shl_assign,
+        shl_assign_value,
+        shr,
+        shr_assign,
+        shr_assign_value,
+        mod,
+        mod_assign,
+        mod_assign_value,
+        @"and",
+        and_assign,
+        and_assign_value,
+        @"or",
+        or_assign,
+        or_assign_value,
+        xor,
+        xor_assign,
+        xor_assign_value,
+        less_than,
+        less_than_equal,
+        greater_than,
+        greater_than_equal,
+        equal,
+        not_equal,
+        bit_and,
+        bit_or,
+        bit_xor,
+
+        /// @import("std").math.Log2Int(operand)
+        std_math_Log2Int,
+        /// @intCast(lhs, rhs)
+        int_cast,
+        /// @rem(lhs, rhs)
+        rem,
+        /// @divTrunc(lhs, rhs)
+        div_trunc,
+
         pub const last_no_payload_tag = Tag.false_literal;
         pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
 
@@ -54,8 +118,70 @@ pub const Node = struct {
                 .opaque_literal,
                 .true_literal,
                 .false_litral,
+                .empty_block,
+                .usingnamespace_builtins,
                 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
 
+                .array_access,
+                .std_mem_zeroes,
+                .@"return",
+                .discard,
+                .std_math_Log2Int,
+                => Payload.UnOp,
+
+                .add,
+                .add_assign,
+                .add_assign_value,
+                .add_wrap,
+                .add_wrap_assign,
+                .add_wrap_assign_value,
+                .sub,
+                .sub_assign,
+                .sub_assign_value,
+                .sub_wrap,
+                .sub_wrap_assign,
+                .sub_wrap_assign_value,
+                .mul,
+                .mul_assign,
+                .mul_assign_value,
+                .mul_wrap,
+                .mul_wrap_assign,
+                .mul_wrap_assign_value,
+                .div,
+                .div_assign,
+                .div_assign_value,
+                .shl,
+                .shl_assign,
+                .shl_assign_value,
+                .shr,
+                .shr_assign,
+                .shr_assign_value,
+                .mod,
+                .mod_assign,
+                .mod_assign_value,
+                .@"and",
+                .and_assign,
+                .and_assign_value,
+                .@"or",
+                .or_assign,
+                .or_assign_value,
+                .xor,
+                .xor_assign,
+                .xor_assign_value,
+                .less_than,
+                .less_than_equal,
+                .greater_than,
+                .greater_than_equal,
+                .equal,
+                .not_equal,
+                .bit_and,
+                .bit_or,
+                .bit_xor,
+                .div_trunc,
+                .rem,
+                .int_cast,
+                => Payload.BinOp,
+
                 .int,
                 .float,
                 .string,
@@ -71,11 +197,6 @@ pub const Node = struct {
                 .@"switch" => Payload.Switch,
                 .@"break" => Payload.Break,
                 .call => Payload.Call,
-                .array_access,
-                .std_mem_zeroes,
-                .@"return",
-                .discard,
-                => Payload.SingleArg,
                 .var_decl => Payload.VarDecl,
                 .func => Payload.Func,
                 .@"enum" => Payload.Enum,
@@ -123,11 +244,19 @@ pub const Payload = struct {
         data: []const u8,
     };
 
-    pub const SingleArg = struct {
+    pub const UnOp = struct {
         base: Node,
         data: *Node,
     };
 
+    pub const BinOp = struct {
+        base: Node,
+        data: struct {
+            lhs: *Node,
+            rhs: *Node,
+        },
+    };
+
     pub const If = struct {
         base: Node = .{ .tag = .@"if" },
         data: struct {
src/translate_c.zig
@@ -3,14 +3,14 @@
 
 const std = @import("std");
 const assert = std.debug.assert;
-const ast = std.zig.ast;
-const Token = std.zig.Token;
 const clang = @import("clang.zig");
 const ctok = std.c.tokenizer;
 const CToken = std.c.Token;
 const mem = std.mem;
 const math = std.math;
 const Type = @import("type.zig").Type;
+const ast = @import("translate_c/ast.zig");
+const Node = ast.Node;
 
 const CallingConvention = std.builtin.CallingConvention;
 
@@ -348,47 +348,6 @@ pub const Context = struct {
     }
 };
 
-fn addCBuiltinsNamespace(c: *Context) Error!void {
-    // pub usingnamespace @import("std").c.builtins;
-    const pub_tok = try appendToken(c, .Keyword_pub, "pub");
-    const use_tok = try appendToken(c, .Keyword_usingnamespace, "usingnamespace");
-    const import_tok = try appendToken(c, .Builtin, "@import");
-    const lparen_tok = try appendToken(c, .LParen, "(");
-    const std_tok = try appendToken(c, .StringLiteral, "\"std\"");
-    const rparen_tok = try appendToken(c, .RParen, ")");
-
-    const std_node = try c.arena.create(ast.Node.OneToken);
-    std_node.* = .{
-        .base = .{ .tag = .StringLiteral },
-        .token = std_tok,
-    };
-
-    const call_node = try ast.Node.BuiltinCall.alloc(c.arena, 1);
-    call_node.* = .{
-        .builtin_token = import_tok,
-        .params_len = 1,
-        .rparen_token = rparen_tok,
-    };
-    call_node.params()[0] = &std_node.base;
-
-    var access_chain = &call_node.base;
-    access_chain = try transCreateNodeFieldAccess(c, access_chain, "c");
-    access_chain = try transCreateNodeFieldAccess(c, access_chain, "builtins");
-
-    const semi_tok = try appendToken(c, .Semicolon, ";");
-
-    const bytes = try c.gpa.alignedAlloc(u8, @alignOf(ast.Node.Use), @sizeOf(ast.Node.Use));
-    const using_node = @ptrCast(*ast.Node.Use, bytes.ptr);
-    using_node.* = .{
-        .doc_comments = null,
-        .visib_token = pub_tok,
-        .use_token = use_tok,
-        .expr = access_chain,
-        .semicolon_token = semi_tok,
-    };
-    try c.root_decls.append(c.gpa, &using_node.base);
-}
-
 pub fn translate(
     gpa: *mem.Allocator,
     args_begin: [*]?[*]const u8,
@@ -446,7 +405,7 @@ pub fn translate(
         context.opaque_demotes.deinit(gpa);
     }
 
-    try addCBuiltinsNamespace(&context);
+    _ = try Node.usingnamespace_builtins.init();
 
     try prepopulateGlobalNameTable(ast_unit, &context);
 
@@ -1318,26 +1277,6 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
     return transCreateNodeIdentifier(c, name);
 }
 
-fn createAlias(c: *Context, alias: anytype) !void {
-    const visib_tok = try appendToken(c, .Keyword_pub, "pub");
-    const mut_tok = try appendToken(c, .Keyword_const, "const");
-    const name_tok = try appendIdentifier(c, alias.alias);
-    const eq_token = try appendToken(c, .Equal, "=");
-    const init_node = try transCreateNodeIdentifier(c, alias.name);
-    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,
-    });
-    return addTopLevelDecl(c, alias.alias, &node.base);
-}
-
 const ResultUsed = enum {
     used,
     unused,
@@ -1349,78 +1288,63 @@ const LRValue = enum {
 };
 
 fn transStmt(
-    rp: RestorePoint,
+    c: *Context,
     scope: *Scope,
     stmt: *const clang.Stmt,
     result_used: ResultUsed,
     lrvalue: LRValue,
-) TransError!*ast.Node {
+) TransError!Node {
     const sc = stmt.getStmtClass();
     switch (sc) {
-        .BinaryOperatorClass => return transBinaryOperator(rp, scope, @ptrCast(*const clang.BinaryOperator, stmt), result_used),
-        .CompoundStmtClass => return transCompoundStmt(rp, scope, @ptrCast(*const clang.CompoundStmt, stmt)),
-        .CStyleCastExprClass => return transCStyleCastExprClass(rp, scope, @ptrCast(*const clang.CStyleCastExpr, stmt), result_used, lrvalue),
-        .DeclStmtClass => return transDeclStmt(rp, scope, @ptrCast(*const clang.DeclStmt, stmt)),
-        .DeclRefExprClass => return transDeclRefExpr(rp, scope, @ptrCast(*const clang.DeclRefExpr, stmt), lrvalue),
-        .ImplicitCastExprClass => return transImplicitCastExpr(rp, scope, @ptrCast(*const clang.ImplicitCastExpr, stmt), result_used),
-        .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const clang.IntegerLiteral, stmt), result_used, .with_as),
-        .ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const clang.ReturnStmt, stmt)),
-        .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const clang.StringLiteral, stmt), result_used),
+        .BinaryOperatorClass => return transBinaryOperator(c, scope, @ptrCast(*const clang.BinaryOperator, stmt), result_used),
+        .CompoundStmtClass => return transCompoundStmt(c, scope, @ptrCast(*const clang.CompoundStmt, stmt)),
+        .CStyleCastExprClass => return transCStyleCastExprClass(c, scope, @ptrCast(*const clang.CStyleCastExpr, stmt), result_used, lrvalue),
+        .DeclStmtClass => return transDeclStmt(c, scope, @ptrCast(*const clang.DeclStmt, stmt)),
+        .DeclRefExprClass => return transDeclRefExpr(c, scope, @ptrCast(*const clang.DeclRefExpr, stmt), lrvalue),
+        .ImplicitCastExprClass => return transImplicitCastExpr(c, scope, @ptrCast(*const clang.ImplicitCastExpr, stmt), result_used),
+        .IntegerLiteralClass => return transIntegerLiteral(c, scope, @ptrCast(*const clang.IntegerLiteral, stmt), result_used, .with_as),
+        .ReturnStmtClass => return transReturnStmt(c, scope, @ptrCast(*const clang.ReturnStmt, stmt)),
+        .StringLiteralClass => return transStringLiteral(c, scope, @ptrCast(*const clang.StringLiteral, stmt), result_used),
         .ParenExprClass => {
-            const expr = try transExpr(rp, scope, @ptrCast(*const clang.ParenExpr, stmt).getSubExpr(), .used, lrvalue);
-            if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
-            const node = try rp.c.arena.create(ast.Node.GroupedExpression);
-            node.* = .{
-                .lparen = try appendToken(rp.c, .LParen, "("),
-                .expr = expr,
-                .rparen = try appendToken(rp.c, .RParen, ")"),
-            };
-            return maybeSuppressResult(rp, scope, result_used, &node.base);
-        },
-        .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const clang.InitListExpr, stmt), result_used),
-        .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const clang.Expr, stmt), result_used),
-        .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const clang.IfStmt, stmt)),
-        .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const clang.WhileStmt, stmt)),
-        .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const clang.DoStmt, stmt)),
+            const expr = try transExpr(c, scope, @ptrCast(*const clang.ParenExpr, stmt).getSubExpr(), .used, lrvalue);
+            return maybeSuppressResult(c, scope, result_used, expr);
+        },
+        .InitListExprClass => return transInitListExpr(c, scope, @ptrCast(*const clang.InitListExpr, stmt), result_used),
+        .ImplicitValueInitExprClass => return transImplicitValueInitExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used),
+        .IfStmtClass => return transIfStmt(c, scope, @ptrCast(*const clang.IfStmt, stmt)),
+        .WhileStmtClass => return transWhileLoop(c, scope, @ptrCast(*const clang.WhileStmt, stmt)),
+        .DoStmtClass => return transDoWhileLoop(c, scope, @ptrCast(*const clang.DoStmt, stmt)),
         .NullStmtClass => {
-            const block = try rp.c.createBlock(0);
-            block.rbrace = try appendToken(rp.c, .RBrace, "}");
-            return &block.base;
-        },
-        .ContinueStmtClass => return try transCreateNodeContinue(rp.c),
-        .BreakStmtClass => return transBreak(rp, scope),
-        .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const clang.ForStmt, stmt)),
-        .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used),
+            return Node.empty_block.init();
+        },
+        .ContinueStmtClass => return try transCreateNodeContinue(c),
+        .BreakStmtClass => return transBreak(c, scope),
+        .ForStmtClass => return transForLoop(c, scope, @ptrCast(*const clang.ForStmt, stmt)),
+        .FloatingLiteralClass => return transFloatingLiteral(c, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used),
         .ConditionalOperatorClass => {
-            return transConditionalOperator(rp, scope, @ptrCast(*const clang.ConditionalOperator, stmt), result_used);
+            return transConditionalOperator(c, scope, @ptrCast(*const clang.ConditionalOperator, stmt), result_used);
         },
         .BinaryConditionalOperatorClass => {
-            return transBinaryConditionalOperator(rp, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used);
-        },
-        .SwitchStmtClass => return transSwitch(rp, scope, @ptrCast(*const clang.SwitchStmt, stmt)),
-        .CaseStmtClass => return transCase(rp, scope, @ptrCast(*const clang.CaseStmt, stmt)),
-        .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const clang.DefaultStmt, stmt)),
-        .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const clang.Expr, stmt), result_used),
-        .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used),
-        .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as),
-        .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const clang.StmtExpr, stmt), result_used),
-        .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const clang.MemberExpr, stmt), result_used),
-        .ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const clang.ArraySubscriptExpr, stmt), result_used),
-        .CallExprClass => return transCallExpr(rp, scope, @ptrCast(*const clang.CallExpr, stmt), result_used),
-        .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(rp, scope, @ptrCast(*const clang.UnaryExprOrTypeTraitExpr, stmt), result_used),
-        .UnaryOperatorClass => return transUnaryOperator(rp, scope, @ptrCast(*const clang.UnaryOperator, stmt), result_used),
-        .CompoundAssignOperatorClass => return transCompoundAssignOperator(rp, scope, @ptrCast(*const clang.CompoundAssignOperator, stmt), result_used),
+            return transBinaryConditionalOperator(c, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used);
+        },
+        .SwitchStmtClass => return transSwitch(c, scope, @ptrCast(*const clang.SwitchStmt, stmt)),
+        .CaseStmtClass => return transCase(c, scope, @ptrCast(*const clang.CaseStmt, stmt)),
+        .DefaultStmtClass => return transDefault(c, scope, @ptrCast(*const clang.DefaultStmt, stmt)),
+        .ConstantExprClass => return transConstantExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used),
+        .PredefinedExprClass => return transPredefinedExpr(c, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used),
+        .CharacterLiteralClass => return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as),
+        .StmtExprClass => return transStmtExpr(c, scope, @ptrCast(*const clang.StmtExpr, stmt), result_used),
+        .MemberExprClass => return transMemberExpr(c, scope, @ptrCast(*const clang.MemberExpr, stmt), result_used),
+        .ArraySubscriptExprClass => return transArrayAccess(c, scope, @ptrCast(*const clang.ArraySubscriptExpr, stmt), result_used),
+        .CallExprClass => return transCallExpr(c, scope, @ptrCast(*const clang.CallExpr, stmt), result_used),
+        .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(c, scope, @ptrCast(*const clang.UnaryExprOrTypeTraitExpr, stmt), result_used),
+        .UnaryOperatorClass => return transUnaryOperator(c, scope, @ptrCast(*const clang.UnaryOperator, stmt), result_used),
+        .CompoundAssignOperatorClass => return transCompoundAssignOperator(c, scope, @ptrCast(*const clang.CompoundAssignOperator, stmt), result_used),
         .OpaqueValueExprClass => {
             const source_expr = @ptrCast(*const clang.OpaqueValueExpr, stmt).getSourceExpr().?;
-            const expr = try transExpr(rp, scope, source_expr, .used, lrvalue);
-            if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
-            const node = try rp.c.arena.create(ast.Node.GroupedExpression);
-            node.* = .{
-                .lparen = try appendToken(rp.c, .LParen, "("),
-                .expr = expr,
-                .rparen = try appendToken(rp.c, .RParen, ")"),
-            };
-            return maybeSuppressResult(rp, scope, result_used, &node.base);
+            const expr = try transExpr(c, scope, source_expr, .used, lrvalue);
+            return maybeSuppressResult(c, scope, result_used, expr);
+            const node = try c.arena.create(Node.GroupedExpression);
         },
         else => {
             return revertAndWarn(
@@ -1435,175 +1359,139 @@ fn transStmt(
 }
 
 fn transBinaryOperator(
-    rp: RestorePoint,
+    c: *Context,
     scope: *Scope,
     stmt: *const clang.BinaryOperator,
     result_used: ResultUsed,
-) TransError!*ast.Node {
+) TransError!Node {
     const op = stmt.getOpcode();
     const qt = stmt.getType();
-    var op_token: ast.TokenIndex = undefined;
-    var op_id: ast.Node.Tag = undefined;
     switch (op) {
-        .Assign => return try transCreateNodeAssign(rp, scope, result_used, stmt.getLHS(), stmt.getRHS()),
+        .Assign => return try transCreateNodeAssign(c, scope, result_used, stmt.getLHS(), stmt.getRHS()),
         .Comma => {
             var block_scope = try Scope.Block.init(rp.c, scope, true);
-            const lparen = try appendToken(rp.c, .LParen, "(");
+            defer block_scope.deinit();
+
 
-            const lhs = try transExpr(rp, &block_scope.base, stmt.getLHS(), .unused, .r_value);
+            const lhs = try transExpr(c, &block_scope.base, stmt.getLHS(), .unused, .r_value);
             try block_scope.statements.append(lhs);
 
             const rhs = try transExpr(rp, &block_scope.base, stmt.getRHS(), .used, .r_value);
-            _ = try appendToken(rp.c, .Semicolon, ";");
-            const break_node = try transCreateNodeBreak(rp.c, block_scope.label, rhs);
-            try block_scope.statements.append(&break_node.base);
+            const break_node = try Node.break_val.create(c.arena, .{
+                .label = block_scope.label,
+                .val = rhs,
+            });
+            try block_scope.statements.append(break_node);
             const block_node = try block_scope.complete(rp.c);
-            const rparen = try appendToken(rp.c, .RParen, ")");
-            const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
-            grouped_expr.* = .{
-                .lparen = lparen,
-                .expr = block_node,
-                .rparen = rparen,
-            };
-            return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base);
+            return maybeSuppressResult(rp, scope, result_used, block_node);
         },
         .Div => {
             if (cIsSignedInteger(qt)) {
                 // signed integer division uses @divTrunc
-                const div_trunc_node = try rp.c.createBuiltinCall("@divTrunc", 2);
-                div_trunc_node.params()[0] = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value);
-                _ = try appendToken(rp.c, .Comma, ",");
-                const rhs = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value);
-                div_trunc_node.params()[1] = rhs;
-                div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-                return maybeSuppressResult(rp, scope, result_used, &div_trunc_node.base);
+                const lhs = try transExpr(c, scope, stmt.getLHS(), .used, .l_value);
+                const rhs = try transExpr(c, scope, stmt.getRHS(), .used, .r_value);
+                const div_trunc = try Node.div_trunc.create(c.arena, .{ .lhs = lhs, .rhs = rhs});
+                return maybeSuppressResult(c, scope, result_used, div_trunc);
             }
         },
         .Rem => {
             if (cIsSignedInteger(qt)) {
                 // signed integer division uses @rem
-                const rem_node = try rp.c.createBuiltinCall("@rem", 2);
-                rem_node.params()[0] = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value);
-                _ = try appendToken(rp.c, .Comma, ",");
-                const rhs = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value);
-                rem_node.params()[1] = rhs;
-                rem_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-                return maybeSuppressResult(rp, scope, result_used, &rem_node.base);
+                const lhs = try transExpr(c, scope, stmt.getLHS(), .used, .l_value);
+                const rhs = try transExpr(c, scope, stmt.getRHS(), .used, .r_value);
+                const rem = try Node.rem.create(c.arena, .{ .lhs = lhs, .rhs = rhs});
+                return maybeSuppressResult(c, scope, result_used, rem);
             }
         },
         .Shl => {
-            const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<");
-            return maybeSuppressResult(rp, scope, result_used, node);
+            const node = try transCreateNodeShiftOp(c, scope, stmt, .shl);
+            return maybeSuppressResult(c, scope, result_used, node);
         },
         .Shr => {
-            const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>");
-            return maybeSuppressResult(rp, scope, result_used, node);
-        },
-        .LAnd => {
-            const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, result_used, true);
-            return maybeSuppressResult(rp, scope, result_used, node);
-        },
-        .LOr => {
-            const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, result_used, true);
-            return maybeSuppressResult(rp, scope, result_used, node);
+            const node = try transCreateNodeShiftOp(c, scope, stmt, .shr);
+            return maybeSuppressResult(c, scope, result_used, node);
         },
         else => {},
     }
-    const lhs_node = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value);
+    var op_id: Node.Tag = undefined;
     switch (op) {
         .Add => {
             if (cIsUnsignedInteger(qt)) {
-                op_token = try appendToken(rp.c, .PlusPercent, "+%");
-                op_id = .AddWrap;
+                op_id = .add_wrap;
             } else {
-                op_token = try appendToken(rp.c, .Plus, "+");
-                op_id = .Add;
+                op_id = .add;
             }
         },
         .Sub => {
             if (cIsUnsignedInteger(qt)) {
-                op_token = try appendToken(rp.c, .MinusPercent, "-%");
-                op_id = .SubWrap;
+                op_id = .sub_wrap;
             } else {
-                op_token = try appendToken(rp.c, .Minus, "-");
-                op_id = .Sub;
+                op_id = .sub;
             }
         },
         .Mul => {
             if (cIsUnsignedInteger(qt)) {
-                op_token = try appendToken(rp.c, .AsteriskPercent, "*%");
-                op_id = .MulWrap;
+                op_id = .mul_wrap;
             } else {
-                op_token = try appendToken(rp.c, .Asterisk, "*");
-                op_id = .Mul;
+                op_id = .mul;
             }
         },
         .Div => {
             // unsigned/float division uses the operator
-            op_id = .Div;
-            op_token = try appendToken(rp.c, .Slash, "/");
+            op_id = .div;
         },
         .Rem => {
             // unsigned/float division uses the operator
-            op_id = .Mod;
-            op_token = try appendToken(rp.c, .Percent, "%");
+            op_id = .mod;
         },
         .LT => {
-            op_id = .LessThan;
-            op_token = try appendToken(rp.c, .AngleBracketLeft, "<");
+            op_id = .less_than;
         },
         .GT => {
-            op_id = .GreaterThan;
-            op_token = try appendToken(rp.c, .AngleBracketRight, ">");
+            op_id = .greater_than;
         },
         .LE => {
-            op_id = .LessOrEqual;
-            op_token = try appendToken(rp.c, .AngleBracketLeftEqual, "<=");
+            op_id = .less_than_equal;
         },
         .GE => {
-            op_id = .GreaterOrEqual;
-            op_token = try appendToken(rp.c, .AngleBracketRightEqual, ">=");
+            op_id = .greater_than_equal;
         },
         .EQ => {
-            op_id = .EqualEqual;
-            op_token = try appendToken(rp.c, .EqualEqual, "==");
+            op_id = .equal;
         },
         .NE => {
-            op_id = .BangEqual;
-            op_token = try appendToken(rp.c, .BangEqual, "!=");
+            op_id = .not_equal;
         },
         .And => {
-            op_id = .BitAnd;
-            op_token = try appendToken(rp.c, .Ampersand, "&");
+            op_id = .bit_and;
         },
         .Xor => {
-            op_id = .BitXor;
-            op_token = try appendToken(rp.c, .Caret, "^");
+            op_id = .bit_xor;
         },
         .Or => {
-            op_id = .BitOr;
-            op_token = try appendToken(rp.c, .Pipe, "|");
+            op_id = .bit_or;
+        },
+        .LAnd => {
+            op_id = .@"and";
+        },
+        .LOr => {
+            op_id = .@"or";
         },
         else => unreachable,
     }
 
-    const rhs_node = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value);
-
-    const lhs = if (isBoolRes(lhs_node)) init: {
-        const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1);
-        cast_node.params()[0] = lhs_node;
-        cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-        break :init &cast_node.base;
-    } else lhs_node;
-
-    const rhs = if (isBoolRes(rhs_node)) init: {
-        const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1);
-        cast_node.params()[0] = rhs_node;
-        cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-        break :init &cast_node.base;
-    } else rhs_node;
+    const lhs = try transExpr(c, scope, stmt.getLHS(), .used, .l_value);
+    const rhs = try transExpr(c, scope, stmt.getRHS(), .used, .r_value);
 
-    return transCreateNodeInfixOp(rp, scope, lhs, op_id, op_token, rhs, result_used, true);
+    const payload = try c.arena.create(ast.Payload.BinOp);
+    payload.* = .{
+        .base = .{ .tag = op_id },
+        .data = .{
+            .lhs = lhs,
+            .rhs = rhs,
+        },
+    };
+    return maybeSuppressResult(c, scope, used, &payload.base);
 }
 
 fn transCompoundStmtInline(
@@ -2365,40 +2253,13 @@ fn transEnumToInt(c: *Context, enum_expr: *ast.Node) TypeError!*ast.Node {
 }
 
 fn transExpr(
-    rp: RestorePoint,
-    scope: *Scope,
-    expr: *const clang.Expr,
-    used: ResultUsed,
-    lrvalue: LRValue,
-) TransError!*ast.Node {
-    return transStmt(rp, scope, @ptrCast(*const clang.Stmt, expr), used, lrvalue);
-}
-
-/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore
-/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals.
-fn transExprCoercing(
-    rp: RestorePoint,
+    c: *Context,
     scope: *Scope,
     expr: *const clang.Expr,
     used: ResultUsed,
     lrvalue: LRValue,
-) TransError!*ast.Node {
-    switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) {
-        .IntegerLiteralClass => {
-            return transIntegerLiteral(rp, scope, @ptrCast(*const clang.IntegerLiteral, expr), .used, .no_as);
-        },
-        .CharacterLiteralClass => {
-            return transCharLiteral(rp, scope, @ptrCast(*const clang.CharacterLiteral, expr), .used, .no_as);
-        },
-        .UnaryOperatorClass => {
-            const un_expr = @ptrCast(*const clang.UnaryOperator, expr);
-            if (un_expr.getOpcode() == .Extension) {
-                return transExprCoercing(rp, scope, un_expr.getSubExpr(), used, lrvalue);
-            }
-        },
-        else => {},
-    }
-    return transExpr(rp, scope, expr, .used, .r_value);
+) TransError!Node {
+    return transStmt(c, scope, @ptrCast(*const clang.Stmt, expr), used, lrvalue);
 }
 
 fn transInitListExprRecord(
@@ -4150,7 +4011,7 @@ fn qualTypeIsBoolean(qt: clang.QualType) bool {
     return qualTypeCanon(qt).isBooleanType();
 }
 
-fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) !u32 {
+fn qualTypeIntBitWidth(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocation) !u32 {
     const ty = qt.getTypePtr();
 
     switch (ty.getTypeClass()) {
@@ -4174,7 +4035,7 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S
         .Typedef => {
             const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
             const typedef_decl = typedef_ty.getDecl();
-            const type_name = try rp.c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin());
+            const type_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin());
 
             if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) {
                 return 8;
@@ -4194,51 +4055,17 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S
     unreachable;
 }
 
-fn qualTypeToLog2IntRef(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) !*ast.Node {
-    const int_bit_width = try qualTypeIntBitWidth(rp, qt, source_loc);
+fn qualTypeToLog2IntRef(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocation) !Node {
+    const int_bit_width = try qualTypeIntBitWidth(c, qt, source_loc);
 
     if (int_bit_width != 0) {
         // we can perform the log2 now.
         const cast_bit_width = math.log2_int(u64, int_bit_width);
-        const node = try rp.c.arena.create(ast.Node.OneToken);
-        node.* = .{
-            .base = .{ .tag = .IntegerLiteral },
-            .token = try appendTokenFmt(rp.c, .Identifier, "u{d}", .{cast_bit_width}),
-        };
-        return &node.base;
-    }
-
-    const zig_type_node = try transQualType(rp, qt, source_loc);
-
-    //    @import("std").math.Log2Int(c_long);
-    //
-    //    FnCall
-    //        FieldAccess
-    //            FieldAccess
-    //                FnCall (.builtin = true)
-    //                    Symbol "import"
-    //                    StringLiteral "std"
-    //                Symbol "math"
-    //            Symbol "Log2Int"
-    //        Symbol <zig_type_node> (var from above)
-
-    const import_fn_call = try rp.c.createBuiltinCall("@import", 1);
-    const std_token = try appendToken(rp.c, .StringLiteral, "\"std\"");
-    const std_node = try rp.c.arena.create(ast.Node.OneToken);
-    std_node.* = .{
-        .base = .{ .tag = .StringLiteral },
-        .token = std_token,
-    };
-    import_fn_call.params()[0] = &std_node.base;
-    import_fn_call.rparen_token = try appendToken(rp.c, .RParen, ")");
-
-    const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math");
-    const outer_field_access = try transCreateNodeFieldAccess(rp.c, inner_field_access, "Log2Int");
-    const log2int_fn_call = try rp.c.createCall(outer_field_access, 1);
-    log2int_fn_call.params()[0] = zig_type_node;
-    log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")");
+        return Node.uint_type.create(c.arena, cast_bit_width);
+    }
 
-    return &log2int_fn_call.base;
+    const zig_type = try transQualType(c, qt, source_loc);
+    return Node.std_math_Log2Int.create(c.arena, zig_type);
 }
 
 fn qualTypeChildIsFnProto(qt: clang.QualType) bool {
@@ -4506,67 +4333,6 @@ fn transCreateNodeSimplePrefixOp(
     return node;
 }
 
-fn transCreateNodeInfixOp(
-    rp: RestorePoint,
-    scope: *Scope,
-    lhs_node: *ast.Node,
-    op: ast.Node.Tag,
-    op_token: ast.TokenIndex,
-    rhs_node: *ast.Node,
-    used: ResultUsed,
-    grouped: bool,
-) !*ast.Node {
-    var lparen = if (grouped)
-        try appendToken(rp.c, .LParen, "(")
-    else
-        null;
-    const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
-    node.* = .{
-        .base = .{ .tag = op },
-        .op_token = op_token,
-        .lhs = lhs_node,
-        .rhs = rhs_node,
-    };
-    if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base);
-    const rparen = try appendToken(rp.c, .RParen, ")");
-    const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
-    grouped_expr.* = .{
-        .lparen = lparen.?,
-        .expr = &node.base,
-        .rparen = rparen,
-    };
-    return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
-}
-
-fn transCreateNodeBoolInfixOp(
-    rp: RestorePoint,
-    scope: *Scope,
-    stmt: *const clang.BinaryOperator,
-    op: ast.Node.Tag,
-    used: ResultUsed,
-    grouped: bool,
-) !*ast.Node {
-    std.debug.assert(op == .BoolAnd or op == .BoolOr);
-
-    const lhs_hode = try transBoolExpr(rp, scope, stmt.getLHS(), .used, .l_value, true);
-    const op_token = if (op == .BoolAnd)
-        try appendToken(rp.c, .Keyword_and, "and")
-    else
-        try appendToken(rp.c, .Keyword_or, "or");
-    const rhs = try transBoolExpr(rp, scope, stmt.getRHS(), .used, .r_value, true);
-
-    return transCreateNodeInfixOp(
-        rp,
-        scope,
-        lhs_hode,
-        op,
-        op_token,
-        rhs,
-        used,
-        grouped,
-    );
-}
-
 fn transCreateNodePtrType(
     c: *Context,
     is_const: bool,
@@ -4968,40 +4734,33 @@ fn transCreateNodeSwitchElse(c: *Context) !*ast.Node {
 }
 
 fn transCreateNodeShiftOp(
-    rp: RestorePoint,
+    c: *Context,
     scope: *Scope,
     stmt: *const clang.BinaryOperator,
-    op: ast.Node.Tag,
-    op_tok_id: std.zig.Token.Id,
-    bytes: []const u8,
-) !*ast.Node {
-    std.debug.assert(op == .BitShiftLeft or op == .BitShiftRight);
+    op: Node.Tag,
+) !Node {
+    std.debug.assert(op == .shl or op == .shr);
 
     const lhs_expr = stmt.getLHS();
     const rhs_expr = stmt.getRHS();
     const rhs_location = rhs_expr.getBeginLoc();
     // lhs >> @as(u5, rh)
 
-    const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value);
-    const op_token = try appendToken(rp.c, op_tok_id, bytes);
+    const lhs = try transExpr(c, scope, lhs_expr, .used, .l_value);
 
-    const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
-    const rhs_type = try qualTypeToLog2IntRef(rp, stmt.getType(), rhs_location);
-    cast_node.params()[0] = rhs_type;
-    _ = try appendToken(rp.c, .Comma, ",");
-    const rhs = try transExprCoercing(rp, scope, rhs_expr, .used, .r_value);
-    cast_node.params()[1] = rhs;
-    cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+    const rhs_type = try qualTypeToLog2IntRef(c, stmt.getType(), rhs_location);
+    const rhs = try transExpr(c, scope, rhs_expr, .used, .r_value);
+    const rhs_casted = try Node.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs_type });
 
-    const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
-    node.* = .{
+    const payload = try c.arena.create(ast.Payload.BinOp);
+    payload.* = .{
         .base = .{ .tag = op },
-        .op_token = op_token,
-        .lhs = lhs,
-        .rhs = &cast_node.base,
+        .data = .{
+            .lhs = lhs,
+            .rhs = rhs_casted,
+        }
     };
-
-    return &node.base;
+    return &payload.base;
 }
 
 fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node {
@@ -5025,161 +4784,7 @@ fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.ArrayAcces
     return node;
 }
 
-const RestorePoint = struct {
-    c: *Context,
-    token_index: ast.TokenIndex,
-    src_buf_index: usize,
-
-    fn activate(self: RestorePoint) void {
-        self.c.token_ids.shrinkAndFree(self.c.gpa, self.token_index);
-        self.c.token_locs.shrinkAndFree(self.c.gpa, self.token_index);
-        self.c.source_buffer.shrinkAndFree(self.src_buf_index);
-    }
-};
-
-fn makeRestorePoint(c: *Context) RestorePoint {
-    return RestorePoint{
-        .c = c,
-        .token_index = c.token_ids.items.len,
-        .src_buf_index = c.source_buffer.items.len,
-    };
-}
-
-fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!*ast.Node {
-    switch (ty.getTypeClass()) {
-        .Builtin => {
-            const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);
-            return transCreateNodeIdentifier(rp.c, switch (builtin_ty.getKind()) {
-                .Void => "c_void",
-                .Bool => "bool",
-                .Char_U, .UChar, .Char_S, .Char8 => "u8",
-                .SChar => "i8",
-                .UShort => "c_ushort",
-                .UInt => "c_uint",
-                .ULong => "c_ulong",
-                .ULongLong => "c_ulonglong",
-                .Short => "c_short",
-                .Int => "c_int",
-                .Long => "c_long",
-                .LongLong => "c_longlong",
-                .UInt128 => "u128",
-                .Int128 => "i128",
-                .Float => "f32",
-                .Double => "f64",
-                .Float128 => "f128",
-                .Float16 => "f16",
-                .LongDouble => "c_longdouble",
-                else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
-            });
-        },
-        .FunctionProto => {
-            const fn_proto_ty = @ptrCast(*const clang.FunctionProtoType, ty);
-            const fn_proto = try transFnProto(rp, null, fn_proto_ty, source_loc, null, false);
-            return &fn_proto.base;
-        },
-        .FunctionNoProto => {
-            const fn_no_proto_ty = @ptrCast(*const clang.FunctionType, ty);
-            const fn_proto = try transFnNoProto(rp, fn_no_proto_ty, source_loc, null, false);
-            return &fn_proto.base;
-        },
-        .Paren => {
-            const paren_ty = @ptrCast(*const clang.ParenType, ty);
-            return transQualType(rp, paren_ty.getInnerType(), source_loc);
-        },
-        .Pointer => {
-            const child_qt = ty.getPointeeType();
-            if (qualTypeChildIsFnProto(child_qt)) {
-                const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
-                optional_node.rhs = try transQualType(rp, child_qt, source_loc);
-                return &optional_node.base;
-            }
-            if (typeIsOpaque(rp.c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(rp.c, child_qt)) {
-                const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
-                const pointer_node = try transCreateNodePtrType(
-                    rp.c,
-                    child_qt.isConstQualified(),
-                    child_qt.isVolatileQualified(),
-                    .Asterisk,
-                );
-                optional_node.rhs = &pointer_node.base;
-                pointer_node.rhs = try transQualType(rp, child_qt, source_loc);
-                return &optional_node.base;
-            }
-            const pointer_node = try transCreateNodePtrType(
-                rp.c,
-                child_qt.isConstQualified(),
-                child_qt.isVolatileQualified(),
-                .Identifier,
-            );
-            pointer_node.rhs = try transQualType(rp, child_qt, source_loc);
-            return &pointer_node.base;
-        },
-        .ConstantArray => {
-            const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty);
-
-            const size_ap_int = const_arr_ty.getSize();
-            const size = size_ap_int.getLimitedValue(math.maxInt(usize));
-            const elem_ty = const_arr_ty.getElementType().getTypePtr();
-            return try transCreateNodeArrayType(rp, source_loc, elem_ty, size);
-        },
-        .IncompleteArray => {
-            const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty);
-
-            const child_qt = incomplete_array_ty.getElementType();
-            var node = try transCreateNodePtrType(
-                rp.c,
-                child_qt.isConstQualified(),
-                child_qt.isVolatileQualified(),
-                .Identifier,
-            );
-            node.rhs = try transQualType(rp, child_qt, source_loc);
-            return &node.base;
-        },
-        .Typedef => {
-            const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
-
-            const typedef_decl = typedef_ty.getDecl();
-            return (try transTypeDef(rp.c, typedef_decl, false)) orelse
-                revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{});
-        },
-        .Record => {
-            const record_ty = @ptrCast(*const clang.RecordType, ty);
-
-            const record_decl = record_ty.getDecl();
-            return (try transRecordDecl(rp.c, record_decl)) orelse
-                revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{});
-        },
-        .Enum => {
-            const enum_ty = @ptrCast(*const clang.EnumType, ty);
-
-            const enum_decl = enum_ty.getDecl();
-            return (try transEnumDecl(rp.c, enum_decl)) orelse
-                revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{});
-        },
-        .Elaborated => {
-            const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty);
-            return transQualType(rp, elaborated_ty.getNamedType(), source_loc);
-        },
-        .Decayed => {
-            const decayed_ty = @ptrCast(*const clang.DecayedType, ty);
-            return transQualType(rp, decayed_ty.getDecayedType(), source_loc);
-        },
-        .Attributed => {
-            const attributed_ty = @ptrCast(*const clang.AttributedType, ty);
-            return transQualType(rp, attributed_ty.getEquivalentType(), source_loc);
-        },
-        .MacroQualified => {
-            const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, ty);
-            return transQualType(rp, macroqualified_ty.getModifiedType(), source_loc);
-        },
-        else => {
-            const type_name = rp.c.str(ty.getTypeClassName());
-            return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name});
-        },
-    }
-}
-
-fn transType1(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Type {
+fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Type {
     switch (ty.getTypeClass()) {
         .Builtin => {
             const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);