Implement Control Flow Statements in Kym #37
|
@ -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
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue