Dynamic Object Indexing for Kym #31
|
@ -6,8 +6,16 @@ options = {
|
||||||
.width = 1280,
|
.width = 1280,
|
||||||
.height = 800,
|
.height = 800,
|
||||||
.tick_rate = 60,
|
.tick_rate = 60,
|
||||||
|
|
||||||
|
["foo"] = "bar",
|
||||||
|
[42] = "42",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options["foo"] = "rab"
|
||||||
|
options[42] = "24"
|
||||||
|
|
||||||
@log_info(options.title)
|
@log_info(options.title)
|
||||||
|
@log_info(options["foo"])
|
||||||
|
@log_info(options[42])
|
||||||
|
|
||||||
return options
|
return options
|
||||||
|
|
|
@ -18,7 +18,7 @@ pub const Manifest = struct {
|
||||||
defer env.discard(manifest);
|
defer env.discard(manifest);
|
||||||
|
|
||||||
const width = @as(u16, get: {
|
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);
|
defer env.discard(ref);
|
||||||
|
|
||||||
const fixed = try env.unbox_fixed(ref);
|
const fixed = try env.unbox_fixed(ref);
|
||||||
|
@ -32,7 +32,7 @@ pub const Manifest = struct {
|
||||||
});
|
});
|
||||||
|
|
||||||
const height = @as(u16, get: {
|
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);
|
defer env.discard(ref);
|
||||||
|
|
||||||
const fixed = try env.unbox_fixed(ref);
|
const fixed = try env.unbox_fixed(ref);
|
||||||
|
@ -46,7 +46,7 @@ pub const Manifest = struct {
|
||||||
});
|
});
|
||||||
|
|
||||||
const tick_rate = @as(f32, get: {
|
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);
|
defer env.discard(ref);
|
||||||
|
|
||||||
break: get @floatCast(try env.unbox_float(ref));
|
break: get @floatCast(try env.unbox_float(ref));
|
||||||
|
@ -55,7 +55,7 @@ pub const Manifest = struct {
|
||||||
break: get self.tick_rate;
|
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);
|
defer env.discard(ref);
|
||||||
|
|
||||||
const title_string = try env.unbox_string(ref);
|
const title_string = try env.unbox_string(ref);
|
||||||
|
|
|
@ -222,6 +222,19 @@ pub const RuntimeEnv = struct {
|
||||||
try self.compile_expression(chunk, field_set.value_expression.*);
|
try self.compile_expression(chunk, field_set.value_expression.*);
|
||||||
try chunk.opcodes.push_one(.object_set);
|
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);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1056,14 +1069,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 {
|
pub fn make(allocator: coral.io.Allocator, error_handler: ErrorHandler) coral.io.AllocationError!RuntimeEnv {
|
||||||
return RuntimeEnv{
|
return RuntimeEnv{
|
||||||
.locals = RefList.make(allocator),
|
.locals = RefList.make(allocator),
|
||||||
|
@ -1399,6 +1404,30 @@ pub fn bind_syscaller(env: *RuntimeEnv, name: []const coral.io.Byte, caller: Cal
|
||||||
try env.bind_system(name, callable);
|
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 {
|
pub fn new_caller(env: *RuntimeEnv, value: Caller) RuntimeError!*RuntimeRef {
|
||||||
const Callable = struct {
|
const Callable = struct {
|
||||||
fn call(method: Typeinfo.Method) RuntimeError!?*RuntimeRef {
|
fn call(method: Typeinfo.Method) RuntimeError!?*RuntimeRef {
|
||||||
|
|
|
@ -33,6 +33,17 @@ pub const Expression = union (enum) {
|
||||||
value_expression: *Expression,
|
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 {
|
binary_operation: struct {
|
||||||
operator: BinaryOperator,
|
operator: BinaryOperator,
|
||||||
lhs_expression: *Expression,
|
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"),
|
else => self.report("expected local or field on left-hand side of expression"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -351,14 +370,6 @@ fn parse_factor(self: *Self) ParseError!Expression {
|
||||||
break: parse .{.table_literal = table_literal};
|
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 => {
|
.symbol_period => {
|
||||||
self.tokenizer.step();
|
self.tokenizer.step();
|
||||||
|
|
||||||
|
@ -370,15 +381,11 @@ fn parse_factor(self: *Self) ParseError!Expression {
|
||||||
self.tokenizer.skip_newlines();
|
self.tokenizer.skip_newlines();
|
||||||
|
|
||||||
if (self.tokenizer.token != .symbol_equals) {
|
if (self.tokenizer.token != .symbol_equals) {
|
||||||
return self.report("expected `=` after key");
|
return self.report("expected `=` after symbol");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tokenizer.skip_newlines();
|
self.tokenizer.skip_newlines();
|
||||||
|
|
||||||
if (self.tokenizer.token == .end) {
|
|
||||||
return self.report("unexpected end after `=`");
|
|
||||||
}
|
|
||||||
|
|
||||||
try table_literal.push_one(.{
|
try table_literal.push_one(.{
|
||||||
.value_expression = try self.parse_expression(),
|
.value_expression = try self.parse_expression(),
|
||||||
.key_expression = .{.symbol_literal = identifier},
|
.key_expression = .{.symbol_literal = identifier},
|
||||||
|
@ -397,6 +404,41 @@ fn parse_factor(self: *Self) ParseError!Expression {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.symbol_bracket_left => {
|
||||||
|
self.tokenizer.step();
|
||||||
|
|
||||||
|
const subscript_expression = try self.parse_expression();
|
||||||
|
|
||||||
|
if (self.tokenizer.token != .symbol_bracket_right) {
|
||||||
|
return self.report("expected `]` after subscript 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 = subscript_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"),
|
else => return self.report("expected `}` or fields in table literal"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -458,6 +500,26 @@ fn parse_factor(self: *Self) ParseError!Expression {
|
||||||
self.tokenizer.skip_newlines();
|
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 => {
|
.symbol_paren_left => {
|
||||||
var argument_expressions = Expression.List.make(allocator);
|
var argument_expressions = Expression.List.make(allocator);
|
||||||
|
|
||||||
|
|
|
@ -287,6 +287,20 @@ pub const Tokenizer = struct {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'[' => {
|
||||||
|
self.token = .symbol_bracket_left;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
']' => {
|
||||||
|
self.token = .symbol_bracket_right;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
',' => {
|
',' => {
|
||||||
self.token = .symbol_comma;
|
self.token = .symbol_comma;
|
||||||
cursor += 1;
|
cursor += 1;
|
||||||
|
|
Loading…
Reference in New Issue