kym-tables-overhaul #30

Merged
kayomn merged 12 commits from kym-tables-overhaul into main 2023-08-12 15:20:18 +02:00
5 changed files with 153 additions and 84 deletions
Showing only changes of commit 1f8f3fd9dc - Show all commits

View File

@ -14,6 +14,7 @@ pub const Any = union (enum) {
float: Float,
string: []const coral.io.Byte,
symbol: []const coral.io.Byte,
lambda,
dynamic: *const DynamicObject,
pub fn expect_dynamic(self: Any) ?*const DynamicObject {
@ -76,22 +77,20 @@ pub const Method = struct {
};
pub const RuntimeEnv = struct {
interned_symbols: SymbolTable,
interned_symbols: RefTable,
allocator: coral.io.Allocator,
error_handler: ErrorHandler,
syscallers: SyscallerTable,
syscallables: RefTable,
local_refs: RefStack,
frames: FrameStack,
ref_values: RefSlab,
const FrameStack = coral.list.Stack(Frame);
const SymbolTable = coral.map.StringTable(*RuntimeRef);
const SyscallerTable = coral.map.StringTable(Caller);
const RefStack = coral.list.Stack(?*RuntimeRef);
const RefTable = coral.map.StringTable(*RuntimeRef);
const RefSlab = coral.map.Slab(struct {
ref_count: usize,
@ -102,15 +101,11 @@ pub const RuntimeEnv = struct {
fixed: Fixed,
string: []coral.io.Byte,
symbol: []coral.io.Byte,
lambda: Caller,
dynamic: *DynamicObject,
},
});
pub const Syscall = struct {
name: []const coral.io.Byte,
caller: Caller,
};
pub fn acquire(self: *RuntimeEnv, ref: *const RuntimeRef) *RuntimeRef {
const key = @intFromPtr(ref);
var ref_data = self.ref_values.remove(key);
@ -124,15 +119,9 @@ pub const RuntimeEnv = struct {
return @ptrFromInt(key);
}
pub fn bind_syscalls(self: *RuntimeEnv, syscalls: []const Syscall) RuntimeError!void {
for (syscalls) |syscall| {
_ = try self.syscallers.replace(syscall.name, syscall.caller);
}
}
pub fn call(
self: *RuntimeEnv,
caller: Caller,
callable_ref: *const RuntimeRef,
arg_count: u8,
name: []const coral.io.Byte,
) RuntimeError!?*RuntimeRef {
@ -154,16 +143,26 @@ pub const RuntimeEnv = struct {
}
}
return caller.invoke(self);
}
return switch ((self.ref_values.lookup(@intFromPtr(callable_ref)) orelse unreachable).object) {
.lambda => |lambda| lambda.invoke(self),
.dynamic => |dynamic| dynamic.typeinfo.call(.{
.env = self,
.userdata = dynamic.userdata,
}),
pub fn callable(self: *RuntimeEnv, ref: *const RuntimeRef) RuntimeError!Caller {
return switch ((self.ref_values.lookup(@intFromPtr(ref)) orelse unreachable).object) {
.dynamic => |dynamic| Caller.bind(DynamicObject, dynamic, DynamicObject.call),
else => self.raise(error.TypeMismatch, "object is not callable"),
};
}
pub fn bind_syscaller(self: *RuntimeEnv, comptime name: []const coral.io.Byte, caller: Caller) RuntimeError!void {
const lambda_ref = try self.new_lambda(caller);
if (try self.syscallables.replace(name, self.acquire(lambda_ref))) |replaced_entry| {
self.discard(replaced_entry.value);
}
}
pub fn discard(self: *RuntimeEnv, ref: ?*RuntimeRef) void {
const key = @intFromPtr(ref orelse return);
var ref_data = self.ref_values.remove(key) orelse unreachable;
@ -174,7 +173,7 @@ pub const RuntimeEnv = struct {
if (ref_data.ref_count == 0) {
switch (ref_data.object) {
.false, .true, .float, .fixed => {},
.false, .true, .float, .fixed, .lambda => {},
.string => |string| self.allocator.deallocate(string),
.symbol => |symbol| self.allocator.deallocate(symbol),
@ -225,7 +224,11 @@ pub const RuntimeEnv = struct {
try chunk.compile_ast(ast);
return self.call(chunk.as_caller(), 0, name);
const chunk_ref = try self.new_lambda(chunk.as_caller());
defer self.discard(chunk_ref);
return self.call(chunk_ref, 0, name);
}
pub fn free(self: *RuntimeEnv) void {
@ -241,9 +244,17 @@ pub const RuntimeEnv = struct {
}
}
{
var iterable = self.syscallables.as_iterable();
while (iterable.next()) |entry| {
self.discard(entry.value);
}
}
self.frames.free();
self.local_refs.free();
self.syscallers.free();
self.syscallables.free();
self.ref_values.free();
self.interned_symbols.free();
}
@ -267,6 +278,14 @@ pub const RuntimeEnv = struct {
return self.acquire(self.local_refs.values[local] orelse return null);
}
pub fn get_syscallable(self: *RuntimeEnv, name: []const coral.io.Byte) ?*RuntimeRef {
if (self.syscallables.lookup(name)) |system_ref| {
return self.acquire(system_ref);
}
return null;
}
pub fn pop_local(self: *RuntimeEnv) RuntimeError!?*RuntimeRef {
return self.local_refs.pop() orelse self.raise(error.IllegalState, "stack underflow");
}
@ -300,8 +319,8 @@ pub const RuntimeEnv = struct {
.local_refs = RefStack.make(allocator),
.ref_values = RefSlab.make(allocator),
.frames = FrameStack.make(allocator),
.syscallers = SyscallerTable.make(allocator, .{}),
.interned_symbols = SymbolTable.make(allocator, .{}),
.syscallables = RefTable.make(allocator, .{}),
.interned_symbols = RefTable.make(allocator, .{}),
.error_handler = error_handler,
.allocator = allocator,
};
@ -350,6 +369,13 @@ pub const RuntimeEnv = struct {
}));
}
pub fn new_lambda(self: *RuntimeEnv, caller: Caller) RuntimeError!*RuntimeRef {
return @ptrFromInt(try self.ref_values.insert(.{
.ref_count = 1,
.object = .{.lambda = caller},
}));
}
pub fn new_string(self: *RuntimeEnv, string_data: []const coral.io.Byte) RuntimeError!*RuntimeRef {
const string_copy = try coral.io.allocate_copy(self.allocator, string_data);
@ -405,10 +431,6 @@ pub const RuntimeEnv = struct {
}, index_ref, value_ref);
}
pub fn syscallable(self: *RuntimeEnv, name: []const coral.io.Byte) RuntimeError!Caller {
return self.syscallers.lookup(name) orelse self.raise(error.BadOperation, "attempt to get undefined syscall");
}
pub fn unbox(self: *RuntimeEnv, ref: *const RuntimeRef) Any {
return switch ((self.ref_values.lookup(@intFromPtr(ref)) orelse unreachable).object) {
.false => .{.boolean = false},
@ -417,6 +439,7 @@ pub const RuntimeEnv = struct {
.float => |float| .{.float = float},
.string => |string| .{.string = string},
.symbol => |symbol| .{.symbol = symbol},
.lambda => .lambda,
.dynamic => |dynamic| .{.dynamic = dynamic},
};
}
@ -484,7 +507,8 @@ pub fn hash(env: *RuntimeEnv, ref: *const RuntimeRef) usize {
.fixed => 0,
.string => |string| coral.io.djb2_hash(@typeInfo(usize).Int, string),
.symbol => 0,
.dynamic => |dynamic| @intFromPtr(dynamic),
.lambda => 0,
.dynamic => 0,
};
}
@ -520,6 +544,7 @@ pub fn test_difference(
else => env.raise(error.TypeMismatch, "right-hand object is not comparable with symbol objects"),
},
.lambda => env.raise(error.TypeMismatch, "lambda objects are not comparable"),
.dynamic => env.raise(error.TypeMismatch, "dynamic objects are not comparable"),
};
}
@ -555,10 +580,7 @@ pub fn test_equality(env: *RuntimeEnv, lhs_ref: *const RuntimeRef, rhs_ref: *con
else => false,
},
.dynamic => |lhs_dynamic| switch (env.unbox(rhs_ref)) {
.dynamic => |rhs_dynamic| rhs_dynamic == lhs_dynamic,
else => false,
},
else => lhs_ref == rhs_ref,
};
}

View File

@ -18,6 +18,7 @@ pub const Expression = union (enum) {
symbol_literal: []const coral.io.Byte,
table_literal: TableLiteral,
grouped_expression: *Expression,
get_system: []const coral.io.Byte,
get_local: []const coral.io.Byte,
set_local: []const coral.io.Byte,
@ -43,6 +44,11 @@ pub const Expression = union (enum) {
expression: *Expression,
},
call: struct {
object_expression: *Expression,
argument_expressions: List,
},
pub const BinaryOperator = enum {
addition,
subtraction,
@ -320,10 +326,16 @@ fn parse_factor(self: *Self) ParseError!Expression {
break: parse .{.string_literal = value};
},
.identifier => |local_identifier| {
.identifier => |identifier| {
self.tokenizer.skip_newlines();
break: parse .{.get_local = local_identifier};
break: parse .{.get_local = identifier};
},
.system_identifier => |system_identifier| {
self.tokenizer.skip_newlines();
break: parse .{.get_system = system_identifier};
},
.symbol_brace_left => {
@ -446,6 +458,40 @@ fn parse_factor(self: *Self) ParseError!Expression {
self.tokenizer.skip_newlines();
},
.symbol_paren_left => {
var argument_expressions = Expression.List.make(allocator);
while (true) {
self.tokenizer.skip_newlines();
switch (self.tokenizer.token) {
.symbol_paren_right => break,
else => {
try argument_expressions.push_one(try self.parse_expression());
switch (self.tokenizer.token) {
.symbol_comma => continue,
.symbol_paren_right => break,
else => return self.report("expected `,` or `)` after function argument expression"),
}
},
}
}
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 = .{
.call = .{
.argument_expressions = argument_expressions,
.object_expression = unnecessary_temp,
},
};
},
else => break,
}
}

View File

@ -95,10 +95,27 @@ const AstCompiler = struct {
});
},
.call => |call| {
if (call.argument_expressions.values.len > coral.math.max_int(@typeInfo(u8).Int)) {
return self.chunk.env.raise(error.BadSyntax, "lambdas may contain a maximum of 255 arguments");
}
for (call.argument_expressions.values) |argument_expression| {
try self.compile_expression(argument_expression);
}
try self.compile_expression(call.object_expression.*);
try self.chunk.append_opcode(.{.call = @intCast(call.argument_expressions.values.len)});
},
.grouped_expression => |grouped_expression| {
try self.compile_expression(grouped_expression.*);
},
.get_system => |get_system| {
try self.chunk.append_opcode(.{.push_system = try self.chunk.declare_constant_string(get_system)});
},
.get_local => |get_local| {
try self.chunk.append_opcode(.{
.push_local = self.resolve_local(get_local) orelse {
@ -154,7 +171,7 @@ const AstCompiler = struct {
fn declare_local(self: *AstCompiler, identifier: []const u8) kym.RuntimeError!void {
if (self.local_identifiers_count == self.local_identifiers_buffer.len) {
return self.chunk.env.raise(error.OutOfMemory, "functions may contain a maximum of 255 locals");
return self.chunk.env.raise(error.OutOfMemory, "lambdas may contain a maximum of 255 locals");
}
self.local_identifiers_buffer[self.local_identifiers_count] = identifier;
@ -180,7 +197,7 @@ const AstCompiler = struct {
}
};
const RefList = coral.list.Stack(?*kym.RuntimeRef);
const RefList = coral.list.Stack(*kym.RuntimeRef);
const LocalsList = coral.list.Stack([]const u8);
@ -192,11 +209,11 @@ pub const Opcode = union (enum) {
push_const: u16,
push_local: u8,
push_table: u32,
push_system: u16,
set_local: u8,
get_dynamic,
set_dynamic,
call: u8,
syscall: u8,
not,
neg,
@ -341,6 +358,17 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
try env.push_ref(table_ref);
},
.push_system => |push_system| {
const system_ref = self.env.get_syscallable(try kym.unbox_string(
self.env,
self.constant_refs.values[push_system],
));
defer self.env.discard(system_ref);
try self.env.push_ref(system_ref);
},
.push_local => |local| {
const ref = try env.get_local(local);
@ -405,25 +433,7 @@ fn execute(self: *Self, env: *kym.RuntimeEnv) kym.RuntimeError!?*kym.RuntimeRef
defer env.discard(callable_ref);
break: call try env.call(try env.callable(callable_ref), arg_count, "");
};
defer env.discard(result_ref);
try env.push_ref(result_ref);
},
.syscall => |arg_count| {
const result_ref = call: {
const identifier_ref = try env.pop_local() orelse {
return env.raise(error.TypeMismatch, "nil is not syscallable");
};
defer env.discard(identifier_ref);
const identifier = try kym.unbox_string(env, identifier_ref);
break: call try env.call(try env.syscallable(identifier), arg_count, identifier);
break: call try env.call(callable_ref, arg_count, "");
};
defer env.discard(result_ref);
@ -644,10 +654,6 @@ pub fn free(self: *Self) void {
self.constant_refs.free();
}
fn is_zero(utf8: []const u8) bool {
return coral.io.equals(utf8, "0") or coral.io.equals(utf8, "0.0");
}
pub fn make(env: *kym.RuntimeEnv) Self {
return Self{
.opcodes = OpcodeList.make(env.allocator),

View File

@ -5,7 +5,7 @@ pub const Token = union(enum) {
unknown: coral.io.Byte,
newline,
special_identifier: []const coral.io.Byte,
system_identifier: []const coral.io.Byte,
identifier: []const coral.io.Byte,
symbol_plus,
@ -46,7 +46,7 @@ pub const Token = union(enum) {
.unknown => |unknown| @as([*]const coral.io.Byte, @ptrCast(&unknown))[0 .. 1],
.newline => "newline",
.special_identifier => |identifier| identifier,
.system_identifier => |identifier| identifier,
.identifier => |identifier| identifier,
.symbol_plus => "+",
@ -224,7 +224,7 @@ pub const Tokenizer = struct {
else => break,
};
self.token = .{.special_identifier = self.source[begin .. cursor]};
self.token = .{.system_identifier = self.source[begin .. cursor]};
return;
},
@ -241,7 +241,7 @@ pub const Tokenizer = struct {
else => cursor += 1,
};
self.token = .{.special_identifier = self.source[begin .. cursor]};
self.token = .{.system_identifier = self.source[begin .. cursor]};
cursor += 1;
return;

View File

@ -69,21 +69,16 @@ pub fn run_app(file_access: file.Access) void {
defer script_env.free();
script_env.bind_syscalls(&.{
.{
.name = "log_info",
.caller = kym.Caller.from(kym_log_info),
},
.{
.name = "log_warn",
.caller = kym.Caller.from(kym_log_warn),
},
.{
.name = "log_fail",
.caller = kym.Caller.from(kym_log_fail),
},
}) catch {
return app.log_fail("failed to bind syscalls to script runtime");
script_env.bind_syscaller("log_info", kym.Caller.from(kym_log_info)) catch {
return app.log_fail("failed to bind `log_info` syscall");
};
script_env.bind_syscaller("log_warn", kym.Caller.from(kym_log_warn)) catch {
return app.log_fail("failed to bind `log_warn` syscall");
};
script_env.bind_syscaller("log_fail", kym.Caller.from(kym_log_fail)) catch {
return app.log_fail("failed to bind `log_fail` syscall");
};
var manifest = app.Manifest{};