Merge pull request #151 from birth-software/continue
Implement continue expression
This commit is contained in:
commit
ba4bc3712a
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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: {
|
||||||
|
29
src/main.nat
29
src/main.nat
@ -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 {
|
||||||
|
22
test/standalone/continue/main.nat
Normal file
22
test/standalone/continue/main.nat
Normal 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);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user