Add if / else

This commit is contained in:
kayomn 2023-08-24 23:45:29 +01:00
parent bb1e689c5a
commit 08a63489b0
4 changed files with 140 additions and 40 deletions

View File

@ -1,12 +1,20 @@
i = 0 i = 0
while i < 5 do while i < 5:
@print("hello, world") @print("hello, world")
i = i + 1 i = i + 1
end end
if i > 6:
@print("`i` greater than `6`")
elif i == 4:
@print("`i` is equal to `4`")
else:
@print("i'unno")
end
return { return {
.title = "Game", .title = "Game",
.width = 1280, .width = 1280,

View File

@ -36,6 +36,7 @@ pub const RuntimeEnv = struct {
}; };
const Opcode = union (enum) { const Opcode = union (enum) {
pop,
push_nil, push_nil,
push_true, push_true,
push_false, push_false,
@ -263,13 +264,19 @@ pub const RuntimeEnv = struct {
fn compile_statement(self: *CompilationUnit, chunk: *Chunk, statement: ast.Statement) RuntimeError!void { fn compile_statement(self: *CompilationUnit, chunk: *Chunk, statement: ast.Statement) RuntimeError!void {
switch (statement) { switch (statement) {
.@"return" => |@"return"| { .@"return" => |@"return"| {
if (@"return".expression) |expression| { if (@"return") |expression| {
try self.compile_expression(chunk, expression); try self.compile_expression(chunk, expression);
} else { } else {
try chunk.opcodes.push_one(.push_nil); try chunk.opcodes.push_one(.push_nil);
} }
}, },
.block => |block| {
for (block.values) |block_statement| {
try self.compile_statement(chunk, block_statement);
}
},
.@"while" => |@"while"| { .@"while" => |@"while"| {
try self.compile_expression(chunk, @"while".condition_expression); try self.compile_expression(chunk, @"while".condition_expression);
try chunk.opcodes.push_one(.{.jf = 0}); try chunk.opcodes.push_one(.{.jf = 0});
@ -280,8 +287,6 @@ pub const RuntimeEnv = struct {
try self.compile_statement(chunk, 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); chunk.opcodes.values[origin_index].jf = @intCast(chunk.opcodes.values.len - 1);
try self.compile_expression(chunk, @"while".condition_expression); try self.compile_expression(chunk, @"while".condition_expression);
@ -289,21 +294,42 @@ pub const RuntimeEnv = struct {
}, },
.@"if" => |@"if"| { .@"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}); 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); 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) { while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) {
switch (self.opcodes.values[opcode_cursor]) { 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_nil => try self.env.locals.push_one(null),
.push_true => try self.env.locals.push_one(try self.env.new_boolean(true)), .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)), .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(); return self.pop_local();

View File

@ -137,12 +137,19 @@ pub const ParseError = error {
}; };
pub const Statement = union (enum) { pub const Statement = union (enum) {
@"return": struct { @"return": ?Expression,
expression: ?Expression,
@"if": struct {
then_block: ConditionalBlock,
else_block: ?struct {
condition_expression: ?Expression,
block_statements: StatementList,
},
}, },
@"if": ConditionalBlock,
@"while": ConditionalBlock, @"while": ConditionalBlock,
block: StatementList,
expression: Expression, expression: Expression,
const ConditionalBlock = struct { const ConditionalBlock = struct {
@ -527,14 +534,15 @@ pub const Tree = struct {
} }
fn parse_statements(self: *Tree) ParseError!StatementList { 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(); self.tokenizer.skip_newlines();
while (true) { while (true) {
try statements.push_one(parse_statement: { try statements.push_one(parse_statement: {
switch (self.tokenizer.token) { switch (self.tokenizer.token) {
.end, .keyword_end => return statements, .end, .keyword_end, .keyword_else, .keyword_elif => return statements,
.keyword_return => { .keyword_return => {
if (self.has_returned) { if (self.has_returned) {
@ -544,7 +552,7 @@ pub const Tree = struct {
self.tokenizer.step(); self.tokenizer.step();
if (self.tokenizer.token != .end and self.tokenizer.token != .newline) { 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) { if (self.tokenizer.token != .end and self.tokenizer.token != .newline) {
@ -553,7 +561,7 @@ pub const Tree = struct {
self.has_returned = true; self.has_returned = true;
break: parse_statement .{.@"return" = .{.expression = null}}; break: parse_statement .{.@"return" = null};
}, },
.keyword_while => { .keyword_while => {
@ -561,12 +569,10 @@ pub const Tree = struct {
const condition_expression = try self.parse_expression(); const condition_expression = try self.parse_expression();
if (self.tokenizer.token != .keyword_do) { if (self.tokenizer.token != .symbol_colon) {
return self.report("expected `do` block after `while` statement"); return self.report("expected `:` after `while` statement");
} }
self.tokenizer.step();
const while_statement = Statement{ const while_statement = Statement{
.@"while" = .{ .@"while" = .{
.block_statements = try self.parse_statements(), .block_statements = try self.parse_statements(),
@ -586,23 +592,55 @@ pub const Tree = struct {
.keyword_if => { .keyword_if => {
self.tokenizer.step(); self.tokenizer.step();
const condition_expression = try self.parse_expression(); const then_condition_expression = try self.parse_expression();
if (self.tokenizer.token != .keyword_do) { if (self.tokenizer.token != .symbol_colon) {
return self.report("expected `do` block after `if` statement"); return self.report("expected `:` after `if` statement condition");
} }
self.tokenizer.step(); var if_statement = Statement{
const if_statement = Statement{
.@"if" = .{ .@"if" = .{
.block_statements = try self.parse_statements(), .then_block = .{
.condition_expression = condition_expression, .block_statements = try self.parse_statements(),
.condition_expression = then_condition_expression,
},
.else_block = null,
}, },
}; };
if (self.tokenizer.token != .keyword_end) { switch (self.tokenizer.token) {
return self.report("expected `end` after block"); .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(); self.tokenizer.skip_newlines();

View File

@ -21,7 +21,7 @@ pub const Token = union(enum) {
symbol_bracket_left, symbol_bracket_left,
symbol_bracket_right, symbol_bracket_right,
symbol_period, symbol_period,
symbol_lambda, symbol_colon,
symbol_less_than, symbol_less_than,
symbol_less_equals, symbol_less_equals,
symbol_greater_than, symbol_greater_than,
@ -42,6 +42,8 @@ pub const Token = union(enum) {
keyword_do, keyword_do,
keyword_end, keyword_end,
keyword_while, keyword_while,
keyword_else,
keyword_elif,
pub fn text(self: Token) []const coral.io.Byte { pub fn text(self: Token) []const coral.io.Byte {
return switch (self) { return switch (self) {
@ -66,7 +68,7 @@ pub const Token = union(enum) {
.symbol_bracket_left => "[", .symbol_bracket_left => "[",
.symbol_bracket_right => "]", .symbol_bracket_right => "]",
.symbol_period => ".", .symbol_period => ".",
.symbol_lambda => "=>", .symbol_colon => ":",
.symbol_less_than => "<", .symbol_less_than => "<",
.symbol_less_equals => "<=", .symbol_less_equals => "<=",
.symbol_greater_than => ">", .symbol_greater_than => ">",
@ -87,6 +89,8 @@ pub const Token = union(enum) {
.keyword_do => "do", .keyword_do => "do",
.keyword_end => "end", .keyword_end => "end",
.keyword_while => "while", .keyword_while => "while",
.keyword_elif => "elif",
.keyword_else => "else",
}; };
} }
}; };
@ -190,6 +194,18 @@ pub const Tokenizer = struct {
}, },
'e' => { '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")) { if (coral.io.ends_with(identifier, "nd")) {
self.token = .keyword_end; self.token = .keyword_end;
@ -378,6 +394,13 @@ pub const Tokenizer = struct {
return; return;
}, },
':' => {
self.token = .symbol_colon;
cursor += 1;
return;
},
'=' => { '=' => {
cursor += 1; cursor += 1;
@ -390,13 +413,6 @@ pub const Tokenizer = struct {
return; return;
}, },
'>' => {
cursor += 1;
self.token = .symbol_lambda;
return;
},
else => {}, else => {},
} }
} }