master
   1const std = @import("std");
   2const math = std.math;
   3const mem = std.mem;
   4const assert = std.debug.assert;
   5
   6const aro = @import("aro");
   7const CToken = aro.Tokenizer.Token;
   8
   9const ast = @import("ast.zig");
  10const builtins = @import("builtins.zig");
  11const ZigNode = ast.Node;
  12const ZigTag = ZigNode.Tag;
  13const Scope = @import("Scope.zig");
  14const Translator = @import("Translator.zig");
  15
  16const Error = Translator.Error;
  17pub const ParseError = Error || error{ParseError};
  18
  19const MacroTranslator = @This();
  20
  21t: *Translator,
  22macro: aro.Preprocessor.Macro,
  23name: []const u8,
  24
  25tokens: []const CToken,
  26source: []const u8,
  27i: usize = 0,
  28/// If an object macro references a global var it needs to be converted into
  29/// an inline function.
  30refs_var_decl: bool = false,
  31
  32fn peek(mt: *MacroTranslator) CToken.Id {
  33    if (mt.i >= mt.tokens.len) return .eof;
  34    return mt.tokens[mt.i].id;
  35}
  36
  37fn eat(mt: *MacroTranslator, expected_id: CToken.Id) bool {
  38    if (mt.peek() == expected_id) {
  39        mt.i += 1;
  40        return true;
  41    }
  42    return false;
  43}
  44
  45fn expect(mt: *MacroTranslator, expected_id: CToken.Id) ParseError!void {
  46    const next_id = mt.peek();
  47    if (next_id != expected_id and !(expected_id == .identifier and next_id == .extended_identifier)) {
  48        try mt.fail(
  49            "unable to translate C expr: expected '{s}' instead got '{s}'",
  50            .{ expected_id.symbol(), next_id.symbol() },
  51        );
  52        return error.ParseError;
  53    }
  54    mt.i += 1;
  55}
  56
  57fn fail(mt: *MacroTranslator, comptime fmt: []const u8, args: anytype) !void {
  58    return mt.t.failDeclExtra(&mt.t.global_scope.base, mt.macro.loc, mt.name, fmt, args);
  59}
  60
  61fn tokSlice(mt: *const MacroTranslator) []const u8 {
  62    const tok = mt.tokens[mt.i];
  63    return mt.source[tok.start..tok.end];
  64}
  65
  66pub fn transFnMacro(mt: *MacroTranslator) ParseError!void {
  67    var block_scope = try Scope.Block.init(mt.t, &mt.t.global_scope.base, false);
  68    defer block_scope.deinit();
  69    const scope = &block_scope.base;
  70
  71    const fn_params = try mt.t.arena.alloc(ast.Payload.Param, mt.macro.params.len);
  72    for (fn_params, mt.macro.params) |*param, param_name| {
  73        const mangled_name = try block_scope.makeMangledName(param_name);
  74        param.* = .{
  75            .is_noalias = false,
  76            .name = mangled_name,
  77            .type = ZigTag.@"anytype".init(),
  78        };
  79        try block_scope.discardVariable(mangled_name);
  80    }
  81
  82    // #define FOO(x)
  83    if (mt.peek() == .eof) {
  84        try block_scope.statements.append(mt.t.gpa, ZigTag.return_void.init());
  85
  86        const fn_decl = try ZigTag.pub_inline_fn.create(mt.t.arena, .{
  87            .name = mt.name,
  88            .params = fn_params,
  89            .return_type = ZigTag.void_type.init(),
  90            .body = try block_scope.complete(),
  91        });
  92        try mt.t.addTopLevelDecl(mt.name, fn_decl);
  93        return;
  94    }
  95
  96    const expr = try mt.parseCExpr(scope);
  97    const last = mt.peek();
  98    if (last != .eof)
  99        return mt.fail("unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
 100
 101    const typeof_arg = if (expr.castTag(.block)) |some| blk: {
 102        const stmts = some.data.stmts;
 103        const blk_last = stmts[stmts.len - 1];
 104        const br = blk_last.castTag(.break_val).?;
 105        break :blk br.data.val;
 106    } else expr;
 107
 108    const return_type = ret: {
 109        if (typeof_arg.castTag(.helper_call)) |some| {
 110            if (std.mem.eql(u8, some.data.name, "cast")) {
 111                break :ret some.data.args[0];
 112            }
 113        }
 114        if (typeof_arg.castTag(.std_mem_zeroinit)) |some| break :ret some.data.lhs;
 115        if (typeof_arg.castTag(.std_mem_zeroes)) |some| break :ret some.data;
 116        break :ret try ZigTag.typeof.create(mt.t.arena, typeof_arg);
 117    };
 118
 119    const return_expr = try ZigTag.@"return".create(mt.t.arena, expr);
 120    try block_scope.statements.append(mt.t.gpa, return_expr);
 121
 122    const fn_decl = try ZigTag.pub_inline_fn.create(mt.t.arena, .{
 123        .name = mt.name,
 124        .params = fn_params,
 125        .return_type = return_type,
 126        .body = try block_scope.complete(),
 127    });
 128    try mt.t.addTopLevelDecl(mt.name, fn_decl);
 129}
 130
 131pub fn transMacro(mt: *MacroTranslator) ParseError!void {
 132    const scope = &mt.t.global_scope.base;
 133
 134    // Check if the macro only uses other blank macros.
 135    while (true) {
 136        switch (mt.peek()) {
 137            .identifier, .extended_identifier => {
 138                if (mt.t.global_scope.blank_macros.contains(mt.tokSlice())) {
 139                    mt.i += 1;
 140                    continue;
 141                }
 142            },
 143            .eof, .nl => {
 144                try mt.t.global_scope.blank_macros.put(mt.t.gpa, mt.name, {});
 145                const init_node = try ZigTag.string_literal.create(mt.t.arena, "\"\"");
 146                const var_decl = try ZigTag.pub_var_simple.create(mt.t.arena, .{ .name = mt.name, .init = init_node });
 147                try mt.t.addTopLevelDecl(mt.name, var_decl);
 148                return;
 149            },
 150            else => {},
 151        }
 152        break;
 153    }
 154
 155    const init_node = try mt.parseCExpr(scope);
 156    const last = mt.peek();
 157    if (last != .eof)
 158        return mt.fail("unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
 159
 160    const node = node: {
 161        const var_decl = try ZigTag.pub_var_simple.create(mt.t.arena, .{ .name = mt.name, .init = init_node });
 162
 163        if (mt.t.getFnProto(var_decl)) |proto_node| {
 164            // If a macro aliases a global variable which is a function pointer, we conclude that
 165            // the macro is intended to represent a function that assumes the function pointer
 166            // variable is non-null and calls it.
 167            break :node try mt.createMacroFn(mt.name, var_decl, proto_node);
 168        } else if (mt.refs_var_decl) {
 169            const return_type = try ZigTag.typeof.create(mt.t.arena, init_node);
 170            const return_expr = try ZigTag.@"return".create(mt.t.arena, init_node);
 171            const block = try ZigTag.block_single.create(mt.t.arena, return_expr);
 172
 173            const loc_str = try mt.t.locStr(mt.macro.loc);
 174            const value = try std.fmt.allocPrint(mt.t.arena, "\n// {s}: warning: macro '{s}' contains a runtime value, translated to function", .{ loc_str, mt.name });
 175            try scope.appendNode(try ZigTag.warning.create(mt.t.arena, value));
 176
 177            break :node try ZigTag.pub_inline_fn.create(mt.t.arena, .{
 178                .name = mt.name,
 179                .params = &.{},
 180                .return_type = return_type,
 181                .body = block,
 182            });
 183        }
 184
 185        break :node var_decl;
 186    };
 187
 188    try mt.t.addTopLevelDecl(mt.name, node);
 189}
 190
 191fn createMacroFn(mt: *MacroTranslator, name: []const u8, ref: ZigNode, proto_alias: *ast.Payload.Func) !ZigNode {
 192    const gpa = mt.t.gpa;
 193    const arena = mt.t.arena;
 194    var fn_params: std.ArrayList(ast.Payload.Param) = .empty;
 195    defer fn_params.deinit(gpa);
 196
 197    var block_scope = try Scope.Block.init(mt.t, &mt.t.global_scope.base, false);
 198    defer block_scope.deinit();
 199
 200    for (proto_alias.data.params) |param| {
 201        const param_name = try block_scope.makeMangledName(param.name orelse "arg");
 202
 203        try fn_params.append(gpa, .{
 204            .name = param_name,
 205            .type = param.type,
 206            .is_noalias = param.is_noalias,
 207        });
 208    }
 209
 210    const init = if (ref.castTag(.var_decl)) |v|
 211        v.data.init.?
 212    else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v|
 213        v.data.init
 214    else
 215        unreachable;
 216
 217    const unwrap_expr = try ZigTag.unwrap.create(arena, init);
 218    const args = try arena.alloc(ZigNode, fn_params.items.len);
 219    for (fn_params.items, 0..) |param, i| {
 220        args[i] = try ZigTag.identifier.create(arena, param.name.?);
 221    }
 222    const call_expr = try ZigTag.call.create(arena, .{
 223        .lhs = unwrap_expr,
 224        .args = args,
 225    });
 226    const return_expr = try ZigTag.@"return".create(arena, call_expr);
 227    const block = try ZigTag.block_single.create(arena, return_expr);
 228
 229    return ZigTag.pub_inline_fn.create(arena, .{
 230        .name = name,
 231        .params = try arena.dupe(ast.Payload.Param, fn_params.items),
 232        .return_type = proto_alias.data.return_type,
 233        .body = block,
 234    });
 235}
 236
 237fn parseCExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 238    const arena = mt.t.arena;
 239    // TODO parseCAssignExpr here
 240    var block_scope = try Scope.Block.init(mt.t, scope, true);
 241    defer block_scope.deinit();
 242
 243    const node = try mt.parseCCondExpr(&block_scope.base);
 244    if (!mt.eat(.comma)) return node;
 245
 246    var last = node;
 247    while (true) {
 248        // suppress result
 249        const ignore = try ZigTag.discard.create(arena, .{ .should_skip = false, .value = last });
 250        try block_scope.statements.append(mt.t.gpa, ignore);
 251
 252        last = try mt.parseCCondExpr(&block_scope.base);
 253        if (!mt.eat(.comma)) break;
 254    }
 255
 256    const break_node = try ZigTag.break_val.create(arena, .{
 257        .label = block_scope.label,
 258        .val = last,
 259    });
 260    try block_scope.statements.append(mt.t.gpa, break_node);
 261    return try block_scope.complete();
 262}
 263
 264fn parseCNumLit(mt: *MacroTranslator) ParseError!ZigNode {
 265    const arena = mt.t.arena;
 266    const lit_bytes = mt.tokSlice();
 267    mt.i += 1;
 268
 269    var bytes = try std.ArrayList(u8).initCapacity(arena, lit_bytes.len + 3);
 270
 271    const prefix = aro.Tree.Token.NumberPrefix.fromString(lit_bytes);
 272    switch (prefix) {
 273        .binary => bytes.appendSliceAssumeCapacity("0b"),
 274        .octal => bytes.appendSliceAssumeCapacity("0o"),
 275        .hex => bytes.appendSliceAssumeCapacity("0x"),
 276        .decimal => {},
 277    }
 278
 279    const after_prefix = lit_bytes[prefix.stringLen()..];
 280    const after_int = for (after_prefix, 0..) |c, i| switch (c) {
 281        '.' => {
 282            if (i == 0) {
 283                bytes.appendAssumeCapacity('0');
 284            }
 285            break after_prefix[i..];
 286        },
 287        'e', 'E' => {
 288            if (prefix != .hex) break after_prefix[i..];
 289            bytes.appendAssumeCapacity(c);
 290        },
 291        'p', 'P' => break after_prefix[i..],
 292        '0'...'9', 'a'...'d', 'A'...'D', 'f', 'F' => {
 293            if (!prefix.digitAllowed(c)) break after_prefix[i..];
 294            bytes.appendAssumeCapacity(c);
 295        },
 296        '\'' => {
 297            bytes.appendAssumeCapacity('_');
 298        },
 299        else => break after_prefix[i..],
 300    } else "";
 301
 302    const after_frac = frac: {
 303        if (after_int.len == 0 or after_int[0] != '.') break :frac after_int;
 304        bytes.appendAssumeCapacity('.');
 305        for (after_int[1..], 1..) |c, i| {
 306            if (c == '\'') {
 307                bytes.appendAssumeCapacity('_');
 308                continue;
 309            }
 310            if (!prefix.digitAllowed(c)) break :frac after_int[i..];
 311            bytes.appendAssumeCapacity(c);
 312        }
 313        break :frac "";
 314    };
 315
 316    const suffix_str = exponent: {
 317        if (after_frac.len == 0) break :exponent after_frac;
 318        switch (after_frac[0]) {
 319            'e', 'E' => {},
 320            'p', 'P' => if (prefix != .hex) break :exponent after_frac,
 321            else => break :exponent after_frac,
 322        }
 323        bytes.appendAssumeCapacity(after_frac[0]);
 324        for (after_frac[1..], 1..) |c, i| switch (c) {
 325            '+', '-', '0'...'9' => {
 326                bytes.appendAssumeCapacity(c);
 327            },
 328            '\'' => {
 329                bytes.appendAssumeCapacity('_');
 330            },
 331            else => break :exponent after_frac[i..],
 332        };
 333        break :exponent "";
 334    };
 335
 336    const is_float = after_int.len != suffix_str.len;
 337    const suffix = aro.Tree.Token.NumberSuffix.fromString(suffix_str, if (is_float) .float else .int) orelse {
 338        try mt.fail("invalid number suffix: '{s}'", .{suffix_str});
 339        return error.ParseError;
 340    };
 341    if (suffix.isImaginary()) {
 342        try mt.fail("TODO: imaginary literals", .{});
 343        return error.ParseError;
 344    }
 345    if (suffix.isBitInt()) {
 346        try mt.fail("TODO: _BitInt literals", .{});
 347        return error.ParseError;
 348    }
 349
 350    if (is_float) {
 351        const type_node = try ZigTag.type.create(arena, switch (suffix) {
 352            .F16 => "f16",
 353            .F => "f32",
 354            .None => "f64",
 355            .L => "c_longdouble",
 356            .W => "f80",
 357            .Q, .F128 => "f128",
 358            else => unreachable,
 359        });
 360        const rhs = try ZigTag.float_literal.create(arena, bytes.items);
 361        return ZigTag.as.create(arena, .{ .lhs = type_node, .rhs = rhs });
 362    } else {
 363        const type_node = try ZigTag.type.create(arena, switch (suffix) {
 364            .None => "c_int",
 365            .U => "c_uint",
 366            .L => "c_long",
 367            .UL => "c_ulong",
 368            .LL => "c_longlong",
 369            .ULL => "c_ulonglong",
 370            else => unreachable,
 371        });
 372        const value = std.fmt.parseInt(i128, bytes.items, 0) catch math.maxInt(i128);
 373
 374        // make the output less noisy by skipping promoteIntLiteral where
 375        // it's guaranteed to not be required because of C standard type constraints
 376        const guaranteed_to_fit = switch (suffix) {
 377            .None => math.cast(i16, value) != null,
 378            .U => math.cast(u16, value) != null,
 379            .L => math.cast(i32, value) != null,
 380            .UL => math.cast(u32, value) != null,
 381            .LL => math.cast(i64, value) != null,
 382            .ULL => math.cast(u64, value) != null,
 383            else => unreachable,
 384        };
 385
 386        const literal_node = try ZigTag.integer_literal.create(arena, bytes.items);
 387        if (guaranteed_to_fit) {
 388            return ZigTag.as.create(arena, .{ .lhs = type_node, .rhs = literal_node });
 389        } else {
 390            return mt.t.createHelperCallNode(.promoteIntLiteral, &.{ type_node, literal_node, try ZigTag.enum_literal.create(arena, @tagName(prefix)) });
 391        }
 392    }
 393}
 394
 395fn zigifyEscapeSequences(mt: *MacroTranslator, slice: []const u8) ![]const u8 {
 396    var source = slice;
 397    for (source, 0..) |c, i| {
 398        if (c == '\"' or c == '\'') {
 399            source = source[i..];
 400            break;
 401        }
 402    }
 403    for (source) |c| {
 404        if (c == '\\' or c == '\t') {
 405            break;
 406        }
 407    } else return source;
 408    const bytes = try mt.t.arena.alloc(u8, source.len * 2);
 409    var state: enum {
 410        start,
 411        escape,
 412        hex,
 413        octal,
 414    } = .start;
 415    var i: usize = 0;
 416    var count: u8 = 0;
 417    var num: u8 = 0;
 418    for (source) |c| {
 419        switch (state) {
 420            .escape => {
 421                switch (c) {
 422                    'n', 'r', 't', '\\', '\'', '\"' => {
 423                        bytes[i] = c;
 424                    },
 425                    '0'...'7' => {
 426                        count += 1;
 427                        num += c - '0';
 428                        state = .octal;
 429                        bytes[i] = 'x';
 430                    },
 431                    'x' => {
 432                        state = .hex;
 433                        bytes[i] = 'x';
 434                    },
 435                    'a' => {
 436                        bytes[i] = 'x';
 437                        i += 1;
 438                        bytes[i] = '0';
 439                        i += 1;
 440                        bytes[i] = '7';
 441                    },
 442                    'b' => {
 443                        bytes[i] = 'x';
 444                        i += 1;
 445                        bytes[i] = '0';
 446                        i += 1;
 447                        bytes[i] = '8';
 448                    },
 449                    'f' => {
 450                        bytes[i] = 'x';
 451                        i += 1;
 452                        bytes[i] = '0';
 453                        i += 1;
 454                        bytes[i] = 'C';
 455                    },
 456                    'v' => {
 457                        bytes[i] = 'x';
 458                        i += 1;
 459                        bytes[i] = '0';
 460                        i += 1;
 461                        bytes[i] = 'B';
 462                    },
 463                    '?' => {
 464                        i -= 1;
 465                        bytes[i] = '?';
 466                    },
 467                    'u', 'U' => {
 468                        try mt.fail("macro tokenizing failed: TODO unicode escape sequences", .{});
 469                        return error.ParseError;
 470                    },
 471                    else => {
 472                        try mt.fail("macro tokenizing failed: unknown escape sequence", .{});
 473                        return error.ParseError;
 474                    },
 475                }
 476                i += 1;
 477                if (state == .escape)
 478                    state = .start;
 479            },
 480            .start => {
 481                if (c == '\t') {
 482                    bytes[i] = '\\';
 483                    i += 1;
 484                    bytes[i] = 't';
 485                    i += 1;
 486                    continue;
 487                }
 488                if (c == '\\') {
 489                    state = .escape;
 490                }
 491                bytes[i] = c;
 492                i += 1;
 493            },
 494            .hex => {
 495                switch (c) {
 496                    '0'...'9' => {
 497                        num = std.math.mul(u8, num, 16) catch {
 498                            try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
 499                            return error.ParseError;
 500                        };
 501                        num += c - '0';
 502                    },
 503                    'a'...'f' => {
 504                        num = std.math.mul(u8, num, 16) catch {
 505                            try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
 506                            return error.ParseError;
 507                        };
 508                        num += c - 'a' + 10;
 509                    },
 510                    'A'...'F' => {
 511                        num = std.math.mul(u8, num, 16) catch {
 512                            try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
 513                            return error.ParseError;
 514                        };
 515                        num += c - 'A' + 10;
 516                    },
 517                    else => {
 518                        i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
 519                        num = 0;
 520                        if (c == '\\')
 521                            state = .escape
 522                        else
 523                            state = .start;
 524                        bytes[i] = c;
 525                        i += 1;
 526                    },
 527                }
 528            },
 529            .octal => {
 530                const accept_digit = switch (c) {
 531                    // The maximum length of a octal literal is 3 digits
 532                    '0'...'7' => count < 3,
 533                    else => false,
 534                };
 535
 536                if (accept_digit) {
 537                    count += 1;
 538                    num = std.math.mul(u8, num, 8) catch {
 539                        try mt.fail("macro tokenizing failed: octal literal overflowed", .{});
 540                        return error.ParseError;
 541                    };
 542                    num += c - '0';
 543                } else {
 544                    i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
 545                    num = 0;
 546                    count = 0;
 547                    if (c == '\\')
 548                        state = .escape
 549                    else
 550                        state = .start;
 551                    bytes[i] = c;
 552                    i += 1;
 553                }
 554            },
 555        }
 556    }
 557    if (state == .hex or state == .octal) {
 558        i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
 559    }
 560
 561    return bytes[0..i];
 562}
 563
 564/// non-ASCII characters (mt > 127) are also treated as non-printable by fmtSliceEscapeLower.
 565/// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape
 566/// non-ASCII characters so that the Zig source we output will itself be UTF-8.
 567fn escapeUnprintables(mt: *MacroTranslator) ![]const u8 {
 568    const slice = mt.tokSlice();
 569    mt.i += 1;
 570
 571    const zigified = try mt.zigifyEscapeSequences(slice);
 572    if (std.unicode.utf8ValidateSlice(zigified)) return zigified;
 573
 574    const formatter = std.ascii.hexEscape(zigified, .lower);
 575    const encoded_size = @as(usize, @intCast(std.fmt.count("{f}", .{formatter})));
 576    const output = try mt.t.arena.alloc(u8, encoded_size);
 577    return std.fmt.bufPrint(output, "{f}", .{formatter}) catch |err| switch (err) {
 578        error.NoSpaceLeft => unreachable,
 579        else => |e| return e,
 580    };
 581}
 582
 583fn parseCPrimaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 584    const arena = mt.t.arena;
 585    const tok = mt.peek();
 586    switch (tok) {
 587        .char_literal,
 588        .char_literal_utf_8,
 589        .char_literal_utf_16,
 590        .char_literal_utf_32,
 591        .char_literal_wide,
 592        => {
 593            const slice = mt.tokSlice();
 594            if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
 595                return ZigTag.char_literal.create(arena, try mt.escapeUnprintables());
 596            } else {
 597                mt.i += 1;
 598
 599                const str = try std.fmt.allocPrint(arena, "0x{x}", .{slice[1 .. slice.len - 1]});
 600                return ZigTag.integer_literal.create(arena, str);
 601            }
 602        },
 603        .string_literal,
 604        .string_literal_utf_16,
 605        .string_literal_utf_8,
 606        .string_literal_utf_32,
 607        .string_literal_wide,
 608        => return ZigTag.string_literal.create(arena, try mt.escapeUnprintables()),
 609        .pp_num => return mt.parseCNumLit(),
 610        .l_paren => {
 611            mt.i += 1;
 612            const inner_node = try mt.parseCExpr(scope);
 613
 614            try mt.expect(.r_paren);
 615            return inner_node;
 616        },
 617        .macro_param, .macro_param_no_expand => {
 618            const param = mt.macro.params[mt.tokens[mt.i].end];
 619            mt.i += 1;
 620
 621            const mangled_name = scope.getAlias(param) orelse param;
 622            return try ZigTag.identifier.create(arena, mangled_name);
 623        },
 624        .identifier, .extended_identifier => {
 625            const slice = mt.tokSlice();
 626            mt.i += 1;
 627
 628            const mangled_name = scope.getAlias(slice) orelse slice;
 629            if (Translator.builtin_typedef_map.get(mangled_name)) |ty| {
 630                return ZigTag.type.create(arena, ty);
 631            }
 632            if (builtins.map.get(mangled_name)) |builtin| {
 633                const builtin_identifier = try ZigTag.identifier.create(arena, "__builtin");
 634                return ZigTag.field_access.create(arena, .{
 635                    .lhs = builtin_identifier,
 636                    .field_name = builtin.name,
 637                });
 638            }
 639
 640            const identifier = try ZigTag.identifier.create(arena, mangled_name);
 641            scope.skipVariableDiscard(mangled_name);
 642            refs_var: {
 643                const ident_node = mt.t.global_scope.sym_table.get(slice) orelse break :refs_var;
 644                const var_decl_node = ident_node.castTag(.var_decl) orelse break :refs_var;
 645                if (!var_decl_node.data.is_const) mt.refs_var_decl = true;
 646            }
 647            return identifier;
 648        },
 649        else => {},
 650    }
 651
 652    // for handling type macros (EVIL)
 653    // TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList?
 654    if (try mt.parseCTypeName(scope)) |type_name| {
 655        return type_name;
 656    }
 657
 658    try mt.fail("unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
 659    return error.ParseError;
 660}
 661
 662fn macroIntFromBool(mt: *MacroTranslator, node: ZigNode) !ZigNode {
 663    if (!node.isBoolRes()) return node;
 664
 665    return ZigTag.int_from_bool.create(mt.t.arena, node);
 666}
 667
 668fn macroIntToBool(mt: *MacroTranslator, node: ZigNode) !ZigNode {
 669    if (node.isBoolRes()) return node;
 670
 671    if (node.tag() == .string_literal) {
 672        // @intFromPtr(node) != 0
 673        const int_from_ptr = try ZigTag.int_from_ptr.create(mt.t.arena, node);
 674        return ZigTag.not_equal.create(mt.t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() });
 675    }
 676    // node != 0
 677    return ZigTag.not_equal.create(mt.t.arena, .{ .lhs = node, .rhs = ZigTag.zero_literal.init() });
 678}
 679
 680fn parseCCondExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 681    const node = try mt.parseCOrExpr(scope);
 682    if (!mt.eat(.question_mark)) return node;
 683
 684    const then_body = try mt.parseCOrExpr(scope);
 685    try mt.expect(.colon);
 686    const else_body = try mt.parseCCondExpr(scope);
 687    return ZigTag.@"if".create(mt.t.arena, .{ .cond = node, .then = then_body, .@"else" = else_body });
 688}
 689
 690fn parseCOrExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 691    var node = try mt.parseCAndExpr(scope);
 692    while (mt.eat(.pipe_pipe)) {
 693        const lhs = try mt.macroIntToBool(node);
 694        const rhs = try mt.macroIntToBool(try mt.parseCAndExpr(scope));
 695        node = try ZigTag.@"or".create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 696    }
 697    return node;
 698}
 699
 700fn parseCAndExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 701    var node = try mt.parseCBitOrExpr(scope);
 702    while (mt.eat(.ampersand_ampersand)) {
 703        const lhs = try mt.macroIntToBool(node);
 704        const rhs = try mt.macroIntToBool(try mt.parseCBitOrExpr(scope));
 705        node = try ZigTag.@"and".create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 706    }
 707    return node;
 708}
 709
 710fn parseCBitOrExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 711    var node = try mt.parseCBitXorExpr(scope);
 712    while (mt.eat(.pipe)) {
 713        const lhs = try mt.macroIntFromBool(node);
 714        const rhs = try mt.macroIntFromBool(try mt.parseCBitXorExpr(scope));
 715        node = try ZigTag.bit_or.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 716    }
 717    return node;
 718}
 719
 720fn parseCBitXorExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 721    var node = try mt.parseCBitAndExpr(scope);
 722    while (mt.eat(.caret)) {
 723        const lhs = try mt.macroIntFromBool(node);
 724        const rhs = try mt.macroIntFromBool(try mt.parseCBitAndExpr(scope));
 725        node = try ZigTag.bit_xor.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 726    }
 727    return node;
 728}
 729
 730fn parseCBitAndExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 731    var node = try mt.parseCEqExpr(scope);
 732    while (mt.eat(.ampersand)) {
 733        const lhs = try mt.macroIntFromBool(node);
 734        const rhs = try mt.macroIntFromBool(try mt.parseCEqExpr(scope));
 735        node = try ZigTag.bit_and.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 736    }
 737    return node;
 738}
 739
 740fn parseCEqExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 741    var node = try mt.parseCRelExpr(scope);
 742    while (true) {
 743        switch (mt.peek()) {
 744            .bang_equal => {
 745                mt.i += 1;
 746                const lhs = try mt.macroIntFromBool(node);
 747                const rhs = try mt.macroIntFromBool(try mt.parseCRelExpr(scope));
 748                node = try ZigTag.not_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 749            },
 750            .equal_equal => {
 751                mt.i += 1;
 752                const lhs = try mt.macroIntFromBool(node);
 753                const rhs = try mt.macroIntFromBool(try mt.parseCRelExpr(scope));
 754                node = try ZigTag.equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 755            },
 756            else => return node,
 757        }
 758    }
 759}
 760
 761fn parseCRelExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 762    var node = try mt.parseCShiftExpr(scope);
 763    while (true) {
 764        switch (mt.peek()) {
 765            .angle_bracket_right => {
 766                mt.i += 1;
 767                const lhs = try mt.macroIntFromBool(node);
 768                const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
 769                node = try ZigTag.greater_than.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 770            },
 771            .angle_bracket_right_equal => {
 772                mt.i += 1;
 773                const lhs = try mt.macroIntFromBool(node);
 774                const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
 775                node = try ZigTag.greater_than_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 776            },
 777            .angle_bracket_left => {
 778                mt.i += 1;
 779                const lhs = try mt.macroIntFromBool(node);
 780                const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
 781                node = try ZigTag.less_than.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 782            },
 783            .angle_bracket_left_equal => {
 784                mt.i += 1;
 785                const lhs = try mt.macroIntFromBool(node);
 786                const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
 787                node = try ZigTag.less_than_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 788            },
 789            else => return node,
 790        }
 791    }
 792}
 793
 794fn parseCShiftExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 795    var node = try mt.parseCAddSubExpr(scope);
 796    while (true) {
 797        switch (mt.peek()) {
 798            .angle_bracket_angle_bracket_left => {
 799                mt.i += 1;
 800                const lhs = try mt.macroIntFromBool(node);
 801                const rhs = try mt.macroIntFromBool(try mt.parseCAddSubExpr(scope));
 802                node = try ZigTag.shl.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 803            },
 804            .angle_bracket_angle_bracket_right => {
 805                mt.i += 1;
 806                const lhs = try mt.macroIntFromBool(node);
 807                const rhs = try mt.macroIntFromBool(try mt.parseCAddSubExpr(scope));
 808                node = try ZigTag.shr.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 809            },
 810            else => return node,
 811        }
 812    }
 813}
 814
 815fn parseCAddSubExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 816    var node = try mt.parseCMulExpr(scope);
 817    while (true) {
 818        switch (mt.peek()) {
 819            .plus => {
 820                mt.i += 1;
 821                const lhs = try mt.macroIntFromBool(node);
 822                const rhs = try mt.macroIntFromBool(try mt.parseCMulExpr(scope));
 823                node = try ZigTag.add.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 824            },
 825            .minus => {
 826                mt.i += 1;
 827                const lhs = try mt.macroIntFromBool(node);
 828                const rhs = try mt.macroIntFromBool(try mt.parseCMulExpr(scope));
 829                node = try ZigTag.sub.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 830            },
 831            else => return node,
 832        }
 833    }
 834}
 835
 836fn parseCMulExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 837    var node = try mt.parseCCastExpr(scope);
 838    while (true) {
 839        switch (mt.peek()) {
 840            .asterisk => {
 841                mt.i += 1;
 842                switch (mt.peek()) {
 843                    .comma, .r_paren, .eof => {
 844                        // This is probably a pointer type
 845                        return ZigTag.c_pointer.create(mt.t.arena, .{
 846                            .is_const = false,
 847                            .is_volatile = false,
 848                            .is_allowzero = false,
 849                            .elem_type = node,
 850                        });
 851                    },
 852                    else => {},
 853                }
 854                const lhs = try mt.macroIntFromBool(node);
 855                const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
 856                node = try ZigTag.mul.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
 857            },
 858            .slash => {
 859                mt.i += 1;
 860                const lhs = try mt.macroIntFromBool(node);
 861                const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
 862                node = try mt.t.createHelperCallNode(.div, &.{ lhs, rhs });
 863            },
 864            .percent => {
 865                mt.i += 1;
 866                const lhs = try mt.macroIntFromBool(node);
 867                const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
 868                node = try mt.t.createHelperCallNode(.rem, &.{ lhs, rhs });
 869            },
 870            else => return node,
 871        }
 872    }
 873}
 874
 875fn parseCCastExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
 876    if (mt.eat(.l_paren)) {
 877        if (try mt.parseCTypeName(scope)) |type_name| {
 878            while (true) {
 879                const next_tok = mt.peek();
 880                if (next_tok == .r_paren) {
 881                    mt.i += 1;
 882                    break;
 883                }
 884                // Skip trailing blank defined before the RParen.
 885                if ((next_tok == .identifier or next_tok == .extended_identifier) and
 886                    mt.t.global_scope.blank_macros.contains(mt.tokSlice()))
 887                {
 888                    mt.i += 1;
 889                    continue;
 890                }
 891
 892                try mt.fail(
 893                    "unable to translate C expr: expected ')' instead got '{s}'",
 894                    .{next_tok.symbol()},
 895                );
 896                return error.ParseError;
 897            }
 898            if (mt.peek() == .l_brace) {
 899                // initializer list
 900                return mt.parseCPostfixExpr(scope, type_name);
 901            }
 902            const node_to_cast = try mt.parseCCastExpr(scope);
 903            return mt.t.createHelperCallNode(.cast, &.{ type_name, node_to_cast });
 904        }
 905        mt.i -= 1; // l_paren
 906    }
 907    return mt.parseCUnaryExpr(scope);
 908}
 909
 910// allow_fail is set when unsure if we are parsing a type-name
 911fn parseCTypeName(mt: *MacroTranslator, scope: *Scope) ParseError!?ZigNode {
 912    if (try mt.parseCSpecifierQualifierList(scope)) |node| {
 913        return try mt.parseCAbstractDeclarator(node);
 914    }
 915    return null;
 916}
 917
 918fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope) ParseError!?ZigNode {
 919    const tok = mt.peek();
 920    switch (tok) {
 921        .macro_param, .macro_param_no_expand => {
 922            const param = mt.macro.params[mt.tokens[mt.i].end];
 923
 924            // Assume that this is only a cast if the next token is ')'
 925            // e.g. param)identifier
 926            if (mt.macro.tokens.len < mt.i + 3 or
 927                mt.macro.tokens[mt.i + 1].id != .r_paren or
 928                mt.macro.tokens[mt.i + 2].id != .identifier)
 929                return null;
 930
 931            mt.i += 1;
 932            const mangled_name = scope.getAlias(param) orelse param;
 933            return try ZigTag.identifier.create(mt.t.arena, mangled_name);
 934        },
 935        .identifier, .extended_identifier => {
 936            const slice = mt.tokSlice();
 937            const mangled_name = scope.getAlias(slice) orelse slice;
 938
 939            if (mt.t.global_scope.blank_macros.contains(slice)) {
 940                mt.i += 1;
 941                return try mt.parseCSpecifierQualifierList(scope);
 942            }
 943
 944            if (mt.t.typedefs.contains(mangled_name)) {
 945                mt.i += 1;
 946                if (Translator.builtin_typedef_map.get(mangled_name)) |ty| {
 947                    return try ZigTag.type.create(mt.t.arena, ty);
 948                }
 949                if (builtins.map.get(mangled_name)) |builtin| {
 950                    const builtin_identifier = try ZigTag.identifier.create(mt.t.arena, "__builtin");
 951                    return try ZigTag.field_access.create(mt.t.arena, .{
 952                        .lhs = builtin_identifier,
 953                        .field_name = builtin.name,
 954                    });
 955                }
 956
 957                return try ZigTag.identifier.create(mt.t.arena, mangled_name);
 958            }
 959        },
 960        .keyword_void => {
 961            mt.i += 1;
 962            return try ZigTag.type.create(mt.t.arena, "anyopaque");
 963        },
 964        .keyword_bool => {
 965            mt.i += 1;
 966            return try ZigTag.type.create(mt.t.arena, "bool");
 967        },
 968        .keyword_char,
 969        .keyword_int,
 970        .keyword_short,
 971        .keyword_long,
 972        .keyword_float,
 973        .keyword_double,
 974        .keyword_signed,
 975        .keyword_unsigned,
 976        .keyword_complex,
 977        => return try mt.parseCNumericType(),
 978        .keyword_enum, .keyword_struct, .keyword_union => {
 979            const tag_name = mt.tokSlice();
 980            mt.i += 1;
 981            if (mt.peek() != .identifier) {
 982                mt.i -= 1;
 983                return null;
 984            }
 985
 986            // struct Foo will be declared as struct_Foo by transRecordDecl
 987            const identifier = mt.tokSlice();
 988            try mt.expect(.identifier);
 989
 990            const name = try std.fmt.allocPrint(mt.t.arena, "{s}_{s}", .{ tag_name, identifier });
 991            if (!mt.t.global_scope.contains(name)) {
 992                try mt.fail("unable to translate C expr: '{s}' not found", .{name});
 993                return error.ParseError;
 994            }
 995
 996            return try ZigTag.identifier.create(mt.t.arena, name);
 997        },
 998        else => {},
 999    }
1000
1001    return null;
1002}
1003
1004fn parseCNumericType(mt: *MacroTranslator) ParseError!ZigNode {
1005    const KwCounter = struct {
1006        double: u8 = 0,
1007        long: u8 = 0,
1008        int: u8 = 0,
1009        float: u8 = 0,
1010        short: u8 = 0,
1011        char: u8 = 0,
1012        unsigned: u8 = 0,
1013        signed: u8 = 0,
1014        complex: u8 = 0,
1015
1016        fn eql(self: @This(), other: @This()) bool {
1017            return std.meta.eql(self, other);
1018        }
1019    };
1020
1021    // Yes, these can be in *any* order
1022    // This still doesn't cover cases where for example volatile is intermixed
1023
1024    var kw = KwCounter{};
1025    // prevent overflow
1026    var i: u8 = 0;
1027    while (i < math.maxInt(u8)) : (i += 1) {
1028        switch (mt.peek()) {
1029            .keyword_double => kw.double += 1,
1030            .keyword_long => kw.long += 1,
1031            .keyword_int => kw.int += 1,
1032            .keyword_float => kw.float += 1,
1033            .keyword_short => kw.short += 1,
1034            .keyword_char => kw.char += 1,
1035            .keyword_unsigned => kw.unsigned += 1,
1036            .keyword_signed => kw.signed += 1,
1037            .keyword_complex => kw.complex += 1,
1038            else => break,
1039        }
1040        mt.i += 1;
1041    }
1042
1043    if (kw.eql(.{ .int = 1 }) or kw.eql(.{ .signed = 1 }) or kw.eql(.{ .signed = 1, .int = 1 }))
1044        return ZigTag.type.create(mt.t.arena, "c_int");
1045
1046    if (kw.eql(.{ .unsigned = 1 }) or kw.eql(.{ .unsigned = 1, .int = 1 }))
1047        return ZigTag.type.create(mt.t.arena, "c_uint");
1048
1049    if (kw.eql(.{ .long = 1 }) or kw.eql(.{ .signed = 1, .long = 1 }) or kw.eql(.{ .long = 1, .int = 1 }) or kw.eql(.{ .signed = 1, .long = 1, .int = 1 }))
1050        return ZigTag.type.create(mt.t.arena, "c_long");
1051
1052    if (kw.eql(.{ .unsigned = 1, .long = 1 }) or kw.eql(.{ .unsigned = 1, .long = 1, .int = 1 }))
1053        return ZigTag.type.create(mt.t.arena, "c_ulong");
1054
1055    if (kw.eql(.{ .long = 2 }) or kw.eql(.{ .signed = 1, .long = 2 }) or kw.eql(.{ .long = 2, .int = 1 }) or kw.eql(.{ .signed = 1, .long = 2, .int = 1 }))
1056        return ZigTag.type.create(mt.t.arena, "c_longlong");
1057
1058    if (kw.eql(.{ .unsigned = 1, .long = 2 }) or kw.eql(.{ .unsigned = 1, .long = 2, .int = 1 }))
1059        return ZigTag.type.create(mt.t.arena, "c_ulonglong");
1060
1061    if (kw.eql(.{ .signed = 1, .char = 1 }))
1062        return ZigTag.type.create(mt.t.arena, "i8");
1063
1064    if (kw.eql(.{ .char = 1 }) or kw.eql(.{ .unsigned = 1, .char = 1 }))
1065        return ZigTag.type.create(mt.t.arena, "u8");
1066
1067    if (kw.eql(.{ .short = 1 }) or kw.eql(.{ .signed = 1, .short = 1 }) or kw.eql(.{ .short = 1, .int = 1 }) or kw.eql(.{ .signed = 1, .short = 1, .int = 1 }))
1068        return ZigTag.type.create(mt.t.arena, "c_short");
1069
1070    if (kw.eql(.{ .unsigned = 1, .short = 1 }) or kw.eql(.{ .unsigned = 1, .short = 1, .int = 1 }))
1071        return ZigTag.type.create(mt.t.arena, "c_ushort");
1072
1073    if (kw.eql(.{ .float = 1 }))
1074        return ZigTag.type.create(mt.t.arena, "f32");
1075
1076    if (kw.eql(.{ .double = 1 }))
1077        return ZigTag.type.create(mt.t.arena, "f64");
1078
1079    if (kw.eql(.{ .long = 1, .double = 1 })) {
1080        try mt.fail("unable to translate: TODO long double", .{});
1081        return error.ParseError;
1082    }
1083
1084    if (kw.eql(.{ .float = 1, .complex = 1 })) {
1085        try mt.fail("unable to translate: TODO _Complex", .{});
1086        return error.ParseError;
1087    }
1088
1089    if (kw.eql(.{ .double = 1, .complex = 1 })) {
1090        try mt.fail("unable to translate: TODO _Complex", .{});
1091        return error.ParseError;
1092    }
1093
1094    if (kw.eql(.{ .long = 1, .double = 1, .complex = 1 })) {
1095        try mt.fail("unable to translate: TODO _Complex", .{});
1096        return error.ParseError;
1097    }
1098
1099    try mt.fail("unable to translate: invalid numeric type", .{});
1100    return error.ParseError;
1101}
1102
1103fn parseCAbstractDeclarator(mt: *MacroTranslator, node: ZigNode) ParseError!ZigNode {
1104    if (mt.eat(.asterisk)) {
1105        if (node.castTag(.type)) |some| {
1106            if (std.mem.eql(u8, some.data, "anyopaque")) {
1107                const ptr = try ZigTag.single_pointer.create(mt.t.arena, .{
1108                    .is_const = false,
1109                    .is_volatile = false,
1110                    .is_allowzero = false,
1111                    .elem_type = node,
1112                });
1113                return ZigTag.optional_type.create(mt.t.arena, ptr);
1114            }
1115        }
1116        return ZigTag.c_pointer.create(mt.t.arena, .{
1117            .is_const = false,
1118            .is_volatile = false,
1119            .is_allowzero = false,
1120            .elem_type = node,
1121        });
1122    }
1123    return node;
1124}
1125
1126fn parseCPostfixExpr(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNode) ParseError!ZigNode {
1127    var node = try mt.parseCPostfixExprInner(scope, type_name);
1128    // In C the preprocessor would handle concatting strings while expanding macros.
1129    // This should do approximately the same by concatting any strings and identifiers
1130    // after a primary or postfix expression.
1131    while (true) {
1132        switch (mt.peek()) {
1133            .string_literal,
1134            .string_literal_utf_16,
1135            .string_literal_utf_8,
1136            .string_literal_utf_32,
1137            .string_literal_wide,
1138            => {},
1139            .identifier, .extended_identifier => {
1140                if (mt.t.global_scope.blank_macros.contains(mt.tokSlice())) {
1141                    mt.i += 1;
1142                    continue;
1143                }
1144            },
1145            else => break,
1146        }
1147        const rhs = try mt.parseCPostfixExprInner(scope, type_name);
1148        node = try ZigTag.array_cat.create(mt.t.arena, .{ .lhs = node, .rhs = rhs });
1149    }
1150    return node;
1151}
1152
1153fn parseCPostfixExprInner(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNode) ParseError!ZigNode {
1154    const gpa = mt.t.gpa;
1155    const arena = mt.t.arena;
1156    var node = type_name orelse try mt.parseCPrimaryExpr(scope);
1157    while (true) {
1158        switch (mt.peek()) {
1159            .period => {
1160                mt.i += 1;
1161                const tok = mt.tokens[mt.i];
1162                if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {
1163                    try mt.fail("unable to translate C expr: field access using macro parameter", .{});
1164                    return error.ParseError;
1165                }
1166                const field_name = mt.tokSlice();
1167                try mt.expect(.identifier);
1168
1169                node = try ZigTag.field_access.create(arena, .{ .lhs = node, .field_name = field_name });
1170            },
1171            .arrow => {
1172                mt.i += 1;
1173                const tok = mt.tokens[mt.i];
1174                if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {
1175                    try mt.fail("unable to translate C expr: field access using macro parameter", .{});
1176                    return error.ParseError;
1177                }
1178                const field_name = mt.tokSlice();
1179                try mt.expect(.identifier);
1180
1181                const deref = try ZigTag.deref.create(arena, node);
1182                node = try ZigTag.field_access.create(arena, .{ .lhs = deref, .field_name = field_name });
1183            },
1184            .l_bracket => {
1185                mt.i += 1;
1186
1187                const index_val = try mt.macroIntFromBool(try mt.parseCExpr(scope));
1188                const index = try ZigTag.as.create(arena, .{
1189                    .lhs = try ZigTag.type.create(arena, "usize"),
1190                    .rhs = try ZigTag.int_cast.create(arena, index_val),
1191                });
1192                node = try ZigTag.array_access.create(arena, .{ .lhs = node, .rhs = index });
1193                try mt.expect(.r_bracket);
1194            },
1195            .l_paren => {
1196                mt.i += 1;
1197
1198                if (mt.eat(.r_paren)) {
1199                    node = try ZigTag.call.create(arena, .{ .lhs = node, .args = &.{} });
1200                } else {
1201                    var args: std.ArrayList(ZigNode) = .empty;
1202                    defer args.deinit(gpa);
1203
1204                    while (true) {
1205                        const arg = try mt.parseCCondExpr(scope);
1206                        try args.append(gpa, arg);
1207
1208                        const next_id = mt.peek();
1209                        switch (next_id) {
1210                            .comma => {
1211                                mt.i += 1;
1212                            },
1213                            .r_paren => {
1214                                mt.i += 1;
1215                                break;
1216                            },
1217                            else => {
1218                                try mt.fail("unable to translate C expr: expected ',' or ')' instead got '{s}'", .{next_id.symbol()});
1219                                return error.ParseError;
1220                            },
1221                        }
1222                    }
1223                    node = try ZigTag.call.create(arena, .{ .lhs = node, .args = try arena.dupe(ZigNode, args.items) });
1224                }
1225            },
1226            .l_brace => {
1227                mt.i += 1;
1228
1229                // Check for designated field initializers
1230                if (mt.peek() == .period) {
1231                    var init_vals: std.ArrayList(ast.Payload.ContainerInitDot.Initializer) = .empty;
1232                    defer init_vals.deinit(gpa);
1233
1234                    while (true) {
1235                        try mt.expect(.period);
1236                        const name = mt.tokSlice();
1237                        try mt.expect(.identifier);
1238                        try mt.expect(.equal);
1239
1240                        const val = try mt.parseCCondExpr(scope);
1241                        try init_vals.append(gpa, .{ .name = name, .value = val });
1242
1243                        const next_id = mt.peek();
1244                        switch (next_id) {
1245                            .comma => {
1246                                mt.i += 1;
1247                            },
1248                            .r_brace => {
1249                                mt.i += 1;
1250                                break;
1251                            },
1252                            else => {
1253                                try mt.fail("unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
1254                                return error.ParseError;
1255                            },
1256                        }
1257                    }
1258                    const tuple_node = try ZigTag.container_init_dot.create(arena, try arena.dupe(ast.Payload.ContainerInitDot.Initializer, init_vals.items));
1259                    node = try ZigTag.std_mem_zeroinit.create(arena, .{ .lhs = node, .rhs = tuple_node });
1260                    continue;
1261                }
1262
1263                var init_vals: std.ArrayList(ZigNode) = .empty;
1264                defer init_vals.deinit(gpa);
1265
1266                while (true) {
1267                    const val = try mt.parseCCondExpr(scope);
1268                    try init_vals.append(gpa, val);
1269
1270                    const next_id = mt.peek();
1271                    switch (next_id) {
1272                        .comma => {
1273                            mt.i += 1;
1274                        },
1275                        .r_brace => {
1276                            mt.i += 1;
1277                            break;
1278                        },
1279                        else => {
1280                            try mt.fail("unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
1281                            return error.ParseError;
1282                        },
1283                    }
1284                }
1285                const tuple_node = try ZigTag.tuple.create(arena, try arena.dupe(ZigNode, init_vals.items));
1286                node = try ZigTag.std_mem_zeroinit.create(arena, .{ .lhs = node, .rhs = tuple_node });
1287            },
1288            .plus_plus, .minus_minus => {
1289                try mt.fail("TODO postfix inc/dec expr", .{});
1290                return error.ParseError;
1291            },
1292            else => return node,
1293        }
1294    }
1295}
1296
1297fn parseCUnaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
1298    switch (mt.peek()) {
1299        .bang => {
1300            mt.i += 1;
1301            const operand = try mt.macroIntToBool(try mt.parseCCastExpr(scope));
1302            return ZigTag.not.create(mt.t.arena, operand);
1303        },
1304        .minus => {
1305            mt.i += 1;
1306            const operand = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
1307            return ZigTag.negate.create(mt.t.arena, operand);
1308        },
1309        .plus => {
1310            mt.i += 1;
1311            return try mt.parseCCastExpr(scope);
1312        },
1313        .tilde => {
1314            mt.i += 1;
1315            const operand = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
1316            return ZigTag.bit_not.create(mt.t.arena, operand);
1317        },
1318        .asterisk => {
1319            mt.i += 1;
1320            const operand = try mt.parseCCastExpr(scope);
1321            return ZigTag.deref.create(mt.t.arena, operand);
1322        },
1323        .ampersand => {
1324            mt.i += 1;
1325            const operand = try mt.parseCCastExpr(scope);
1326            return ZigTag.address_of.create(mt.t.arena, operand);
1327        },
1328        .keyword_sizeof => {
1329            mt.i += 1;
1330            const operand = if (mt.eat(.l_paren)) blk: {
1331                const inner = (try mt.parseCTypeName(scope)) orelse try mt.parseCUnaryExpr(scope);
1332                try mt.expect(.r_paren);
1333                break :blk inner;
1334            } else try mt.parseCUnaryExpr(scope);
1335
1336            return mt.t.createHelperCallNode(.sizeof, &.{operand});
1337        },
1338        .keyword_alignof => {
1339            mt.i += 1;
1340            // TODO this won't work if using <stdalign.h>'s
1341            // #define alignof _Alignof
1342            try mt.expect(.l_paren);
1343            const operand = (try mt.parseCTypeName(scope)) orelse try mt.parseCUnaryExpr(scope);
1344            try mt.expect(.r_paren);
1345
1346            return ZigTag.alignof.create(mt.t.arena, operand);
1347        },
1348        .plus_plus, .minus_minus => {
1349            try mt.fail("TODO unary inc/dec expr", .{});
1350            return error.ParseError;
1351        },
1352        else => {},
1353    }
1354
1355    return try mt.parseCPostfixExpr(scope, null);
1356}