authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-07-23 09:29:25+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-07-23 09:29:25+03:00
log8ad23d7beba9403d83ab961912d62002d9cb9602
tree2fe47b5a5fb4412f9e4eab6902b383d04870452b
parente3fe3acce0fc65a7f0c7227085456e8d167ed2a7
parentb33efa373943f8e13dc432f37862da7ee8bf1b6e
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9265 from ehaas/translate-c-macro-matching

translate-c: add framework for special-casing macros

4 files changed, 409 insertions(+), 15 deletions(-)

lib/std/zig/c_translation.zig+132
...@@ -350,3 +350,135 @@ test "Flexible Array Type" {...@@ -350,3 +350,135 @@ test "Flexible Array Type" {
350 try testing.expectEqual(FlexibleArrayType(*volatile Container, c_int), [*c]volatile c_int);350 try testing.expectEqual(FlexibleArrayType(*volatile Container, c_int), [*c]volatile c_int);
351 try testing.expectEqual(FlexibleArrayType(*const volatile Container, c_int), [*c]const volatile c_int);351 try testing.expectEqual(FlexibleArrayType(*const volatile Container, c_int), [*c]const volatile c_int);
352}352}
353
354pub const Macros = struct {
355 pub fn U_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_uint, n, .decimal)) {
356 return promoteIntLiteral(c_uint, n, .decimal);
357 }
358
359 fn L_SUFFIX_ReturnType(comptime number: anytype) type {
360 switch (@TypeOf(number)) {
361 comptime_int => return @TypeOf(promoteIntLiteral(c_long, number, .decimal)),
362 comptime_float => return c_longdouble,
363 else => @compileError("Invalid value for L suffix"),
364 }
365 }
366 pub fn L_SUFFIX(comptime number: anytype) L_SUFFIX_ReturnType(number) {
367 switch (@TypeOf(number)) {
368 comptime_int => return promoteIntLiteral(c_long, number, .decimal),
369 comptime_float => @compileError("TODO: c_longdouble initialization from comptime_float not supported"),
370 else => @compileError("Invalid value for L suffix"),
371 }
372 }
373
374 pub fn UL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulong, n, .decimal)) {
375 return promoteIntLiteral(c_ulong, n, .decimal);
376 }
377
378 pub fn LL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_longlong, n, .decimal)) {
379 return promoteIntLiteral(c_longlong, n, .decimal);
380 }
381
382 pub fn ULL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulonglong, n, .decimal)) {
383 return promoteIntLiteral(c_ulonglong, n, .decimal);
384 }
385
386 pub fn F_SUFFIX(comptime f: comptime_float) f32 {
387 return @as(f32, f);
388 }
389
390 pub fn WL_CONTAINER_OF(ptr: anytype, sample: anytype, comptime member: []const u8) @TypeOf(sample) {
391 return @fieldParentPtr(@TypeOf(sample.*), member, ptr);
392 }
393
394 /// A 2-argument function-like macro defined as #define FOO(A, B) (A)(B)
395 /// could be either: cast B to A, or call A with the value B.
396 pub fn CAST_OR_CALL(a: anytype, b: anytype) switch (@typeInfo(@TypeOf(a))) {
397 .Type => a,
398 .Fn => |fn_info| fn_info.return_type orelse void,
399 else => |info| @compileError("Unexpected argument type: " ++ @tagName(info)),
400 } {
401 switch (@typeInfo(@TypeOf(a))) {
402 .Type => return cast(a, b),
403 .Fn => return a(b),
404 else => unreachable, // return type will be a compile error otherwise
405 }
406 }
407};
408
409test "Macro suffix functions" {
410 try testing.expect(@TypeOf(Macros.F_SUFFIX(1)) == f32);
411
412 try testing.expect(@TypeOf(Macros.U_SUFFIX(1)) == c_uint);
413 if (math.maxInt(c_ulong) > math.maxInt(c_uint)) {
414 try testing.expect(@TypeOf(Macros.U_SUFFIX(math.maxInt(c_uint) + 1)) == c_ulong);
415 }
416 if (math.maxInt(c_ulonglong) > math.maxInt(c_ulong)) {
417 try testing.expect(@TypeOf(Macros.U_SUFFIX(math.maxInt(c_ulong) + 1)) == c_ulonglong);
418 }
419
420 try testing.expect(@TypeOf(Macros.L_SUFFIX(1)) == c_long);
421 if (math.maxInt(c_long) > math.maxInt(c_int)) {
422 try testing.expect(@TypeOf(Macros.L_SUFFIX(math.maxInt(c_int) + 1)) == c_long);
423 }
424 if (math.maxInt(c_longlong) > math.maxInt(c_long)) {
425 try testing.expect(@TypeOf(Macros.L_SUFFIX(math.maxInt(c_long) + 1)) == c_longlong);
426 }
427
428 try testing.expect(@TypeOf(Macros.UL_SUFFIX(1)) == c_ulong);
429 if (math.maxInt(c_ulonglong) > math.maxInt(c_ulong)) {
430 try testing.expect(@TypeOf(Macros.UL_SUFFIX(math.maxInt(c_ulong) + 1)) == c_ulonglong);
431 }
432
433 try testing.expect(@TypeOf(Macros.LL_SUFFIX(1)) == c_longlong);
434 try testing.expect(@TypeOf(Macros.ULL_SUFFIX(1)) == c_ulonglong);
435}
436
437test "WL_CONTAINER_OF" {
438 const S = struct {
439 a: u32 = 0,
440 b: u32 = 0,
441 };
442 var x = S{};
443 var y = S{};
444 var ptr = Macros.WL_CONTAINER_OF(&x.b, &y, "b");
445 try testing.expectEqual(&x, ptr);
446}
447
448test "CAST_OR_CALL casting" {
449 var arg = @as(c_int, 1000);
450 var casted = Macros.CAST_OR_CALL(u8, arg);
451 try testing.expectEqual(cast(u8, arg), casted);
452
453 const S = struct {
454 x: u32 = 0,
455 };
456 var s = S{};
457 var casted_ptr = Macros.CAST_OR_CALL(*u8, &s);
458 try testing.expectEqual(cast(*u8, &s), casted_ptr);
459}
460
461test "CAST_OR_CALL calling" {
462 const Helper = struct {
463 var last_val: bool = false;
464 fn returnsVoid(val: bool) void {
465 last_val = val;
466 }
467 fn returnsBool(f: f32) bool {
468 return f > 0;
469 }
470 fn identity(self: c_uint) c_uint {
471 return self;
472 }
473 };
474
475 Macros.CAST_OR_CALL(Helper.returnsVoid, true);
476 try testing.expectEqual(true, Helper.last_val);
477 Macros.CAST_OR_CALL(Helper.returnsVoid, false);
478 try testing.expectEqual(false, Helper.last_val);
479
480 try testing.expectEqual(Helper.returnsBool(1), Macros.CAST_OR_CALL(Helper.returnsBool, @as(f32, 1)));
481 try testing.expectEqual(Helper.returnsBool(-1), Macros.CAST_OR_CALL(Helper.returnsBool, @as(f32, -1)));
482
483 try testing.expectEqual(Helper.identity(@as(c_uint, 100)), Macros.CAST_OR_CALL(Helper.identity, @as(c_uint, 100)));
484}
src/translate_c.zig+257-15
...@@ -2,6 +2,7 @@...@@ -2,6 +2,7 @@
2//! and stage2.2//! and stage2.
33
4const std = @import("std");4const std = @import("std");
5const testing = std.testing;
5const assert = std.debug.assert;6const assert = std.debug.assert;
6const clang = @import("clang.zig");7const clang = @import("clang.zig");
7const ctok = std.c.tokenizer;8const ctok = std.c.tokenizer;
...@@ -18,6 +19,7 @@ const CallingConvention = std.builtin.CallingConvention;...@@ -18,6 +19,7 @@ const CallingConvention = std.builtin.CallingConvention;
18pub const ClangErrMsg = clang.Stage2ErrorMsg;19pub const ClangErrMsg = clang.Stage2ErrorMsg;
1920
20pub const Error = std.mem.Allocator.Error;21pub const Error = std.mem.Allocator.Error;
22const MacroProcessingError = Error || error{UnexpectedMacroToken};
21const TypeError = Error || error{UnsupportedType};23const TypeError = Error || error{UnsupportedType};
22const TransError = TypeError || error{UnsupportedTranslation};24const TransError = TypeError || error{UnsupportedTranslation};
2325
...@@ -27,6 +29,10 @@ const AliasList = std.ArrayList(struct {...@@ -27,6 +29,10 @@ const AliasList = std.ArrayList(struct {
27 name: []const u8,29 name: []const u8,
28});30});
2931
32// Maps macro parameter names to token position, for determining if different
33// identifiers refer to the same positional argument in different macros.
34const ArgsPositionMap = std.StringArrayHashMapUnmanaged(usize);
35
30const Scope = struct {36const Scope = struct {
31 id: Id,37 id: Id,
32 parent: ?*Scope,38 parent: ?*Scope,
...@@ -322,6 +328,8 @@ pub const Context = struct {...@@ -322,6 +328,8 @@ pub const Context = struct {
322 /// up front in a pre-processing step.328 /// up front in a pre-processing step.
323 global_names: std.StringArrayHashMapUnmanaged(void) = .{},329 global_names: std.StringArrayHashMapUnmanaged(void) = .{},
324330
331 pattern_list: PatternList,
332
325 fn getMangle(c: *Context) u32 {333 fn getMangle(c: *Context) u32 {
326 c.mangle_count += 1;334 c.mangle_count += 1;
327 return c.mangle_count;335 return c.mangle_count;
...@@ -375,6 +383,7 @@ pub fn translate(...@@ -375,6 +383,7 @@ pub fn translate(
375 .alias_list = AliasList.init(gpa),383 .alias_list = AliasList.init(gpa),
376 .global_scope = try arena.allocator.create(Scope.Root),384 .global_scope = try arena.allocator.create(Scope.Root),
377 .clang_context = ast_unit.getASTContext(),385 .clang_context = ast_unit.getASTContext(),
386 .pattern_list = try PatternList.init(gpa),
378 };387 };
379 context.global_scope.* = Scope.Root.init(&context);388 context.global_scope.* = Scope.Root.init(&context);
380 defer {389 defer {
...@@ -385,6 +394,7 @@ pub fn translate(...@@ -385,6 +394,7 @@ pub fn translate(
385 context.unnamed_typedefs.deinit(gpa);394 context.unnamed_typedefs.deinit(gpa);
386 context.typedefs.deinit(gpa);395 context.typedefs.deinit(gpa);
387 context.global_scope.deinit();396 context.global_scope.deinit();
397 context.pattern_list.deinit(gpa);
388 }398 }
389399
390 try context.global_scope.nodes.append(Tag.usingnamespace_builtins.init());400 try context.global_scope.nodes.append(Tag.usingnamespace_builtins.init());
...@@ -4829,6 +4839,220 @@ fn isZigPrimitiveType(name: []const u8) bool {...@@ -4829,6 +4839,220 @@ fn isZigPrimitiveType(name: []const u8) bool {
4829 return @import("AstGen.zig").simple_types.has(name);4839 return @import("AstGen.zig").simple_types.has(name);
4830}4840}
48314841
4842const PatternList = struct {
4843 patterns: []Pattern,
4844
4845 /// Templates must be function-like macros
4846 /// first element is macro source, second element is the name of the function
4847 /// in std.lib.zig.c_translation.Macros which implements it
4848 const templates = [_][2][]const u8{
4849 [2][]const u8{ "f_SUFFIX(X) (X ## f)", "F_SUFFIX" },
4850 [2][]const u8{ "F_SUFFIX(X) (X ## F)", "F_SUFFIX" },
4851
4852 [2][]const u8{ "u_SUFFIX(X) (X ## u)", "U_SUFFIX" },
4853 [2][]const u8{ "U_SUFFIX(X) (X ## U)", "U_SUFFIX" },
4854
4855 [2][]const u8{ "l_SUFFIX(X) (X ## l)", "L_SUFFIX" },
4856 [2][]const u8{ "L_SUFFIX(X) (X ## L)", "L_SUFFIX" },
4857
4858 [2][]const u8{ "ul_SUFFIX(X) (X ## ul)", "UL_SUFFIX" },
4859 [2][]const u8{ "uL_SUFFIX(X) (X ## uL)", "UL_SUFFIX" },
4860 [2][]const u8{ "Ul_SUFFIX(X) (X ## Ul)", "UL_SUFFIX" },
4861 [2][]const u8{ "UL_SUFFIX(X) (X ## UL)", "UL_SUFFIX" },
4862
4863 [2][]const u8{ "ll_SUFFIX(X) (X ## ll)", "LL_SUFFIX" },
4864 [2][]const u8{ "LL_SUFFIX(X) (X ## LL)", "LL_SUFFIX" },
4865
4866 [2][]const u8{ "ull_SUFFIX(X) (X ## ull)", "ULL_SUFFIX" },
4867 [2][]const u8{ "uLL_SUFFIX(X) (X ## uLL)", "ULL_SUFFIX" },
4868 [2][]const u8{ "Ull_SUFFIX(X) (X ## Ull)", "ULL_SUFFIX" },
4869 [2][]const u8{ "ULL_SUFFIX(X) (X ## ULL)", "ULL_SUFFIX" },
4870
4871 [2][]const u8{ "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL" },
4872
4873 [2][]const u8{
4874 \\wl_container_of(ptr, sample, member) \
4875 \\(__typeof__(sample))((char *)(ptr) - \
4876 \\ offsetof(__typeof__(*sample), member))
4877 ,
4878 "WL_CONTAINER_OF",
4879 },
4880 };
4881
4882 /// Assumes that `ms` represents a tokenized function-like macro.
4883 fn buildArgsHash(allocator: *mem.Allocator, ms: MacroSlicer, hash: *ArgsPositionMap) MacroProcessingError!void {
4884 assert(ms.tokens.len > 2);
4885 assert(ms.tokens[0].id == .Identifier);
4886 assert(ms.tokens[1].id == .LParen);
4887
4888 var i: usize = 2;
4889 while (true) : (i += 1) {
4890 const token = ms.tokens[i];
4891 switch (token.id) {
4892 .RParen => break,
4893 .Comma => continue,
4894 .Identifier => {
4895 const identifier = ms.slice(token);
4896 try hash.put(allocator, identifier, i);
4897 },
4898 else => return error.UnexpectedMacroToken,
4899 }
4900 }
4901 }
4902
4903 const Pattern = struct {
4904 tokens: []const CToken,
4905 source: []const u8,
4906 impl: []const u8,
4907 args_hash: ArgsPositionMap,
4908
4909 fn init(self: *Pattern, allocator: *mem.Allocator, template: [2][]const u8) Error!void {
4910 const source = template[0];
4911 const impl = template[1];
4912
4913 var tok_list = std.ArrayList(CToken).init(allocator);
4914 defer tok_list.deinit();
4915 try tokenizeMacro(source, &tok_list);
4916 const tokens = try allocator.dupe(CToken, tok_list.items);
4917
4918 self.* = .{
4919 .tokens = tokens,
4920 .source = source,
4921 .impl = impl,
4922 .args_hash = .{},
4923 };
4924 const ms = MacroSlicer{ .source = source, .tokens = tokens };
4925 buildArgsHash(allocator, ms, &self.args_hash) catch |err| switch (err) {
4926 error.UnexpectedMacroToken => unreachable,
4927 else => |e| return e,
4928 };
4929 }
4930
4931 fn deinit(self: *Pattern, allocator: *mem.Allocator) void {
4932 self.args_hash.deinit(allocator);
4933 allocator.free(self.tokens);
4934 }
4935
4936 /// This function assumes that `ms` has already been validated to contain a function-like
4937 /// macro, and that the parsed template macro in `self` also contains a function-like
4938 /// macro. Please review this logic carefully if changing that assumption. Two
4939 /// function-like macros are considered equivalent if and only if they contain the same
4940 /// list of tokens, modulo parameter names.
4941 fn isEquivalent(self: Pattern, ms: MacroSlicer, args_hash: ArgsPositionMap) bool {
4942 if (self.tokens.len != ms.tokens.len) return false;
4943 if (args_hash.count() != self.args_hash.count()) return false;
4944
4945 var i: usize = 2;
4946 while (self.tokens[i].id != .RParen) : (i += 1) {}
4947
4948 const pattern_slicer = MacroSlicer{ .source = self.source, .tokens = self.tokens };
4949 while (i < self.tokens.len) : (i += 1) {
4950 const pattern_token = self.tokens[i];
4951 const macro_token = ms.tokens[i];
4952 if (meta.activeTag(pattern_token.id) != meta.activeTag(macro_token.id)) return false;
4953
4954 const pattern_bytes = pattern_slicer.slice(pattern_token);
4955 const macro_bytes = ms.slice(macro_token);
4956 switch (pattern_token.id) {
4957 .Identifier => {
4958 const pattern_arg_index = self.args_hash.get(pattern_bytes);
4959 const macro_arg_index = args_hash.get(macro_bytes);
4960
4961 if (pattern_arg_index == null and macro_arg_index == null) {
4962 if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
4963 } else if (pattern_arg_index != null and macro_arg_index != null) {
4964 if (pattern_arg_index.? != macro_arg_index.?) return false;
4965 } else {
4966 return false;
4967 }
4968 },
4969 .MacroString, .StringLiteral, .CharLiteral, .IntegerLiteral, .FloatLiteral => {
4970 if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
4971 },
4972 else => {
4973 // other tags correspond to keywords and operators that do not contain a "payload"
4974 // that can vary
4975 },
4976 }
4977 }
4978 return true;
4979 }
4980 };
4981
4982 fn init(allocator: *mem.Allocator) Error!PatternList {
4983 const patterns = try allocator.alloc(Pattern, templates.len);
4984 for (templates) |template, i| {
4985 try patterns[i].init(allocator, template);
4986 }
4987 return PatternList{ .patterns = patterns };
4988 }
4989
4990 fn deinit(self: *PatternList, allocator: *mem.Allocator) void {
4991 for (self.patterns) |*pattern| pattern.deinit(allocator);
4992 allocator.free(self.patterns);
4993 }
4994
4995 fn match(self: PatternList, allocator: *mem.Allocator, ms: MacroSlicer) Error!?Pattern {
4996 var args_hash: ArgsPositionMap = .{};
4997 defer args_hash.deinit(allocator);
4998
4999 buildArgsHash(allocator, ms, &args_hash) catch |err| switch (err) {
5000 error.UnexpectedMacroToken => return null,
5001 else => |e| return e,
5002 };
5003
5004 for (self.patterns) |pattern| if (pattern.isEquivalent(ms, args_hash)) return pattern;
5005 return null;
5006 }
5007};
5008
5009const MacroSlicer = struct {
5010 source: []const u8,
5011 tokens: []const CToken,
5012 fn slice(self: MacroSlicer, token: CToken) []const u8 {
5013 return self.source[token.start..token.end];
5014 }
5015};
5016
5017// Testing here instead of test/translate_c.zig allows us to also test that the
5018// mapped function exists in `std.zig.c_translation.Macros`
5019test "Macro matching" {
5020 const helper = struct {
5021 const MacroFunctions = @import("std").zig.c_translation.Macros;
5022 fn checkMacro(allocator: *mem.Allocator, pattern_list: PatternList, source: []const u8, comptime expected_match: ?[]const u8) !void {
5023 var tok_list = std.ArrayList(CToken).init(allocator);
5024 defer tok_list.deinit();
5025 try tokenizeMacro(source, &tok_list);
5026 const macro_slicer = MacroSlicer{ .source = source, .tokens = tok_list.items };
5027 const matched = try pattern_list.match(allocator, macro_slicer);
5028 if (expected_match) |expected| {
5029 try testing.expectEqualStrings(expected, matched.?.impl);
5030 try testing.expect(@hasDecl(MacroFunctions, expected));
5031 } else {
5032 try testing.expectEqual(@as(@TypeOf(matched), null), matched);
5033 }
5034 }
5035 };
5036 const allocator = std.testing.allocator;
5037 var pattern_list = try PatternList.init(allocator);
5038 defer pattern_list.deinit(allocator);
5039
5040 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## F)", "F_SUFFIX");
5041 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## U)", "U_SUFFIX");
5042 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## L)", "L_SUFFIX");
5043 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## LL)", "LL_SUFFIX");
5044 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## UL)", "UL_SUFFIX");
5045 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## ULL)", "ULL_SUFFIX");
5046 try helper.checkMacro(allocator, pattern_list,
5047 \\container_of(a, b, c) \
5048 \\(__typeof__(b))((char *)(a) - \
5049 \\ offsetof(__typeof__(*b), c))
5050 , "WL_CONTAINER_OF");
5051
5052 try helper.checkMacro(allocator, pattern_list, "NO_MATCH(X, Y) (X + Y)", null);
5053 try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL");
5054}
5055
4832const MacroCtx = struct {5056const MacroCtx = struct {
4833 source: []const u8,5057 source: []const u8,
4834 list: []const CToken,5058 list: []const CToken,
...@@ -4855,8 +5079,30 @@ const MacroCtx = struct {...@@ -4855,8 +5079,30 @@ const MacroCtx = struct {
4855 fn fail(self: *MacroCtx, c: *Context, comptime fmt: []const u8, args: anytype) !void {5079 fn fail(self: *MacroCtx, c: *Context, comptime fmt: []const u8, args: anytype) !void {
4856 return failDecl(c, self.loc, self.name, fmt, args);5080 return failDecl(c, self.loc, self.name, fmt, args);
4857 }5081 }
5082
5083 fn makeSlicer(self: *const MacroCtx) MacroSlicer {
5084 return MacroSlicer{ .source = self.source, .tokens = self.list };
5085 }
4858};5086};
48595087
5088fn tokenizeMacro(source: []const u8, tok_list: *std.ArrayList(CToken)) Error!void {
5089 var tokenizer = std.c.Tokenizer{
5090 .buffer = source,
5091 };
5092 while (true) {
5093 const tok = tokenizer.next();
5094 switch (tok.id) {
5095 .Nl, .Eof => {
5096 try tok_list.append(tok);
5097 break;
5098 },
5099 .LineComment, .MultiLineComment => continue,
5100 else => {},
5101 }
5102 try tok_list.append(tok);
5103 }
5104}
5105
4860fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {5106fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
4861 // TODO if we see #undef, delete it from the table5107 // TODO if we see #undef, delete it from the table
4862 var it = unit.getLocalPreprocessingEntities_begin();5108 var it = unit.getLocalPreprocessingEntities_begin();
...@@ -4888,21 +5134,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {...@@ -4888,21 +5134,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
4888 const slice_len = @ptrToInt(end_c) - @ptrToInt(begin_c);5134 const slice_len = @ptrToInt(end_c) - @ptrToInt(begin_c);
4889 const slice = begin_c[0..slice_len];5135 const slice = begin_c[0..slice_len];
48905136
4891 var tokenizer = std.c.Tokenizer{5137 try tokenizeMacro(slice, &tok_list);
4892 .buffer = slice,
4893 };
4894 while (true) {
4895 const tok = tokenizer.next();
4896 switch (tok.id) {
4897 .Nl, .Eof => {
4898 try tok_list.append(tok);
4899 break;
4900 },
4901 .LineComment, .MultiLineComment => continue,
4902 else => {},
4903 }
4904 try tok_list.append(tok);
4905 }
49065138
4907 var macro_ctx = MacroCtx{5139 var macro_ctx = MacroCtx{
4908 .source = slice,5140 .source = slice,
...@@ -4960,6 +5192,16 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {...@@ -4960,6 +5192,16 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
4960}5192}
49615193
4962fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {5194fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
5195 const macro_slicer = m.makeSlicer();
5196 if (try c.pattern_list.match(c.gpa, macro_slicer)) |pattern| {
5197 const decl = try Tag.pub_var_simple.create(c.arena, .{
5198 .name = m.name,
5199 .init = try Tag.helpers_macro.create(c.arena, pattern.impl),
5200 });
5201 try c.global_scope.macro_table.put(m.name, decl);
5202 return;
5203 }
5204
4963 var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);5205 var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
4964 defer block_scope.deinit();5206 defer block_scope.deinit();
4965 const scope = &block_scope.base;5207 const scope = &block_scope.base;
src/translate_c/ast.zig+14
...@@ -193,6 +193,8 @@ pub const Node = extern union {...@@ -193,6 +193,8 @@ pub const Node = extern union {
193 helpers_flexible_array_type,193 helpers_flexible_array_type,
194 /// @import("std").zig.c_translation.shuffleVectorIndex(lhs, rhs)194 /// @import("std").zig.c_translation.shuffleVectorIndex(lhs, rhs)
195 helpers_shuffle_vector_index,195 helpers_shuffle_vector_index,
196 /// @import("std").zig.c_translation.Macro.<operand>
197 helpers_macro,
196 /// @import("std").meta.Vector(lhs, rhs)198 /// @import("std").meta.Vector(lhs, rhs)
197 std_meta_vector,199 std_meta_vector,
198 /// @import("std").mem.zeroes(operand)200 /// @import("std").mem.zeroes(operand)
...@@ -339,6 +341,7 @@ pub const Node = extern union {...@@ -339,6 +341,7 @@ pub const Node = extern union {
339 .identifier,341 .identifier,
340 .warning,342 .warning,
341 .type,343 .type,
344 .helpers_macro,
342 => Payload.Value,345 => Payload.Value,
343 .discard => Payload.Discard,346 .discard => Payload.Discard,
344 .@"if" => Payload.If,347 .@"if" => Payload.If,
...@@ -1112,6 +1115,16 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1112,6 +1115,16 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1112 .data = undefined,1115 .data = undefined,
1113 });1116 });
1114 },1117 },
1118 .helpers_macro => {
1119 const payload = node.castTag(.helpers_macro).?.data;
1120 const chain = [_][]const u8{
1121 "zig",
1122 "c_translation",
1123 "Macros",
1124 payload,
1125 };
1126 return renderStdImport(c, &chain);
1127 },
1115 .string_slice => {1128 .string_slice => {
1116 const payload = node.castTag(.string_slice).?.data;1129 const payload = node.castTag(.string_slice).?.data;
11171130
...@@ -2310,6 +2323,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2310,6 +2323,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2310 .bit_or_assign,2323 .bit_or_assign,
2311 .bit_xor_assign,2324 .bit_xor_assign,
2312 .assign,2325 .assign,
2326 .helpers_macro,
2313 => {2327 => {
2314 // these should never appear in places where grouping might be needed.2328 // these should never appear in places where grouping might be needed.
2315 unreachable;2329 unreachable;
test/translate_c.zig+6
...@@ -3624,4 +3624,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3624,4 +3624,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3624 ,3624 ,
3625 \\pub export var @"_": c_int = 42;3625 \\pub export var @"_": c_int = 42;
3626 });3626 });
3627
3628 cases.add("Macro matching",
3629 \\#define FOO(X) (X ## U)
3630 , &[_][]const u8{
3631 \\pub const FOO = @import("std").zig.c_translation.Macros.U_SUFFIX;
3632 });
3627}3633}