diff --git a/debug/app.ona b/debug/app.ona index d25f721..2a29d7f 100644 --- a/debug/app.ona +++ b/debug/app.ona @@ -1,28 +1,15 @@ -# Test comment. +i = 0 -pos = @vec3(10, 20, 0.3) -coord = pos.xz +while i < 5 do + @print("hello, world") -options = { - .title = "Afterglow", + i = i + 1 +end + +return { + .title = "Game", .width = 1280, .height = 800, .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 diff --git a/source/ona/kym.zig b/source/ona/kym.zig index a066249..cb35f37 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -203,21 +203,23 @@ pub const RuntimeEnv = struct { .local_get => |local_get| { 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"); }, }); }, .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}); } else { if (self.local_identifiers_count == self.local_identifiers_buffer.len) { 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; } }, @@ -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"| { try self.compile_expression(chunk, @"if".condition_expression); 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| { 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)); - 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), @@ -357,6 +377,10 @@ pub const RuntimeEnv = struct { }, .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| { try self.env.locals.push_one(try self.env.acquire(local)); } else { diff --git a/source/ona/kym/ast.zig b/source/ona/kym/ast.zig index 586590b..c055c38 100644 --- a/source/ona/kym/ast.zig +++ b/source/ona/kym/ast.zig @@ -73,8 +73,15 @@ pub const Expression = union (enum) { symbol_literal: []const coral.io.Byte, table_literal: TableLiteral, 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 { object_expression: *Expression, @@ -114,7 +121,7 @@ pub const Expression = union (enum) { argument_expressions: ExpressionList, }, - pub const TableLiteral = coral.list.Stack(struct { + const TableLiteral = coral.list.Stack(struct { key_expression: Expression, value_expression: Expression, }); @@ -134,12 +141,14 @@ pub const Statement = union (enum) { expression: ?Expression, }, - @"if": struct { + @"if": ConditionalBlock, + @"while": ConditionalBlock, + expression: Expression, + + const ConditionalBlock = struct { condition_expression: Expression, block_statements: StatementList, - }, - - expression: Expression, + }; }; pub const StatementList = coral.list.Stack(Statement); @@ -218,7 +227,12 @@ pub const Tree = struct { } 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_set = .{ @@ -299,7 +313,7 @@ pub const Tree = struct { .identifier => |identifier| { self.tokenizer.skip_newlines(); - break: parse .{.local_get = identifier}; + break: parse .{.local_get = .{.identifier = identifier}}; }, .builtin => |builtin| { @@ -542,13 +556,40 @@ pub const Tree = struct { 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 => { self.tokenizer.step(); const condition_expression = try self.parse_expression(); 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(); diff --git a/source/ona/kym/tokens.zig b/source/ona/kym/tokens.zig index c18aaa4..aed3dae 100755 --- a/source/ona/kym/tokens.zig +++ b/source/ona/kym/tokens.zig @@ -41,6 +41,7 @@ pub const Token = union(enum) { keyword_if, keyword_do, keyword_end, + keyword_while, pub fn text(self: Token) []const coral.io.Byte { return switch (self) { @@ -85,6 +86,7 @@ pub const Token = union(enum) { .keyword_if => "if", .keyword_do => "do", .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 => {}, }