diff --git a/debug/app.ona b/debug/app.ona index 2a29d7f..f787fc7 100644 --- a/debug/app.ona +++ b/debug/app.ona @@ -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, diff --git a/source/ona/kym.zig b/source/ona/kym.zig index cb35f37..60d46f4 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -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(); diff --git a/source/ona/kym/ast.zig b/source/ona/kym/ast.zig index c055c38..5dcb1fc 100644 --- a/source/ona/kym/ast.zig +++ b/source/ona/kym/ast.zig @@ -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" = .{ - .block_statements = try self.parse_statements(), - .condition_expression = condition_expression, + .then_block = .{ + .block_statements = try self.parse_statements(), + .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(); diff --git a/source/ona/kym/tokens.zig b/source/ona/kym/tokens.zig index aed3dae..1c1e05e 100755 --- a/source/ona/kym/tokens.zig +++ b/source/ona/kym/tokens.zig @@ -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 => {}, } }