Commit c6a0a4e728

Veikka Tuominen <git@vexu.eu>
2021-05-31 20:06:12
translate-c: support designated initializers in macros
1 parent 99b6305
Changed files (3)
src/translate_c/ast.zig
@@ -71,6 +71,7 @@ pub const Node = extern union {
         array_init,
         tuple,
         container_init,
+        container_init_dot,
         std_meta_cast,
         /// _ = operand;
         discard,
@@ -332,6 +333,7 @@ pub const Node = extern union {
                 .ptr_cast,
                 .div_exact,
                 .byte_offset_of,
+                .std_meta_cast,
                 => Payload.BinOp,
 
                 .integer_literal,
@@ -354,7 +356,7 @@ pub const Node = extern union {
                 .@"struct", .@"union" => Payload.Record,
                 .tuple => Payload.TupleInit,
                 .container_init => Payload.ContainerInit,
-                .std_meta_cast => Payload.Infix,
+                .container_init_dot => Payload.ContainerInitDot,
                 .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral,
                 .block => Payload.Block,
                 .c_pointer, .single_pointer => Payload.Pointer,
@@ -448,14 +450,6 @@ pub const Node = extern union {
 pub const Payload = struct {
     tag: Node.Tag,
 
-    pub const Infix = struct {
-        base: Payload,
-        data: struct {
-            lhs: Node,
-            rhs: Node,
-        },
-    };
-
     pub const Value = struct {
         base: Payload,
         data: []const u8,
@@ -600,6 +594,16 @@ pub const Payload = struct {
         };
     };
 
+    pub const ContainerInitDot = struct {
+        base: Payload,
+        data: []Initializer,
+
+        pub const Initializer = struct {
+            name: []const u8,
+            value: Node,
+        };
+    };
+
     pub const Block = struct {
         base: Payload,
         data: struct {
@@ -1893,6 +1897,44 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
                 });
             }
         },
+        .container_init_dot => {
+            const payload = node.castTag(.container_init_dot).?.data;
+            _ = try c.addToken(.period, ".");
+            const l_brace = try c.addToken(.l_brace, "{");
+            var inits = try c.gpa.alloc(NodeIndex, std.math.max(payload.len, 2));
+            defer c.gpa.free(inits);
+            inits[0] = 0;
+            inits[1] = 0;
+            for (payload) |init, i| {
+                _ = try c.addToken(.period, ".");
+                _ = try c.addIdentifier(init.name);
+                _ = try c.addToken(.equal, "=");
+                inits[i] = try renderNode(c, init.value);
+                _ = try c.addToken(.comma, ",");
+            }
+            _ = try c.addToken(.r_brace, "}");
+
+            if (payload.len < 3) {
+                return c.addNode(.{
+                    .tag = .struct_init_dot_two_comma,
+                    .main_token = l_brace,
+                    .data = .{
+                        .lhs = inits[0],
+                        .rhs = inits[1],
+                    },
+                });
+            } else {
+                const span = try c.listToSpan(inits);
+                return c.addNode(.{
+                    .tag = .struct_init_dot_comma,
+                    .main_token = l_brace,
+                    .data = .{
+                        .lhs = span.start,
+                        .rhs = span.end,
+                    },
+                });
+            }
+        },
         .container_init => {
             const payload = node.castTag(.container_init).?.data;
             const lhs = try renderNode(c, payload.lhs);
@@ -2257,6 +2299,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
         .array_init,
         .tuple,
         .container_init,
+        .container_init_dot,
         .block,
         => return c.addNode(.{
             .tag = .grouped_expression,
src/translate_c.zig
@@ -4813,8 +4813,11 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
         const br = blk_last.castTag(.break_val).?;
         break :blk br.data.val;
     } else expr;
-    const return_type = if (typeof_arg.castTag(.std_meta_cast)) |some|
+
+    const return_type = if (typeof_arg.castTag(.std_meta_cast) orelse typeof_arg.castTag(.std_mem_zeroinit)) |some|
         some.data.lhs
+    else if (typeof_arg.castTag(.std_mem_zeroes)) |some|
+        some.data
     else
         try Tag.typeof.create(c.arena, typeof_arg);
 
@@ -4932,6 +4935,7 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
             }
         },
         .FloatLiteral => |suffix| {
+            if (suffix != .none) lit_bytes = lit_bytes[0 .. lit_bytes.len - 1];
             const dot_index = mem.indexOfScalar(u8, lit_bytes, '.').?;
             if (dot_index == 0) {
                 lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes});
@@ -4943,15 +4947,16 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
                     lit_bytes[dot_index + 1 ..],
                 });
             }
-            if (suffix == .none) {
+
+            if (suffix == .none)
                 return transCreateNodeNumber(c, lit_bytes, .float);
-            }
+
             const type_node = try Tag.type.create(c.arena, switch (suffix) {
                 .f => "f32",
                 .l => "c_longdouble",
                 else => unreachable,
             });
-            const rhs = try transCreateNodeNumber(c, lit_bytes[0 .. lit_bytes.len - 1], .float);
+            const rhs = try transCreateNodeNumber(c, lit_bytes, .float);
             return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs });
         },
         else => unreachable,
@@ -5540,6 +5545,42 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
                 }
             },
             .LBrace => {
+                // Check for designated field initializers
+                if (m.peek().? == .Period) {
+                    var init_vals = std.ArrayList(ast.Payload.ContainerInitDot.Initializer).init(c.gpa);
+                    defer init_vals.deinit();
+
+                    while (true) {
+                        if (m.next().? != .Period) {
+                            try m.fail(c, "unable to translate C expr: expected '.'", .{});
+                            return error.ParseError;
+                        }
+                        if (m.next().? != .Identifier) {
+                            try m.fail(c, "unable to translate C expr: expected identifier", .{});
+                            return error.ParseError;
+                        }
+                        const name = m.slice();
+                        if (m.next().? != .Equal) {
+                            try m.fail(c, "unable to translate C expr: expected '='", .{});
+                            return error.ParseError;
+                        }
+
+                        const val = try parseCCondExpr(c, m, scope);
+                        try init_vals.append(.{ .name = name, .value = val });
+                        switch (m.next().?) {
+                            .Comma => {},
+                            .RBrace => break,
+                            else => {
+                                try m.fail(c, "unable to translate C expr: expected ',' or '}}'", .{});
+                                return error.ParseError;
+                            },
+                        }
+                    }
+                    const tuple_node = try Tag.container_init_dot.create(c.arena, try c.arena.dupe(ast.Payload.ContainerInitDot.Initializer, init_vals.items));
+                    node = try Tag.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node });
+                    continue;
+                }
+
                 var init_vals = std.ArrayList(Node).init(c.gpa);
                 defer init_vals.deinit();
 
test/translate_c.zig
@@ -336,6 +336,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    int i1;
         \\} boom_t;
         \\#define FOO ((boom_t){1})
+        \\typedef struct { float x; } MyCStruct;
+        \\#define A(_x)   (MyCStruct) { .x = (_x) }
+        \\#define B A(0.f)
     , &[_][]const u8{
         \\pub const struct_Color = extern struct {
         \\    r: u8,
@@ -357,6 +360,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\pub const boom_t = struct_boom_t;
         ,
         \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{@as(c_int, 1)});
+        ,
+        \\pub const MyCStruct = extern struct {
+        \\    x: f32,
+        \\};
+        ,
+        \\pub inline fn A(_x: anytype) MyCStruct {
+        \\    return @import("std").mem.zeroInit(MyCStruct, .{
+        \\        .x = _x,
+        \\    });
+        \\}
+        ,
+        \\pub const B = A(@as(f32, 0.0));
     });
 
     cases.add("complex switch",