Commit 6a3d48353b

Vexu <git@vexu.eu>
2019-12-16 20:45:38
translate-c-2 while loops
1 parent e3f1bfe
Changed files (3)
src-self-hosted/clang.zig
@@ -1040,3 +1040,12 @@ pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getEnd(*const ZigClan
 pub extern fn ZigClangIfStmt_getThen(*const ZigClangIfStmt) *const ZigClangStmt;
 pub extern fn ZigClangIfStmt_getElse(*const ZigClangIfStmt) ?*const ZigClangStmt;
 pub extern fn ZigClangIfStmt_getCond(*const ZigClangIfStmt) *const ZigClangStmt;
+
+pub extern fn ZigClangWhileStmt_getCond(*const ZigClangWhileStmt) *const ZigClangExpr;
+pub extern fn ZigClangWhileStmt_getBody(*const ZigClangWhileStmt) *const ZigClangStmt;
+
+pub extern fn ZigClangDoStmt_getCond(*const ZigClangDoStmt) *const ZigClangExpr;
+pub extern fn ZigClangDoStmt_getBody(*const ZigClangDoStmt) *const ZigClangStmt;
+
+pub extern fn ZigClangDoStmt_getCond(*const  ZigClangDoStmt) *const ZigClangExpr;
+pub extern fn ZigClangDoStmt_getBody(*const  ZigClangDoStmt) *const ZigClangStmt;
src-self-hosted/translate_c.zig
@@ -172,7 +172,7 @@ const Scope = struct {
                     // comma operator used
                     return try Block.init(c, scope, true);
                 },
-                else => scope = inner,
+                else => scope = scope.parent.?,
             }
         }
     }
@@ -610,7 +610,9 @@ fn transStmt(
         .ParenExprClass => return transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), result_used, lrvalue),
         .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const ZigClangInitListExpr, stmt), result_used),
         .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used),
-        .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt), result_used),
+        .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt)),
+        .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)),
+        .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)),
         else => {
             return revertAndWarn(
                 rp,
@@ -1184,7 +1186,6 @@ fn transIfStmt(
     rp: RestorePoint,
     scope: *Scope,
     stmt: *const ZigClangIfStmt,
-    used: ResultUsed,
 ) TransError!*ast.Node {
     // if (c) t
     // if (c) t else e
@@ -1196,7 +1197,7 @@ fn transIfStmt(
             .id = .Condition,
         },
     };
-    if_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value);
+    if_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value, false);
     _ = try appendToken(rp.c, .RParen, ")");
 
     if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .used, .r_value);
@@ -1209,6 +1210,87 @@ fn transIfStmt(
     return &if_node.base;
 }
 
+fn transWhileLoop(
+    rp: RestorePoint,
+    scope: *Scope,
+    stmt: *const ZigClangWhileStmt,
+) TransError!*ast.Node {
+    const while_node = try transCreateNodeWhile(rp.c);
+
+    var cond_scope = Scope.Condition{
+        .base = .{
+            .parent = scope,
+            .id = .Condition,
+        },
+    };
+    while_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt)), .used, .r_value, false);
+    _ = try appendToken(rp.c, .RParen, ")");
+
+    while_node.body = try transStmt(rp, scope, ZigClangWhileStmt_getBody(stmt), .unused, .r_value);
+    return &while_node.base;
+}
+
+fn transDoWhileLoop(
+    rp: RestorePoint,
+    scope: *Scope,
+    stmt: *const ZigClangDoStmt,
+) TransError!*ast.Node {
+    const while_node = try transCreateNodeWhile(rp.c);
+
+    while_node.condition = try transCreateNodeBoolLiteral(rp.c, true);
+    _ = try appendToken(rp.c, .RParen, ")");
+    var new = false;
+
+    const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass)
+        // there's already a block in C, so we'll append our condition to it.
+        // c: do {
+        // c:   a;
+        // c:   b;
+        // c: } while(c);
+        // zig: while (true) {
+        // zig:   a;
+        // zig:   b;
+        // zig:   if (!cond) break;
+        // zig: }
+        (try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?
+    else blk: {
+        // the C statement is without a block, so we need to create a block to contain it.
+        // c: do
+        // c:   a;
+        // c: while(c);
+        // zig: while (true) {
+        // zig:   a;
+        // zig:   if (!cond) break;
+        // zig: }
+        
+        new = true;
+        const block = try transCreateNodeBlock(rp.c, null);
+        try block.statements.push(try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value));
+        break :blk block;
+    };
+
+    // if (!cond) break;
+    const if_node = try transCreateNodeIf(rp.c);
+    var cond_scope = Scope.Condition{
+        .base = .{
+            .parent = scope,
+            .id = .Condition,
+        },
+    };
+    const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
+    prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, false);
+    _ = try appendToken(rp.c, .RParen, ")");
+    if_node.condition = &prefix_op.base;
+    if_node.body = &(try transCreateNodeBreak(rp.c, null)).base;
+    _ = try appendToken(rp.c, .Semicolon, ";");
+
+    try body_node.statements.push(&if_node.base);
+    if (new)
+        body_node.rbrace = try appendToken(rp.c, .RBrace, "}");
+    while_node.body = &body_node.base;
+    return &while_node.base;
+}
+
 fn transCPtrCast(
     rp: RestorePoint,
     loc: ZigClangSourceLocation,
@@ -1682,7 +1764,7 @@ fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.Builti
     const builtin_token = try appendToken(c, .Builtin, name);
     _ = try appendToken(c, .LParen, "(");
     const node = try c.a().create(ast.Node.BuiltinCall);
-    node.* = ast.Node.BuiltinCall{
+    node.* = .{
         .builtin_token = builtin_token,
         .params = ast.Node.BuiltinCall.ParamList.init(c.a()),
         .rparen_token = undefined, // set after appending args
@@ -1693,10 +1775,10 @@ fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.Builti
 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{
+    node.* = .{
         .lhs = .{ .node = fn_expr },
-        .op = ast.Node.SuffixOp.Op{
-            .Call = ast.Node.SuffixOp.Op.Call{
+        .op = .{
+            .Call = .{
                 .params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()),
                 .async_token = null,
             },
@@ -1744,7 +1826,7 @@ fn transCreateNodeInfixOp(
     if (!grouped) return &node.base;
     const rparen = try appendToken(rp.c, .RParen, ")");
     const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression);
-    grouped_expr.* = ast.Node.GroupedExpression{
+    grouped_expr.* = .{
         .lparen = lparen,
         .expr = &node.base,
         .rparen = rparen,
@@ -1776,9 +1858,9 @@ fn transCreateNodePtrType(
         .Asterisk => try appendToken(c, .Asterisk, "*"),
         else => unreachable,
     };
-    node.* = ast.Node.PrefixOp{
+    node.* = .{
         .op_token = op_token,
-        .op = ast.Node.PrefixOp.Op{
+        .op = .{
             .PtrType = .{
                 .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,
                 .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
@@ -1811,7 +1893,7 @@ fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*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{
+    node.* = .{
         .ltoken = ltoken,
         .kind = .Return,
         .rhs = null,
@@ -1822,7 +1904,7 @@ fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression {
 fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node {
     const token = try appendToken(c, .Keyword_undefined, "undefined");
     const node = try c.a().create(ast.Node.UndefinedLiteral);
-    node.* = ast.Node.UndefinedLiteral{
+    node.* = .{
         .token = token,
     };
     return &node.base;
@@ -1831,7 +1913,7 @@ fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node {
 fn transCreateNodeNullLiteral(c: *Context) !*ast.Node {
     const token = try appendToken(c, .Keyword_null, "null");
     const node = try c.a().create(ast.Node.NullLiteral);
-    node.* = ast.Node.NullLiteral{
+    node.* = .{
         .token = token,
     };
     return &node.base;
@@ -1843,7 +1925,7 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node {
     else
         try appendToken(c, .Keyword_false, "false");
     const node = try c.a().create(ast.Node.BoolLiteral);
-    node.* = ast.Node.BoolLiteral{
+    node.* = .{
         .token = token,
     };
     return &node.base;
@@ -2032,7 +2114,7 @@ fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowE
         break :blk try transCreateNodeIdentifier(c, l);
     } else null;
     const node = try c.a().create(ast.Node.ControlFlowExpression);
-    node.* = ast.Node.ControlFlowExpression{
+    node.* = .{
         .ltoken = ltoken,
         .kind = .{ .Break = label_node },
         .rhs = null,
@@ -2046,7 +2128,7 @@ fn transCreateNodeVarDecl(c: *Context, is_pub: bool, is_const: bool, name: []con
     const name_tok = try appendIdentifier(c, name);
 
     const node = try c.a().create(ast.Node.VarDecl);
-    node.* = ast.Node.VarDecl{
+    node.* = .{
         .doc_comments = null,
         .visib_token = visib_tok,
         .thread_local_token = null,
@@ -2065,6 +2147,24 @@ fn transCreateNodeVarDecl(c: *Context, is_pub: bool, is_const: bool, name: []con
     return node;
 }
 
+fn transCreateNodeWhile(c: *Context) !*ast.Node.While {
+    const while_tok = try appendToken(c, .Keyword_while, "while");
+    _ = try appendToken(c, .LParen, "(");
+
+    const node = try c.a().create(ast.Node.While);
+    node.* = .{
+        .label = null,
+        .inline_token = null,
+        .while_token = while_tok,
+        .condition = undefined,
+        .payload = null,
+        .continue_expr = null,
+        .body = undefined,
+        .@"else" = null,
+    };
+    return node;
+}
+
 const RestorePoint = struct {
     c: *Context,
     token_index: ast.TokenIndex,
test/translate_c.zig
@@ -755,6 +755,46 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\}
     });
 
+    cases.add_2("while loops",
+        \\int foo() {
+        \\    int a = 5;
+        \\    while (2)
+        \\        a = 2;
+        \\    while (4) {
+        \\        int a = 4;
+        \\        a = 9;
+        \\        return 6, a;
+        \\    }
+        \\    do {
+        \\        int a = 2;
+        \\        a = 12;
+        \\    } while (4);
+        \\    do
+        \\        a = 7;
+        \\    while (4);
+        \\}
+    , &[_][]const u8{
+        \\pub export fn foo() c_int {
+        \\    var a: c_int = 5;
+        \\    while (2 != 0) a = 2;
+        \\    while (4 != 0) {
+        \\        var a: c_int = 4;
+        \\        a = 9;
+        \\        _ = 6;
+        \\        return a;
+        \\    }
+        \\    while (true) {
+        \\        var a: c_int = 2;
+        \\        a = 12;
+        \\        if (!4 != 0) break;
+        \\    }
+        \\    while (true) {
+        \\        a = 7;
+        \\        if (!4 != 0) break;
+        \\    }
+        \\}
+    });
+
     /////////////// Cases for only stage1 which are TODO items for stage2 ////////////////
 
     if (builtin.os != builtin.Os.windows) {