diff --git a/debug/app.ona b/debug/app.ona index 54676d5..f787fc7 100644 --- a/debug/app.ona +++ b/debug/app.ona @@ -1,24 +1,23 @@ -# Test comment. +i = 0 -pos = @vec3(10, 20, 0.3) -coord = pos.xz +while i < 5: + @print("hello, world") -options = { - .title = "Afterglow", + 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, .height = 800, .tick_rate = 60, - - ["foo"] = "bar", - [42] = "42", } - -options["foo"] = "rab" -options[42] = "24" - -@print(options.title) -@print(options["foo"]) -@print(options[42]) - -return options diff --git a/source/ona/kym.zig b/source/ona/kym.zig index 3a7a437..59803b9 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -1,4 +1,4 @@ -const Ast = @import("./kym/Ast.zig"); +const ast = @import("./kym/ast.zig"); const coral = @import("coral"); @@ -36,6 +36,7 @@ pub const RuntimeEnv = struct { }; const Opcode = union (enum) { + pop, push_nil, push_true, push_false, @@ -61,6 +62,9 @@ pub const RuntimeEnv = struct { clt, cge, cle, + + jt: u32, + jf: u32, }; const OpcodeList = coral.list.Stack(Opcode); @@ -69,7 +73,7 @@ pub const RuntimeEnv = struct { local_identifiers_buffer: [255][]const coral.io.Byte = [_][]const coral.io.Byte{""} ** 255, local_identifiers_count: u8 = 0, - fn compile_expression(self: *CompilationUnit, chunk: *Chunk, expression: Ast.Expression) RuntimeError!void { + fn compile_expression(self: *CompilationUnit, chunk: *Chunk, expression: ast.Expression) RuntimeError!void { const number_format = coral.utf8.DecimalFormat{ .delimiter = "_", .positive_prefix = .none, @@ -132,9 +136,9 @@ pub const RuntimeEnv = struct { .subtraction => .sub, .multiplication => .mul, .divsion => .div, - .greater_equals_comparison => .eql, + .greater_equals_comparison => .cge, .greater_than_comparison => .cgt, - .equals_comparison => .cge, + .equals_comparison => .eql, .less_than_comparison => .clt, .less_equals_comparison => .cle, }); @@ -200,21 +204,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; } }, @@ -255,17 +261,62 @@ 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) { .@"return" => |@"return"| { - if (@"return".expression) |expression| { + if (@"return") |expression| { try self.compile_expression(chunk, expression); } else { try chunk.opcodes.push_one(.push_nil); } }, - .expression => |expression| try self.compile_expression(chunk, expression), + .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}); + + 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); + } + + 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 origin_index = @as(u32, @intCast(chunk.opcodes.values.len - 1)); + + for (@"if".block_statements.values) |block_statement| { + try self.compile_statement(chunk, block_statement); + } + + chunk.opcodes.values[origin_index].jf = @intCast(chunk.opcodes.values.len - 1); + + if (@"if".else_statement) |else_statement| { + try self.compile_statement(chunk, else_statement.*); + } + }, + + .expression => |expression| { + try self.compile_expression(chunk, expression); + + if (expression == .invoke) { + try chunk.opcodes.push_one(.pop); + } + }, } } @@ -288,10 +339,10 @@ pub const RuntimeEnv = struct { } }; - fn compile(self: *Chunk, ast: Ast) RuntimeError!void { + fn compile(self: *Chunk, statements: []const ast.Statement) RuntimeError!void { var unit = CompilationUnit{}; - for (ast.list_statements()) |statement| { + for (statements) |statement| { try unit.compile_statement(self, statement); } } @@ -322,8 +373,16 @@ pub const RuntimeEnv = struct { defer coral.debug.assert(self.env.frames.pop() != null); - for (self.opcodes.values) |opcode| { - switch (opcode) { + var opcode_cursor = @as(u32, 0); + + 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)), @@ -337,6 +396,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 { @@ -401,15 +464,11 @@ pub const RuntimeEnv = struct { }, .object_get => { - const index = (try self.pop_local()) orelse { - return self.env.raise(error.TypeMismatch, "nil is not a valid index"); - }; + const index = try self.env.expect(try self.pop_local()); defer self.env.discard(index); - const indexable = (try self.pop_local()) orelse { - return self.env.raise(error.TypeMismatch, "nil is not a valid indexable"); - }; + const indexable = try self.env.expect(try self.pop_local()); defer self.env.discard(indexable); @@ -791,6 +850,29 @@ pub const RuntimeEnv = struct { })); }, + .jf => |jf| { + const condition = (try self.pop_local()) orelse { + opcode_cursor = jf; + + continue; + }; + + self.env.discard(condition); + + if (!condition.is_truthy()) { + opcode_cursor = jf; + } + }, + + .jt => |jt| { + const condition = (try self.pop_local()) orelse continue; + + self.env.discard(condition); + + if (condition.is_truthy()) { + opcode_cursor = jt; + } + }, } } @@ -1044,20 +1126,18 @@ pub const RuntimeEnv = struct { defer self.allocator.deallocate(file_data); const file_name = file_path.to_string() orelse "