Implement 'orelse'

This commit is contained in:
David Gonzalez Martin 2024-06-06 20:47:20 -06:00
parent 02de0f1eb3
commit ef662415d9
3 changed files with 116 additions and 49 deletions

View File

@ -1,5 +1,3 @@
assert
orelse
size size
trailing zeroes trailing zeroes
leading zeroes leading zeroes

View File

@ -176,7 +176,7 @@ const Parser = struct{
fn skip_space(parser: *Parser, file: []const u8) void { fn skip_space(parser: *Parser, file: []const u8) void {
const original_i = parser.i; const original_i = parser.i;
if (!is_space(file[original_i])) return; if (original_i == file.len or !is_space(file[original_i])) return;
while (parser.i < file.len) : (parser.i += 1) { while (parser.i < file.len) : (parser.i += 1) {
const ch = file[parser.i]; const ch = file[parser.i];
@ -859,6 +859,8 @@ const Parser = struct{
compare_unsigned_less_equal, compare_unsigned_less_equal,
compare_signed_less, compare_signed_less,
compare_signed_less_equal, compare_signed_less_equal,
@"orelse",
}; };
fn parse_constant_expression(parser: *Parser, thread: *Thread, file: *File, maybe_type: ?*Type) *Value { fn parse_constant_expression(parser: *Parser, thread: *Thread, file: *File, maybe_type: ?*Type) *Value {
@ -971,10 +973,61 @@ const Parser = struct{
previous_value = &i.instruction.value; previous_value = &i.instruction.value;
}, },
.assign, .add_assign, .sub_assign, .mul_assign, .udiv_assign, .sdiv_assign, .and_assign, .or_assign, .xor_assign, .shift_left_assign, .logical_shift_right_assign, .arithmetic_shift_right_assign => unreachable, .assign, .add_assign, .sub_assign, .mul_assign, .udiv_assign, .sdiv_assign, .and_assign, .or_assign, .xor_assign, .shift_left_assign, .logical_shift_right_assign, .arithmetic_shift_right_assign => unreachable,
.@"orelse" => {
const orelse_type = ty orelse unreachable;
const condition = emit_condition(analyzer, thread, previous_value);
const true_block = create_basic_block(thread);
const false_block = create_basic_block(thread);
const phi_block = create_basic_block(thread);
_ = emit_branch(analyzer, thread, condition, true_block, false_block);
const phi = thread.phis.append(.{
.instruction = .{
.id = .phi,
.value = .{
.sema = .{
.id = .instruction,
.thread = thread.get_index(),
.resolved = true,
},
},
},
.type = orelse_type,
});
_ = phi.nodes.append(.{
.value = previous_value,
.basic_block = true_block,
});
_ = phi.nodes.append(.{
.value = current_value,
.basic_block = false_block,
});
analyzer.current_basic_block = true_block;
_ = emit_jump(analyzer, thread, phi_block);
analyzer.current_basic_block = false_block;
_ = emit_jump(analyzer, thread, phi_block);
analyzer.current_basic_block = phi_block;
_ = analyzer.current_basic_block.instructions.append(&phi.instruction);
previous_value = &phi.instruction.value;
},
} }
switch (src[parser.i]) { const original_index = parser.i;
const original = src[original_index];
switch (original) {
')', ';', ',' => return previous_value, ')', ';', ',' => return previous_value,
'o' => {
const identifier = parser.parse_raw_identifier(src);
if (byte_equal(identifier, "orelse")) {
current_operation = .@"orelse";
} else {
parser.i = original;
exit(1);
}
parser.skip_space(src);
},
'=' => { '=' => {
current_operation = .assign; current_operation = .assign;
parser.i += 1; parser.i += 1;
@ -1206,49 +1259,7 @@ const Parser = struct{
parser.skip_space(src); parser.skip_space(src);
const condition_type = condition.get_type(); return emit_condition(analyzer, thread, condition);
const compare = switch (condition_type.sema.id) {
.integer => int: {
const integer_ty = condition_type.get_payload(.integer);
if (integer_ty.bit_count == 1) {
break :int condition;
} else {
const zero = thread.constant_ints.append(.{
.value = .{
.sema = .{
.thread = thread.get_index(),
.resolved = true,
.id = .constant_int,
},
},
.n = 0,
.type = condition_type,
});
const compare = thread.integer_compares.append(.{
.instruction = .{
.value = .{
.sema = .{
.thread = thread.get_index(),
.resolved = true,
.id = .instruction,
},
},
.id = .integer_compare,
},
.left = condition,
.right = &zero.value,
.id = .not_zero,
});
_ = analyzer.current_basic_block.instructions.append(&compare.instruction);
break :int &compare.instruction.value;
}
},
else => |t| @panic(@tagName(t)),
};
return compare;
} }
fn parse_if_expression(parser: *Parser, analyzer: *Analyzer, thread: *Thread, file: *File) IfResult { fn parse_if_expression(parser: *Parser, analyzer: *Analyzer, thread: *Thread, file: *File) IfResult {
@ -1535,11 +1546,12 @@ const Type = struct {
}; };
const Keyword = enum{ const Keyword = enum{
@"break",
@"else", @"else",
@"for", @"for",
@"if", @"if",
@"loop", @"loop",
@"break", @"orelse",
}; };
const Intrinsic = enum{ const Intrinsic = enum{
@ -4661,6 +4673,52 @@ fn emit_branch(analyzer: *Analyzer, thread: *Thread, condition: *Value, taken: *
}; };
} }
fn emit_condition(analyzer: *Analyzer, thread: *Thread, condition: *Value) *Value {
const condition_type = condition.get_type();
const compare = switch (condition_type.sema.id) {
.integer => int: {
const integer_ty = condition_type.get_payload(.integer);
if (integer_ty.bit_count == 1) {
break :int condition;
} else {
const zero = thread.constant_ints.append(.{
.value = .{
.sema = .{
.thread = thread.get_index(),
.resolved = true,
.id = .constant_int,
},
},
.n = 0,
.type = condition_type,
});
const compare = thread.integer_compares.append(.{
.instruction = .{
.value = .{
.sema = .{
.thread = thread.get_index(),
.resolved = true,
.id = .instruction,
},
},
.id = .integer_compare,
},
.left = condition,
.right = &zero.value,
.id = .not_zero,
});
_ = analyzer.current_basic_block.instructions.append(&compare.instruction);
break :int &compare.instruction.value;
}
},
else => |t| @panic(@tagName(t)),
};
return compare;
}
pub const LLVM = struct { pub const LLVM = struct {
const bindings = @import("backend/llvm_bindings.zig"); const bindings = @import("backend/llvm_bindings.zig");
pub const x86_64 = struct { pub const x86_64 = struct {
@ -5853,3 +5911,5 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, return_
}, },
} }
} }

View File

@ -0,0 +1,9 @@
fn[cc(.c)] main[export]() s32 {
>a: s32 = 5;
>b: s32 = a orelse 4;
#assert(b == a);
>c: s32 = 0;
>d: s32 = c orelse a;
#assert(d == a);
return 0;
}