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}