Implement Control Flow Statements in Kym #37
|
@ -1,12 +1,20 @@
|
|||
|
||||
i = 0
|
||||
|
||||
while i < 5 do
|
||||
while i < 5:
|
||||
@print("hello, world")
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if i > 6:
|
||||
@print("`i` greater than `6`")
|
||||
elif i == 4:
|
||||
@print("`i` is equal to `4`")
|
||||
else:
|
||||
@print("i'unno")
|
||||
end
|
||||
|
||||
return {
|
||||
.title = "Game",
|
||||
.width = 1280,
|
||||
|
|
|
@ -36,6 +36,7 @@ pub const RuntimeEnv = struct {
|
|||
};
|
||||
|
||||
const Opcode = union (enum) {
|
||||
pop,
|
||||
push_nil,
|
||||
push_true,
|
||||
push_false,
|
||||
|
@ -263,13 +264,19 @@ pub const RuntimeEnv = struct {
|
|||
fn compile_statement(self: *CompilationUnit, chunk: *Chunk, statement: ast.Statement) RuntimeError!void {
|
||||
switch (statement) {
|
||||
.@"return" => |@"return"| {
|
||||
if (@"return".expression) |expression| {
|
||||
if (@"return") |expression| {
|
||||
try self.compile_expression(chunk, expression);
|
||||
} else {
|
||||
try chunk.opcodes.push_one(.push_nil);
|
||||
}
|
||||
},
|
||||
|
||||
.block => |block| {
|
||||
for (block.values) |block_statement| {
|
||||
try self.compile_statement(chunk, block_statement);
|
||||
}
|
||||
},
|
||||
|
||||
.@"while" => |@"while"| {
|
||||
try self.compile_expression(chunk, @"while".condition_expression);
|
||||
try chunk.opcodes.push_one(.{.jf = 0});
|
||||
|
@ -280,8 +287,6 @@ pub const RuntimeEnv = struct {
|
|||
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);
|
||||
|
@ -289,21 +294,42 @@ pub const RuntimeEnv = struct {
|
|||
},
|
||||
|
||||
.@"if" => |@"if"| {
|
||||
try self.compile_expression(chunk, @"if".condition_expression);
|
||||
try self.compile_expression(chunk, @"if".then_block.condition_expression);
|
||||
try chunk.opcodes.push_one(.{.jf = 0});
|
||||
|
||||
const origin_index = @as(u32, @intCast(chunk.opcodes.values.len - 1));
|
||||
const then_origin_index = @as(u32, @intCast(chunk.opcodes.values.len - 1));
|
||||
|
||||
for (@"if".block_statements.values) |block_statement| {
|
||||
for (@"if".then_block.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[then_origin_index].jf = @intCast(chunk.opcodes.values.len - 1);
|
||||
|
||||
chunk.opcodes.values[origin_index].jf = @intCast(chunk.opcodes.values.len - 1);
|
||||
if (@"if".else_block) |else_block| {
|
||||
if (else_block.condition_expression) |condition_expression| {
|
||||
try self.compile_expression(chunk, condition_expression);
|
||||
try chunk.opcodes.push_one(.{.jf = 0});
|
||||
}
|
||||
|
||||
const else_origin_index = @as(u32, @intCast(chunk.opcodes.values.len - 1));
|
||||
|
||||
for (else_block.block_statements.values) |block_statement| {
|
||||
try self.compile_statement(chunk, block_statement);
|
||||
}
|
||||
|
||||
if (else_block.condition_expression != null) {
|
||||
chunk.opcodes.values[else_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);
|
||||
|
||||
if (expression == .invoke) {
|
||||
try chunk.opcodes.push_one(.pop);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,6 +390,12 @@ pub const RuntimeEnv = struct {
|
|||
|
||||
while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) {
|
||||
switch (self.opcodes.values[opcode_cursor]) {
|
||||
.pop => {
|
||||
if (try self.pop_local()) |ref| {
|
||||
self.env.discard(ref);
|
||||
}
|
||||
},
|
||||
|
||||
.push_nil => try self.env.locals.push_one(null),
|
||||
.push_true => try self.env.locals.push_one(try self.env.new_boolean(true)),
|
||||
.push_false => try self.env.locals.push_one(try self.env.new_boolean(false)),
|
||||
|
@ -855,6 +887,12 @@ pub const RuntimeEnv = struct {
|
|||
}
|
||||
},
|
||||
}
|
||||
|
||||
// for (self.env.locals.values) |local| {
|
||||
// self.env.print(if (local) |ref| ref.typename() else "nil");
|
||||
// }
|
||||
|
||||
// self.env.print("------------");
|
||||
}
|
||||
|
||||
return self.pop_local();
|
||||
|
|
|
@ -137,12 +137,19 @@ pub const ParseError = error {
|
|||
};
|
||||
|
||||
pub const Statement = union (enum) {
|
||||
@"return": struct {
|
||||
expression: ?Expression,
|
||||
@"return": ?Expression,
|
||||
|
||||
@"if": struct {
|
||||
then_block: ConditionalBlock,
|
||||
|
||||
else_block: ?struct {
|
||||
condition_expression: ?Expression,
|
||||
block_statements: StatementList,
|
||||
},
|
||||
},
|
||||
|
||||
@"if": ConditionalBlock,
|
||||
@"while": ConditionalBlock,
|
||||
block: StatementList,
|
||||
expression: Expression,
|
||||
|
||||
const ConditionalBlock = struct {
|
||||
|
@ -527,14 +534,15 @@ pub const Tree = struct {
|
|||
}
|
||||
|
||||
fn parse_statements(self: *Tree) ParseError!StatementList {
|
||||
var statements = StatementList.make(self.arena.as_allocator());
|
||||
const allocator = self.arena.as_allocator();
|
||||
var statements = StatementList.make(allocator);
|
||||
|
||||
self.tokenizer.skip_newlines();
|
||||
|
||||
while (true) {
|
||||
try statements.push_one(parse_statement: {
|
||||
switch (self.tokenizer.token) {
|
||||
.end, .keyword_end => return statements,
|
||||
.end, .keyword_end, .keyword_else, .keyword_elif => return statements,
|
||||
|
||||
.keyword_return => {
|
||||
if (self.has_returned) {
|
||||
|
@ -544,7 +552,7 @@ pub const Tree = struct {
|
|||
self.tokenizer.step();
|
||||
|
||||
if (self.tokenizer.token != .end and self.tokenizer.token != .newline) {
|
||||
break: parse_statement .{.@"return" = .{.expression = try self.parse_expression()}};
|
||||
break: parse_statement .{.@"return" = try self.parse_expression()};
|
||||
}
|
||||
|
||||
if (self.tokenizer.token != .end and self.tokenizer.token != .newline) {
|
||||
|
@ -553,7 +561,7 @@ pub const Tree = struct {
|
|||
|
||||
self.has_returned = true;
|
||||
|
||||
break: parse_statement .{.@"return" = .{.expression = null}};
|
||||
break: parse_statement .{.@"return" = null};
|
||||
},
|
||||
|
||||
.keyword_while => {
|
||||
|
@ -561,12 +569,10 @@ pub const Tree = struct {
|
|||
|
||||
const condition_expression = try self.parse_expression();
|
||||
|
||||
if (self.tokenizer.token != .keyword_do) {
|
||||
return self.report("expected `do` block after `while` statement");
|
||||
if (self.tokenizer.token != .symbol_colon) {
|
||||
return self.report("expected `:` after `while` statement");
|
||||
}
|
||||
|
||||
self.tokenizer.step();
|
||||
|
||||
const while_statement = Statement{
|
||||
.@"while" = .{
|
||||
.block_statements = try self.parse_statements(),
|
||||
|
@ -586,23 +592,55 @@ pub const Tree = struct {
|
|||
.keyword_if => {
|
||||
self.tokenizer.step();
|
||||
|
||||
const condition_expression = try self.parse_expression();
|
||||
const then_condition_expression = try self.parse_expression();
|
||||
|
||||
if (self.tokenizer.token != .keyword_do) {
|
||||
return self.report("expected `do` block after `if` statement");
|
||||
if (self.tokenizer.token != .symbol_colon) {
|
||||
return self.report("expected `:` after `if` statement condition");
|
||||
}
|
||||
|
||||
self.tokenizer.step();
|
||||
|
||||
const if_statement = Statement{
|
||||
var if_statement = Statement{
|
||||
.@"if" = .{
|
||||
.then_block = .{
|
||||
.block_statements = try self.parse_statements(),
|
||||
.condition_expression = condition_expression,
|
||||
.condition_expression = then_condition_expression,
|
||||
},
|
||||
|
||||
.else_block = null,
|
||||
},
|
||||
};
|
||||
|
||||
if (self.tokenizer.token != .keyword_end) {
|
||||
return self.report("expected `end` after block");
|
||||
switch (self.tokenizer.token) {
|
||||
.keyword_end => {},
|
||||
|
||||
.keyword_else => {
|
||||
self.tokenizer.step();
|
||||
|
||||
if (self.tokenizer.token != .symbol_colon) {
|
||||
return self.report("expected newline after `else` statement");
|
||||
}
|
||||
|
||||
if_statement.@"if".else_block = .{
|
||||
.condition_expression = null,
|
||||
.block_statements = try self.parse_statements(),
|
||||
};
|
||||
},
|
||||
|
||||
.keyword_elif => {
|
||||
self.tokenizer.step();
|
||||
|
||||
const else_condition_expression = try self.parse_expression();
|
||||
|
||||
if (self.tokenizer.token != .symbol_colon) {
|
||||
return self.report("expected newline after `elif` statement condition");
|
||||
}
|
||||
|
||||
if_statement.@"if".else_block = .{
|
||||
.condition_expression = else_condition_expression,
|
||||
.block_statements = try self.parse_statements(),
|
||||
};
|
||||
},
|
||||
|
||||
else => return self.report("expected closing `end`, `elif`, or `else` statement on if block"),
|
||||
}
|
||||
|
||||
self.tokenizer.skip_newlines();
|
||||
|
|
|
@ -21,7 +21,7 @@ pub const Token = union(enum) {
|
|||
symbol_bracket_left,
|
||||
symbol_bracket_right,
|
||||
symbol_period,
|
||||
symbol_lambda,
|
||||
symbol_colon,
|
||||
symbol_less_than,
|
||||
symbol_less_equals,
|
||||
symbol_greater_than,
|
||||
|
@ -42,6 +42,8 @@ pub const Token = union(enum) {
|
|||
keyword_do,
|
||||
keyword_end,
|
||||
keyword_while,
|
||||
keyword_else,
|
||||
keyword_elif,
|
||||
|
||||
pub fn text(self: Token) []const coral.io.Byte {
|
||||
return switch (self) {
|
||||
|
@ -66,7 +68,7 @@ pub const Token = union(enum) {
|
|||
.symbol_bracket_left => "[",
|
||||
.symbol_bracket_right => "]",
|
||||
.symbol_period => ".",
|
||||
.symbol_lambda => "=>",
|
||||
.symbol_colon => ":",
|
||||
.symbol_less_than => "<",
|
||||
.symbol_less_equals => "<=",
|
||||
.symbol_greater_than => ">",
|
||||
|
@ -87,6 +89,8 @@ pub const Token = union(enum) {
|
|||
.keyword_do => "do",
|
||||
.keyword_end => "end",
|
||||
.keyword_while => "while",
|
||||
.keyword_elif => "elif",
|
||||
.keyword_else => "else",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -190,6 +194,18 @@ pub const Tokenizer = struct {
|
|||
},
|
||||
|
||||
'e' => {
|
||||
if (coral.io.ends_with(identifier, "lse")) {
|
||||
self.token = .keyword_else;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (coral.io.ends_with(identifier, "lif")) {
|
||||
self.token = .keyword_elif;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (coral.io.ends_with(identifier, "nd")) {
|
||||
self.token = .keyword_end;
|
||||
|
||||
|
@ -378,6 +394,13 @@ pub const Tokenizer = struct {
|
|||
return;
|
||||
},
|
||||
|
||||
':' => {
|
||||
self.token = .symbol_colon;
|
||||
cursor += 1;
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
'=' => {
|
||||
cursor += 1;
|
||||
|
||||
|
@ -390,13 +413,6 @@ pub const Tokenizer = struct {
|
|||
return;
|
||||
},
|
||||
|
||||
'>' => {
|
||||
cursor += 1;
|
||||
self.token = .symbol_lambda;
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue