Closures, Higher-Order Functions, and Everything in Between #44
|
@ -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):
|
||||||
|
|
|
@ -197,7 +197,7 @@ pub const HexadecimalFormat = struct {
|
||||||
_ = writer;
|
_ = writer;
|
||||||
_ = value;
|
_ = value;
|
||||||
|
|
||||||
return null;
|
unreachable;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,6 @@ opcodes: OpcodeList,
|
||||||
constants: ConstList,
|
constants: ConstList,
|
||||||
bindings: []?*kym.RuntimeRef,
|
bindings: []?*kym.RuntimeRef,
|
||||||
kayomn marked this conversation as resolved
|
|||||||
|
|
||||||
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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
kayomn
commented
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;
|
||||||
|
|
|
@ -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,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
May be worth replacing with VM managed Buffer object rather than raw Zig array.
Actually this is out of scope and really a non-issue right now.