Merge pull request #211 from birth-software/break

Implement 'break' and 'continue
This commit is contained in:
David 2024-06-06 13:51:11 -06:00 committed by GitHub
commit d998376bc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 233 additions and 149 deletions

View File

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

View 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;
}

View 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;
}