Commit cf7a5b7a4a

Vexu <git@vexu.eu>
2019-12-18 21:29:42
translate-c-2 member access
1 parent e65b9e8
Changed files (4)
src-self-hosted/c_tokenizer.zig
@@ -27,6 +27,7 @@ pub const CToken = struct {
         Lt,
         Comma,
         Fn,
+        Arrow,
     };
 
     pub const NumLitSuffix = enum {
@@ -164,6 +165,8 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
         NumLitIntSuffixL,
         NumLitIntSuffixLL,
         NumLitIntSuffixUL,
+        Minus,
+        Done,
     } = .Start;
 
     var result = CToken{
@@ -178,9 +181,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
         const c = chars[i.*];
         if (c == 0) {
             switch (state) {
-                .Start => {
-                    return result;
-                },
                 .Identifier,
                 .Decimal,
                 .Hex,
@@ -193,6 +193,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     result.bytes = chars[begin_index..i.*];
                     return result;
                 },
+                .Start,
+                .Minus,
+                .Done,
                 .NumLitIntSuffixU,
                 .NumLitIntSuffixL,
                 .NumLitIntSuffixUL,
@@ -212,7 +215,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                 => return error.TokenizingFailed,
             }
         }
-        i.* += 1;
         switch (state) {
             .Start => {
                 switch (c) {
@@ -220,12 +222,12 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     '\'' => {
                         state = .CharLit;
                         result.id = .CharLit;
-                        begin_index = i.* - 1;
+                        begin_index = i.*;
                     },
                     '\"' => {
                         state = .String;
                         result.id = .StrLit;
-                        begin_index = i.* - 1;
+                        begin_index = i.*;
                     },
                     '/' => {
                         state = .OpenComment;
@@ -239,21 +241,21 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     'a'...'z', 'A'...'Z', '_' => {
                         state = .Identifier;
                         result.id = .Identifier;
-                        begin_index = i.* - 1;
+                        begin_index = i.*;
                     },
                     '1'...'9' => {
                         state = .Decimal;
                         result.id = .NumLitInt;
-                        begin_index = i.* - 1;
+                        begin_index = i.*;
                     },
                     '0' => {
                         state = .GotZero;
                         result.id = .NumLitInt;
-                        begin_index = i.* - 1;
+                        begin_index = i.*;
                     },
                     '.' => {
                         result.id = .Dot;
-                        return result;
+                        state = .Done;
                     },
                     '<' => {
                         result.id = .Lt;
@@ -261,40 +263,52 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     },
                     '(' => {
                         result.id = .LParen;
-                        return result;
+                        state = .Done;
                     },
                     ')' => {
                         result.id = .RParen;
-                        return result;
+                        state = .Done;
                     },
                     '*' => {
                         result.id = .Asterisk;
-                        return result;
+                        state = .Done;
                     },
                     '-' => {
+                        state = .Minus;
                         result.id = .Minus;
-                        return result;
                     },
                     '!' => {
                         result.id = .Bang;
-                        return result;
+                        state = .Done;
                     },
                     '~' => {
                         result.id = .Tilde;
-                        return result;
+                        state = .Done;
                     },
                     ',' => {
                         result.id = .Comma;
-                        return result;
+                        state = .Done;
                     },
                     else => return error.TokenizingFailed,
                 }
             },
+            .Done => return result,
+            .Minus => {
+                switch (c) {
+                    '>' => {
+                        result.id = .Arrow;
+                        state = .Done;
+                    },
+                    else => {
+                        return result;
+                    },
+                }
+            },
             .GotLt => {
                 switch (c) {
                     '<' => {
                         result.id = .Shl;
-                        return result;
+                        state = .Done;
                     },
                     else => {
                         return result;
@@ -310,19 +324,16 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     'f',
                     'F',
                     => {
-                        i.* -= 1;
                         result.num_lit_suffix = .F;
                         result.bytes = chars[begin_index..i.*];
-                        return result;
+                        state = .Done;
                     },
                     'l', 'L' => {
-                        i.* -= 1;
                         result.num_lit_suffix = .L;
                         result.bytes = chars[begin_index..i.*];
-                        return result;
+                        state = .Done;
                     },
                     else => {
-                        i.* -= 1;
                         result.bytes = chars[begin_index..i.*];
                         return result;
                     },
@@ -352,16 +363,15 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     '0'...'9' => {},
                     'f', 'F' => {
                         result.num_lit_suffix = .F;
-                        result.bytes = chars[begin_index .. i.* - 1];
-                        return result;
+                        result.bytes = chars[begin_index..i.*];
+                        state = .Done;
                     },
                     'l', 'L' => {
                         result.num_lit_suffix = .L;
-                        result.bytes = chars[begin_index .. i.* - 1];
-                        return result;
+                        result.bytes = chars[begin_index..i.*];
+                        state = .Done;
                     },
                     else => {
-                        i.* -= 1;
                         result.bytes = chars[begin_index..i.*];
                         return result;
                     },
@@ -374,19 +384,18 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     'u', 'U' => {
                         state = .NumLitIntSuffixU;
                         result.num_lit_suffix = .U;
-                        result.bytes = chars[begin_index .. i.* - 1];
+                        result.bytes = chars[begin_index..i.*];
                     },
                     'l', 'L' => {
                         state = .NumLitIntSuffixL;
                         result.num_lit_suffix = .L;
-                        result.bytes = chars[begin_index .. i.* - 1];
+                        result.bytes = chars[begin_index..i.*];
                     },
                     '.' => {
                         result.id = .NumLitFloat;
                         state = .Float;
                     },
                     else => {
-                        i.* -= 1;
                         result.bytes = chars[begin_index..i.*];
                         return result;
                     },
@@ -407,12 +416,12 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     'u', 'U' => {
                         state = .NumLitIntSuffixU;
                         result.num_lit_suffix = .U;
-                        result.bytes = chars[begin_index .. i.* - 1];
+                        result.bytes = chars[begin_index..i.*];
                     },
                     'l', 'L' => {
                         state = .NumLitIntSuffixL;
                         result.num_lit_suffix = .L;
-                        result.bytes = chars[begin_index .. i.* - 1];
+                        result.bytes = chars[begin_index..i.*];
                     },
                     else => {
                         i.* -= 1;
@@ -425,7 +434,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     '0'...'7' => {},
                     '8', '9' => return error.TokenizingFailed,
                     else => {
-                        i.* -= 1;
                         result.bytes = chars[begin_index..i.*];
                         return result;
                     },
@@ -438,16 +446,15 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                         // marks the number literal as unsigned
                         state = .NumLitIntSuffixU;
                         result.num_lit_suffix = .U;
-                        result.bytes = chars[begin_index .. i.* - 1];
+                        result.bytes = chars[begin_index..i.*];
                     },
                     'l', 'L' => {
                         // marks the number literal as long
                         state = .NumLitIntSuffixL;
                         result.num_lit_suffix = .L;
-                        result.bytes = chars[begin_index .. i.* - 1];
+                        result.bytes = chars[begin_index..i.*];
                     },
                     else => {
-                        i.* -= 1;
                         result.bytes = chars[begin_index..i.*];
                         return result;
                     },
@@ -461,16 +468,15 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                         // marks the number literal as unsigned
                         state = .NumLitIntSuffixU;
                         result.num_lit_suffix = .U;
-                        result.bytes = chars[begin_index .. i.* - 1];
+                        result.bytes = chars[begin_index..i.*];
                     },
                     'l', 'L' => {
                         // marks the number literal as long
                         state = .NumLitIntSuffixL;
                         result.num_lit_suffix = .L;
-                        result.bytes = chars[begin_index .. i.* - 1];
+                        result.bytes = chars[begin_index..i.*];
                     },
                     else => {
-                        i.* -= 1;
                         result.bytes = chars[begin_index..i.*];
                         return result;
                     },
@@ -483,7 +489,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                         state = .NumLitIntSuffixUL;
                     },
                     else => {
-                        i.* -= 1;
                         return result;
                     },
                 }
@@ -496,10 +501,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     },
                     'u', 'U' => {
                         result.num_lit_suffix = .LU;
-                        return result;
+                        state = .Done;
                     },
                     else => {
-                        i.* -= 1;
                         return result;
                     },
                 }
@@ -508,10 +512,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                 switch (c) {
                     'u', 'U' => {
                         result.num_lit_suffix = .LLU;
-                        return result;
+                        state = .Done;
                     },
                     else => {
-                        i.* -= 1;
                         return result;
                     },
                 }
@@ -523,7 +526,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                         return result;
                     },
                     else => {
-                        i.* -= 1;
                         return result;
                     },
                 }
@@ -532,17 +534,16 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                 switch (c) {
                     '_', 'a'...'z', 'A'...'Z', '0'...'9' => {},
                     else => {
-                        i.* -= 1;
                         result.bytes = chars[begin_index..i.*];
                         return result;
                     },
                 }
             },
-            .String => { // TODO char escapes
+            .String => {
                 switch (c) {
                     '\"' => {
-                        result.bytes = chars[begin_index..i.*];
-                        return result;
+                        result.bytes = chars[begin_index .. i.* + 1];
+                        state = .Done;
                     },
                     else => {},
                 }
@@ -550,8 +551,8 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
             .CharLit => {
                 switch (c) {
                     '\'' => {
-                        result.bytes = chars[begin_index..i.*];
-                        return result;
+                        result.bytes = chars[begin_index .. i.* + 1];
+                        state = .Done;
                     },
                     else => {},
                 }
@@ -566,7 +567,7 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                     },
                     else => {
                         result.id = .Slash;
-                        return result;
+                        state = .Done;
                     },
                 }
             },
@@ -598,6 +599,7 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken {
                 }
             },
         }
+        i.* += 1;
     }
     unreachable;
 }
src-self-hosted/clang.zig
@@ -1096,5 +1096,9 @@ pub extern fn ZigClangCharacterLiteral_getBeginLoc(*const ZigClangCharacterLiter
 pub extern fn ZigClangCharacterLiteral_getKind(*const ZigClangCharacterLiteral) ZigClangCharacterLiteral_CharacterKind;
 pub extern fn ZigClangCharacterLiteral_getValue(*const ZigClangCharacterLiteral) c_uint;
 
-pub extern fn ZigClangStmtExpr_getSubStmt( *const ZigClangStmtExpr) *const ZigClangCompoundStmt;
+pub extern fn ZigClangStmtExpr_getSubStmt(*const ZigClangStmtExpr) *const ZigClangCompoundStmt;
+
+pub extern fn ZigClangMemberExpr_getBase(*const ZigClangMemberExpr) *const ZigClangExpr;
+pub extern fn ZigClangMemberExpr_isArrow(*const ZigClangMemberExpr) bool;
+pub extern fn ZigClangMemberExpr_getMemberDecl(*const ZigClangMemberExpr) *const ZigClangValueDecl;
 
src-self-hosted/translate_c.zig
@@ -49,9 +49,6 @@ const Scope = struct {
         Root,
         Condition,
         FnDef,
-
-        /// used when getting a member `a.b`
-        Ref,
         Loop,
     };
 
@@ -181,7 +178,6 @@ const Scope = struct {
     fn getAlias(scope: *Scope, name: []const u8) ?[]const u8 {
         return switch (scope.id) {
             .Root => null,
-            .Ref => null,
             .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name),
             .Block => @fieldParentPtr(Block, "base", scope).getAlias(name),
             .Switch, .Loop, .Condition => scope.parent.?.getAlias(name),
@@ -190,7 +186,6 @@ const Scope = struct {
 
     fn contains(scope: *Scope, name: []const u8) bool {
         return switch (scope.id) {
-            .Ref => false,
             .Root => @fieldParentPtr(Root, "base", scope).contains(name),
             .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name),
             .Block => @fieldParentPtr(Block, "base", scope).contains(name),
@@ -230,7 +225,6 @@ const Context = struct {
     decl_table: DeclTable,
     alias_list: AliasList,
     global_scope: *Scope.Root,
-    ptr_params: std.BufSet,
     clang_context: *ZigClangASTContext,
     mangle_count: u64 = 0,
 
@@ -316,7 +310,6 @@ pub fn translate(
         .decl_table = DeclTable.init(arena),
         .alias_list = AliasList.init(arena),
         .global_scope = try arena.create(Scope.Root),
-        .ptr_params = std.BufSet.init(arena),
         .clang_context = ZigClangASTUnit_getASTContext(ast_unit).?,
     };
     context.global_scope.* = Scope.Root.init(&context);
@@ -856,6 +849,7 @@ fn transStmt(
         .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used),
         .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used),
         .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const ZigClangStmtExpr, stmt), result_used),
+        .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const ZigClangMemberExpr, stmt), result_used),
         else => {
             return revertAndWarn(
                 rp,
@@ -1147,7 +1141,6 @@ fn transDeclRefExpr(
     const value_decl = ZigClangDeclRefExpr_getDecl(expr);
     const name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, value_decl)));
     const checked_name = if (scope.getAlias(name)) |a| a else name;
-    if (lrvalue == .l_value) try rp.c.ptr_params.put(checked_name);
     return transCreateNodeIdentifier(rp.c, checked_name);
 }
 
@@ -1990,6 +1983,18 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr,
     return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
 }
 
+fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberExpr, result_used: ResultUsed) TransError!*ast.Node {
+    var container_node = try transExpr(rp, scope, ZigClangMemberExpr_getBase(stmt), .used, .r_value);
+
+    if (ZigClangMemberExpr_isArrow(stmt)) {
+        container_node = try transCreateNodePtrDeref(rp.c, container_node);
+    }
+
+    const name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, ZigClangMemberExpr_getMemberDecl(stmt))));
+    const node = try transCreateNodeFieldAccess(rp.c, container_node, name);
+    return maybeSuppressResult(rp, scope, result_used, node);
+}
+
 fn transCPtrCast(
     rp: RestorePoint,
     loc: ZigClangSourceLocation,
@@ -2213,8 +2218,8 @@ fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigC
     import_fn_call.rparen_token = try appendToken(rp.c, .RParen, ")");
 
     const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math");
-    const outer_field_access = try transCreateNodeFieldAccess(rp.c, &inner_field_access.base, "Log2Int");
-    const log2int_fn_call = try transCreateNodeFnCall(rp.c, &outer_field_access.base);
+    const outer_field_access = try transCreateNodeFieldAccess(rp.c, inner_field_access, "Log2Int");
+    const log2int_fn_call = try transCreateNodeFnCall(rp.c, outer_field_access);
     try @ptrCast(*ast.Node.SuffixOp.Op.Call, &log2int_fn_call.op).params.push(zig_type_node);
     log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")");
 
@@ -2446,7 +2451,7 @@ fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp {
     return node;
 }
 
-fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node.InfixOp {
+fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node {
     const field_access_node = try c.a().create(ast.Node.InfixOp);
     field_access_node.* = .{
         .op_token = try appendToken(c, .Period, "."),
@@ -2454,7 +2459,7 @@ fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []c
         .op = .Period,
         .rhs = try transCreateNodeIdentifier(c, field_name),
     };
-    return field_access_node;
+    return &field_access_node.base;
 }
 
 fn transCreateNodePrefixOp(
@@ -2924,9 +2929,9 @@ fn transCreateNodeShiftOp(
     rp: RestorePoint,
     scope: *Scope,
     stmt: *const ZigClangBinaryOperator,
-    comptime op: ast.Node.InfixOp.Op,
-    comptime op_tok_id: std.zig.Token.Id,
-    comptime bytes: []const u8,
+    op: ast.Node.InfixOp.Op,
+    op_tok_id: std.zig.Token.Id,
+    bytes: []const u8,
 ) !*ast.Node {
     std.debug.assert(op == .BitShiftLeft or op == .BitShiftRight);
 
@@ -2957,6 +2962,16 @@ fn transCreateNodeShiftOp(
     return &node.base;
 }
 
+fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node {
+    const node = try c.a().create(ast.Node.SuffixOp);
+    node.* = .{
+        .lhs = .{ .node = lhs },
+        .op = .Deref,
+        .rtoken = try appendToken(c, .PeriodAsterisk, ".*"),
+    };
+    return &node.base;
+}
+
 const RestorePoint = struct {
     c: *Context,
     token_index: ast.TokenIndex,
@@ -3862,16 +3877,21 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
                         .{},
                     );
 
-                const op_token = try appendToken(rp.c, .Period, ".");
-                const rhs = try transCreateNodeIdentifier(rp.c, name_tok.bytes);
-                const access_node = try rp.c.a().create(ast.Node.InfixOp);
-                access_node.* = .{
-                    .op_token = op_token,
-                    .lhs = node,
-                    .op = .Period,
-                    .rhs = rhs,
-                };
-                node = &access_node.base;
+                node = try transCreateNodeFieldAccess(rp.c, node, name_tok.bytes);
+            },
+            .Arrow => {
+                const name_tok = it.next().?;
+                if (name_tok.id != .Identifier)
+                    return revertAndWarn(
+                        rp,
+                        error.ParseError,
+                        source_loc,
+                        "unable to translate C expr",
+                        .{},
+                    );
+
+                const deref = try transCreateNodePtrDeref(rp.c, node);
+                node = try transCreateNodeFieldAccess(rp.c, deref, name_tok.bytes);
             },
             .Asterisk => {
                 if (it.peek().?.id == .RParen) {
@@ -3887,14 +3907,14 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
                     // expr * expr
                     const op_token = try appendToken(rp.c, .Asterisk, "*");
                     const rhs = try parseCPrimaryExpr(rp, it, source_loc, scope);
-                    const bitshift_node = try rp.c.a().create(ast.Node.InfixOp);
-                    bitshift_node.* = .{
+                    const mul_node = try rp.c.a().create(ast.Node.InfixOp);
+                    mul_node.* = .{
                         .op_token = op_token,
                         .lhs = node,
                         .op = .BitShiftLeft,
                         .rhs = rhs,
                     };
-                    node = &bitshift_node.base;
+                    node = &mul_node.base;
                 }
             },
             .Shl => {
@@ -3938,13 +3958,7 @@ fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc
         },
         .Asterisk => {
             const prefix_op_expr = try parseCPrefixOpExpr(rp, it, source_loc, scope);
-            const node = try rp.c.a().create(ast.Node.SuffixOp);
-            node.* = .{
-                .lhs = .{ .node = prefix_op_expr },
-                .op = .Deref,
-                .rtoken = try appendToken(rp.c, .PeriodAsterisk, ".*"),
-            };
-            return &node.base;
+            return try transCreateNodePtrDeref(rp.c, prefix_op_expr);
         },
         else => {
             _ = it.prev();
test/translate_c.zig
@@ -1494,6 +1494,35 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\}
     });
 
+    cases.add_2("field access expression",
+        \\#define ARROW a->b
+        \\#define DOT a.b
+        \\extern struct Foo {
+        \\    int b;
+        \\}a;
+        \\float b = 2.0f;
+        \\int foo(void) {
+        \\    struct Foo *c;
+        \\    a.b;
+        \\    c->b;
+        \\}
+    , &[_][]const u8{
+        \\pub const struct_Foo = extern struct {
+        \\    b: c_int,
+        \\};
+        \\pub extern var a: struct_Foo;
+        \\pub export var b: f32 = 2;
+        \\pub export fn foo() c_int {
+        \\    var c: [*c]struct_Foo = undefined;
+        \\    _ = a.b;
+        \\    _ = c.*.b;
+        \\}
+    ,
+        \\pub const DOT = a.b;
+    ,
+        \\pub const ARROW = a.*.b;
+    });
+
     /////////////// Cases for only stage1 which are TODO items for stage2 ////////////////
 
     cases.addAllowWarnings("simple data types",
@@ -1702,22 +1731,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\}
     });
 
-    cases.addC("field access expression",
-        \\struct Foo {
-        \\    int field;
-        \\};
-        \\int read_field(struct Foo *foo) {
-        \\    return foo->field;
-        \\}
-    , &[_][]const u8{
-        \\pub const struct_Foo = extern struct {
-        \\    field: c_int,
-        \\};
-        \\pub export fn read_field(foo: [*c]struct_Foo) c_int {
-        \\    return foo.*.field;
-        \\}
-    });
-
     cases.addC("array access",
         \\int array[100];
         \\int foo(int index) {
@@ -2627,4 +2640,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    };
         \\}
     });
+
+    cases.addC("field access expression",
+        \\struct Foo {
+        \\    int field;
+        \\};
+        \\int read_field(struct Foo *foo) {
+        \\    return foo->field;
+        \\}
+    , &[_][]const u8{
+        \\pub const struct_Foo = extern struct {
+        \\    field: c_int,
+        \\};
+        \\pub export fn read_field(foo: [*c]struct_Foo) c_int {
+        \\    return foo.*.field;
+        \\}
+    });
 }