Closures, Higher-Order Functions, and Everything in Between #44

Merged
kayomn merged 4 commits from kym-captures into main 2023-11-05 16:52:52 +01:00
7 changed files with 44 additions and 88 deletions
Showing only changes of commit 816051f066 - Show all commits

View File

@ -1,7 +1,7 @@
let test_param = "monkey wrench" let test_param = "monkey wrench"
let printer = lambda (pfx, arbitrary): let printer = lambda (pfx):
@print(test_param) @print(test_param)
return lambda (msg): return lambda (msg):

View File

@ -197,7 +197,7 @@ pub const HexadecimalFormat = struct {
_ = writer; _ = writer;
_ = value; _ = value;
return null; unreachable;
} }
}; };

View File

@ -650,7 +650,7 @@ pub const RuntimeEnv = struct {
break: convert self.new_string(string[0 .. length.?]); break: convert self.new_string(string[0 .. length.?]);
}, },
.boxed => unreachable, .boxed => |boxed| if (boxed) |boxed_value| self.to_string(boxed_value) else self.new_string("nil"),
.symbol => |symbol| self.new_string(coral.io.slice_sentineled(@as(coral.io.Byte, 0), symbol)), .symbol => |symbol| self.new_string(coral.io.slice_sentineled(@as(coral.io.Byte, 0), symbol)),
.string => value.acquire(), .string => value.acquire(),
@ -658,7 +658,7 @@ pub const RuntimeEnv = struct {
var string = [_:0]coral.io.Byte{0} ** 64; var string = [_:0]coral.io.Byte{0} ** 64;
var buffer = coral.io.FixedBuffer{.bytes = &string}; var buffer = coral.io.FixedBuffer{.bytes = &string};
const length = coral.utf8.print_formatted(buffer.as_writer(), "vec3({x}, {y})", .{ const length = coral.utf8.print_formatted(buffer.as_writer(), "@vec2({x}, {y})", .{
.x = vector2[0], .x = vector2[0],
.y = vector2[1], .y = vector2[1],
}); });
@ -672,7 +672,7 @@ pub const RuntimeEnv = struct {
var string = [_:0]coral.io.Byte{0} ** 96; var string = [_:0]coral.io.Byte{0} ** 96;
var buffer = coral.io.FixedBuffer{.bytes = &string}; var buffer = coral.io.FixedBuffer{.bytes = &string};
const length = coral.utf8.print_formatted(buffer.as_writer(), "vec3({x}, {y}, {z})", .{ const length = coral.utf8.print_formatted(buffer.as_writer(), "@vec3({x}, {y}, {z})", .{
.x = vector3[0], .x = vector3[0],
.y = vector3[1], .y = vector3[1],
.z = vector3[2], .z = vector3[2],
@ -688,11 +688,10 @@ pub const RuntimeEnv = struct {
}; };
} }
pub fn unwrap_dynamic(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError![]coral.io.Byte { pub fn unwrap_dynamic(self: *RuntimeEnv, value: *const RuntimeRef, typeinfo: *const Typeinfo) RuntimeError![]coral.io.Byte {
return switch (value.object().payload) { return value.as_dynamic(typeinfo) orelse self.raise(error.TypeMismatch, "expected dynamic object, not {typename}", .{
.dynamic => |dynamic| dynamic.userdata(), .typename = value.typename(),
else => self.raise(error.TypeMismatch, "expected fixed object") });
};
} }
pub fn unwrap_float(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError!Float { pub fn unwrap_float(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError!Float {
@ -705,7 +704,7 @@ pub const RuntimeEnv = struct {
pub fn unwrap_fixed(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError!Fixed { pub fn unwrap_fixed(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError!Fixed {
return value.as_fixed() orelse self.raise(error.TypeMismatch, "expected fixed, not {typename}", .{ return value.as_fixed() orelse self.raise(error.TypeMismatch, "expected fixed, not {typename}", .{
.typename = value.typename() .typename = value.typename(),
}); });
} }
@ -861,7 +860,17 @@ pub const RuntimeRef = opaque {
else => false, else => false,
}, },
.boxed => unreachable, .boxed => |boxed| unbox: {
if (boxed) |boxed_value| {
break: unbox boxed_value.equals(other);
}
if (other.as_boxed()) |boxed_value| {
break: unbox boxed_value.* == null;
}
break: unbox false;
},
.vector2 => |self_vector| switch (other.object().payload) { .vector2 => |self_vector| switch (other.object().payload) {
.vector2 => |other_vector| coral.io.are_equal(coral.io.bytes_of(&self_vector), coral.io.bytes_of(&other_vector)), .vector2 => |other_vector| coral.io.are_equal(coral.io.bytes_of(&self_vector), coral.io.bytes_of(&other_vector)),
@ -965,6 +974,12 @@ pub const Typeinfo = struct {
} }
}; };
pub fn assert(env: *RuntimeEnv, condition: bool) RuntimeError!void {
if (!condition) {
return env.raise(error.IllegalState, "assertion", .{});
}
}
pub fn get_field(env: *RuntimeEnv, indexable: *RuntimeRef, field: []const coral.io.Byte) RuntimeError!?*RuntimeRef { pub fn get_field(env: *RuntimeEnv, indexable: *RuntimeRef, field: []const coral.io.Byte) RuntimeError!?*RuntimeRef {
const field_symbol = try env.new_symbol(field); const field_symbol = try env.new_symbol(field);

View File

@ -18,10 +18,6 @@ opcodes: OpcodeList,
constants: ConstList, constants: ConstList,
bindings: []?*kym.RuntimeRef, bindings: []?*kym.RuntimeRef,
kayomn marked this conversation as resolved
Review

May be worth replacing with VM managed Buffer object rather than raw Zig array.

May be worth replacing with VM managed Buffer object rather than raw Zig array.
Review

Actually this is out of scope and really a non-issue right now.

Actually this is out of scope and really a non-issue right now.
const Box = struct {
};
const Builtin = enum { const Builtin = enum {
import, import,
print, print,
@ -93,9 +89,7 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
.push_false => coral.utf8.print_string(writer, "push false\n"), .push_false => coral.utf8.print_string(writer, "push false\n"),
.push_const => |push_const| print: { .push_const => |push_const| print: {
if (push_const >= chunk.constants.values.len) { try kym.assert(env, push_const < chunk.constants.values.len);
return env.raise(error.IllegalState, "invalid constant", .{});
}
const string_ref = try env.to_string(chunk.constants.values[push_const]); const string_ref = try env.to_string(chunk.constants.values[push_const]);
@ -183,17 +177,12 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr
.push_false => try env.locals.push_one(try env.new_boolean(false)), .push_false => try env.locals.push_one(try env.new_boolean(false)),
.push_const => |push_const| { .push_const => |push_const| {
if (push_const >= self.constants.values.len) { try kym.assert(env, push_const < self.constants.values.len);
return env.raise(error.IllegalState, "invalid constant", .{});
}
try env.locals.push_one(self.constants.values[push_const].acquire()); try env.locals.push_one(self.constants.values[push_const].acquire());
}, },
.push_local => |push_local| { .push_local => |push_local| {
if (push_local >= (env.locals.values.len - frame.locals_top)) { try kym.assert(env, push_local < (env.locals.values.len - frame.locals_top));
return env.raise(error.IllegalState, "invalid local", .{});
}
if (env.locals.values[frame.locals_top + push_local]) |local| { if (env.locals.values[frame.locals_top + push_local]) |local| {
try env.locals.push_one(local.acquire()); try env.locals.push_one(local.acquire());
@ -205,9 +194,7 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr
.push_top => { .push_top => {
const frame_locals = env.locals.values[frame.locals_top ..]; const frame_locals = env.locals.values[frame.locals_top ..];
if (frame_locals.len == 0) { try kym.assert(env, frame_locals.len != 0);
return env.raise(error.IllegalState, "stack overflow", .{});
}
if (frame_locals[frame_locals.len - 1]) |local| { if (frame_locals[frame_locals.len - 1]) |local| {
try env.locals.push_one(local.acquire()); try env.locals.push_one(local.acquire());
@ -257,23 +244,16 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr
}, },
.push_binding => |push_binding| { .push_binding => |push_binding| {
if (push_binding > self.bindings.len) { try kym.assert(env, push_binding <= self.bindings.len);
return env.raise(error.IllegalState, "binding out of range", .{});
}
try env.locals.push_one(if (self.bindings[push_binding]) |value| value.acquire() else null); try env.locals.push_one(if (self.bindings[push_binding]) |value| value.acquire() else null);
}, },
.bind => |bind| { .bind => |bind| {
const lambda = try env.expect(try env.pop_local()); const callable = try env.expect(try env.pop_local());
errdefer env.discard(lambda); errdefer env.discard(callable);
const chunk = @as(*Self, @ptrCast(@alignCast(lambda.as_dynamic(typeinfo) orelse { const chunk = @as(*Self, @ptrCast(@alignCast(try env.unwrap_dynamic(callable, typeinfo))));
return env.raise(error.IllegalState, "cannot bind objects to a {typename}", .{
.typename = lambda.typename(),
});
})));
chunk.bindings = try coral.io.allocate_many(env.allocator, bind, @as(?*kym.RuntimeRef, null)); chunk.bindings = try coral.io.allocate_many(env.allocator, bind, @as(?*kym.RuntimeRef, null));
@ -295,7 +275,7 @@ pub fn execute(self: Self, env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeEr
binding.* = value; binding.* = value;
} }
try env.locals.push_one(lambda); try env.locals.push_one(callable);
}, },
.push_builtin => |push_builtin| { .push_builtin => |push_builtin| {
@ -612,31 +592,6 @@ pub fn make(env: *kym.RuntimeEnv, name: *const kym.RuntimeRef, environment: *con
try compiler.compile_environment(environment); try compiler.compile_environment(environment);
if (builtin.mode == .Debug) {
const name_string = try env.to_string(name);
defer env.discard(name_string);
const name_bytes = name_string.as_string();
coral.debug.assert(name_bytes != null);
const allocation = try coral.utf8.alloc_formatted(env.allocator, "compiled {name}...", .{.name = name_bytes.?});
defer env.allocator.deallocate(allocation);
app.log_info(allocation);
const string_ref = try chunk.dump(env);
defer env.discard(string_ref);
const string = string_ref.as_string();
coral.debug.assert(string != null);
app.log_info(string.?);
}
return chunk; return chunk;
} }
@ -676,7 +631,7 @@ fn syscall_vec3(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.R
pub const typeinfo = &kym.Typeinfo{ pub const typeinfo = &kym.Typeinfo{
.size = @sizeOf(Self), .size = @sizeOf(Self),
.name = "func", .name = "lambda",
.destruct = typeinfo_destruct, .destruct = typeinfo_destruct,
.call = typeinfo_call, .call = typeinfo_call,
}; };

View File

@ -158,7 +158,7 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
return self.chunk.opcodes.push_one(.{.push_local = index}); return self.chunk.opcodes.push_one(.{.push_local = index});
} }
if (get_binding_index(environment, declaration_get.declaration)) |index| { if (try self.get_binding_index(environment, declaration_get.declaration)) |index| {
try self.chunk.opcodes.push_one(.{.push_binding = index}); try self.chunk.opcodes.push_one(.{.push_binding = index});
if (is_declaration_boxed(declaration_get.declaration)) { if (is_declaration_boxed(declaration_get.declaration)) {
@ -178,7 +178,7 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
return self.chunk.opcodes.push_one(.{.set_local = index}); return self.chunk.opcodes.push_one(.{.set_local = index});
} }
if (get_binding_index(environment, declaration_set.declaration)) |index| { if (try self.get_binding_index(environment, declaration_set.declaration)) |index| {
try self.chunk.opcodes.push_one(.{.push_binding = index}); try self.chunk.opcodes.push_one(.{.push_binding = index});
try self.compile_expression(environment, declaration_set.assign, null); try self.compile_expression(environment, declaration_set.assign, null);
@ -376,7 +376,7 @@ fn declare_symbol(self: Self, symbol: []const coral.io.Byte) kym.RuntimeError!u1
return @intCast(self.chunk.constants.values.len - 1); return @intCast(self.chunk.constants.values.len - 1);
} }
pub fn get_binding_index(environment: *const tree.Environment, declaration: *const tree.Declaration) ?u8 { pub fn get_binding_index(self: *const Self, environment: *const tree.Environment, declaration: *const tree.Declaration) kym.RuntimeError!?u8 {
var binding_index = @as(u8, 0); var binding_index = @as(u8, 0);
while (binding_index < environment.capture_count) : (binding_index += 1) { while (binding_index < environment.capture_count) : (binding_index += 1) {
@ -388,7 +388,7 @@ pub fn get_binding_index(environment: *const tree.Environment, declaration: *con
target_environment = target_environment.enclosing orelse return null; target_environment = target_environment.enclosing orelse return null;
} }
coral.debug.assert(capture.* == .declaration_index); try kym.assert(self.env, capture.* == .declaration_index);
if (&target_environment.declarations[capture.declaration_index] == declaration) { if (&target_environment.declarations[capture.declaration_index] == declaration) {
kayomn marked this conversation as resolved
Review

This shouldn't be capable of crashing the process - needs better handling.

This shouldn't be capable of crashing the process - needs better handling.
return binding_index; return binding_index;

View File

@ -120,26 +120,12 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
.declaration = declare: { .declaration = declare: {
if (is_constant) { if (is_constant) {
break: declare environment.declare_constant(identifier) catch |declaration_error| { break: declare environment.declare_constant(identifier) catch |declaration_error| {
return switch (declaration_error) { return root.report_declare_error(stream, identifier, declaration_error);
error.OutOfMemory => error.OutOfMemory,
error.DeclarationExists =>
root.report_error(stream, "declaration `{identifier}` already exists", .{
.identifier = identifier,
}),
};
}; };
} }
break: declare environment.declare_variable(identifier) catch |declaration_error| { break: declare environment.declare_variable(identifier) catch |declaration_error| {
return switch (declaration_error) { return root.report_declare_error(stream, identifier, declaration_error);
error.OutOfMemory => error.OutOfMemory,
error.DeclarationExists =>
root.report_error(stream, "declaration `{identifier}` already exists", .{
.identifier = identifier,
}),
};
}; };
}, },
}, },

View File

@ -29,7 +29,7 @@ pub const Environment = struct {
capture_index: u8, capture_index: u8,
}; };
const DeclareError = coral.io.AllocationError || error { pub const DeclareError = coral.io.AllocationError || error {
DeclarationExists, DeclarationExists,
}; };