From 62162a07171ccbdb753650205880c3745a599366 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 14 Feb 2021 16:32:31 +0200 Subject: [PATCH] translate-c: render control flow --- src/translate_c.zig | 41 ++++--- src/translate_c/ast.zig | 251 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 256 insertions(+), 36 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 6fa3110118..894502ecc5 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -36,6 +36,7 @@ const Scope = struct { root, condition, loop, + do_loop, }; /// Represents an in-progress Node.Switch. This struct is stack-allocated. @@ -103,15 +104,22 @@ const Scope = struct { } fn complete(self: *Block, c: *Context) !Node { - // We reserve 1 extra statement if the parent is a Loop. This is in case of - // do while, we want to put `if (cond) break;` at the end. - const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .loop); - var stmts = try c.arena.alloc(Node, alloc_len); - stmts.len = self.statements.items.len; - mem.copy(Node, stmts, self.statements.items); + if (self.base.parent.?.id == .do_loop) { + // We reserve 1 extra statement if the parent is a do_loop. This is in case of + // do while, we want to put `if (cond) break;` at the end. + const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .do_loop); + var stmts = try c.arena.alloc(Node, alloc_len); + stmts.len = self.statements.items.len; + mem.copy(Node, stmts, self.statements.items); + return Tag.block.create(c.arena, .{ + .label = self.label, + .stmts = stmts, + }); + } + if (self.statements.items.len == 0) return Tag.empty_block.init(); return Tag.block.create(c.arena, .{ .label = self.label, - .stmts = stmts, + .stmts = try c.arena.dupe(Node, self.statements.items), }); } @@ -222,7 +230,7 @@ const Scope = struct { return switch (scope.id) { .root => return name, .block => @fieldParentPtr(Block, "base", scope).getAlias(name), - .@"switch", .loop, .condition => scope.parent.?.getAlias(name), + .@"switch", .loop, .do_loop, .condition => scope.parent.?.getAlias(name), }; } @@ -230,7 +238,7 @@ const Scope = struct { return switch (scope.id) { .root => @fieldParentPtr(Root, "base", scope).contains(name), .block => @fieldParentPtr(Block, "base", scope).contains(name), - .@"switch", .loop, .condition => scope.parent.?.contains(name), + .@"switch", .loop, .do_loop, .condition => scope.parent.?.contains(name), }; } @@ -240,7 +248,7 @@ const Scope = struct { switch (scope.id) { .root => unreachable, .@"switch" => return scope, - .loop => return scope, + .loop, .do_loop => return scope, else => scope = scope.parent.?, } } @@ -2063,7 +2071,7 @@ fn transDoWhileLoop( ) TransError!Node { var loop_scope = Scope{ .parent = scope, - .id = .loop, + .id = .do_loop, }; // if (!cond) break; @@ -2075,7 +2083,14 @@ fn transDoWhileLoop( }; defer cond_scope.deinit(); const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used); - const if_not_break = try Tag.if_not_break.create(c.arena, cond); + const if_not_break = switch (cond.tag()) { + .false_literal => try Tag.@"break".create(c.arena, null), + .true_literal => { + const body_node = try transStmt(c, scope, stmt.getBody(), .unused); + return Tag.while_true.create(c.arena, body_node); + }, + else => try Tag.if_not_break.create(c.arena, cond), + }; const body_node = if (stmt.getBody().getStmtClass() == .CompoundStmtClass) blk: { // there's already a block in C, so we'll append our condition to it. @@ -4099,7 +4114,7 @@ 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)) |some| some.data.lhs else try Tag.typeof.create(c.arena, typeof_arg); diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 2c3b9f29fe..d108a2852e 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -848,8 +848,8 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { break :blk try c.addIdentifier(some); } else 0; return c.addNode(.{ - .tag = .identifier, - .main_token = try c.addToken(.keyword_break, "break"), + .tag = .@"break", + .main_token = tok, .data = .{ .lhs = break_label, .rhs = 0, @@ -864,8 +864,8 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { break :blk try c.addIdentifier(some); } else 0; return c.addNode(.{ - .tag = .identifier, - .main_token = try c.addToken(.keyword_break, "break"), + .tag = .@"break", + .main_token = tok, .data = .{ .lhs = break_label, .rhs = try renderNode(c, payload.val), @@ -1183,7 +1183,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const l_brace = try c.addToken(.l_brace, "{"); const stmt = try renderNode(c, payload); - _ = try c.addToken(.semicolon, ";"); + try addSemicolonIfNeeded(c, payload); _ = try c.addToken(.r_brace, "}"); return c.addNode(.{ @@ -1207,11 +1207,8 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { defer stmts.deinit(); for (payload.stmts) |stmt| { const res = try renderNode(c, stmt); - switch (stmt.tag()) { - .warning => continue, - .var_decl, .var_simple => {}, - else => _ = try c.addToken(.semicolon, ";"), - } + if (res == 0) continue; + try addSemicolonIfNeeded(c, stmt); try stmts.append(res); } const span = try c.listToSpan(stmts.items); @@ -1245,6 +1242,194 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, }); }, + .@"while" => { + const payload = node.castTag(.@"while").?.data; + const while_tok = try c.addToken(.keyword_while, "while"); + _ = try c.addToken(.l_paren, "("); + const cond = try renderNode(c, payload.cond); + _ = try c.addToken(.r_paren, ")"); + + const cont_expr = if (payload.cont_expr) |some| blk: { + _ = try c.addToken(.colon, ":"); + _ = try c.addToken(.l_paren, "("); + const res = try renderNode(c, some); + _ = try c.addToken(.r_paren, ")"); + break :blk res; + } else 0; + const body = try renderNode(c, payload.body); + + if (cont_expr == 0) { + return c.addNode(.{ + .tag = .while_simple, + .main_token = while_tok, + .data = .{ + .lhs = cond, + .rhs = body, + }, + }); + } else { + return c.addNode(.{ + .tag = .while_cont, + .main_token = while_tok, + .data = .{ + .lhs = cond, + .rhs = try c.addExtra(std.zig.ast.Node.WhileCont{ + .cont_expr = cont_expr, + .then_expr = body, + }), + }, + }); + } + }, + .while_true => { + const payload = node.castTag(.while_true).?.data; + const while_tok = try c.addToken(.keyword_while, "while"); + _ = try c.addToken(.l_paren, "("); + const cond = try c.addNode(.{ + .tag = .true_literal, + .main_token = try c.addToken(.keyword_true, "true"), + .data = undefined, + }); + _ = try c.addToken(.r_paren, ")"); + const body = try renderNode(c, payload); + + return c.addNode(.{ + .tag = .while_simple, + .main_token = while_tok, + .data = .{ + .lhs = cond, + .rhs = body, + }, + }); + }, + .@"if" => { + const payload = node.castTag(.@"if").?.data; + const if_tok = try c.addToken(.keyword_if, "if"); + _ = try c.addToken(.l_paren, "("); + const cond = try renderNode(c, payload.cond); + _ = try c.addToken(.r_paren, ")"); + + const then_expr = try renderNode(c, payload.then); + const else_node = payload.@"else" orelse return c.addNode(.{ + .tag = .if_simple, + .main_token = if_tok, + .data = .{ + .lhs = cond, + .rhs = then_expr, + }, + }); + _ = try c.addToken(.keyword_else, "else"); + const else_expr = try renderNode(c, else_node); + + return c.addNode(.{ + .tag = .@"if", + .main_token = if_tok, + .data = .{ + .lhs = cond, + .rhs = try c.addExtra(std.zig.ast.Node.If{ + .then_expr = then_expr, + .else_expr = else_expr, + }), + }, + }); + }, + .if_not_break => { + const payload = node.castTag(.if_not_break).?.data; + const if_tok = try c.addToken(.keyword_if, "if"); + _ = try c.addToken(.l_paren, "("); + const cond = try c.addNode(.{ + .tag = .bool_not, + .main_token = try c.addToken(.bang, "!"), + .data = .{ + .lhs = try renderNodeGrouped(c, payload), + .rhs = undefined, + }, + }); + _ = try c.addToken(.r_paren, ")"); + const then_expr = try c.addNode(.{ + .tag = .@"break", + .main_token = try c.addToken(.keyword_break, "break"), + .data = .{ + .lhs = 0, + .rhs = 0, + }, + }); + + return c.addNode(.{ + .tag = .if_simple, + .main_token = if_tok, + .data = .{ + .lhs = cond, + .rhs = then_expr, + }, + }); + }, + .@"switch" => { + const payload = node.castTag(.@"switch").?.data; + const switch_tok = try c.addToken(.keyword_switch, "switch"); + _ = try c.addToken(.l_paren, "("); + const cond = try renderNode(c, payload.cond); + _ = try c.addToken(.r_paren, ")"); + + _ = try c.addToken(.l_brace, "{"); + var cases = try c.gpa.alloc(NodeIndex, payload.cases.len); + defer c.gpa.free(cases); + for (payload.cases) |case, i| { + if (i != 0) _ = try c.addToken(.comma, ","); + cases[i] = try renderNode(c, case); + } + const span = try c.listToSpan(cases); + _ = try c.addToken(.r_brace, "}"); + return c.addNode(.{ + .tag = .@"switch", + .main_token = switch_tok, + .data = .{ + .lhs = cond, + .rhs = try c.addExtra(NodeSubRange{ + .start = span.start, + .end = span.end, + }), + }, + }); + }, + .switch_else => { + const payload = node.castTag(.switch_else).?.data; + _ = try c.addToken(.keyword_else, "else"); + return c.addNode(.{ + .tag = .switch_case_one, + .main_token = try c.addToken(.equal_angle_bracket_right, "=>"), + .data = .{ + .lhs = 0, + .rhs = try renderNode(c, payload), + }, + }); + }, + .switch_prong => { + const payload = node.castTag(.switch_prong).?.data; + const item = try renderNode(c, payload.lhs); + return c.addNode(.{ + .tag = .switch_case_one, + .main_token = try c.addToken(.equal_angle_bracket_right, "=>"), + .data = .{ + .lhs = item, + .rhs = try renderNode(c, payload.rhs), + }, + }); + }, + .opaque_literal => { + const opaque_tok = try c.addToken(.keyword_opaque, "opaque"); + _ = try c.addToken(.l_brace, "{"); + _ = try c.addToken(.r_brace, "}"); + + return c.addNode(.{ + .tag = .container_decl_two, + .main_token = opaque_tok, + .data = .{ + .lhs = 0, + .rhs = 0, + }, + }); + }, else => return c.addNode(.{ .tag = .identifier, .main_token = try c.addTokenFmt(.identifier, "@\"TODO {}\"", .{node.tag()}), @@ -1256,6 +1441,28 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { } } +fn addSemicolonIfNeeded(c: *Context, node: Node) !void { + switch (node.tag()) { + .warning => unreachable, + .var_decl, .var_simple, .block, .empty_block, .@"switch" => {}, + .while_true => { + const payload = node.castTag(.while_true).?.data; + return addSemicolonIfNeeded(c, payload); + }, + .@"while" => { + const payload = node.castTag(.@"while").?.data; + return addSemicolonIfNeeded(c, payload.body); + }, + .@"if" => { + const payload = node.castTag(.@"if").?.data; + if (payload.@"else") |some| + return addSemicolonIfNeeded(c, some); + return addSemicolonIfNeeded(c, payload.then); + }, + else => _ = try c.addToken(.semicolon, ";"), + } +} + fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { switch (node.tag()) { .null_literal, @@ -1303,19 +1510,19 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .optional_type, .c_pointer, .single_pointer, + .unwrap, + .deref, + .address_of, + .not, + .negate, + .negate_wrap, + .bit_not, => { // no grouping needed return renderNode(c, node); }, - .negate, - .negate_wrap, - .bit_not, .opaque_literal, - .not, - .address_of, - .unwrap, - .deref, .empty_array, .block_single, .bool_to_int, @@ -1407,13 +1614,11 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { fn renderPrefixOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex { const payload = @fieldParentPtr(Payload.UnOp, "base", node.ptr_otherwise).data; - const tok = try c.addToken(tok_tag, bytes); - const operand = try renderNodeGrouped(c, payload); return c.addNode(.{ .tag = tag, - .main_token = tok, + .main_token = try c.addToken(tok_tag, bytes), .data = .{ - .lhs = operand, + .lhs = try renderNodeGrouped(c, payload), .rhs = undefined, }, }); @@ -1508,7 +1713,7 @@ fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex { .main_token = lparen, .data = .{ .lhs = lhs, - .rhs = try c.addExtra(std.zig.ast.Node.SubRange{ + .rhs = try c.addExtra(NodeSubRange{ .start = span.start, .end = span.end, }), @@ -1728,7 +1933,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { .tag = .fn_proto_multi, .main_token = fn_token, .data = .{ - .lhs = try c.addExtra(std.zig.ast.Node.SubRange{ + .lhs = try c.addExtra(NodeSubRange{ .start = span.start, .end = span.end, }),