Implement Control Flow Statements in Kym #37

Merged
kayomn merged 8 commits from kym-control-flow into main 2023-08-26 00:01:25 +02:00
4 changed files with 98 additions and 36 deletions
Showing only changes of commit bb1e689c5a - Show all commits

View File

@ -1,28 +1,15 @@
# Test comment. i = 0
pos = @vec3(10, 20, 0.3) while i < 5 do
coord = pos.xz @print("hello, world")
options = { i = i + 1
.title = "Afterglow", end
return {
.title = "Game",
.width = 1280, .width = 1280,
.height = 800, .height = 800,
.tick_rate = 60, .tick_rate = 60,
["foo"] = "bar",
[42] = "42",
} }
if pos.y > 0 do
options["foo"] = "rab"
options[42] = "24"
@print("Conditional hit!")
end
@print(options.title)
@print(options["foo"])
@print(options[42])
return options

View File

@ -203,21 +203,23 @@ pub const RuntimeEnv = struct {
.local_get => |local_get| { .local_get => |local_get| {
try chunk.opcodes.push_one(.{ try chunk.opcodes.push_one(.{
.push_local = self.resolve_local(local_get) orelse { .push_local = self.resolve_local(local_get.identifier) orelse {
return chunk.env.raise(error.OutOfMemory, "undefined local"); return chunk.env.raise(error.OutOfMemory, "undefined local");
}, },
}); });
}, },
.local_set => |local_set| { .local_set => |local_set| {
if (self.resolve_local(local_set)) |index| { try self.compile_expression(chunk, local_set.value_expression.*);
if (self.resolve_local(local_set.identifier)) |index| {
try chunk.opcodes.push_one(.{.local_set = index}); try chunk.opcodes.push_one(.{.local_set = index});
} else { } else {
if (self.local_identifiers_count == self.local_identifiers_buffer.len) { if (self.local_identifiers_count == self.local_identifiers_buffer.len) {
return chunk.env.raise(error.BadSyntax, "chunks may have a maximum of 255 locals"); return chunk.env.raise(error.BadSyntax, "chunks may have a maximum of 255 locals");
} }
self.local_identifiers_buffer[self.local_identifiers_count] = local_set; self.local_identifiers_buffer[self.local_identifiers_count] = local_set.identifier;
self.local_identifiers_count += 1; self.local_identifiers_count += 1;
} }
}, },
@ -268,11 +270,29 @@ pub const RuntimeEnv = struct {
} }
}, },
.@"while" => |@"while"| {
try self.compile_expression(chunk, @"while".condition_expression);
try chunk.opcodes.push_one(.{.jf = 0});
const origin_index = @as(u32, @intCast(chunk.opcodes.values.len - 1));
for (@"while".block_statements.values) |block_statement| {
try self.compile_statement(chunk, block_statement);
}
coral.debug.assert(chunk.opcodes.values.len < coral.math.max_int(@typeInfo(u32).Int));
chunk.opcodes.values[origin_index].jf = @intCast(chunk.opcodes.values.len - 1);
try self.compile_expression(chunk, @"while".condition_expression);
try chunk.opcodes.push_one(.{.jt = origin_index});
},
.@"if" => |@"if"| { .@"if" => |@"if"| {
try self.compile_expression(chunk, @"if".condition_expression); try self.compile_expression(chunk, @"if".condition_expression);
try chunk.opcodes.push_one(.{.jf = 0}); try chunk.opcodes.push_one(.{.jf = 0});
const jump_opcode_index = chunk.opcodes.values.len - 1; const origin_index = @as(u32, @intCast(chunk.opcodes.values.len - 1));
for (@"if".block_statements.values) |block_statement| { for (@"if".block_statements.values) |block_statement| {
try self.compile_statement(chunk, block_statement); try self.compile_statement(chunk, block_statement);
@ -280,7 +300,7 @@ pub const RuntimeEnv = struct {
coral.debug.assert(chunk.opcodes.values.len < coral.math.max_int(@typeInfo(u32).Int)); coral.debug.assert(chunk.opcodes.values.len < coral.math.max_int(@typeInfo(u32).Int));
chunk.opcodes.values[jump_opcode_index].jf = @intCast(chunk.opcodes.values.len - 1); chunk.opcodes.values[origin_index].jf = @intCast(chunk.opcodes.values.len - 1);
}, },
.expression => |expression| try self.compile_expression(chunk, expression), .expression => |expression| try self.compile_expression(chunk, expression),
@ -357,6 +377,10 @@ pub const RuntimeEnv = struct {
}, },
.push_local => |push_local| { .push_local => |push_local| {
if (push_local >= self.env.locals.values.len) {
return self.env.raise(error.IllegalState, "invalid local");
}
if (self.env.locals.values[push_local]) |local| { if (self.env.locals.values[push_local]) |local| {
try self.env.locals.push_one(try self.env.acquire(local)); try self.env.locals.push_one(try self.env.acquire(local));
} else { } else {

View File

@ -73,8 +73,15 @@ pub const Expression = union (enum) {
symbol_literal: []const coral.io.Byte, symbol_literal: []const coral.io.Byte,
table_literal: TableLiteral, table_literal: TableLiteral,
grouped_expression: *Expression, grouped_expression: *Expression,
local_get: []const coral.io.Byte,
local_set: []const coral.io.Byte, local_get: struct {
identifier: []const coral.io.Byte,
},
local_set: struct {
identifier: []const coral.io.Byte,
value_expression: *Expression,
},
field_get: struct { field_get: struct {
object_expression: *Expression, object_expression: *Expression,
@ -114,7 +121,7 @@ pub const Expression = union (enum) {
argument_expressions: ExpressionList, argument_expressions: ExpressionList,
}, },
pub const TableLiteral = coral.list.Stack(struct { const TableLiteral = coral.list.Stack(struct {
key_expression: Expression, key_expression: Expression,
value_expression: Expression, value_expression: Expression,
}); });
@ -134,12 +141,14 @@ pub const Statement = union (enum) {
expression: ?Expression, expression: ?Expression,
}, },
@"if": struct { @"if": ConditionalBlock,
@"while": ConditionalBlock,
expression: Expression,
const ConditionalBlock = struct {
condition_expression: Expression, condition_expression: Expression,
block_statements: StatementList, block_statements: StatementList,
}, };
expression: Expression,
}; };
pub const StatementList = coral.list.Stack(Statement); pub const StatementList = coral.list.Stack(Statement);
@ -218,7 +227,12 @@ pub const Tree = struct {
} }
return switch (expression) { return switch (expression) {
.local_get => |local_get| .{.local_set = local_get}, .local_get => |local_get| .{
.local_set = .{
.identifier = local_get.identifier,
.value_expression = try coral.io.allocate_one(allocator, try self.parse_expression()),
},
},
.field_get => |field_get| .{ .field_get => |field_get| .{
.field_set = .{ .field_set = .{
@ -299,7 +313,7 @@ pub const Tree = struct {
.identifier => |identifier| { .identifier => |identifier| {
self.tokenizer.skip_newlines(); self.tokenizer.skip_newlines();
break: parse .{.local_get = identifier}; break: parse .{.local_get = .{.identifier = identifier}};
}, },
.builtin => |builtin| { .builtin => |builtin| {
@ -542,13 +556,40 @@ pub const Tree = struct {
break: parse_statement .{.@"return" = .{.expression = null}}; break: parse_statement .{.@"return" = .{.expression = null}};
}, },
.keyword_while => {
self.tokenizer.step();
const condition_expression = try self.parse_expression();
if (self.tokenizer.token != .keyword_do) {
return self.report("expected `do` block after `while` statement");
}
self.tokenizer.step();
const while_statement = Statement{
.@"while" = .{
.block_statements = try self.parse_statements(),
.condition_expression = condition_expression,
},
};
if (self.tokenizer.token != .keyword_end) {
return self.report("expected `end` after block");
}
self.tokenizer.skip_newlines();
break: parse_statement while_statement;
},
.keyword_if => { .keyword_if => {
self.tokenizer.step(); self.tokenizer.step();
const condition_expression = try self.parse_expression(); const condition_expression = try self.parse_expression();
if (self.tokenizer.token != .keyword_do) { if (self.tokenizer.token != .keyword_do) {
return self.report("expected `do` block after if statement"); return self.report("expected `do` block after `if` statement");
} }
self.tokenizer.step(); self.tokenizer.step();

View File

@ -41,6 +41,7 @@ pub const Token = union(enum) {
keyword_if, keyword_if,
keyword_do, keyword_do,
keyword_end, keyword_end,
keyword_while,
pub fn text(self: Token) []const coral.io.Byte { pub fn text(self: Token) []const coral.io.Byte {
return switch (self) { return switch (self) {
@ -85,6 +86,7 @@ pub const Token = union(enum) {
.keyword_if => "if", .keyword_if => "if",
.keyword_do => "do", .keyword_do => "do",
.keyword_end => "end", .keyword_end => "end",
.keyword_while => "while",
}; };
} }
}; };
@ -243,6 +245,14 @@ pub const Tokenizer = struct {
} }
}, },
'w' => {
if (coral.io.ends_with(identifier, "hile")) {
self.token = .keyword_while;
return;
}
},
else => {}, else => {},
} }