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 // +3 for prefix and +2 for suffix
270 var bytes = try std.ArrayList(u8).initCapacity(arena, lit_bytes.len + 3 + 2);
271
272 const prefix = aro.Tree.Token.NumberPrefix.fromString(lit_bytes);
273 switch (prefix) {
274 .binary => bytes.appendSliceAssumeCapacity("0b"),
275 .octal => bytes.appendSliceAssumeCapacity("0o"),
276 .hex => bytes.appendSliceAssumeCapacity("0x"),
277 .decimal => {},
278 }
279
280 const after_prefix = lit_bytes[prefix.stringLen()..];
281 const after_int = for (after_prefix, 0..) |c, i| switch (c) {
282 '.' => {
283 if (i == 0) {
284 bytes.appendAssumeCapacity('0');
285 }
286 break after_prefix[i..];
287 },
288 'e', 'E' => {
289 if (prefix != .hex) break after_prefix[i..];
290 bytes.appendAssumeCapacity(c);
291 },
292 'p', 'P' => break after_prefix[i..],
293 '0'...'9', 'a'...'d', 'A'...'D', 'f', 'F' => {
294 if (!prefix.digitAllowed(c)) break after_prefix[i..];
295 bytes.appendAssumeCapacity(c);
296 },
297 '\'' => {
298 bytes.appendAssumeCapacity('_');
299 },
300 else => break after_prefix[i..],
301 } else "";
302
303 const after_frac = frac: {
304 if (after_int.len == 0 or after_int[0] != '.') break :frac after_int;
305 bytes.appendAssumeCapacity('.');
306 for (after_int[1..], 1..) |c, i| {
307 if (c == '\'') {
308 bytes.appendAssumeCapacity('_');
309 continue;
310 }
311 if (!prefix.digitAllowed(c)) break :frac after_int[i..];
312 bytes.appendAssumeCapacity(c);
313 }
314 break :frac "";
315 };
316
317 const suffix_str = exponent: {
318 if (after_frac.len == 0) break :exponent after_frac;
319 switch (after_frac[0]) {
320 'e', 'E' => {},
321 'p', 'P' => if (prefix != .hex) break :exponent after_frac,
322 else => break :exponent after_frac,
323 }
324 bytes.appendAssumeCapacity(after_frac[0]);
325 for (after_frac[1..], 1..) |c, i| switch (c) {
326 '+', '-', '0'...'9' => {
327 bytes.appendAssumeCapacity(c);
328 },
329 '\'' => {
330 bytes.appendAssumeCapacity('_');
331 },
332 else => break :exponent after_frac[i..],
333 };
334 break :exponent "";
335 };
336
337 const is_float = after_int.len != suffix_str.len;
338 const suffix = aro.Tree.Token.NumberSuffix.fromString(suffix_str, if (is_float) .float else .int) orelse {
339 try mt.fail("invalid number suffix: '{s}'", .{suffix_str});
340 return error.ParseError;
341 };
342 if (suffix.isImaginary()) {
343 try mt.fail("TODO: imaginary literals", .{});
344 return error.ParseError;
345 }
346 if (suffix.isBitInt()) {
347 try mt.fail("TODO: _BitInt literals", .{});
348 return error.ParseError;
349 }
350
351 if (is_float) {
352 const type_node = try ZigTag.type.create(arena, switch (suffix) {
353 .F16 => "f16",
354 .F, .F32 => "f32",
355 .None, .F32x, .F64 => "f64",
356 .L, .F64x => "c_longdouble",
357 .W => "f80",
358 .Q, .F128 => "f128",
359 else => {
360 try mt.fail("TODO: float literal suffix: '{s}'", .{suffix_str});
361 return error.ParseError;
362 },
363 });
364 if (bytes.last().? == '.') {
365 bytes.appendAssumeCapacity('0');
366 } else if (mem.findAny(u8, bytes.items, ".eEpP") == null) {
367 bytes.appendSliceAssumeCapacity(".0");
368 }
369 const rhs = try ZigTag.float_literal.create(arena, bytes.items);
370 return ZigTag.as.create(arena, .{ .lhs = type_node, .rhs = rhs });
371 } else {
372 const type_node = try ZigTag.type.create(arena, switch (suffix) {
373 .None => "c_int",
374 .U => "c_uint",
375 .L => "c_long",
376 .UL => "c_ulong",
377 .LL => "c_longlong",
378 .ULL => "c_ulonglong",
379 else => unreachable,
380 });
381 const value = std.fmt.parseInt(i128, bytes.items, 0) catch math.maxInt(i128);
382
383 // make the output less noisy by skipping promoteIntLiteral where
384 // it's guaranteed to not be required because of C standard type constraints
385 const guaranteed_to_fit = switch (suffix) {
386 .None => math.cast(i16, value) != null,
387 .U => math.cast(u16, value) != null,
388 .L => math.cast(i32, value) != null,
389 .UL => math.cast(u32, value) != null,
390 .LL => math.cast(i64, value) != null,
391 .ULL => math.cast(u64, value) != null,
392 else => unreachable,
393 };
394
395 const literal_node = try ZigTag.integer_literal.create(arena, bytes.items);
396 if (guaranteed_to_fit) {
397 return ZigTag.as.create(arena, .{ .lhs = type_node, .rhs = literal_node });
398 } else {
399 return mt.t.createHelperCallNode(.promoteIntLiteral, &.{ type_node, literal_node, try ZigTag.enum_literal.create(arena, @tagName(prefix)) });
400 }
401 }
402}
403
404fn zigifyEscapeSequences(mt: *MacroTranslator, slice: []const u8) ![]const u8 {
405 var source = slice;
406 for (source, 0..) |c, i| {
407 if (c == '\"' or c == '\'') {
408 source = source[i..];
409 break;
410 }
411 }
412 for (source) |c| {
413 if (c == '\\' or c == '\t') {
414 break;
415 }
416 } else return source;
417 const bytes = try mt.t.arena.alloc(u8, source.len * 2);
418 var state: enum {
419 start,
420 escape,
421 hex,
422 octal,
423 } = .start;
424 var i: usize = 0;
425 var count: u8 = 0;
426 var num: u8 = 0;
427 for (source) |c| {
428 switch (state) {
429 .escape => {
430 switch (c) {
431 'n', 'r', 't', '\\', '\'', '\"' => {
432 bytes[i] = c;
433 },
434 '0'...'7' => {
435 count += 1;
436 num += c - '0';
437 state = .octal;
438 bytes[i] = 'x';
439 },
440 'x' => {
441 state = .hex;
442 bytes[i] = 'x';
443 },
444 'a' => {
445 bytes[i] = 'x';
446 i += 1;
447 bytes[i] = '0';
448 i += 1;
449 bytes[i] = '7';
450 },
451 'b' => {
452 bytes[i] = 'x';
453 i += 1;
454 bytes[i] = '0';
455 i += 1;
456 bytes[i] = '8';
457 },
458 'f' => {
459 bytes[i] = 'x';
460 i += 1;
461 bytes[i] = '0';
462 i += 1;
463 bytes[i] = 'C';
464 },
465 'v' => {
466 bytes[i] = 'x';
467 i += 1;
468 bytes[i] = '0';
469 i += 1;
470 bytes[i] = 'B';
471 },
472 '?' => {
473 i -= 1;
474 bytes[i] = '?';
475 },
476 'u', 'U' => {
477 try mt.fail("macro tokenizing failed: TODO unicode escape sequences", .{});
478 return error.ParseError;
479 },
480 else => {
481 try mt.fail("macro tokenizing failed: unknown escape sequence", .{});
482 return error.ParseError;
483 },
484 }
485 i += 1;
486 if (state == .escape)
487 state = .start;
488 },
489 .start => {
490 if (c == '\t') {
491 bytes[i] = '\\';
492 i += 1;
493 bytes[i] = 't';
494 i += 1;
495 continue;
496 }
497 if (c == '\\') {
498 state = .escape;
499 }
500 bytes[i] = c;
501 i += 1;
502 },
503 .hex => {
504 switch (c) {
505 '0'...'9' => {
506 num = std.math.mul(u8, num, 16) catch {
507 try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
508 return error.ParseError;
509 };
510 num += c - '0';
511 },
512 'a'...'f' => {
513 num = std.math.mul(u8, num, 16) catch {
514 try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
515 return error.ParseError;
516 };
517 num += c - 'a' + 10;
518 },
519 'A'...'F' => {
520 num = std.math.mul(u8, num, 16) catch {
521 try mt.fail("macro tokenizing failed: hex literal overflowed", .{});
522 return error.ParseError;
523 };
524 num += c - 'A' + 10;
525 },
526 else => {
527 i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
528 num = 0;
529 if (c == '\\')
530 state = .escape
531 else
532 state = .start;
533 bytes[i] = c;
534 i += 1;
535 },
536 }
537 },
538 .octal => {
539 const accept_digit = switch (c) {
540 // The maximum length of a octal literal is 3 digits
541 '0'...'7' => count < 3,
542 else => false,
543 };
544
545 if (accept_digit) {
546 count += 1;
547 num = std.math.mul(u8, num, 8) catch {
548 try mt.fail("macro tokenizing failed: octal literal overflowed", .{});
549 return error.ParseError;
550 };
551 num += c - '0';
552 } else {
553 i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
554 num = 0;
555 count = 0;
556 if (c == '\\')
557 state = .escape
558 else
559 state = .start;
560 bytes[i] = c;
561 i += 1;
562 }
563 },
564 }
565 }
566 if (state == .hex or state == .octal) {
567 i += std.fmt.printInt(bytes[i..], num, 16, .lower, .{ .fill = '0', .width = 2 });
568 }
569
570 return bytes[0..i];
571}
572
573/// non-ASCII characters (mt > 127) are also treated as non-printable by fmtSliceEscapeLower.
574/// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape
575/// non-ASCII characters so that the Zig source we output will itself be UTF-8.
576fn escapeUnprintables(mt: *MacroTranslator) ![]const u8 {
577 const slice = mt.tokSlice();
578 mt.i += 1;
579
580 const zigified = try mt.zigifyEscapeSequences(slice);
581 if (std.unicode.utf8ValidateSlice(zigified)) return zigified;
582
583 const formatter = std.ascii.hexEscape(zigified, .lower);
584 const encoded_size = @as(usize, @intCast(std.fmt.count("{f}", .{formatter})));
585 const output = try mt.t.arena.alloc(u8, encoded_size);
586 return std.mem.print(output, "{f}", .{formatter}) catch |err| switch (err) {
587 error.NoSpaceLeft => unreachable,
588 else => |e| return e,
589 };
590}
591
592fn parseCPrimaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
593 const arena = mt.t.arena;
594 const gpa = mt.t.gpa;
595 const tok = mt.peek();
596 switch (tok) {
597 .char_literal,
598 .char_literal_utf_8,
599 .char_literal_utf_16,
600 .char_literal_utf_32,
601 .char_literal_wide,
602 => {
603 const slice = mt.tokSlice();
604 if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
605 return ZigTag.char_literal.create(arena, try mt.escapeUnprintables());
606 } else {
607 mt.i += 1;
608
609 const str = try std.fmt.allocPrint(arena, "0x{x}", .{slice[1 .. slice.len - 1]});
610 return ZigTag.integer_literal.create(arena, str);
611 }
612 },
613 .string_literal,
614 .string_literal_utf_16,
615 .string_literal_utf_8,
616 .string_literal_utf_32,
617 .string_literal_wide,
618 => return ZigTag.string_literal.create(arena, try mt.escapeUnprintables()),
619 .pp_num => return mt.parseCNumLit(),
620 .l_paren => {
621 mt.i += 1;
622 const inner_node = try mt.parseCExpr(scope);
623
624 try mt.expect(.r_paren);
625 return inner_node;
626 },
627 .macro_param, .macro_param_no_expand => {
628 const param = mt.macro.params[mt.tokens[mt.i].end];
629 mt.i += 1;
630
631 const mangled_name = scope.getAlias(param) orelse param;
632 return try ZigTag.identifier.create(arena, mangled_name);
633 },
634 .identifier, .extended_identifier => {
635 const slice = mt.tokSlice();
636 mt.i += 1;
637
638 const mangled_name = scope.getAlias(slice) orelse slice;
639 if (Translator.builtin_typedef_map.get(mangled_name)) |ty| {
640 return ZigTag.type.create(arena, ty);
641 }
642 if (builtins.map.get(mangled_name)) |builtin| {
643 const builtin_identifier = try ZigTag.identifier.create(arena, "__builtin");
644 return ZigTag.field_access.create(arena, .{
645 .lhs = builtin_identifier,
646 .field_name = builtin.name,
647 });
648 }
649
650 const identifier = try ZigTag.identifier.create(arena, mangled_name);
651 scope.skipVariableDiscard(mangled_name);
652 refs_var: {
653 const ident_node = mt.t.global_scope.sym_table.get(slice) orelse break :refs_var;
654 const var_decl_node = ident_node.castTag(.var_decl) orelse break :refs_var;
655 if (!var_decl_node.data.is_const) mt.refs_var_decl = true;
656 }
657 return identifier;
658 },
659 .keyword_generic => {
660 mt.i += 1;
661
662 try mt.expect(.l_paren);
663 const param = try mt.parseCCondExpr(scope);
664 const typeof_param = try ZigTag.typeof.create(arena, param);
665 try mt.expect(.comma);
666
667 var cases: std.ArrayList(ZigNode) = .empty;
668 defer cases.deinit(gpa);
669 var has_default = false;
670 while (true) {
671 const case = if (mt.eat(.keyword_default)) blk: {
672 has_default = true;
673 try mt.expect(.colon);
674 const expr = try mt.parseCCondExpr(scope);
675 break :blk try ZigTag.switch_else.create(arena, expr);
676 } else blk: {
677 const case_type = try mt.parseCTypeName(scope) orelse {
678 try mt.fail("unable to translate C expr: expected type instead got '{s}'", .{mt.peek().symbol()});
679 return error.ParseError;
680 };
681 try mt.expect(.colon);
682 const expr = try mt.parseCCondExpr(scope);
683 break :blk try ZigTag.switch_prong.create(arena, .{
684 .cases = try arena.dupe(ZigNode, &.{case_type}),
685 .cond = expr,
686 });
687 };
688 try cases.append(gpa, case);
689 if (!mt.eat(.comma)) break;
690 }
691 try mt.expect(.r_paren);
692
693 if (!has_default) try cases.append(gpa, try ZigTag.switch_else.create(
694 arena,
695 try ZigTag.@"comptime".create(arena, ZigTag.@"unreachable".init()),
696 ));
697
698 const sw = try ZigTag.@"switch".create(arena, .{
699 .cond = typeof_param,
700 .cases = try arena.dupe(ZigNode, cases.items),
701 });
702 return sw;
703 },
704 else => {},
705 }
706
707 // for handling type macros (EVIL)
708 // TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList?
709 if (try mt.parseCTypeName(scope)) |type_name| {
710 return type_name;
711 }
712
713 try mt.fail("unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
714 return error.ParseError;
715}
716
717fn macroIntFromBool(mt: *MacroTranslator, node: ZigNode) !ZigNode {
718 if (!node.isBoolRes()) return node;
719
720 return ZigTag.int_from_bool.create(mt.t.arena, node);
721}
722
723fn macroIntToBool(mt: *MacroTranslator, node: ZigNode) !ZigNode {
724 if (node.isBoolRes()) return node;
725
726 if (node.tag() == .string_literal) {
727 // @intFromPtr(node) != 0
728 const int_from_ptr = try ZigTag.int_from_ptr.create(mt.t.arena, node);
729 return ZigTag.not_equal.create(mt.t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() });
730 }
731 // node != 0
732 return ZigTag.not_equal.create(mt.t.arena, .{ .lhs = node, .rhs = ZigTag.zero_literal.init() });
733}
734
735fn parseCCondExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
736 const condition = try mt.parseCOrExpr(scope);
737 if (!mt.eat(.question_mark)) return condition;
738 const bool_ty = try ZigTag.type.create(mt.t.arena, "bool");
739 const node = try mt.t.createHelperCallNode(.cast, &.{ bool_ty, condition });
740
741 const then_body = try mt.parseCOrExpr(scope);
742 try mt.expect(.colon);
743 const else_body = try mt.parseCCondExpr(scope);
744 return ZigTag.@"if".create(mt.t.arena, .{ .cond = node, .then = then_body, .@"else" = else_body });
745}
746
747fn parseCOrExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
748 var node = try mt.parseCAndExpr(scope);
749 while (mt.eat(.pipe_pipe)) {
750 const lhs = try mt.macroIntToBool(node);
751 const rhs = try mt.macroIntToBool(try mt.parseCAndExpr(scope));
752 node = try ZigTag.@"or".create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
753 }
754 return node;
755}
756
757fn parseCAndExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
758 var node = try mt.parseCBitOrExpr(scope);
759 while (mt.eat(.ampersand_ampersand)) {
760 const lhs = try mt.macroIntToBool(node);
761 const rhs = try mt.macroIntToBool(try mt.parseCBitOrExpr(scope));
762 node = try ZigTag.@"and".create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
763 }
764 return node;
765}
766
767fn parseCBitOrExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
768 var node = try mt.parseCBitXorExpr(scope);
769 while (mt.eat(.pipe)) {
770 const lhs = try mt.macroIntFromBool(node);
771 const rhs = try mt.macroIntFromBool(try mt.parseCBitXorExpr(scope));
772 node = try ZigTag.bit_or.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
773 }
774 return node;
775}
776
777fn parseCBitXorExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
778 var node = try mt.parseCBitAndExpr(scope);
779 while (mt.eat(.caret)) {
780 const lhs = try mt.macroIntFromBool(node);
781 const rhs = try mt.macroIntFromBool(try mt.parseCBitAndExpr(scope));
782 node = try ZigTag.bit_xor.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
783 }
784 return node;
785}
786
787fn parseCBitAndExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
788 var node = try mt.parseCEqExpr(scope);
789 while (mt.eat(.ampersand)) {
790 const lhs = try mt.macroIntFromBool(node);
791 const rhs = try mt.macroIntFromBool(try mt.parseCEqExpr(scope));
792 node = try ZigTag.bit_and.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
793 }
794 return node;
795}
796
797fn parseCEqExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
798 var node = try mt.parseCRelExpr(scope);
799 while (true) {
800 switch (mt.peek()) {
801 .bang_equal => {
802 mt.i += 1;
803 const lhs = try mt.macroIntFromBool(node);
804 const rhs = try mt.macroIntFromBool(try mt.parseCRelExpr(scope));
805 node = try ZigTag.not_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
806 },
807 .equal_equal => {
808 mt.i += 1;
809 const lhs = try mt.macroIntFromBool(node);
810 const rhs = try mt.macroIntFromBool(try mt.parseCRelExpr(scope));
811 node = try ZigTag.equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
812 },
813 else => return node,
814 }
815 }
816}
817
818fn parseCRelExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
819 var node = try mt.parseCShiftExpr(scope);
820 while (true) {
821 switch (mt.peek()) {
822 .angle_bracket_right => {
823 mt.i += 1;
824 const lhs = try mt.macroIntFromBool(node);
825 const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
826 node = try ZigTag.greater_than.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
827 },
828 .angle_bracket_right_equal => {
829 mt.i += 1;
830 const lhs = try mt.macroIntFromBool(node);
831 const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
832 node = try ZigTag.greater_than_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
833 },
834 .angle_bracket_left => {
835 mt.i += 1;
836 const lhs = try mt.macroIntFromBool(node);
837 const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
838 node = try ZigTag.less_than.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
839 },
840 .angle_bracket_left_equal => {
841 mt.i += 1;
842 const lhs = try mt.macroIntFromBool(node);
843 const rhs = try mt.macroIntFromBool(try mt.parseCShiftExpr(scope));
844 node = try ZigTag.less_than_equal.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
845 },
846 else => return node,
847 }
848 }
849}
850
851fn parseCShiftExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
852 var node = try mt.parseCAddSubExpr(scope);
853 while (true) {
854 switch (mt.peek()) {
855 .angle_bracket_angle_bracket_left => {
856 mt.i += 1;
857 const lhs = try mt.macroIntFromBool(node);
858 const rhs = try mt.macroIntFromBool(try mt.parseCAddSubExpr(scope));
859 node = try ZigTag.shl.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
860 },
861 .angle_bracket_angle_bracket_right => {
862 mt.i += 1;
863 const lhs = try mt.macroIntFromBool(node);
864 const rhs = try mt.macroIntFromBool(try mt.parseCAddSubExpr(scope));
865 node = try ZigTag.shr.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
866 },
867 else => return node,
868 }
869 }
870}
871
872fn parseCAddSubExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
873 var node = try mt.parseCMulExpr(scope);
874 while (true) {
875 switch (mt.peek()) {
876 .plus => {
877 mt.i += 1;
878 const lhs = try mt.macroIntFromBool(node);
879 const rhs = try mt.macroIntFromBool(try mt.parseCMulExpr(scope));
880 node = try ZigTag.add.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
881 },
882 .minus => {
883 mt.i += 1;
884 const lhs = try mt.macroIntFromBool(node);
885 const rhs = try mt.macroIntFromBool(try mt.parseCMulExpr(scope));
886 node = try ZigTag.sub.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
887 },
888 else => return node,
889 }
890 }
891}
892
893fn parseCMulExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
894 var node = try mt.parseCCastExpr(scope);
895 while (true) {
896 switch (mt.peek()) {
897 .asterisk => {
898 mt.i += 1;
899 switch (mt.peek()) {
900 .comma, .r_paren, .eof => {
901 // This is probably a pointer type
902 return ZigTag.c_pointer.create(mt.t.arena, .{
903 .is_const = false,
904 .is_volatile = false,
905 .is_allowzero = false,
906 .elem_type = node,
907 });
908 },
909 else => {},
910 }
911 const lhs = try mt.macroIntFromBool(node);
912 const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
913 node = try ZigTag.mul.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
914 },
915 .slash => {
916 mt.i += 1;
917 const lhs = try mt.macroIntFromBool(node);
918 const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
919 node = try mt.t.createHelperCallNode(.div, &.{ lhs, rhs });
920 },
921 .percent => {
922 mt.i += 1;
923 const lhs = try mt.macroIntFromBool(node);
924 const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
925 node = try mt.t.createHelperCallNode(.rem, &.{ lhs, rhs });
926 },
927 else => return node,
928 }
929 }
930}
931
932fn parseCCastExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
933 if (mt.eat(.l_paren)) {
934 if (try mt.parseCTypeName(scope)) |type_name| {
935 while (true) {
936 const next_tok = mt.peek();
937 if (next_tok == .r_paren) {
938 mt.i += 1;
939 break;
940 }
941 // Skip trailing blank defined before the RParen.
942 if ((next_tok == .identifier or next_tok == .extended_identifier) and
943 mt.t.global_scope.blank_macros.contains(mt.tokSlice()))
944 {
945 mt.i += 1;
946 continue;
947 }
948
949 try mt.fail(
950 "unable to translate C expr: expected ')' instead got '{s}'",
951 .{next_tok.symbol()},
952 );
953 return error.ParseError;
954 }
955 if (mt.peek() == .l_brace) {
956 // initializer list
957 return mt.parseCPostfixExpr(scope, type_name);
958 }
959 const node_to_cast = try mt.parseCCastExpr(scope);
960 return mt.t.createHelperCallNode(.cast, &.{ type_name, node_to_cast });
961 }
962 mt.i -= 1; // l_paren
963 }
964 return mt.parseCUnaryExpr(scope);
965}
966
967// allow_fail is set when unsure if we are parsing a type-name
968fn parseCTypeName(mt: *MacroTranslator, scope: *Scope) ParseError!?ZigNode {
969 if (try mt.parseCSpecifierQualifierList(scope)) |node| {
970 return try mt.parseCAbstractDeclarator(node);
971 }
972 return null;
973}
974
975fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope) ParseError!?ZigNode {
976 const tok = mt.peek();
977 switch (tok) {
978 .macro_param, .macro_param_no_expand => {
979 const param = mt.macro.params[mt.tokens[mt.i].end];
980
981 // Assume that this is only a cast if the next token is ')'
982 // e.g. param)identifier
983 if (mt.macro.tokens.len < mt.i + 3 or
984 mt.macro.tokens[mt.i + 1].id != .r_paren or
985 mt.macro.tokens[mt.i + 2].id != .identifier)
986 return null;
987
988 mt.i += 1;
989 const mangled_name = scope.getAlias(param) orelse param;
990 return try ZigTag.identifier.create(mt.t.arena, mangled_name);
991 },
992 .identifier, .extended_identifier => {
993 const slice = mt.tokSlice();
994 const mangled_name = scope.getAlias(slice) orelse slice;
995
996 if (mt.t.global_scope.blank_macros.contains(slice)) {
997 mt.i += 1;
998 return try mt.parseCSpecifierQualifierList(scope);
999 }
1000
1001 if (mt.t.typedefs.contains(mangled_name)) {
1002 mt.i += 1;
1003 if (Translator.builtin_typedef_map.get(mangled_name)) |ty| {
1004 return try ZigTag.type.create(mt.t.arena, ty);
1005 }
1006 if (builtins.map.get(mangled_name)) |builtin| {
1007 const builtin_identifier = try ZigTag.identifier.create(mt.t.arena, "__builtin");
1008 return try ZigTag.field_access.create(mt.t.arena, .{
1009 .lhs = builtin_identifier,
1010 .field_name = builtin.name,
1011 });
1012 }
1013
1014 return try ZigTag.identifier.create(mt.t.arena, mangled_name);
1015 }
1016 },
1017 .keyword_void => {
1018 mt.i += 1;
1019 return try ZigTag.type.create(mt.t.arena, "anyopaque");
1020 },
1021 .keyword_bool => {
1022 mt.i += 1;
1023 return try ZigTag.type.create(mt.t.arena, "bool");
1024 },
1025 .keyword_char,
1026 .keyword_int,
1027 .keyword_short,
1028 .keyword_long,
1029 .keyword_float,
1030 .keyword_double,
1031 .keyword_signed,
1032 .keyword_unsigned,
1033 .keyword_complex,
1034 => return try mt.parseCNumericType(),
1035 .keyword_enum, .keyword_struct, .keyword_union => {
1036 const tag_name = mt.tokSlice();
1037 mt.i += 1;
1038 if (mt.peek() != .identifier) {
1039 mt.i -= 1;
1040 return null;
1041 }
1042
1043 // struct Foo will be declared as struct_Foo by transRecordDecl
1044 const identifier = mt.tokSlice();
1045 try mt.expect(.identifier);
1046
1047 const name = try std.fmt.allocPrint(mt.t.arena, "{s}_{s}", .{ tag_name, identifier });
1048 if (!mt.t.global_scope.contains(name)) {
1049 try mt.fail("unable to translate C expr: '{s}' not found", .{name});
1050 return error.ParseError;
1051 }
1052
1053 return try ZigTag.identifier.create(mt.t.arena, name);
1054 },
1055 else => {},
1056 }
1057
1058 return null;
1059}
1060
1061fn parseCNumericType(mt: *MacroTranslator) ParseError!ZigNode {
1062 const KwCounter = struct {
1063 double: u8 = 0,
1064 long: u8 = 0,
1065 int: u8 = 0,
1066 float: u8 = 0,
1067 short: u8 = 0,
1068 char: u8 = 0,
1069 unsigned: u8 = 0,
1070 signed: u8 = 0,
1071 complex: u8 = 0,
1072
1073 fn eql(self: @This(), other: @This()) bool {
1074 return std.meta.eql(self, other);
1075 }
1076 };
1077
1078 // Yes, these can be in *any* order
1079 // This still doesn't cover cases where for example volatile is intermixed
1080
1081 var kw = KwCounter{};
1082 // prevent overflow
1083 var i: u8 = 0;
1084 while (i < math.maxInt(u8)) : (i += 1) {
1085 switch (mt.peek()) {
1086 .keyword_double => kw.double += 1,
1087 .keyword_long => kw.long += 1,
1088 .keyword_int => kw.int += 1,
1089 .keyword_float => kw.float += 1,
1090 .keyword_short => kw.short += 1,
1091 .keyword_char => kw.char += 1,
1092 .keyword_unsigned => kw.unsigned += 1,
1093 .keyword_signed => kw.signed += 1,
1094 .keyword_complex => kw.complex += 1,
1095 else => break,
1096 }
1097 mt.i += 1;
1098 }
1099
1100 if (kw.eql(.{ .int = 1 }) or kw.eql(.{ .signed = 1 }) or kw.eql(.{ .signed = 1, .int = 1 }))
1101 return ZigTag.type.create(mt.t.arena, "c_int");
1102
1103 if (kw.eql(.{ .unsigned = 1 }) or kw.eql(.{ .unsigned = 1, .int = 1 }))
1104 return ZigTag.type.create(mt.t.arena, "c_uint");
1105
1106 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 }))
1107 return ZigTag.type.create(mt.t.arena, "c_long");
1108
1109 if (kw.eql(.{ .unsigned = 1, .long = 1 }) or kw.eql(.{ .unsigned = 1, .long = 1, .int = 1 }))
1110 return ZigTag.type.create(mt.t.arena, "c_ulong");
1111
1112 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 }))
1113 return ZigTag.type.create(mt.t.arena, "c_longlong");
1114
1115 if (kw.eql(.{ .unsigned = 1, .long = 2 }) or kw.eql(.{ .unsigned = 1, .long = 2, .int = 1 }))
1116 return ZigTag.type.create(mt.t.arena, "c_ulonglong");
1117
1118 if (kw.eql(.{ .signed = 1, .char = 1 }))
1119 return ZigTag.type.create(mt.t.arena, "i8");
1120
1121 if (kw.eql(.{ .char = 1 }) or kw.eql(.{ .unsigned = 1, .char = 1 }))
1122 return ZigTag.type.create(mt.t.arena, "u8");
1123
1124 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 }))
1125 return ZigTag.type.create(mt.t.arena, "c_short");
1126
1127 if (kw.eql(.{ .unsigned = 1, .short = 1 }) or kw.eql(.{ .unsigned = 1, .short = 1, .int = 1 }))
1128 return ZigTag.type.create(mt.t.arena, "c_ushort");
1129
1130 if (kw.eql(.{ .float = 1 }))
1131 return ZigTag.type.create(mt.t.arena, "f32");
1132
1133 if (kw.eql(.{ .double = 1 }))
1134 return ZigTag.type.create(mt.t.arena, "f64");
1135
1136 if (kw.eql(.{ .long = 1, .double = 1 })) {
1137 try mt.fail("unable to translate: TODO long double", .{});
1138 return error.ParseError;
1139 }
1140
1141 if (kw.eql(.{ .float = 1, .complex = 1 })) {
1142 try mt.fail("unable to translate: TODO _Complex", .{});
1143 return error.ParseError;
1144 }
1145
1146 if (kw.eql(.{ .double = 1, .complex = 1 })) {
1147 try mt.fail("unable to translate: TODO _Complex", .{});
1148 return error.ParseError;
1149 }
1150
1151 if (kw.eql(.{ .long = 1, .double = 1, .complex = 1 })) {
1152 try mt.fail("unable to translate: TODO _Complex", .{});
1153 return error.ParseError;
1154 }
1155
1156 try mt.fail("unable to translate: invalid numeric type", .{});
1157 return error.ParseError;
1158}
1159
1160fn parseCAbstractDeclarator(mt: *MacroTranslator, node: ZigNode) ParseError!ZigNode {
1161 if (mt.eat(.asterisk)) {
1162 if (node.castTag(.type)) |some| {
1163 if (std.mem.eql(u8, some.data, "anyopaque")) {
1164 const ptr = try ZigTag.single_pointer.create(mt.t.arena, .{
1165 .is_const = false,
1166 .is_volatile = false,
1167 .is_allowzero = false,
1168 .elem_type = node,
1169 });
1170 return ZigTag.optional_type.create(mt.t.arena, ptr);
1171 }
1172 }
1173 return ZigTag.c_pointer.create(mt.t.arena, .{
1174 .is_const = false,
1175 .is_volatile = false,
1176 .is_allowzero = false,
1177 .elem_type = node,
1178 });
1179 }
1180 return node;
1181}
1182
1183fn parseCPostfixExpr(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNode) ParseError!ZigNode {
1184 var node = try mt.parseCPostfixExprInner(scope, type_name);
1185 // In C the preprocessor would handle concatting strings while expanding macros.
1186 // This should do approximately the same by concatting any strings and identifiers
1187 // after a primary or postfix expression.
1188 while (true) {
1189 switch (mt.peek()) {
1190 .string_literal,
1191 .string_literal_utf_16,
1192 .string_literal_utf_8,
1193 .string_literal_utf_32,
1194 .string_literal_wide,
1195 .macro_param,
1196 .macro_param_no_expand,
1197 => {},
1198 .identifier, .extended_identifier => {
1199 if (mt.t.global_scope.blank_macros.contains(mt.tokSlice())) {
1200 mt.i += 1;
1201 continue;
1202 }
1203 },
1204 else => break,
1205 }
1206 const rhs = try mt.parseCPostfixExprInner(scope, type_name);
1207 node = try ZigTag.array_cat.create(mt.t.arena, .{ .lhs = node, .rhs = rhs });
1208 }
1209 return node;
1210}
1211
1212fn parseCPostfixExprInner(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNode) ParseError!ZigNode {
1213 const gpa = mt.t.gpa;
1214 const arena = mt.t.arena;
1215 var node = type_name orelse try mt.parseCPrimaryExpr(scope);
1216 while (true) {
1217 switch (mt.peek()) {
1218 .period => {
1219 mt.i += 1;
1220 const tok = mt.tokens[mt.i];
1221 if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {
1222 const param = mt.macro.params[tok.end];
1223 mt.i += 1;
1224
1225 const mangled_name = scope.getAlias(param) orelse param;
1226 const field_name = try ZigTag.identifier.create(arena, mangled_name);
1227 node = try ZigTag.field_builtin.create(arena, .{ .lhs = node, .rhs = field_name });
1228 continue;
1229 }
1230 const field_name = mt.tokSlice();
1231 try mt.expect(.identifier);
1232
1233 node = try ZigTag.field_access.create(arena, .{ .lhs = node, .field_name = field_name });
1234 },
1235 .arrow => {
1236 mt.i += 1;
1237 const tok = mt.tokens[mt.i];
1238 if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {
1239 const param = mt.macro.params[tok.end];
1240 mt.i += 1;
1241
1242 const mangled_name = scope.getAlias(param) orelse param;
1243 const field_name = try ZigTag.identifier.create(arena, mangled_name);
1244 node = try ZigTag.field_builtin.create(arena, .{ .lhs = node, .rhs = field_name });
1245 continue;
1246 }
1247 const field_name = mt.tokSlice();
1248 try mt.expect(.identifier);
1249
1250 const deref = try ZigTag.deref.create(arena, node);
1251 node = try ZigTag.field_access.create(arena, .{ .lhs = deref, .field_name = field_name });
1252 },
1253 .l_bracket => {
1254 mt.i += 1;
1255
1256 const index_val = try mt.macroIntFromBool(try mt.parseCExpr(scope));
1257 const index = try ZigTag.as.create(arena, .{
1258 .lhs = try ZigTag.type.create(arena, "usize"),
1259 .rhs = try ZigTag.int_cast.create(arena, index_val),
1260 });
1261 node = try ZigTag.array_access.create(arena, .{ .lhs = node, .rhs = index });
1262 try mt.expect(.r_bracket);
1263 },
1264 .l_paren => {
1265 mt.i += 1;
1266
1267 if (mt.eat(.r_paren)) {
1268 node = try ZigTag.call.create(arena, .{ .lhs = node, .args = &.{} });
1269 } else {
1270 var args: std.ArrayList(ZigNode) = .empty;
1271 defer args.deinit(gpa);
1272
1273 while (true) {
1274 const arg = try mt.parseCCondExpr(scope);
1275 try args.append(gpa, arg);
1276
1277 const next_id = mt.peek();
1278 switch (next_id) {
1279 .comma => {
1280 mt.i += 1;
1281 },
1282 .r_paren => {
1283 mt.i += 1;
1284 break;
1285 },
1286 else => {
1287 try mt.fail("unable to translate C expr: expected ',' or ')' instead got '{s}'", .{next_id.symbol()});
1288 return error.ParseError;
1289 },
1290 }
1291 }
1292 node = try ZigTag.call.create(arena, .{ .lhs = node, .args = try arena.dupe(ZigNode, args.items) });
1293 }
1294 },
1295 .l_brace => {
1296 mt.i += 1;
1297
1298 // Check for designated field initializers
1299 if (mt.peek() == .period) {
1300 var init_vals: std.ArrayList(ast.Payload.ContainerInitDot.Initializer) = .empty;
1301 defer init_vals.deinit(gpa);
1302
1303 while (true) {
1304 try mt.expect(.period);
1305 const name = mt.tokSlice();
1306 try mt.expect(.identifier);
1307 try mt.expect(.equal);
1308
1309 const val = try mt.parseCCondExpr(scope);
1310 try init_vals.append(gpa, .{ .name = name, .value = val });
1311
1312 const next_id = mt.peek();
1313 switch (next_id) {
1314 .comma => {
1315 mt.i += 1;
1316 },
1317 .r_brace => {
1318 mt.i += 1;
1319 break;
1320 },
1321 else => {
1322 try mt.fail("unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
1323 return error.ParseError;
1324 },
1325 }
1326 }
1327 const tuple_node = try ZigTag.container_init_dot.create(arena, try arena.dupe(ast.Payload.ContainerInitDot.Initializer, init_vals.items));
1328 node = try ZigTag.std_mem_zeroinit.create(arena, .{ .lhs = node, .rhs = tuple_node });
1329 continue;
1330 }
1331
1332 var init_vals: std.ArrayList(ZigNode) = .empty;
1333 defer init_vals.deinit(gpa);
1334
1335 while (true) {
1336 const val = try mt.parseCCondExpr(scope);
1337 try init_vals.append(gpa, val);
1338
1339 const next_id = mt.peek();
1340 switch (next_id) {
1341 .comma => {
1342 mt.i += 1;
1343 },
1344 .r_brace => {
1345 mt.i += 1;
1346 break;
1347 },
1348 else => {
1349 try mt.fail("unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
1350 return error.ParseError;
1351 },
1352 }
1353 }
1354 const tuple_node = try ZigTag.tuple.create(arena, try arena.dupe(ZigNode, init_vals.items));
1355 node = try ZigTag.std_mem_zeroinit.create(arena, .{ .lhs = node, .rhs = tuple_node });
1356 },
1357 .plus_plus, .minus_minus => {
1358 try mt.fail("TODO postfix inc/dec expr", .{});
1359 return error.ParseError;
1360 },
1361 else => return node,
1362 }
1363 }
1364}
1365
1366fn parseCUnaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
1367 switch (mt.peek()) {
1368 .bang => {
1369 mt.i += 1;
1370 const operand = try mt.macroIntToBool(try mt.parseCCastExpr(scope));
1371 return ZigTag.not.create(mt.t.arena, operand);
1372 },
1373 .minus => {
1374 mt.i += 1;
1375 const operand = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
1376 return ZigTag.negate.create(mt.t.arena, operand);
1377 },
1378 .plus => {
1379 mt.i += 1;
1380 return try mt.parseCCastExpr(scope);
1381 },
1382 .tilde => {
1383 mt.i += 1;
1384 const operand = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
1385 return ZigTag.bit_not.create(mt.t.arena, operand);
1386 },
1387 .asterisk => {
1388 mt.i += 1;
1389 const operand = try mt.parseCCastExpr(scope);
1390 return ZigTag.deref.create(mt.t.arena, operand);
1391 },
1392 .ampersand => {
1393 mt.i += 1;
1394 const operand = try mt.parseCCastExpr(scope);
1395 return ZigTag.address_of.create(mt.t.arena, operand);
1396 },
1397 .keyword_sizeof => {
1398 mt.i += 1;
1399 const operand = if (mt.eat(.l_paren)) blk: {
1400 const inner = (try mt.parseCTypeName(scope)) orelse try mt.parseCUnaryExpr(scope);
1401 try mt.expect(.r_paren);
1402 break :blk inner;
1403 } else try mt.parseCUnaryExpr(scope);
1404
1405 return mt.t.createHelperCallNode(.sizeof, &.{operand});
1406 },
1407 .keyword_alignof => {
1408 mt.i += 1;
1409 // TODO this won't work if using <stdalign.h>'s
1410 // #define alignof _Alignof
1411 try mt.expect(.l_paren);
1412 const operand = (try mt.parseCTypeName(scope)) orelse try mt.parseCUnaryExpr(scope);
1413 try mt.expect(.r_paren);
1414
1415 return ZigTag.alignof.create(mt.t.arena, operand);
1416 },
1417 .plus_plus, .minus_minus => {
1418 try mt.fail("TODO unary inc/dec expr", .{});
1419 return error.ParseError;
1420 },
1421 else => {},
1422 }
1423
1424 return try mt.parseCPostfixExpr(scope, null);
1425}