Merge pull request #211 from birth-software/break
Implement 'break' and 'continue
This commit is contained in:
commit
d998376bc0
@ -195,12 +195,10 @@ const Parser = struct{
|
|||||||
|
|
||||||
fn parse_identifier(parser: *Parser, thread: *Thread, file: []const u8) u32 {
|
fn parse_identifier(parser: *Parser, thread: *Thread, file: []const u8) u32 {
|
||||||
const identifier = parser.parse_raw_identifier(file);
|
const identifier = parser.parse_raw_identifier(file);
|
||||||
if (identifier[0] != '"') {
|
|
||||||
const keyword = parse_keyword(identifier);
|
const keyword = parse_keyword(identifier);
|
||||||
if (keyword != ~(@as(u32, 0))) {
|
if (keyword != ~(@as(u32, 0))) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
const hash = intern_identifier(&thread.identifiers, identifier);
|
const hash = intern_identifier(&thread.identifiers, identifier);
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
@ -1405,18 +1403,22 @@ const Keyword = enum{
|
|||||||
@"else",
|
@"else",
|
||||||
@"for",
|
@"for",
|
||||||
@"if",
|
@"if",
|
||||||
|
@"break",
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_keyword(identifier: []const u8) u32 {
|
fn parse_keyword(identifier: []const u8) u32 {
|
||||||
|
assert(identifier.len > 0);
|
||||||
|
if (identifier[0] != '"') {
|
||||||
inline for (@typeInfo(Keyword).Enum.fields) |keyword| {
|
inline for (@typeInfo(Keyword).Enum.fields) |keyword| {
|
||||||
if (byte_equal(identifier, keyword.name)) {
|
if (byte_equal(identifier, keyword.name)) {
|
||||||
return keyword.value;
|
return keyword.value;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return ~@as(u32, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ~@as(u32, 0);
|
||||||
|
}
|
||||||
|
|
||||||
const Scope = struct {
|
const Scope = struct {
|
||||||
declarations: PinnedHashMap(u32, *Declaration) = .{},
|
declarations: PinnedHashMap(u32, *Declaration) = .{},
|
||||||
parent: ?*Scope,
|
parent: ?*Scope,
|
||||||
@ -2656,10 +2658,16 @@ const Analyzer = struct{
|
|||||||
current_function: *Function,
|
current_function: *Function,
|
||||||
current_scope: *Scope,
|
current_scope: *Scope,
|
||||||
exit_blocks: PinnedArray(*BasicBlock) = .{},
|
exit_blocks: PinnedArray(*BasicBlock) = .{},
|
||||||
|
loops: PinnedArray(LoopData) = .{},
|
||||||
return_block: ?*BasicBlock = null,
|
return_block: ?*BasicBlock = null,
|
||||||
return_phi: ?*Phi = null,
|
return_phi: ?*Phi = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const LoopData = struct {
|
||||||
|
break_block: *BasicBlock,
|
||||||
|
continue_block: *BasicBlock,
|
||||||
|
};
|
||||||
|
|
||||||
const brace_open = '{';
|
const brace_open = '{';
|
||||||
const brace_close = '}';
|
const brace_close = '}';
|
||||||
|
|
||||||
@ -3439,60 +3447,6 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (statement_start_ch) {
|
switch (statement_start_ch) {
|
||||||
'r' => {
|
|
||||||
const identifier = parser.parse_raw_identifier(src);
|
|
||||||
|
|
||||||
if (byte_equal(identifier, "return")) {
|
|
||||||
parser.skip_space(src);
|
|
||||||
|
|
||||||
if (function.declaration.return_type.sema.id != .unresolved) {
|
|
||||||
const return_type = function.declaration.return_type;
|
|
||||||
const return_value = parser.parse_expression(analyzer, thread, file, return_type, .right);
|
|
||||||
parser.expect_character(src, ';');
|
|
||||||
|
|
||||||
if (analyzer.return_block) |return_block| {
|
|
||||||
const return_phi = analyzer.return_phi.?;
|
|
||||||
_ = return_phi.nodes.append(.{
|
|
||||||
.value = return_value,
|
|
||||||
.basic_block = analyzer.current_basic_block,
|
|
||||||
});
|
|
||||||
assert(analyzer.current_basic_block != return_block);
|
|
||||||
|
|
||||||
_ = emit_jump(analyzer, thread, return_block);
|
|
||||||
} else if (analyzer.exit_blocks.length > 0) {
|
|
||||||
const return_phi = thread.phis.append(.{
|
|
||||||
.instruction = .{
|
|
||||||
.id = .phi,
|
|
||||||
.value = .{
|
|
||||||
.sema = .{
|
|
||||||
.id = .instruction,
|
|
||||||
.thread = thread.get_index(),
|
|
||||||
.resolved = true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.type = return_type,
|
|
||||||
});
|
|
||||||
analyzer.return_phi = return_phi;
|
|
||||||
const return_block = create_basic_block(thread);
|
|
||||||
analyzer.return_block = return_block;
|
|
||||||
_ = return_phi.nodes.append(.{
|
|
||||||
.value = return_value,
|
|
||||||
.basic_block = analyzer.current_basic_block,
|
|
||||||
});
|
|
||||||
|
|
||||||
_ = emit_jump(analyzer, thread, return_block);
|
|
||||||
} else {
|
|
||||||
build_return(thread, analyzer, return_value);
|
|
||||||
}
|
|
||||||
terminated = true;
|
|
||||||
} else {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parser.i = statement_start_ch_index;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Local variable
|
// Local variable
|
||||||
'>' => {
|
'>' => {
|
||||||
parser.i += 1;
|
parser.i += 1;
|
||||||
@ -3603,12 +3557,104 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser
|
|||||||
|
|
||||||
local_block.scope.declarations.put_no_clobber(local_name, &local_symbol.local_declaration.declaration);
|
local_block.scope.declarations.put_no_clobber(local_name, &local_symbol.local_declaration.declaration);
|
||||||
},
|
},
|
||||||
|
'b' => {
|
||||||
|
const identifier = parser.parse_raw_identifier(src);
|
||||||
|
const break_kw = "break";
|
||||||
|
if (byte_equal(identifier, break_kw)) {
|
||||||
|
parser.skip_space(src);
|
||||||
|
|
||||||
|
parser.expect_character(src, ';');
|
||||||
|
|
||||||
|
if (analyzer.loops.length == 0) {
|
||||||
|
exit_with_error("break when no loop");
|
||||||
|
}
|
||||||
|
|
||||||
|
const inner_loop = analyzer.loops.const_slice()[analyzer.loops.length - 1];
|
||||||
|
_ = emit_jump(analyzer, thread, inner_loop.break_block);
|
||||||
|
terminated = true;
|
||||||
|
} else {
|
||||||
|
parser.i = statement_start_ch_index;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'c' => {
|
||||||
|
const identifier = parser.parse_raw_identifier(src);
|
||||||
|
const continue_kw = "continue";
|
||||||
|
if (byte_equal(identifier, continue_kw)) {
|
||||||
|
parser.skip_space(src);
|
||||||
|
|
||||||
|
parser.expect_character(src, ';');
|
||||||
|
|
||||||
|
if (analyzer.loops.length == 0) {
|
||||||
|
exit_with_error("continue when no loop");
|
||||||
|
}
|
||||||
|
|
||||||
|
const inner_loop = analyzer.loops.const_slice()[analyzer.loops.length - 1];
|
||||||
|
_ = emit_jump(analyzer, thread, inner_loop.continue_block);
|
||||||
|
terminated = true;
|
||||||
|
} else {
|
||||||
|
parser.i = statement_start_ch_index;
|
||||||
|
}
|
||||||
|
},
|
||||||
'i' => {
|
'i' => {
|
||||||
if (src[parser.i + 1] == 'f') {
|
if (src[parser.i + 1] == 'f') {
|
||||||
const if_block = parser.parse_if_expression(analyzer, thread, file);
|
const if_block = parser.parse_if_expression(analyzer, thread, file);
|
||||||
terminated = terminated or if_block.terminated;
|
terminated = terminated or if_block.terminated;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'r' => {
|
||||||
|
const identifier = parser.parse_raw_identifier(src);
|
||||||
|
|
||||||
|
if (byte_equal(identifier, "return")) {
|
||||||
|
parser.skip_space(src);
|
||||||
|
|
||||||
|
if (function.declaration.return_type.sema.id != .unresolved) {
|
||||||
|
const return_type = function.declaration.return_type;
|
||||||
|
const return_value = parser.parse_expression(analyzer, thread, file, return_type, .right);
|
||||||
|
parser.expect_character(src, ';');
|
||||||
|
|
||||||
|
if (analyzer.return_block) |return_block| {
|
||||||
|
const return_phi = analyzer.return_phi.?;
|
||||||
|
_ = return_phi.nodes.append(.{
|
||||||
|
.value = return_value,
|
||||||
|
.basic_block = analyzer.current_basic_block,
|
||||||
|
});
|
||||||
|
assert(analyzer.current_basic_block != return_block);
|
||||||
|
|
||||||
|
_ = emit_jump(analyzer, thread, return_block);
|
||||||
|
} else if (analyzer.exit_blocks.length > 0) {
|
||||||
|
const return_phi = thread.phis.append(.{
|
||||||
|
.instruction = .{
|
||||||
|
.id = .phi,
|
||||||
|
.value = .{
|
||||||
|
.sema = .{
|
||||||
|
.id = .instruction,
|
||||||
|
.thread = thread.get_index(),
|
||||||
|
.resolved = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.type = return_type,
|
||||||
|
});
|
||||||
|
analyzer.return_phi = return_phi;
|
||||||
|
const return_block = create_basic_block(thread);
|
||||||
|
analyzer.return_block = return_block;
|
||||||
|
_ = return_phi.nodes.append(.{
|
||||||
|
.value = return_value,
|
||||||
|
.basic_block = analyzer.current_basic_block,
|
||||||
|
});
|
||||||
|
|
||||||
|
_ = emit_jump(analyzer, thread, return_block);
|
||||||
|
} else {
|
||||||
|
build_return(thread, analyzer, return_value);
|
||||||
|
}
|
||||||
|
terminated = true;
|
||||||
|
} else {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parser.i = statement_start_ch_index;
|
||||||
|
}
|
||||||
|
},
|
||||||
'w' => {
|
'w' => {
|
||||||
const identifier = parser.parse_raw_identifier(src);
|
const identifier = parser.parse_raw_identifier(src);
|
||||||
|
|
||||||
@ -3629,6 +3675,10 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser
|
|||||||
|
|
||||||
_ = emit_branch(analyzer, thread, condition, loop_body_block, loop_exit_block);
|
_ = emit_branch(analyzer, thread, condition, loop_body_block, loop_exit_block);
|
||||||
analyzer.current_basic_block = loop_body_block;
|
analyzer.current_basic_block = loop_body_block;
|
||||||
|
_ = analyzer.loops.append(.{
|
||||||
|
.continue_block = loop_header,
|
||||||
|
.break_block = loop_exit_block,
|
||||||
|
});
|
||||||
|
|
||||||
switch (src[parser.i]) {
|
switch (src[parser.i]) {
|
||||||
'{' => {
|
'{' => {
|
||||||
@ -3643,6 +3693,7 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
analyzer.current_basic_block = loop_exit_block;
|
analyzer.current_basic_block = loop_exit_block;
|
||||||
|
analyzer.loops.length -= 1;
|
||||||
} else {
|
} else {
|
||||||
parser.i = statement_start_ch_index;
|
parser.i = statement_start_ch_index;
|
||||||
}
|
}
|
||||||
@ -3652,7 +3703,16 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser
|
|||||||
|
|
||||||
if (statement_start_ch_index == parser.i) {
|
if (statement_start_ch_index == parser.i) {
|
||||||
if (is_identifier_char_start(statement_start_ch)) {
|
if (is_identifier_char_start(statement_start_ch)) {
|
||||||
const identifier = parser.parse_identifier(thread, src);
|
const raw_identifier = parser.parse_raw_identifier(src);
|
||||||
|
const keyword_index = parse_keyword(raw_identifier);
|
||||||
|
const is_keyword = keyword_index != ~@as(u32, 0);
|
||||||
|
if (is_keyword) {
|
||||||
|
const keyword: Keyword = @enumFromInt(keyword_index);
|
||||||
|
switch (keyword) {
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const identifier = intern_identifier(&thread.identifiers, raw_identifier);
|
||||||
parser.skip_space(src);
|
parser.skip_space(src);
|
||||||
|
|
||||||
const declaration_lookup = analyzer.current_scope.get_declaration(identifier) orelse unreachable;
|
const declaration_lookup = analyzer.current_scope.get_declaration(identifier) orelse unreachable;
|
||||||
@ -3744,6 +3804,7 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser
|
|||||||
},
|
},
|
||||||
else => @panic((src.ptr + parser.i)[0..1]),
|
else => @panic((src.ptr + parser.i)[0..1]),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
exit_with_error("Unrecognized statement initial char");
|
exit_with_error("Unrecognized statement initial char");
|
||||||
}
|
}
|
||||||
@ -5513,5 +5574,3 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, return_
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
12
retest/standalone/while_break/main.nat
Normal file
12
retest/standalone/while_break/main.nat
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
fn[cc(.c)] main[export]() s32 {
|
||||||
|
>i: s32 = 0;
|
||||||
|
>n: s32 = 5;
|
||||||
|
while (i < 10) {
|
||||||
|
i += 1;
|
||||||
|
if (i == n) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i - n;
|
||||||
|
}
|
13
retest/standalone/while_continue/main.nat
Normal file
13
retest/standalone/while_continue/main.nat
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
fn[cc(.c)] main[export]() s32 {
|
||||||
|
>i: s32 = 0;
|
||||||
|
>n: s32 = 0;
|
||||||
|
while (i < 10) {
|
||||||
|
i += 1;
|
||||||
|
if (n == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user