From 931d1fe6ed1a94ca1ae77bcd6f13bb7dd0bc59f3 Mon Sep 17 00:00:00 2001 From: kayomn Date: Sat, 12 Aug 2023 14:51:47 +0100 Subject: [PATCH 1/3] Add parser support for keys and indices --- source/ona/kym/Ast.zig | 49 ++++++++++++++++++++++++++++----------- source/ona/kym/tokens.zig | 14 +++++++++++ 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/source/ona/kym/Ast.zig b/source/ona/kym/Ast.zig index 48f1346..cf2b6f7 100755 --- a/source/ona/kym/Ast.zig +++ b/source/ona/kym/Ast.zig @@ -351,14 +351,6 @@ fn parse_factor(self: *Self) ParseError!Expression { break: parse .{.table_literal = table_literal}; }, - .symbol_bracket_left => { - self.tokenizer.skip_newlines(); - - if (self.tokenizer.token != .symbol_equals) { - return self.report("expected expression after identifier"); - } - }, - .symbol_period => { self.tokenizer.step(); @@ -370,15 +362,11 @@ fn parse_factor(self: *Self) ParseError!Expression { self.tokenizer.skip_newlines(); if (self.tokenizer.token != .symbol_equals) { - return self.report("expected `=` after key"); + return self.report("expected `=` after symbol"); } self.tokenizer.skip_newlines(); - if (self.tokenizer.token == .end) { - return self.report("unexpected end after `=`"); - } - try table_literal.push_one(.{ .value_expression = try self.parse_expression(), .key_expression = .{.symbol_literal = identifier}, @@ -397,6 +385,41 @@ fn parse_factor(self: *Self) ParseError!Expression { } }, + .symbol_bracket_left => { + self.tokenizer.step(); + + const index_expression = try self.parse_expression(); + + if (self.tokenizer.token != .symbol_bracket_right) { + return self.report("expected `]` expression"); + } + + self.tokenizer.skip_newlines(); + + if (self.tokenizer.token != .symbol_equals) { + return self.report("expected `=` after `]`"); + } + + self.tokenizer.skip_newlines(); + + try table_literal.push_one(.{ + .value_expression = try self.parse_expression(), + .key_expression = index_expression, + }); + + switch (self.tokenizer.token) { + .symbol_comma => self.tokenizer.skip_newlines(), + + .symbol_brace_right => { + self.tokenizer.skip_newlines(); + + break: parse .{.table_literal = table_literal}; + }, + + else => return self.report("expected `,` or `}` after expression"), + } + }, + else => return self.report("expected `}` or fields in table literal"), } } diff --git a/source/ona/kym/tokens.zig b/source/ona/kym/tokens.zig index bc916d4..2604bf8 100755 --- a/source/ona/kym/tokens.zig +++ b/source/ona/kym/tokens.zig @@ -287,6 +287,20 @@ pub const Tokenizer = struct { return; }, + '[' => { + self.token = .symbol_bracket_left; + cursor += 1; + + return; + }, + + ']' => { + self.token = .symbol_bracket_right; + cursor += 1; + + return; + }, + ',' => { self.token = .symbol_comma; cursor += 1; -- 2.34.1 From 6c0e3477c22208b08ab0088990b7bd575e70f29a Mon Sep 17 00:00:00 2001 From: kayomn Date: Sat, 12 Aug 2023 14:53:27 +0100 Subject: [PATCH 2/3] Separate get-extension functions from Kym env. --- debug/app.ona | 3 +++ source/ona/app.zig | 8 ++++---- source/ona/kym.zig | 32 ++++++++++++++++++++++++-------- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/debug/app.ona b/debug/app.ona index ea265f4..6f78ba6 100644 --- a/debug/app.ona +++ b/debug/app.ona @@ -6,6 +6,9 @@ options = { .width = 1280, .height = 800, .tick_rate = 60, + + ["foo"] = "bar", + [42] = 42, } @log_info(options.title) diff --git a/source/ona/app.zig b/source/ona/app.zig index 81d51f5..296b82f 100644 --- a/source/ona/app.zig +++ b/source/ona/app.zig @@ -18,7 +18,7 @@ pub const Manifest = struct { defer env.discard(manifest); const width = @as(u16, get: { - if (try env.get_field(manifest, "width")) |ref| { + if (try kym.get_field(env, manifest, "width")) |ref| { defer env.discard(ref); const fixed = try env.unbox_fixed(ref); @@ -32,7 +32,7 @@ pub const Manifest = struct { }); const height = @as(u16, get: { - if (try env.get_field(manifest, "height")) |ref| { + if (try kym.get_field(env, manifest, "height")) |ref| { defer env.discard(ref); const fixed = try env.unbox_fixed(ref); @@ -46,7 +46,7 @@ pub const Manifest = struct { }); const tick_rate = @as(f32, get: { - if (try env.get_field(manifest, "tick_rate")) |ref| { + if (try kym.get_field(env, manifest, "tick_rate")) |ref| { defer env.discard(ref); break: get @floatCast(try env.unbox_float(ref)); @@ -55,7 +55,7 @@ pub const Manifest = struct { break: get self.tick_rate; }); - if (try env.get_field(manifest, "title")) |ref| { + if (try kym.get_field(env, manifest, "title")) |ref| { defer env.discard(ref); const title_string = try env.unbox_string(ref); diff --git a/source/ona/kym.zig b/source/ona/kym.zig index 0ee0442..fb4b6f1 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -1056,14 +1056,6 @@ pub const RuntimeEnv = struct { }; } - pub fn get_field(self: *RuntimeEnv, indexable: *RuntimeRef, field: []const coral.io.Byte) RuntimeError!?*RuntimeRef { - const field_symbol = try self.new_symbol(field); - - defer self.discard(field_symbol); - - return self.get(indexable, field_symbol); - } - pub fn make(allocator: coral.io.Allocator, error_handler: ErrorHandler) coral.io.AllocationError!RuntimeEnv { return RuntimeEnv{ .locals = RefList.make(allocator), @@ -1399,6 +1391,30 @@ pub fn bind_syscaller(env: *RuntimeEnv, name: []const coral.io.Byte, caller: Cal try env.bind_system(name, callable); } +pub fn get_field(env: *RuntimeEnv, indexable: *RuntimeRef, field: []const coral.io.Byte) RuntimeError!?*RuntimeRef { + const field_symbol = try env.new_symbol(field); + + defer env.discard(field_symbol); + + return env.get(indexable, field_symbol); +} + +pub fn get_index(env: *RuntimeEnv, indexable: *RuntimeRef, index: Fixed) RuntimeError!?*RuntimeRef { + const index_number = try env.new_fixed(index); + + defer env.discard(index_number); + + return env.get(indexable, index_number); +} + +pub fn get_key(env: *RuntimeEnv, indexable: *RuntimeRef, key: []const coral.io.Byte) RuntimeError!?*RuntimeRef { + const key_string = try env.new_string(key); + + defer env.discard(key_string); + + return env.get(indexable, key_string); +} + pub fn new_caller(env: *RuntimeEnv, value: Caller) RuntimeError!*RuntimeRef { const Callable = struct { fn call(method: Typeinfo.Method) RuntimeError!?*RuntimeRef { -- 2.34.1 From 66df9e3a1ee29f73a52c212ee7eeb591dc48bf75 Mon Sep 17 00:00:00 2001 From: kayomn Date: Sat, 12 Aug 2023 15:10:46 +0100 Subject: [PATCH 3/3] Add support for subscript operations on objects --- debug/app.ona | 7 ++++++- source/ona/kym.zig | 13 ++++++++++++ source/ona/kym/Ast.zig | 45 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/debug/app.ona b/debug/app.ona index 6f78ba6..dad79ec 100644 --- a/debug/app.ona +++ b/debug/app.ona @@ -8,9 +8,14 @@ options = { .tick_rate = 60, ["foo"] = "bar", - [42] = 42, + [42] = "42", } +options["foo"] = "rab" +options[42] = "24" + @log_info(options.title) +@log_info(options["foo"]) +@log_info(options[42]) return options diff --git a/source/ona/kym.zig b/source/ona/kym.zig index fb4b6f1..179eaa3 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -222,6 +222,19 @@ pub const RuntimeEnv = struct { try self.compile_expression(chunk, field_set.value_expression.*); try chunk.opcodes.push_one(.object_set); }, + + .subscript_get => |subscript_get| { + try self.compile_expression(chunk, subscript_get.object_expression.*); + try self.compile_expression(chunk, subscript_get.subscript_expression.*); + try chunk.opcodes.push_one(.object_get); + }, + + .subscript_set => |subscript_set| { + try self.compile_expression(chunk, subscript_set.object_expression.*); + try self.compile_expression(chunk, subscript_set.subscript_expression.*); + try self.compile_expression(chunk, subscript_set.value_expression.*); + try chunk.opcodes.push_one(.object_set); + }, } } diff --git a/source/ona/kym/Ast.zig b/source/ona/kym/Ast.zig index cf2b6f7..f286ab7 100755 --- a/source/ona/kym/Ast.zig +++ b/source/ona/kym/Ast.zig @@ -33,6 +33,17 @@ pub const Expression = union (enum) { value_expression: *Expression, }, + subscript_get: struct { + object_expression: *Expression, + subscript_expression: *Expression, + }, + + subscript_set: struct { + object_expression: *Expression, + subscript_expression: *Expression, + value_expression: *Expression, + }, + binary_operation: struct { operator: BinaryOperator, lhs_expression: *Expression, @@ -266,6 +277,14 @@ pub fn parse_expression(self: *Self) ParseError!Expression { }, }, + .subscript_get => |subscript_get| .{ + .subscript_set = .{ + .object_expression = subscript_get.object_expression, + .subscript_expression = subscript_get.subscript_expression, + .value_expression = try coral.io.allocate_one(allocator, try self.parse_expression()), + }, + }, + else => self.report("expected local or field on left-hand side of expression"), }; } @@ -388,10 +407,10 @@ fn parse_factor(self: *Self) ParseError!Expression { .symbol_bracket_left => { self.tokenizer.step(); - const index_expression = try self.parse_expression(); + const subscript_expression = try self.parse_expression(); if (self.tokenizer.token != .symbol_bracket_right) { - return self.report("expected `]` expression"); + return self.report("expected `]` after subscript expression"); } self.tokenizer.skip_newlines(); @@ -404,7 +423,7 @@ fn parse_factor(self: *Self) ParseError!Expression { try table_literal.push_one(.{ .value_expression = try self.parse_expression(), - .key_expression = index_expression, + .key_expression = subscript_expression, }); switch (self.tokenizer.token) { @@ -481,6 +500,26 @@ fn parse_factor(self: *Self) ParseError!Expression { self.tokenizer.skip_newlines(); }, + .symbol_bracket_left => { + self.tokenizer.skip_newlines(); + + // TODO: Remove when Zig fixes miscompilation with in-place struct re-assignment. + const unnecessary_temp = try coral.io.allocate_one(allocator, expression); + + expression = .{ + .subscript_get = .{ + .subscript_expression = try coral.io.allocate_one(allocator, try self.parse_expression()), + .object_expression = unnecessary_temp, + }, + }; + + if (self.tokenizer.token != .symbol_bracket_right) { + return self.report("expected `]` subscript expression"); + } + + self.tokenizer.skip_newlines(); + }, + .symbol_paren_left => { var argument_expressions = Expression.List.make(allocator); -- 2.34.1