Commit 57170f9eb6

Vexu <git@vexu.eu>
2019-12-15 15:50:20
translate-c-2 macro inline fn
1 parent 75218d4
Changed files (2)
src-self-hosted
test
src-self-hosted/translate_c.zig
@@ -33,8 +33,7 @@ fn addrEql(a: usize, b: usize) bool {
     return a == b;
 }
 
-const MacroTable = std.StringHashMap(*ast.Node);
-const SymbolTable = std.StringHashMap(void);
+const SymbolTable = std.StringHashMap(*ast.Node);
 const AliasList = std.SegmentedList(struct {
     alias: []const u8,
     name: []const u8,
@@ -109,7 +108,7 @@ const Context = struct {
     decl_table: DeclTable,
     alias_list: AliasList,
     sym_table: SymbolTable,
-    macro_table: MacroTable,
+    macro_table: SymbolTable,
     global_scope: *Scope.Root,
     ptr_params: std.BufSet,
     clang_context: *ZigClangASTContext,
@@ -197,7 +196,7 @@ pub fn translate(
         .decl_table = DeclTable.init(arena),
         .alias_list = AliasList.init(arena),
         .sym_table = SymbolTable.init(arena),
-        .macro_table = MacroTable.init(arena),
+        .macro_table = SymbolTable.init(arena),
         .global_scope = try arena.create(Scope.Root),
         .ptr_params = std.BufSet.init(arena),
         .clang_context = ZigClangASTUnit_getASTContext(ast_unit).?,
@@ -215,11 +214,7 @@ pub fn translate(
 
     try transPreprocessorEntities(&context, ast_unit);
 
-    var macro_it = context.macro_table.iterator();
-    while (macro_it.next()) |kv| {
-        try addTopLevelDecl(&context, kv.key, kv.value);
-    }
-
+    try addMacros(&context);
     var it = context.alias_list.iterator(0);
     while (it.next()) |alias| {
         if (!context.sym_table.contains(alias.alias)) {
@@ -962,12 +957,11 @@ fn transReturnStmt(
 ) !TransResult {
     const node = try transCreateNodeReturnExpr(rp.c);
     if (ZigClangReturnStmt_getRetValue(expr)) |val_expr| {
-        const ret_node = node.cast(ast.Node.ControlFlowExpression).?;
-        ret_node.rhs = (try transExpr(rp, scope, val_expr, .used, .r_value)).node;
+        node.rhs = (try transExpr(rp, scope, val_expr, .used, .r_value)).node;
     }
     _ = try appendToken(rp.c, .Semicolon, ";");
     return TransResult{
-        .node = node,
+        .node = &node.base,
         .child_scope = scope,
         .node_scope = scope,
     };
@@ -1327,7 +1321,7 @@ fn maybeSuppressResult(
 
 fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void {
     try c.tree.root_node.decls.push(decl_node);
-    _ = try c.sym_table.put(name, {});
+    _ = try c.sym_table.put(name, decl_node);
 }
 
 fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) TypeError!*ast.Node {
@@ -1767,7 +1761,7 @@ fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp {
     _ = try appendToken(c, .LParen, "(");
     const node = try c.a().create(ast.Node.SuffixOp);
     node.* = ast.Node.SuffixOp{
-        .lhs = fn_expr,
+        .lhs = .{ .node = fn_expr },
         .op = ast.Node.SuffixOp.Op{
             .Call = ast.Node.SuffixOp.Op.Call{
                 .params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()),
@@ -1881,7 +1875,7 @@ fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node {
     return &node.base;
 }
 
-fn transCreateNodeReturnExpr(c: *Context) !*ast.Node {
+fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression {
     const ltoken = try appendToken(c, .Keyword_return, "return");
     const node = try c.a().create(ast.Node.ControlFlowExpression);
     node.* = ast.Node.ControlFlowExpression{
@@ -1889,7 +1883,7 @@ fn transCreateNodeReturnExpr(c: *Context) !*ast.Node {
         .kind = .Return,
         .rhs = null,
     };
-    return &node.base;
+    return node;
 }
 
 fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node {
@@ -1959,6 +1953,100 @@ fn transCreateNodeOpaqueType(c: *Context) !*ast.Node {
     return &call_node.base;
 }
 
+fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias_node: *ast.Node) !*ast.Node {
+    const pub_tok = try appendToken(c, .Keyword_pub, "pub");
+    const inline_tok = try appendToken(c, .Keyword_inline, "inline");
+    const fn_tok = try appendToken(c, .Keyword_fn, "fn");
+    const name_tok = try appendIdentifier(c, name);
+    _ = try appendToken(c, .LParen, "(");
+
+    const proto_alias = proto_alias_node.cast(ast.Node.FnProto).?;
+
+    var fn_params = ast.Node.FnProto.ParamList.init(c.a());
+    var it = proto_alias.params.iterator(0);
+    while (it.next()) |pn| {
+        if (it.index != 0) {
+            _ = try appendToken(c, .Comma, ",");
+        }
+        const param = pn.*.cast(ast.Node.ParamDecl).?;
+
+        const param_name_tok = param.name_token orelse
+            try appendTokenFmt(c, .Identifier, "arg_{}", .{c.getMangle()});
+
+        _ = try appendToken(c, .Colon, ":");
+
+        const param_node = try c.a().create(ast.Node.ParamDecl);
+        param_node.* = .{
+            .doc_comments = null,
+            .comptime_token = null,
+            .noalias_token = param.noalias_token,
+            .name_token = param_name_tok,
+            .type_node = param.type_node,
+            .var_args_token = null,
+        };
+        try fn_params.push(&param_node.base);
+    }
+
+    _ = try appendToken(c, .RParen, ")");
+
+    const fn_proto = try c.a().create(ast.Node.FnProto);
+    fn_proto.* = .{
+        .doc_comments = null,
+        .visib_token = pub_tok,
+        .fn_token = fn_tok,
+        .name_token = name_tok,
+        .params = fn_params,
+        .return_type = proto_alias.return_type,
+        .var_args_token = null,
+        .extern_export_inline_token = inline_tok,
+        .cc_token = null,
+        .body_node = null,
+        .lib_name = null,
+        .align_expr = null,
+        .section_expr = null,
+    };
+
+    const block = try c.a().create(ast.Node.Block);
+    block.* = .{
+        .label = null,
+        .lbrace = try appendToken(c, .LBrace, "{"),
+        .statements = ast.Node.Block.StatementList.init(c.a()),
+        .rbrace = undefined,
+    };
+
+    const return_expr = try transCreateNodeReturnExpr(c);
+    const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.init_node.?);
+    const call_expr = try transCreateNodeFnCall(c, unwrap_expr);
+    it = fn_params.iterator(0);
+    while (it.next()) |pn| {
+        if (it.index != 0) {
+            _ = try appendToken(c, .Comma, ",");
+        }
+        const param = pn.*.cast(ast.Node.ParamDecl).?;
+        try call_expr.op.Call.params.push(try transCreateNodeIdentifier(c, tokenSlice(c, param.name_token.?)));
+    }
+    call_expr.rtoken = try appendToken(c, .RParen, ")");
+    return_expr.rhs = &call_expr.base;
+    _ = try appendToken(c, .Semicolon, ";");
+
+    block.rbrace = try appendToken(c, .RBrace, "}");
+    try block.statements.push(&return_expr.base);
+    fn_proto.body_node = &block.base;
+    return &fn_proto.base;
+}
+
+fn transCreateNodeUnwrapNull(c: *Context, wrapped: *ast.Node) !*ast.Node {
+    _ = try appendToken(c, .Period, ".");
+    const qm = try appendToken(c, .QuestionMark, "?");
+    const node = try c.a().create(ast.Node.SuffixOp);
+    node.* = ast.Node.SuffixOp{
+        .op = .UnwrapOptional,
+        .lhs = .{ .node = wrapped },
+        .rtoken = qm,
+    };
+    return &node.base;
+}
+
 const RestorePoint = struct {
     c: *Context,
     token_index: ast.TokenIndex,
@@ -1982,28 +2070,28 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
     switch (ZigClangType_getTypeClass(ty)) {
         .Builtin => {
             const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
-            switch (ZigClangBuiltinType_getKind(builtin_ty)) {
-                .Void => return transCreateNodeIdentifier(rp.c, "c_void"),
-                .Bool => return transCreateNodeIdentifier(rp.c, "bool"),
-                .Char_U, .UChar, .Char_S, .Char8 => return transCreateNodeIdentifier(rp.c, "u8"),
-                .SChar => return transCreateNodeIdentifier(rp.c, "i8"),
-                .UShort => return transCreateNodeIdentifier(rp.c, "c_ushort"),
-                .UInt => return transCreateNodeIdentifier(rp.c, "c_uint"),
-                .ULong => return transCreateNodeIdentifier(rp.c, "c_ulong"),
-                .ULongLong => return transCreateNodeIdentifier(rp.c, "c_ulonglong"),
-                .Short => return transCreateNodeIdentifier(rp.c, "c_short"),
-                .Int => return transCreateNodeIdentifier(rp.c, "c_int"),
-                .Long => return transCreateNodeIdentifier(rp.c, "c_long"),
-                .LongLong => return transCreateNodeIdentifier(rp.c, "c_longlong"),
-                .UInt128 => return transCreateNodeIdentifier(rp.c, "u128"),
-                .Int128 => return transCreateNodeIdentifier(rp.c, "i128"),
-                .Float => return transCreateNodeIdentifier(rp.c, "f32"),
-                .Double => return transCreateNodeIdentifier(rp.c, "f64"),
-                .Float128 => return transCreateNodeIdentifier(rp.c, "f128"),
-                .Float16 => return transCreateNodeIdentifier(rp.c, "f16"),
-                .LongDouble => return transCreateNodeIdentifier(rp.c, "c_longdouble"),
+            return transCreateNodeIdentifier(rp.c, switch (ZigClangBuiltinType_getKind(builtin_ty)) {
+                .Void => "c_void",
+                .Bool => "bool",
+                .Char_U, .UChar, .Char_S, .Char8 => "u8",
+                .SChar => "i8",
+                .UShort => "c_ushort",
+                .UInt => "c_uint",
+                .ULong => "c_ulong",
+                .ULongLong => "c_ulonglong",
+                .Short => "c_short",
+                .Int => "c_int",
+                .Long => "c_long",
+                .LongLong => "c_longlong",
+                .UInt128 => "u128",
+                .Int128 => "i128",
+                .Float => "f32",
+                .Double => "f64",
+                .Float128 => "f128",
+                .Float16 => "f16",
+                .LongDouble => "c_longdouble",
                 else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
-            }
+            });
         },
         .FunctionProto => {
             const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty);
@@ -2693,8 +2781,6 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
                 node = &access_node.base;
             },
             .Shl => {
-                const rhs_node = try parseCPrimaryExpr(rp, it, source_loc);
-
                 const op_token = try appendToken(rp.c, .AngleBracketAngleBracketLeft, "<<");
                 const rhs = try parseCPrimaryExpr(rp, it, source_loc);
                 const bitshift_node = try rp.c.a().create(ast.Node.InfixOp);
@@ -2718,9 +2804,64 @@ fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
     const op_tok = it.next().?;
 
     switch (op_tok.id) {
+        .Bang => {
+            const node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
+            node.rhs = try parseCPrefixOpExpr(rp, it, source_loc);
+            return &node.base;
+        },
+        .Minus => {
+            const node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-");
+            node.rhs = try parseCPrefixOpExpr(rp, it, source_loc);
+            return &node.base;
+        },
+        .Tilde => {
+            const node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~");
+            node.rhs = try parseCPrefixOpExpr(rp, it, source_loc);
+            return &node.base;
+        },
         else => {
             _ = it.prev();
             return try parseCSuffixOpExpr(rp, it, source_loc);
         },
     }
 }
+
+fn tokenSlice(c: *Context, token: ast.TokenIndex) []const u8 {
+    const tok = c.tree.tokens.at(token);
+    return c.source_buffer.toSliceConst()[tok.start..tok.end];
+}
+
+fn getFnDecl(c: *Context, ref: *ast.Node) ?*ast.Node {
+    const init = ref.cast(ast.Node.VarDecl).?.init_node.?;
+    const name = if (init.cast(ast.Node.Identifier)) |id| 
+        tokenSlice(c, id.token)
+    else
+        return null;
+    // TODO a.b.c
+    if (c.sym_table.get(name)) |kv| {
+        if (kv.value.cast(ast.Node.VarDecl)) |val| {
+            if (val.type_node) |type_node| {
+                if (type_node.cast(ast.Node.PrefixOp)) |casted| {
+                    if (casted.rhs.id == .FnProto) {
+                        return casted.rhs;
+                    }
+                }
+            }
+        }
+    }
+    return null;
+}
+
+fn addMacros(c: *Context) !void {
+    var macro_it = c.macro_table.iterator();
+    while (macro_it.next()) |kv| {
+        if (getFnDecl(c, kv.value)) |proto_node| {
+            // If a macro aliases a global variable which is a function pointer, we conclude that
+            // the macro is intended to represent a function that assumes the function pointer
+            // variable is non-null and calls it.
+            try addTopLevelDecl(c, kv.key, try transCreateNodeMacroFn(c, kv.key, kv.value, proto_node));
+        } else {
+            try addTopLevelDecl(c, kv.key, kv.value);
+        }
+    }
+}
test/translate_c.zig
@@ -339,6 +339,64 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020);
     });
 
+    cases.add_2("generate inline func for #define global extern fn",
+        \\extern void (*fn_ptr)(void);
+        \\#define foo fn_ptr
+        \\
+        \\extern char (*fn_ptr2)(int, float);
+        \\#define bar fn_ptr2
+    , &[_][]const u8{
+        \\pub extern var fn_ptr: ?extern fn () void;
+    ,
+        \\pub inline fn foo() void {
+        \\    return fn_ptr.?();
+        \\}
+    ,
+        \\pub extern var fn_ptr2: ?extern fn (c_int, f32) u8;
+    ,
+        \\pub inline fn bar(arg_1: c_int, arg_2: f32) u8 {
+        \\    return fn_ptr2.?(arg_1, arg_2);
+        \\}
+    });
+
+    cases.add_2("macros with field targets",
+        \\typedef unsigned int GLbitfield;
+        \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);
+        \\typedef void(*OpenGLProc)(void);
+        \\union OpenGLProcs {
+        \\    OpenGLProc ptr[1];
+        \\    struct {
+        \\        PFNGLCLEARPROC Clear;
+        \\    } gl;
+        \\};
+        \\extern union OpenGLProcs glProcs;
+        \\#define glClearUnion glProcs.gl.Clear
+        \\#define glClearPFN PFNGLCLEARPROC
+    , &[_][]const u8{
+        \\pub const GLbitfield = c_uint;
+    ,
+        \\pub const PFNGLCLEARPROC = ?extern fn (GLbitfield) void;
+    ,
+        \\pub const OpenGLProc = ?extern fn () void;
+    ,
+        \\pub const union_OpenGLProcs = extern union {
+        \\    ptr: [1]OpenGLProc,
+        \\    gl: extern struct {
+        \\        Clear: PFNGLCLEARPROC,
+        \\    },
+        \\};
+    ,
+        \\pub extern var glProcs: union_OpenGLProcs;
+    ,
+        \\pub const glClearPFN = PFNGLCLEARPROC;
+    // , // TODO
+    //     \\pub inline fn glClearUnion(arg_1: GLbitfield) void {
+    //     \\    return glProcs.gl.Clear.?(arg_1);
+    //     \\}
+    ,
+        \\pub const OpenGLProcs = union_OpenGLProcs;
+    });
+
     /////////////// Cases for only stage1 which are TODO items for stage2 ////////////////
 
     cases.add_both("typedef of function in struct field",
@@ -1844,7 +1902,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         },
     );
 
-    cases.addC(//todo
+    cases.add_both(
         "bitwise not on u-suffixed 0 (zero) in macro definition",
         "#define NOT_ZERO (~0U)",
         &[_][]const u8{