translate-c: render control flow

This commit is contained in:
Veikka Tuominen 2021-02-14 16:32:31 +02:00
parent c4dfabf4dc
commit 62162a0717
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
2 changed files with 256 additions and 36 deletions

View File

@ -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);

View File

@ -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,
}),