mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
translate-c: render control flow
This commit is contained in:
parent
c4dfabf4dc
commit
62162a0717
@ -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);
|
||||
|
||||
@ -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,
|
||||
}),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user