Implement continue expression

This commit is contained in:
David Gonzalez Martin 2024-04-18 10:13:44 -06:00
parent 26a21c828e
commit 213082f532
5 changed files with 180 additions and 52 deletions

View File

@ -3073,7 +3073,7 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, return_
write(.panic, "\nPANIC: ") catch {}; write(.panic, "\nPANIC: ") catch {};
write(.panic, message) catch {}; write(.panic, message) catch {};
write(.panic, "\n") catch {}; write(.panic, "\n") catch {};
if (@import("builtin").os.tag != .windows) @breakpoint(); @breakpoint();
std.posix.abort(); std.posix.abort();
}, },
} }
@ -4390,6 +4390,7 @@ pub const Builder = struct {
current_basic_block: BasicBlock.Index = .null, current_basic_block: BasicBlock.Index = .null,
exit_blocks: UnpinnedArray(BasicBlock.Index) = .{}, exit_blocks: UnpinnedArray(BasicBlock.Index) = .{},
loop_exit_block: BasicBlock.Index = .null, loop_exit_block: BasicBlock.Index = .null,
loop_header_block: BasicBlock.Index = .null,
return_phi: Instruction.Index = .null, return_phi: Instruction.Index = .null,
return_block: BasicBlock.Index = .null, return_block: BasicBlock.Index = .null,
last_check_point: struct { last_check_point: struct {
@ -13516,9 +13517,12 @@ pub const Builder = struct {
assert(statement_node.left != .null); assert(statement_node.left != .null);
assert(statement_node.right != .null); assert(statement_node.right != .null);
const loop_header_block = try builder.newBasicBlock(unit, context); const old_loop_header_block = builder.loop_header_block;
try builder.jump(unit, context, loop_header_block); defer builder.loop_header_block = old_loop_header_block;
builder.current_basic_block = loop_header_block;
builder.loop_header_block = try builder.newBasicBlock(unit, context);
try builder.jump(unit, context, builder.loop_header_block);
builder.current_basic_block = builder.loop_header_block;
const condition = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .bool }, statement_node.left, .right); const condition = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .bool }, statement_node.left, .right);
const body_block = try builder.newBasicBlock(unit, context); const body_block = try builder.newBasicBlock(unit, context);
@ -13530,33 +13534,11 @@ pub const Builder = struct {
switch (condition.value) { switch (condition.value) {
.runtime => |condition_instruction| { .runtime => |condition_instruction| {
try builder.branch(unit, context, condition_instruction, body_block, exit_block); try builder.branch(unit, context, condition_instruction, body_block, exit_block);
builder.current_basic_block = body_block;
builder.loop_exit_block = exit_block;
const body_value = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .void }, statement_node.right, .right);
_ = body_value; // autofix
try builder.jump(unit, context, loop_header_block);
builder.current_basic_block = exit_block;
}, },
.@"comptime" => |ct| switch (ct) { .@"comptime" => |ct| switch (ct) {
.bool => |boolean| switch (boolean) { .bool => |boolean| switch (boolean) {
true => { true => {
try builder.jump(unit, context, body_block); try builder.jump(unit, context, body_block);
builder.current_basic_block = body_block;
builder.loop_exit_block = exit_block;
const body_value = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .void }, statement_node.right, .right);
switch (unit.types.get(body_value.type).*) {
.void => {
try builder.jump(unit, context, loop_header_block);
},
.noreturn => {},
else => |t| @panic(@tagName(t)),
}
builder.current_basic_block = exit_block;
}, },
false => unreachable, false => unreachable,
}, },
@ -13564,6 +13546,20 @@ pub const Builder = struct {
}, },
else => unreachable, else => unreachable,
} }
builder.current_basic_block = body_block;
builder.loop_exit_block = exit_block;
const body_value = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .void }, statement_node.right, .right);
switch (unit.types.get(body_value.type).*) {
.void => {
try builder.jump(unit, context, builder.loop_header_block);
},
.noreturn => {},
else => |t| @panic(@tagName(t)),
}
builder.current_basic_block = exit_block;
}, },
.for_loop => { .for_loop => {
assert(statement_node.left != .null); assert(statement_node.left != .null);
@ -13676,9 +13672,12 @@ pub const Builder = struct {
}, },
}; };
const loop_header = try builder.newBasicBlock(unit, context); const old_loop_header_block = builder.loop_header_block;
try builder.jump(unit, context, loop_header); defer builder.loop_header_block = old_loop_header_block;
builder.current_basic_block = loop_header;
builder.loop_header_block = try builder.newBasicBlock(unit, context);
try builder.jump(unit, context, builder.loop_header_block);
builder.current_basic_block = builder.loop_header_block;
const pointer_to_usize = try unit.getPointerType(context, .{ const pointer_to_usize = try unit.getPointerType(context, .{
.type = Type.usize, .type = Type.usize,
@ -13874,13 +13873,16 @@ pub const Builder = struct {
try builder.appendInstruction(unit, context, increment_store); try builder.appendInstruction(unit, context, increment_store);
try builder.jump(unit, context, loop_header); try builder.jump(unit, context, builder.loop_header_block);
builder.current_basic_block = exit_block; builder.current_basic_block = exit_block;
}, },
.break_expression => { .break_expression => {
try builder.jump(unit, context, builder.loop_exit_block); try builder.jump(unit, context, builder.loop_exit_block);
}, },
.continue_expression => {
try builder.jump(unit, context, builder.loop_header_block);
},
.@"if" => { .@"if" => {
assert(statement_node.left != .null); assert(statement_node.left != .null);
assert(statement_node.right != .null); assert(statement_node.right != .null);
@ -14440,9 +14442,42 @@ pub const Builder = struct {
.node_list => { .node_list => {
const condition_nodes = unit.getNodeListFromNode(condition_node); const condition_nodes = unit.getNodeListFromNode(condition_node);
try conditions.ensure_capacity(context.my_allocator, @intCast(condition_nodes.len)); try conditions.ensure_capacity(context.my_allocator, @intCast(condition_nodes.len));
for (condition_nodes) |condition_node_index| { for (condition_nodes) |condition_node_index| {
const condition = try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = condition_type }, .{}, condition_node_index, null, .right, &.{}, null, &.{}); const cn = unit.getNode(condition_node_index);
conditions.append_with_capacity(condition); switch (cn.id) {
.range => {
const left = try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = condition_type }, .{}, cn.left, null, .right, &.{}, null, &.{});
const right = try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = condition_type }, .{}, cn.right, null, .right, &.{}, null, &.{});
switch (condition_type) {
.u8 => {
var left_ch: u8 = switch (left) {
.constant_int => |ci| @intCast(ci.value),
else => |t| @panic(@tagName(t)),
};
const right_ch: u8 = switch (right) {
.constant_int => |ci| @intCast(ci.value),
else => |t| @panic(@tagName(t)),
};
if (left_ch < right_ch) {
while (left_ch <= right_ch) : (left_ch += 1) {
try conditions.append(context.my_allocator, .{
.constant_int = .{
.value = left_ch,
},
});
}
} else {
unreachable;
}
},
else => unreachable,
}
},
else => try conditions.append(context.my_allocator, try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = condition_type }, .{}, condition_node_index, null, .right, &.{}, null, &.{})),
}
} }
}, },
else => { else => {
@ -16709,6 +16744,7 @@ pub const FixedKeyword = enum {
Self, Self,
any, any,
type, type,
@"continue"
}; };
pub const Descriptor = struct { pub const Descriptor = struct {
@ -16853,6 +16889,8 @@ pub const Token = struct {
operator_at, operator_at,
operator_comma, operator_comma,
operator_dot, operator_dot,
operator_double_dot,
operator_triple_dot,
operator_colon, operator_colon,
operator_bang, operator_bang,
operator_optional, operator_optional,
@ -16925,6 +16963,8 @@ pub const Token = struct {
fixed_keyword_Self, fixed_keyword_Self,
fixed_keyword_any, fixed_keyword_any,
fixed_keyword_type, fixed_keyword_type,
fixed_keyword_continue,
unused1, unused1,
unused2, unused2,
unused3, unused3,

View File

@ -259,7 +259,18 @@ pub fn analyze(allocator: *MyAllocator, text: []const u8, token_buffer: *Token.B
}, },
'.' => blk: { '.' => blk: {
index += 1; index += 1;
if (text[index] == '.') {
index += 1;
if (text[index] == '.') {
index += 1;
break :blk .operator_triple_dot;
} else {
break :blk .operator_double_dot;
}
} else {
break :blk .operator_dot; break :blk .operator_dot;
}
}, },
':' => blk: { ':' => blk: {
index += 1; index += 1;

View File

@ -176,6 +176,7 @@ pub const Node = struct {
anonymous_empty_literal, anonymous_empty_literal,
empty_container_literal_guess, empty_container_literal_guess,
break_expression, break_expression,
continue_expression,
character_literal, character_literal,
function_attribute_naked, function_attribute_naked,
function_attribute_cc, function_attribute_cc,
@ -601,13 +602,30 @@ const Analyzer = struct {
else => blk: { else => blk: {
var array_list = UnpinnedArray(Node.Index){}; var array_list = UnpinnedArray(Node.Index){};
while (true) { while (true) {
const switch_case_node = try analyzer.expression(); const token = analyzer.token_i;
const left = try analyzer.expression();
const switch_case_node = switch (analyzer.peekToken()) {
.operator_triple_dot => try analyzer.addNode(.{
.id = .range,
.token = b: {
analyzer.consumeToken();
break :b token;
},
.left = left,
.right = try analyzer.expression(),
}),
else => left,
};
try array_list.append(analyzer.my_allocator, switch_case_node); try array_list.append(analyzer.my_allocator, switch_case_node);
switch (analyzer.peekToken()) { switch (analyzer.peekToken()) {
.operator_comma => analyzer.consumeToken(), .operator_comma => analyzer.consumeToken(),
.operator_switch_case => break, .operator_switch_case => break,
else => {}, else => {},
} }
} }
break :blk switch (array_list.length) { break :blk switch (array_list.length) {
@ -726,10 +744,8 @@ const Analyzer = struct {
const first = try analyzer.expression(); const first = try analyzer.expression();
const node_index = switch (analyzer.peekToken()) { const node_index = switch (analyzer.peekToken()) {
.operator_dot => switch (analyzer.peekTokenAhead(1)) { .operator_double_dot => blk: {
.operator_dot => blk: { analyzer.consumeToken();
analyzer.consumeTokens(2);
break :blk try analyzer.addNode(.{ break :blk try analyzer.addNode(.{
.id = .range, .id = .range,
.token = expression_token, .token = expression_token,
@ -740,8 +756,6 @@ const Analyzer = struct {
}, },
}); });
}, },
else => |t| @panic(@tagName(t)),
},
.operator_right_parenthesis, .operator_right_parenthesis,
.operator_comma, .operator_comma,
=> first, => first,
@ -825,6 +839,17 @@ const Analyzer = struct {
return for_node; return for_node;
} }
fn continueExpression(analyzer: *Analyzer) !Node.Index {
const t = try analyzer.expectToken(.fixed_keyword_continue);
const node_index = try analyzer.addNode(.{
.id = .continue_expression,
.token = t,
.left = Node.Index.null,
.right = Node.Index.null,
});
return node_index;
}
fn breakExpression(analyzer: *Analyzer) !Node.Index { fn breakExpression(analyzer: *Analyzer) !Node.Index {
const t = try analyzer.expectToken(.fixed_keyword_break); const t = try analyzer.expectToken(.fixed_keyword_break);
const node_index = try analyzer.addNode(.{ const node_index = try analyzer.addNode(.{
@ -1139,6 +1164,8 @@ const Analyzer = struct {
.operator_div_assign, .operator_div_assign,
.operator_mod_assign, .operator_mod_assign,
.operator_dot, .operator_dot,
.operator_double_dot,
.operator_triple_dot,
.operator_switch_case, .operator_switch_case,
.fixed_keyword_const, .fixed_keyword_const,
.fixed_keyword_var, .fixed_keyword_var,
@ -1148,6 +1175,7 @@ const Analyzer = struct {
.identifier, .identifier,
.discard, .discard,
.fixed_keyword_test, .fixed_keyword_test,
.fixed_keyword_break,
=> break, => break,
.operator_compare_equal => .compare_equal, .operator_compare_equal => .compare_equal,
.operator_compare_not_equal => .compare_not_equal, .operator_compare_not_equal => .compare_not_equal,
@ -1292,6 +1320,7 @@ const Analyzer = struct {
.right = Node.Index.null, .right = Node.Index.null,
}), }),
.fixed_keyword_break => try analyzer.breakExpression(), .fixed_keyword_break => try analyzer.breakExpression(),
.fixed_keyword_continue => try analyzer.continueExpression(),
// todo:? // todo:?
.operator_left_brace => try analyzer.block(), .operator_left_brace => try analyzer.block(),
.fixed_keyword_if => try analyzer.ifExpression(), .fixed_keyword_if => try analyzer.ifExpression(),
@ -2142,8 +2171,8 @@ const Analyzer = struct {
analyzer.consumeToken(); analyzer.consumeToken();
const index_expression = try analyzer.expression(); const index_expression = try analyzer.expression();
if (analyzer.peekToken() == .operator_dot and analyzer.peekTokenAhead(1) == .operator_dot) { if (analyzer.peekToken() == .operator_double_dot) {
analyzer.consumeTokens(2); analyzer.consumeToken();
const range_end_expression = switch (analyzer.peekToken()) { const range_end_expression = switch (analyzer.peekToken()) {
.operator_right_bracket => Node.Index.null, .operator_right_bracket => Node.Index.null,
else => try analyzer.expression(), else => try analyzer.expression(),
@ -2206,7 +2235,6 @@ const Analyzer = struct {
.right = Node.Index.null, .right = Node.Index.null,
}), }),
}), }),
.operator_dot => Node.Index.null,
.operator_ampersand => try analyzer.addNode(.{ .operator_ampersand => try analyzer.addNode(.{
.id = .address_of, .id = .address_of,
.token = blk: { .token = blk: {

View File

@ -10,6 +10,11 @@ const ArgumentProcessingError = error{
no_arguments, no_arguments,
}; };
const Token = struct {
const Id = enum(u8) {
};
};
const lex = fn (arena: &Arena, bytes: []const u8) *!void { const lex = fn (arena: &Arena, bytes: []const u8) *!void {
if (bytes.length >= 0xffffffff) { if (bytes.length >= 0xffffffff) {
unreachable; unreachable;
@ -26,11 +31,33 @@ const lex = fn (arena: &Arena, bytes: []const u8) *!void {
var index: u32 = 0; var index: u32 = 0;
while (index < length) { while (index < length) {
const byte = bytes[index]; const byte = bytes[index];
line_offsets[line_count] = index;
line_count += #cast(byte == '\n'); line_count += #cast(byte == '\n');
line_offsets[line_count] = index;
index += 1; index += 1;
} }
index = 0;
//while (index < length) {
// const start_index = index;
// const start_character = bytes[index];
// switch (start_character) {
// 'a'...'z', 'A'...'Z', '_' => {
// while (true) {
// const ch = bytes[index];
// if ((ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z') or ch == '_' or (ch >= '0' and ch <= '9')) {
// index += 1;
// continue;
// }
// break;
// }
// },
// else => unreachable,
// }
//}
} }
const get_argument = fn (real_argument: []const u8, wanted_argument: []const u8, command_arguments: []const [&:0]const u8, i_ptr: &usize) ?[]const u8 { const get_argument = fn (real_argument: []const u8, wanted_argument: []const u8, command_arguments: []const [&:0]const u8, i_ptr: &usize) ?[]const u8 {

View File

@ -0,0 +1,22 @@
const std = #import("std");
const expect = std.testing.expect;
const main = fn () *!void {
var i: u32 = 0;
var b: bool = false;
while (i < 10) {
if (b) {
break;
}
i += 1;
if (i == 5) {
break;
}
}
try expect(i == 5);
}