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 printer = lambda (pfx, arbitrary):
let printer = lambda (pfx):
@print(test_param)
return lambda (msg):

View File

@ -197,7 +197,7 @@ pub const HexadecimalFormat = struct {
_ = writer;
_ = value;
return null;
unreachable;
kayomn marked this conversation as resolved Outdated

TODO functions should be unreachable and have a comment.

TODO functions should be unreachable and have a comment.
}
};

View File

@ -650,7 +650,7 @@ pub const RuntimeEnv = struct {
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"),
kayomn marked this conversation as resolved Outdated

Why unreachable?

Why unreachable?
.symbol => |symbol| self.new_string(coral.io.slice_sentineled(@as(coral.io.Byte, 0), symbol)),
.string => value.acquire(),
@ -658,7 +658,7 @@ pub const RuntimeEnv = struct {
var string = [_:0]coral.io.Byte{0} ** 64;
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],
.y = vector2[1],
});
@ -672,7 +672,7 @@ pub const RuntimeEnv = struct {
var string = [_:0]coral.io.Byte{0} ** 96;
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],
.y = vector3[1],
.z = vector3[2],
@ -688,11 +688,10 @@ pub const RuntimeEnv = struct {
};
}
pub fn unwrap_dynamic(self: *RuntimeEnv, value: *const RuntimeRef) RuntimeError![]coral.io.Byte {
return switch (value.object().payload) {
.dynamic => |dynamic| dynamic.userdata(),
else => self.raise(error.TypeMismatch, "expected fixed object")
};
pub fn unwrap_dynamic(self: *RuntimeEnv, value: *const RuntimeRef, typeinfo: *const Typeinfo) RuntimeError![]coral.io.Byte {
return value.as_dynamic(typeinfo) orelse self.raise(error.TypeMismatch, "expected dynamic object, not {typename}", .{
.typename = value.typename(),
});
}
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 {
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,
},
.boxed => unreachable,
.boxed => |boxed| unbox: {
if (boxed) |boxed_value| {
kayomn marked this conversation as resolved Outdated

Why unreachable?

Why unreachable?
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 => |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 {
const field_symbol = try env.new_symbol(field);

View File

@ -18,10 +18,6 @@ opcodes: OpcodeList,
constants: ConstList,
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 {
kayomn marked this conversation as resolved Outdated

What is this for?

What is this for?
import,
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_const => |push_const| print: {
if (push_const >= chunk.constants.values.len) {
return env.raise(error.IllegalState, "invalid constant", .{});
}
try kym.assert(env, push_const < chunk.constants.values.len);
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_const => |push_const| {
if (push_const >= self.constants.values.len) {
return env.raise(error.IllegalState, "invalid constant", .{});
}
try kym.assert(env, push_const < self.constants.values.len);
try env.locals.push_one(self.constants.values[push_const].acquire());
},
.push_local => |push_local| {
if (push_local >= (env.locals.values.len - frame.locals_top)) {
return env.raise(error.IllegalState, "invalid local", .{});
}
try kym.assert(env, push_local < (env.locals.values.len - frame.locals_top));
if (env.locals.values[frame.locals_top + push_local]) |local| {
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 => {
const frame_locals = env.locals.values[frame.locals_top ..];
if (frame_locals.len == 0) {
return env.raise(error.IllegalState, "stack overflow", .{});
}
try kym.assert(env, frame_locals.len != 0);
if (frame_locals[frame_locals.len - 1]) |local| {
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| {
if (push_binding > self.bindings.len) {
return env.raise(error.IllegalState, "binding out of range", .{});
}
try kym.assert(env, push_binding <= self.bindings.len);
try env.locals.push_one(if (self.bindings[push_binding]) |value| value.acquire() else null);
},
.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 {
return env.raise(error.IllegalState, "cannot bind objects to a {typename}", .{
.typename = lambda.typename(),
});
})));
const chunk = @as(*Self, @ptrCast(@alignCast(try env.unwrap_dynamic(callable, typeinfo))));
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;
}
try env.locals.push_one(lambda);
try env.locals.push_one(callable);
},
.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);
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;
}
@ -676,7 +631,7 @@ fn syscall_vec3(env: *kym.RuntimeEnv, frame: kym.Frame) kym.RuntimeError!?*kym.R
pub const typeinfo = &kym.Typeinfo{
.size = @sizeOf(Self),
.name = "func",
.name = "lambda",
.destruct = typeinfo_destruct,
.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});
}
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});
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});
}
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.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);
}
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);
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;
}
coral.debug.assert(capture.* == .declaration_index);
try kym.assert(self.env, capture.* == .declaration_index);
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;

View File

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

Shorten to use root.report_declare_error.

Shorten to use `root.report_declare_error`.
};
}
break: declare environment.declare_variable(identifier) catch |declaration_error| {
return switch (declaration_error) {
error.OutOfMemory => error.OutOfMemory,
error.DeclarationExists =>
root.report_error(stream, "declaration `{identifier}` already exists", .{
.identifier = identifier,
}),
};
return root.report_declare_error(stream, identifier, declaration_error);
};
},
},

View File

@ -29,7 +29,7 @@ pub const Environment = struct {
capture_index: u8,
};
const DeclareError = coral.io.AllocationError || error {
pub const DeclareError = coral.io.AllocationError || error {
kayomn marked this conversation as resolved Outdated

Should ideally be marked pub as is used on public interface for struct.

Should ideally be marked pub as is used on public interface for struct.
DeclarationExists,
};